render: Render resize indicator
This commit is contained in:
parent
2004705080
commit
99f29187af
7 changed files with 407 additions and 83 deletions
|
|
@ -476,19 +476,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state
|
|
||||||
.shell
|
|
||||||
.space_for_handle_mut(¤t.0)
|
|
||||||
.ok_or(OutputNoMode)?
|
|
||||||
.update_animations(&state.event_loop_handle);
|
|
||||||
if let Some((previous, _, _)) = previous.as_ref() {
|
|
||||||
state
|
|
||||||
.shell
|
|
||||||
.space_for_handle_mut(&previous)
|
|
||||||
.ok_or(OutputNoMode)?
|
|
||||||
.update_animations(&state.event_loop_handle);
|
|
||||||
}
|
|
||||||
let overview = state.shell.overview_mode();
|
let overview = state.shell.overview_mode();
|
||||||
|
let (resize_mode, resize_indicator) = state.shell.resize_mode();
|
||||||
|
let resize_indicator = resize_indicator.map(|indicator| (resize_mode, indicator));
|
||||||
|
|
||||||
let last_active_seat = state.last_active_seat().clone();
|
let last_active_seat = state.last_active_seat().clone();
|
||||||
let move_active = last_active_seat
|
let move_active = last_active_seat
|
||||||
.user_data()
|
.user_data()
|
||||||
|
|
@ -552,6 +543,7 @@ where
|
||||||
state.xwayland_state.as_mut(),
|
state.xwayland_state.as_mut(),
|
||||||
(!move_active && is_active_space).then_some(&last_active_seat),
|
(!move_active && is_active_space).then_some(&last_active_seat),
|
||||||
overview.clone(),
|
overview.clone(),
|
||||||
|
resize_indicator.clone(),
|
||||||
state.config.static_conf.active_hint,
|
state.config.static_conf.active_hint,
|
||||||
)
|
)
|
||||||
.map_err(|_| OutputNoMode)?
|
.map_err(|_| OutputNoMode)?
|
||||||
|
|
@ -598,6 +590,7 @@ where
|
||||||
state.xwayland_state.as_mut(),
|
state.xwayland_state.as_mut(),
|
||||||
(!move_active && is_active_space).then_some(&last_active_seat),
|
(!move_active && is_active_space).then_some(&last_active_seat),
|
||||||
overview,
|
overview,
|
||||||
|
resize_indicator,
|
||||||
state.config.static_conf.active_hint,
|
state.config.static_conf.active_hint,
|
||||||
)
|
)
|
||||||
.map_err(|_| OutputNoMode)?
|
.map_err(|_| OutputNoMode)?
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ pub mod stack;
|
||||||
pub use self::stack::CosmicStack;
|
pub use self::stack::CosmicStack;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
pub use self::window::CosmicWindow;
|
pub use self::window::CosmicWindow;
|
||||||
|
pub mod resize_indicator;
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
use egui::plot::{Corner, Legend, Plot, PlotPoints, Polygon};
|
use egui::plot::{Corner, Legend, Plot, PlotPoints, Polygon};
|
||||||
|
|
|
||||||
223
src/shell/element/resize_indicator.rs
Normal file
223
src/shell/element/resize_indicator.rs
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::{Action, Config},
|
||||||
|
fl,
|
||||||
|
shell::{grabs::ResizeEdge, ResizeDirection},
|
||||||
|
utils::iced::{IcedElement, Program},
|
||||||
|
};
|
||||||
|
|
||||||
|
use apply::Apply;
|
||||||
|
use calloop::LoopHandle;
|
||||||
|
use cosmic::{
|
||||||
|
iced::widget::{column, container, horizontal_space, row, vertical_space},
|
||||||
|
iced_core::{Background, Color, Length},
|
||||||
|
theme,
|
||||||
|
widget::{icon, text},
|
||||||
|
};
|
||||||
|
use smithay::utils::Size;
|
||||||
|
|
||||||
|
pub type ResizeIndicator = IcedElement<ResizeIndicatorInternal>;
|
||||||
|
|
||||||
|
pub fn resize_indicator(
|
||||||
|
direction: ResizeDirection,
|
||||||
|
config: &Config,
|
||||||
|
evlh: LoopHandle<'static, crate::state::Data>,
|
||||||
|
) -> ResizeIndicator {
|
||||||
|
ResizeIndicator::new(
|
||||||
|
ResizeIndicatorInternal {
|
||||||
|
edges: Mutex::new(ResizeEdge::all()),
|
||||||
|
direction,
|
||||||
|
shortcut1: config
|
||||||
|
.static_conf
|
||||||
|
.key_bindings
|
||||||
|
.iter()
|
||||||
|
.find_map(|(pattern, action)| {
|
||||||
|
(*action == Action::Resizing(ResizeDirection::Outwards)).then_some(pattern)
|
||||||
|
})
|
||||||
|
.map(|pattern| format!("{}: ", pattern.to_string()))
|
||||||
|
.unwrap_or_else(|| crate::fl!("unknown-keybinding")),
|
||||||
|
shortcut2: config
|
||||||
|
.static_conf
|
||||||
|
.key_bindings
|
||||||
|
.iter()
|
||||||
|
.find_map(|(pattern, action)| {
|
||||||
|
(*action == Action::Resizing(ResizeDirection::Inwards)).then_some(pattern)
|
||||||
|
})
|
||||||
|
.map(|pattern| format!("{}: ", pattern.to_string()))
|
||||||
|
.unwrap_or_else(|| crate::fl!("unknown-keybinding")),
|
||||||
|
},
|
||||||
|
Size::from((1, 1)),
|
||||||
|
evlh,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ResizeIndicatorInternal {
|
||||||
|
pub edges: Mutex<ResizeEdge>,
|
||||||
|
pub direction: ResizeDirection,
|
||||||
|
pub shortcut1: String,
|
||||||
|
pub shortcut2: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program for ResizeIndicatorInternal {
|
||||||
|
type Message = ();
|
||||||
|
|
||||||
|
fn view(&self) -> crate::utils::iced::Element<'_, Self::Message> {
|
||||||
|
let edges = self.edges.lock().unwrap();
|
||||||
|
column(vec![
|
||||||
|
if edges.contains(ResizeEdge::TOP) {
|
||||||
|
icon(
|
||||||
|
if self.direction == ResizeDirection::Outwards {
|
||||||
|
"go-up-symbolic"
|
||||||
|
} else {
|
||||||
|
"go-down-symbolic"
|
||||||
|
},
|
||||||
|
32,
|
||||||
|
)
|
||||||
|
.force_svg(true)
|
||||||
|
.apply(container)
|
||||||
|
.padding(2)
|
||||||
|
.style(theme::Container::custom(|theme| container::Appearance {
|
||||||
|
text_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||||
|
background: Some(Background::Color(theme.cosmic().accent_color().into())),
|
||||||
|
border_radius: 18.0.into(),
|
||||||
|
border_width: 0.0,
|
||||||
|
border_color: Color::TRANSPARENT,
|
||||||
|
}))
|
||||||
|
.width(Length::Shrink)
|
||||||
|
.apply(container)
|
||||||
|
.center_x()
|
||||||
|
.width(Length::Fill)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
vertical_space(36).into()
|
||||||
|
},
|
||||||
|
row(vec![
|
||||||
|
if edges.contains(ResizeEdge::LEFT) {
|
||||||
|
icon(
|
||||||
|
if self.direction == ResizeDirection::Outwards {
|
||||||
|
"go-previous-symbolic"
|
||||||
|
} else {
|
||||||
|
"go-next-symbolic"
|
||||||
|
},
|
||||||
|
32,
|
||||||
|
)
|
||||||
|
.force_svg(true)
|
||||||
|
.apply(container)
|
||||||
|
.padding(4)
|
||||||
|
.style(theme::Container::custom(|theme| container::Appearance {
|
||||||
|
text_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||||
|
background: Some(Background::Color(theme.cosmic().accent_color().into())),
|
||||||
|
border_radius: 18.0.into(),
|
||||||
|
border_width: 0.0,
|
||||||
|
border_color: Color::TRANSPARENT,
|
||||||
|
}))
|
||||||
|
.width(Length::Shrink)
|
||||||
|
.apply(container)
|
||||||
|
.center_y()
|
||||||
|
.height(Length::Fill)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
horizontal_space(36).into()
|
||||||
|
},
|
||||||
|
row(vec![
|
||||||
|
text(&self.shortcut1)
|
||||||
|
.font(cosmic::font::FONT_SEMIBOLD)
|
||||||
|
.size(14)
|
||||||
|
.into(),
|
||||||
|
text(fl!("grow-window"))
|
||||||
|
.font(cosmic::font::FONT)
|
||||||
|
.size(14)
|
||||||
|
.into(),
|
||||||
|
horizontal_space(40).into(),
|
||||||
|
text(&self.shortcut2)
|
||||||
|
.font(cosmic::font::FONT_SEMIBOLD)
|
||||||
|
.size(14)
|
||||||
|
.into(),
|
||||||
|
text(fl!("shrink-window"))
|
||||||
|
.font(cosmic::font::FONT)
|
||||||
|
.size(14)
|
||||||
|
.into(),
|
||||||
|
])
|
||||||
|
.apply(container)
|
||||||
|
.center_x()
|
||||||
|
.center_y()
|
||||||
|
.padding(16)
|
||||||
|
.apply(container)
|
||||||
|
.style(theme::Container::custom(|theme| container::Appearance {
|
||||||
|
text_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||||
|
background: Some(Background::Color(theme.cosmic().accent_color().into())),
|
||||||
|
border_radius: 18.0.into(),
|
||||||
|
border_width: 0.0,
|
||||||
|
border_color: Color::TRANSPARENT,
|
||||||
|
}))
|
||||||
|
.width(Length::Shrink)
|
||||||
|
.height(Length::Shrink)
|
||||||
|
.apply(container)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.center_x()
|
||||||
|
.center_y()
|
||||||
|
.into(),
|
||||||
|
if edges.contains(ResizeEdge::RIGHT) {
|
||||||
|
icon(
|
||||||
|
if self.direction == ResizeDirection::Outwards {
|
||||||
|
"go-next-symbolic"
|
||||||
|
} else {
|
||||||
|
"go-previous-symbolic"
|
||||||
|
},
|
||||||
|
32,
|
||||||
|
)
|
||||||
|
.force_svg(true)
|
||||||
|
.apply(container)
|
||||||
|
.padding(4)
|
||||||
|
.style(theme::Container::custom(|theme| container::Appearance {
|
||||||
|
text_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||||
|
background: Some(Background::Color(theme.cosmic().accent_color().into())),
|
||||||
|
border_radius: 18.0.into(),
|
||||||
|
border_width: 0.0,
|
||||||
|
border_color: Color::TRANSPARENT,
|
||||||
|
}))
|
||||||
|
.height(Length::Shrink)
|
||||||
|
.apply(container)
|
||||||
|
.center_y()
|
||||||
|
.height(Length::Fill)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
horizontal_space(36).into()
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.into(),
|
||||||
|
if edges.contains(ResizeEdge::BOTTOM) {
|
||||||
|
icon(
|
||||||
|
if self.direction == ResizeDirection::Outwards {
|
||||||
|
"go-down-symbolic"
|
||||||
|
} else {
|
||||||
|
"go-up-symbolic"
|
||||||
|
},
|
||||||
|
32,
|
||||||
|
)
|
||||||
|
.force_svg(true)
|
||||||
|
.apply(container)
|
||||||
|
.padding(4)
|
||||||
|
.style(theme::Container::custom(|theme| container::Appearance {
|
||||||
|
text_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||||
|
background: Some(Background::Color(theme.cosmic().accent_color().into())),
|
||||||
|
border_radius: 18.0.into(),
|
||||||
|
border_width: 0.0,
|
||||||
|
border_color: Color::TRANSPARENT,
|
||||||
|
}))
|
||||||
|
.width(Length::Shrink)
|
||||||
|
.apply(container)
|
||||||
|
.center_x()
|
||||||
|
.width(Length::Fill)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
vertical_space(36).into()
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,8 +16,8 @@ use crate::{
|
||||||
backend::render::{element::AsGlowRenderer, IndicatorShader},
|
backend::render::{element::AsGlowRenderer, IndicatorShader},
|
||||||
shell::{
|
shell::{
|
||||||
element::{
|
element::{
|
||||||
stack::CosmicStackRenderElement, window::CosmicWindowRenderElement, CosmicMapped,
|
resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement,
|
||||||
CosmicMappedRenderElement,
|
window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement,
|
||||||
},
|
},
|
||||||
focus::target::KeyboardFocusTarget,
|
focus::target::KeyboardFocusTarget,
|
||||||
grabs::ResizeEdge,
|
grabs::ResizeEdge,
|
||||||
|
|
@ -421,6 +421,7 @@ impl FloatingLayout {
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
focused: Option<&CosmicMapped>,
|
focused: Option<&CosmicMapped>,
|
||||||
|
mut resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
|
||||||
indicator_thickness: u8,
|
indicator_thickness: u8,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
) -> Vec<CosmicMappedRenderElement<R>>
|
) -> Vec<CosmicMappedRenderElement<R>>
|
||||||
|
|
@ -435,14 +436,15 @@ impl FloatingLayout {
|
||||||
puffin::profile_function!();
|
puffin::profile_function!();
|
||||||
|
|
||||||
let output_scale = output.current_scale().fractional_scale();
|
let output_scale = output.current_scale().fractional_scale();
|
||||||
let output_loc = self.space.output_geometry(output).unwrap().loc;
|
let output_geo = self.space.output_geometry(output).unwrap();
|
||||||
|
|
||||||
self.space
|
self.space
|
||||||
.elements_for_output(output)
|
.elements_for_output(output)
|
||||||
.rev()
|
.rev()
|
||||||
.flat_map(|elem| {
|
.flat_map(|elem| {
|
||||||
let render_location =
|
let render_location = self.space.element_location(elem).unwrap()
|
||||||
self.space.element_location(elem).unwrap() - output_loc - elem.geometry().loc;
|
- output_geo.loc
|
||||||
|
- elem.geometry().loc;
|
||||||
let mut elements = elem.render_elements(
|
let mut elements = elem.render_elements(
|
||||||
renderer,
|
renderer,
|
||||||
render_location.to_physical_precise_round(output_scale),
|
render_location.to_physical_precise_round(output_scale),
|
||||||
|
|
@ -450,19 +452,41 @@ impl FloatingLayout {
|
||||||
alpha,
|
alpha,
|
||||||
);
|
);
|
||||||
if focused == Some(elem) {
|
if focused == Some(elem) {
|
||||||
|
let mut indicator_geometry = Rectangle::from_loc_and_size(
|
||||||
|
self.space.element_location(elem).unwrap() - output_geo.loc,
|
||||||
|
elem.geometry().size,
|
||||||
|
);
|
||||||
|
|
||||||
if indicator_thickness > 0 {
|
if indicator_thickness > 0 {
|
||||||
let element = IndicatorShader::focus_element(
|
let element = IndicatorShader::focus_element(
|
||||||
renderer,
|
renderer,
|
||||||
elem.clone(),
|
elem.clone(),
|
||||||
Rectangle::from_loc_and_size(
|
indicator_geometry,
|
||||||
self.space.element_location(elem).unwrap() - output_loc,
|
|
||||||
elem.geometry().size,
|
|
||||||
),
|
|
||||||
indicator_thickness,
|
indicator_thickness,
|
||||||
alpha,
|
alpha,
|
||||||
);
|
);
|
||||||
elements.insert(0, element.into());
|
elements.insert(0, element.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some((mode, resize)) = resize_indicator.as_mut() {
|
||||||
|
indicator_geometry.loc -= (18, 18).into();
|
||||||
|
indicator_geometry.size += (36, 36).into();
|
||||||
|
resize.resize(indicator_geometry.size);
|
||||||
|
resize.output_enter(output, output_geo);
|
||||||
|
elements = resize
|
||||||
|
.render_elements::<CosmicWindowRenderElement<R>>(
|
||||||
|
renderer,
|
||||||
|
indicator_geometry
|
||||||
|
.loc
|
||||||
|
.to_physical_precise_round(output_scale),
|
||||||
|
output_scale.into(),
|
||||||
|
alpha * mode.alpha().unwrap_or(1.0),
|
||||||
|
)
|
||||||
|
.into_iter()
|
||||||
|
.map(CosmicMappedRenderElement::Window)
|
||||||
|
.chain(elements.into_iter())
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
elements
|
elements
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use crate::{
|
||||||
backend::render::{element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, GROUP_COLOR},
|
backend::render::{element::AsGlowRenderer, BackdropShader, IndicatorShader, Key, GROUP_COLOR},
|
||||||
shell::{
|
shell::{
|
||||||
element::{
|
element::{
|
||||||
|
resize_indicator::ResizeIndicator,
|
||||||
stack::{CosmicStackRenderElement, MoveResult as StackMoveResult},
|
stack::{CosmicStackRenderElement, MoveResult as StackMoveResult},
|
||||||
window::CosmicWindowRenderElement,
|
window::CosmicWindowRenderElement,
|
||||||
CosmicMapped, CosmicMappedRenderElement, CosmicStack, CosmicWindow,
|
CosmicMapped, CosmicMappedRenderElement, CosmicStack, CosmicWindow,
|
||||||
|
|
@ -1915,6 +1916,7 @@ impl TilingLayout {
|
||||||
seat: Option<&Seat<State>>,
|
seat: Option<&Seat<State>>,
|
||||||
non_exclusive_zone: Rectangle<i32, Logical>,
|
non_exclusive_zone: Rectangle<i32, Logical>,
|
||||||
overview: OverviewMode,
|
overview: OverviewMode,
|
||||||
|
resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
|
||||||
indicator_thickness: u8,
|
indicator_thickness: u8,
|
||||||
) -> Result<Vec<CosmicMappedRenderElement<R>>, OutputNotMapped>
|
) -> Result<Vec<CosmicMappedRenderElement<R>>, OutputNotMapped>
|
||||||
where
|
where
|
||||||
|
|
@ -1955,26 +1957,7 @@ impl TilingLayout {
|
||||||
} else {
|
} else {
|
||||||
1.0
|
1.0
|
||||||
};
|
};
|
||||||
let draw_groups = match overview {
|
let draw_groups = overview.alpha();
|
||||||
OverviewMode::Started(_, start) => {
|
|
||||||
let percentage = (Instant::now().duration_since(start).as_millis() as f32
|
|
||||||
/ ANIMATION_DURATION.as_millis() as f32)
|
|
||||||
.min(1.0);
|
|
||||||
Some(Ease::Cubic(Cubic::Out).tween(percentage))
|
|
||||||
}
|
|
||||||
OverviewMode::Ended(end) => {
|
|
||||||
let percentage = (1.0
|
|
||||||
- Instant::now().duration_since(end).as_millis() as f32
|
|
||||||
/ ANIMATION_DURATION.as_millis() as f32)
|
|
||||||
.max(0.0);
|
|
||||||
if percentage > 0.0 {
|
|
||||||
Some(Ease::Cubic(Cubic::Out).tween(percentage))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OverviewMode::None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut elements = Vec::new();
|
let mut elements = Vec::new();
|
||||||
|
|
||||||
|
|
@ -2032,7 +2015,7 @@ impl TilingLayout {
|
||||||
geometries,
|
geometries,
|
||||||
old_geometries,
|
old_geometries,
|
||||||
seat,
|
seat,
|
||||||
output_scale,
|
output,
|
||||||
percentage,
|
percentage,
|
||||||
if let Some(transition) = draw_groups {
|
if let Some(transition) = draw_groups {
|
||||||
let diff = (4u8.abs_diff(indicator_thickness) as f32 * transition).round() as u8;
|
let diff = (4u8.abs_diff(indicator_thickness) as f32 * transition).round() as u8;
|
||||||
|
|
@ -2044,6 +2027,7 @@ impl TilingLayout {
|
||||||
} else {
|
} else {
|
||||||
indicator_thickness
|
indicator_thickness
|
||||||
},
|
},
|
||||||
|
resize_indicator,
|
||||||
));
|
));
|
||||||
|
|
||||||
// tiling hints
|
// tiling hints
|
||||||
|
|
@ -2425,9 +2409,10 @@ fn render_new_tree<R>(
|
||||||
geometries: Option<HashMap<NodeId, Rectangle<i32, Logical>>>,
|
geometries: Option<HashMap<NodeId, Rectangle<i32, Logical>>>,
|
||||||
old_geometries: Option<HashMap<NodeId, Rectangle<i32, Logical>>>,
|
old_geometries: Option<HashMap<NodeId, Rectangle<i32, Logical>>>,
|
||||||
seat: Option<&Seat<State>>,
|
seat: Option<&Seat<State>>,
|
||||||
output_scale: f64,
|
output: &Output,
|
||||||
percentage: f32,
|
percentage: f32,
|
||||||
indicator_thickness: u8,
|
indicator_thickness: u8,
|
||||||
|
mut resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
|
||||||
) -> Vec<CosmicMappedRenderElement<R>>
|
) -> Vec<CosmicMappedRenderElement<R>>
|
||||||
where
|
where
|
||||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||||
|
|
@ -2452,11 +2437,16 @@ where
|
||||||
.map(|(id, _)| id);
|
.map(|(id, _)| id);
|
||||||
|
|
||||||
let mut group_backdrop = None;
|
let mut group_backdrop = None;
|
||||||
|
let mut indicator = None;
|
||||||
|
let mut resize_elements = None;
|
||||||
|
|
||||||
|
let output_geo = output.geometry();
|
||||||
|
let output_scale = output.current_scale().fractional_scale();
|
||||||
|
|
||||||
if let Some(root) = target_tree.root_node_id() {
|
if let Some(root) = target_tree.root_node_id() {
|
||||||
let old_geometries = old_geometries.unwrap_or_default();
|
let old_geometries = old_geometries.unwrap_or_default();
|
||||||
let geometries = geometries.unwrap_or_default();
|
let geometries = geometries.unwrap_or_default();
|
||||||
let mut elements: Vec<CosmicMappedRenderElement<R>> = target_tree
|
let elements: Vec<CosmicMappedRenderElement<R>> = target_tree
|
||||||
.traverse_pre_order_ids(root)
|
.traverse_pre_order_ids(root)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.flat_map(|node_id| {
|
.flat_map(|node_id| {
|
||||||
|
|
@ -2546,7 +2536,7 @@ where
|
||||||
|
|
||||||
let mut elements = Vec::new();
|
let mut elements = Vec::new();
|
||||||
|
|
||||||
if focused == Some(node_id) {
|
if focused.as_ref() == Some(&node_id) {
|
||||||
if indicator_thickness > 0 || data.is_group() {
|
if indicator_thickness > 0 || data.is_group() {
|
||||||
let mut geo = geo.clone();
|
let mut geo = geo.clone();
|
||||||
if data.is_group() {
|
if data.is_group() {
|
||||||
|
|
@ -2554,25 +2544,56 @@ where
|
||||||
geo.loc += (outer_gap, outer_gap).into();
|
geo.loc += (outer_gap, outer_gap).into();
|
||||||
geo.size -= (outer_gap * 2, outer_gap * 2).into();
|
geo.size -= (outer_gap * 2, outer_gap * 2).into();
|
||||||
|
|
||||||
group_backdrop = Some(
|
group_backdrop = Some(BackdropShader::element(
|
||||||
BackdropShader::element(
|
renderer,
|
||||||
renderer,
|
match data {
|
||||||
match data {
|
Data::Group { alive, .. } => Key::Group(Arc::downgrade(alive)),
|
||||||
Data::Group { alive, .. } => {
|
_ => unreachable!(),
|
||||||
Key::Group(Arc::downgrade(alive))
|
},
|
||||||
}
|
geo,
|
||||||
_ => unreachable!(),
|
8.,
|
||||||
},
|
0.4,
|
||||||
geo,
|
GROUP_COLOR,
|
||||||
8.,
|
));
|
||||||
0.4,
|
|
||||||
GROUP_COLOR,
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let element = IndicatorShader::focus_element(
|
if let Some((mode, resize)) = resize_indicator.as_mut() {
|
||||||
|
let mut geo = geo.clone();
|
||||||
|
geo.loc -= (18, 18).into();
|
||||||
|
geo.size += (36, 36).into();
|
||||||
|
|
||||||
|
resize.resize(geo.size);
|
||||||
|
resize.output_enter(output, output_geo);
|
||||||
|
let possible_edges =
|
||||||
|
TilingLayout::possible_resizes(target_tree, node_id);
|
||||||
|
if !possible_edges.is_empty() {
|
||||||
|
if resize.with_program(|internal| {
|
||||||
|
let mut edges = internal.edges.lock().unwrap();
|
||||||
|
if *edges != possible_edges {
|
||||||
|
*edges = possible_edges;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
resize.force_update();
|
||||||
|
}
|
||||||
|
resize_elements = Some(
|
||||||
|
resize
|
||||||
|
.render_elements::<CosmicWindowRenderElement<R>>(
|
||||||
|
renderer,
|
||||||
|
geo.loc.to_physical_precise_round(output_scale),
|
||||||
|
output_scale.into(),
|
||||||
|
alpha * mode.alpha().unwrap_or(1.0),
|
||||||
|
)
|
||||||
|
.into_iter()
|
||||||
|
.map(CosmicMappedRenderElement::from)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
indicator = Some(IndicatorShader::focus_element(
|
||||||
renderer,
|
renderer,
|
||||||
match data {
|
match data {
|
||||||
Data::Mapped { mapped, .. } => mapped.clone().into(),
|
Data::Mapped { mapped, .. } => mapped.clone().into(),
|
||||||
|
|
@ -2585,8 +2606,7 @@ where
|
||||||
indicator_thickness
|
indicator_thickness
|
||||||
},
|
},
|
||||||
1.0,
|
1.0,
|
||||||
);
|
));
|
||||||
elements.push(element.into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2654,8 +2674,14 @@ where
|
||||||
elements
|
elements
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
elements.extend(group_backdrop);
|
|
||||||
elements
|
resize_elements
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.chain(indicator.into_iter().map(Into::into))
|
||||||
|
.chain(elements)
|
||||||
|
.chain(group_backdrop.into_iter().map(Into::into))
|
||||||
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,13 @@ use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
time::Instant,
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use wayland_backend::server::ClientId;
|
use wayland_backend::server::ClientId;
|
||||||
|
|
||||||
use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState;
|
use cosmic_protocols::workspace::v1::server::zcosmic_workspace_handle_v1::State as WState;
|
||||||
|
use cosmic_time::{Cubic, Ease, Tween};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
desktop::{
|
desktop::{
|
||||||
layer_map_for_output, space::SpaceElement, LayerSurface, PopupManager, WindowSurfaceType,
|
layer_map_for_output, space::SpaceElement, LayerSurface, PopupManager, WindowSurfaceType,
|
||||||
|
|
@ -63,10 +64,12 @@ use self::{
|
||||||
grabs::ResizeEdge,
|
grabs::ResizeEdge,
|
||||||
layout::{
|
layout::{
|
||||||
floating::{FloatingLayout, ResizeState},
|
floating::{FloatingLayout, ResizeState},
|
||||||
tiling::{Direction, TilingLayout, ANIMATION_DURATION},
|
tiling::{Direction, TilingLayout},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ANIMATION_DURATION: Duration = Duration::from_millis(200);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum OverviewMode {
|
pub enum OverviewMode {
|
||||||
None,
|
None,
|
||||||
|
|
@ -74,6 +77,31 @@ pub enum OverviewMode {
|
||||||
Ended(Instant),
|
Ended(Instant),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl OverviewMode {
|
||||||
|
pub fn alpha(&self) -> Option<f32> {
|
||||||
|
match self {
|
||||||
|
OverviewMode::Started(_, start) => {
|
||||||
|
let percentage = (Instant::now().duration_since(*start).as_millis() as f32
|
||||||
|
/ ANIMATION_DURATION.as_millis() as f32)
|
||||||
|
.min(1.0);
|
||||||
|
Some(Ease::Cubic(Cubic::Out).tween(percentage))
|
||||||
|
}
|
||||||
|
OverviewMode::Ended(end) => {
|
||||||
|
let percentage = (1.0
|
||||||
|
- Instant::now().duration_since(*end).as_millis() as f32
|
||||||
|
/ ANIMATION_DURATION.as_millis() as f32)
|
||||||
|
.max(0.0);
|
||||||
|
if percentage > 0.0 {
|
||||||
|
Some(Ease::Cubic(Cubic::Out).tween(percentage))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OverviewMode::None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, serde::Deserialize, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, serde::Deserialize, PartialEq, Eq, Hash)]
|
||||||
pub enum ResizeDirection {
|
pub enum ResizeDirection {
|
||||||
Inwards,
|
Inwards,
|
||||||
|
|
@ -87,6 +115,31 @@ pub enum ResizeMode {
|
||||||
Ended(Instant, ResizeDirection),
|
Ended(Instant, ResizeDirection),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ResizeMode {
|
||||||
|
pub fn alpha(&self) -> Option<f32> {
|
||||||
|
match self {
|
||||||
|
ResizeMode::Started(_, start, _) => {
|
||||||
|
let percentage = (Instant::now().duration_since(*start).as_millis() as f32
|
||||||
|
/ ANIMATION_DURATION.as_millis() as f32)
|
||||||
|
.min(1.0);
|
||||||
|
Some(Ease::Cubic(Cubic::Out).tween(percentage))
|
||||||
|
}
|
||||||
|
ResizeMode::Ended(end, _) => {
|
||||||
|
let percentage = (1.0
|
||||||
|
- Instant::now().duration_since(*end).as_millis() as f32
|
||||||
|
/ ANIMATION_DURATION.as_millis() as f32)
|
||||||
|
.max(0.0);
|
||||||
|
if percentage > 0.0 {
|
||||||
|
Some(Ease::Cubic(Cubic::Out).tween(percentage))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ResizeMode::None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Shell {
|
pub struct Shell {
|
||||||
pub popups: PopupManager,
|
pub popups: PopupManager,
|
||||||
pub outputs: Vec<Output>,
|
pub outputs: Vec<Output>,
|
||||||
|
|
@ -114,6 +167,7 @@ pub struct Shell {
|
||||||
usize,
|
usize,
|
||||||
Output,
|
Output,
|
||||||
)>,
|
)>,
|
||||||
|
resize_indicator: Option<ResizeIndicator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -574,6 +628,7 @@ impl Shell {
|
||||||
overview_mode: OverviewMode::None,
|
overview_mode: OverviewMode::None,
|
||||||
resize_mode: ResizeMode::None,
|
resize_mode: ResizeMode::None,
|
||||||
resize_state: None,
|
resize_state: None,
|
||||||
|
resize_indicator: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1177,7 +1232,12 @@ impl Shell {
|
||||||
self.overview_mode.clone()
|
self.overview_mode.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_resize_mode(&mut self, enabled: Option<(KeyPattern, ResizeDirection)>) {
|
pub fn set_resize_mode(
|
||||||
|
&mut self,
|
||||||
|
enabled: Option<(KeyPattern, ResizeDirection)>,
|
||||||
|
config: &Config,
|
||||||
|
evlh: LoopHandle<'static, crate::state::Data>,
|
||||||
|
) {
|
||||||
if let Some((pattern, direction)) = enabled {
|
if let Some((pattern, direction)) = enabled {
|
||||||
if let ResizeMode::Started(old_pattern, _, old_direction) = &mut self.resize_mode {
|
if let ResizeMode::Started(old_pattern, _, old_direction) = &mut self.resize_mode {
|
||||||
*old_pattern = pattern;
|
*old_pattern = pattern;
|
||||||
|
|
@ -1185,6 +1245,7 @@ impl Shell {
|
||||||
} else {
|
} else {
|
||||||
self.resize_mode = ResizeMode::Started(pattern, Instant::now(), direction);
|
self.resize_mode = ResizeMode::Started(pattern, Instant::now(), direction);
|
||||||
}
|
}
|
||||||
|
self.resize_indicator = Some(resize_indicator(direction, config, evlh));
|
||||||
} else {
|
} else {
|
||||||
if let ResizeMode::Started(_, _, direction) = &self.resize_mode {
|
if let ResizeMode::Started(_, _, direction) = &self.resize_mode {
|
||||||
self.resize_mode = ResizeMode::Ended(Instant::now(), *direction);
|
self.resize_mode = ResizeMode::Ended(Instant::now(), *direction);
|
||||||
|
|
@ -1192,25 +1253,15 @@ impl Shell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize_mode(&mut self) -> ResizeMode {
|
pub fn resize_mode(&mut self) -> (ResizeMode, Option<ResizeIndicator>) {
|
||||||
if let ResizeMode::Ended(timestamp, _) = self.resize_mode {
|
if let ResizeMode::Ended(timestamp, _) = self.resize_mode {
|
||||||
if Instant::now().duration_since(timestamp) > ANIMATION_DURATION {
|
if Instant::now().duration_since(timestamp) > ANIMATION_DURATION {
|
||||||
self.resize_mode = ResizeMode::None;
|
self.resize_mode = ResizeMode::None;
|
||||||
|
self.resize_indicator = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.resize_mode.clone()
|
(self.resize_mode.clone(), self.resize_indicator.clone())
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resize_active_window(
|
|
||||||
&mut self,
|
|
||||||
seat: &Seat<State>,
|
|
||||||
direction: ResizeDirection,
|
|
||||||
edge: ResizeEdge,
|
|
||||||
) {
|
|
||||||
self.workspaces
|
|
||||||
.active_mut(&seat.active_output())
|
|
||||||
.resize(seat, direction, edge);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh(&mut self) {
|
pub fn refresh(&mut self) {
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,10 @@ use tracing::warn;
|
||||||
use wayland_backend::server::ClientId;
|
use wayland_backend::server::ClientId;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
element::{stack::CosmicStackRenderElement, window::CosmicWindowRenderElement, CosmicMapped},
|
element::{
|
||||||
|
resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement,
|
||||||
|
window::CosmicWindowRenderElement, CosmicMapped,
|
||||||
|
},
|
||||||
focus::{target::KeyboardFocusTarget, FocusStack, FocusStackMut},
|
focus::{target::KeyboardFocusTarget, FocusStack, FocusStackMut},
|
||||||
grabs::{ResizeEdge, ResizeGrab},
|
grabs::{ResizeEdge, ResizeGrab},
|
||||||
CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode,
|
CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode,
|
||||||
|
|
@ -500,6 +503,7 @@ impl Workspace {
|
||||||
xwm_state: Option<&'a mut XWaylandState>,
|
xwm_state: Option<&'a mut XWaylandState>,
|
||||||
draw_focus_indicator: Option<&Seat<State>>,
|
draw_focus_indicator: Option<&Seat<State>>,
|
||||||
overview: OverviewMode,
|
overview: OverviewMode,
|
||||||
|
resize_indicator: Option<(ResizeMode, ResizeIndicator)>,
|
||||||
indicator_thickness: u8,
|
indicator_thickness: u8,
|
||||||
) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
|
) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
|
||||||
where
|
where
|
||||||
|
|
@ -606,6 +610,7 @@ impl Workspace {
|
||||||
renderer,
|
renderer,
|
||||||
output,
|
output,
|
||||||
focused.as_ref(),
|
focused.as_ref(),
|
||||||
|
resize_indicator.clone(),
|
||||||
indicator_thickness,
|
indicator_thickness,
|
||||||
alpha,
|
alpha,
|
||||||
)
|
)
|
||||||
|
|
@ -622,6 +627,7 @@ impl Workspace {
|
||||||
draw_focus_indicator,
|
draw_focus_indicator,
|
||||||
layer_map.non_exclusive_zone(),
|
layer_map.non_exclusive_zone(),
|
||||||
overview.clone(),
|
overview.clone(),
|
||||||
|
resize_indicator,
|
||||||
indicator_thickness,
|
indicator_thickness,
|
||||||
)?
|
)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue