diff options
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/floating.rs | 9 | ||||
| -rw-r--r-- | src/layout/mod.rs | 7 | ||||
| -rw-r--r-- | src/layout/scrolling.rs | 55 | ||||
| -rw-r--r-- | src/layout/tab_indicator.rs | 30 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 4 |
5 files changed, 98 insertions, 7 deletions
diff --git a/src/layout/floating.rs b/src/layout/floating.rs index 342663ac..fef92309 100644 --- a/src/layout/floating.rs +++ b/src/layout/floating.rs @@ -641,6 +641,15 @@ impl<W: LayoutElement> FloatingSpace<W> { self.interactive_resize_end(Some(&id)); } + pub fn start_open_animation(&mut self, id: &W::Id) -> bool { + let Some(idx) = self.idx_of(id) else { + return false; + }; + + self.tiles[idx].start_open_animation(); + true + } + pub fn toggle_window_height(&mut self, id: Option<&W::Id>) { let Some(id) = id.or(self.active_window_id.as_ref()).cloned() else { return; diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 4dec85f6..bdddf462 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -4112,11 +4112,8 @@ impl<W: LayoutElement> Layout<W> { } for ws in self.workspaces_mut() { - for tile in ws.tiles_mut() { - if tile.window().id() == window { - tile.start_open_animation(); - return; - } + if ws.start_open_animation(window) { + return; } } } diff --git a/src/layout/scrolling.rs b/src/layout/scrolling.rs index 18da2b46..6a876910 100644 --- a/src/layout/scrolling.rs +++ b/src/layout/scrolling.rs @@ -1429,6 +1429,12 @@ impl<W: LayoutElement> ScrollingSpace<W> { } } + pub fn start_open_animation(&mut self, id: &W::Id) -> bool { + self.columns + .iter_mut() + .any(|col| col.start_open_animation(id)) + } + pub fn focus_left(&mut self) -> bool { if self.active_column_idx == 0 { return false; @@ -3432,6 +3438,17 @@ impl<W: LayoutElement> Column<W> { rv.set_fullscreen(true); } + // Animate the tab indicator for new columns. + if display_mode == ColumnDisplay::Tabbed + && !rv.options.tab_indicator.hide_when_single_tab + && !is_pending_fullscreen + { + // Usually new columns are created together with window movement actions. For new + // windows, we handle that in start_open_animation(). + rv.tab_indicator + .start_open_animation(rv.clock.clone(), rv.options.animations.window_movement.0); + } + rv } @@ -3499,10 +3516,14 @@ impl<W: LayoutElement> Column<W> { for tile in &mut self.tiles { tile.advance_animations(); } + + self.tab_indicator.advance_animations(); } pub fn are_animations_ongoing(&self) -> bool { - self.move_animation.is_some() || self.tiles.iter().any(Tile::are_animations_ongoing) + self.move_animation.is_some() + || self.tab_indicator.are_animations_ongoing() + || self.tiles.iter().any(Tile::are_animations_ongoing) } pub fn update_render_elements(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) { @@ -4375,6 +4396,14 @@ impl<W: LayoutElement> Column<W> { } } + // Animate the appearance of the tab indicator. + if display == ColumnDisplay::Tabbed { + self.tab_indicator.start_open_animation( + self.clock.clone(), + self.options.animations.window_movement.0, + ); + } + // Now switch the display mode for real. self.display_mode = display; self.update_tile_sizes(true); @@ -4555,6 +4584,30 @@ impl<W: LayoutElement> Column<W> { Rectangle::new(self.tiles_origin(), area_size) } + pub fn start_open_animation(&mut self, id: &W::Id) -> bool { + for tile in &mut self.tiles { + if tile.window().id() == id { + tile.start_open_animation(); + + // Animate the appearance of the tab indicator. + if self.display_mode == ColumnDisplay::Tabbed + && !self.is_fullscreen + && self.tiles.len() == 1 + && !self.tab_indicator.config().hide_when_single_tab + { + self.tab_indicator.start_open_animation( + self.clock.clone(), + self.options.animations.window_open.anim, + ); + } + + return true; + } + } + + false + } + #[cfg(test)] fn verify_invariants(&self) { assert!(!self.tiles.is_empty(), "columns can't be empty"); diff --git a/src/layout/tab_indicator.rs b/src/layout/tab_indicator.rs index cbe1e932..e0e9ce75 100644 --- a/src/layout/tab_indicator.rs +++ b/src/layout/tab_indicator.rs @@ -6,6 +6,7 @@ use smithay::utils::{Logical, Point, Rectangle, Size}; use super::tile::Tile; use super::LayoutElement; +use crate::animation::{Animation, Clock}; use crate::niri_render_elements; use crate::render_helpers::border::BorderRenderElement; use crate::render_helpers::renderer::NiriRenderer; @@ -15,6 +16,7 @@ use crate::utils::{floor_logical_in_physical_max1, round_logical_in_physical}; pub struct TabIndicator { shader_locs: Vec<Point<f64, Logical>>, shaders: Vec<BorderRenderElement>, + open_anim: Option<Animation>, config: niri_config::TabIndicator, } @@ -37,6 +39,7 @@ impl TabIndicator { Self { shader_locs: Vec::new(), shaders: Vec::new(), + open_anim: None, config, } } @@ -51,6 +54,22 @@ impl TabIndicator { } } + pub fn advance_animations(&mut self) { + if let Some(anim) = &mut self.open_anim { + if anim.is_done() { + self.open_anim = None; + } + } + } + + pub fn are_animations_ongoing(&self) -> bool { + self.open_anim.is_some() + } + + pub fn start_open_animation(&mut self, clock: Clock, config: niri_config::Animation) { + self.open_anim = Some(Animation::new(clock, 0., 1., 0., config)); + } + fn tab_rects( &self, area: Rectangle<f64, Logical>, @@ -59,6 +78,8 @@ impl TabIndicator { ) -> impl Iterator<Item = Rectangle<f64, Logical>> { let round = |logical: f64| round_logical_in_physical(scale, logical); + let progress = self.open_anim.as_ref().map_or(1., |a| a.value().max(0.)); + let width = round(self.config.width.0); let gap = round(self.config.gap.0); let gaps_between = round(self.config.gaps_between_tabs.0); @@ -71,13 +92,20 @@ impl TabIndicator { let total_prop = self.config.length.total_proportion.unwrap_or(0.5); let min_length = round(side * total_prop.clamp(0., 2.)); + // Compute px_per_tab before applying the animation to gaps_between in order to avoid it + // growing and shrinking over the duration of the animation. let pixel = 1. / scale; let shortest_length = count as f64 * (pixel + gaps_between) - gaps_between; let length = f64::max(min_length, shortest_length); let px_per_tab = (length + gaps_between) / count as f64 - gaps_between; + + let px_per_tab = px_per_tab * progress; + let gaps_between = round(self.config.gaps_between_tabs.0 * progress); + + let length = count as f64 * (px_per_tab + gaps_between) - gaps_between; let px_per_tab = floor_logical_in_physical_max1(scale, px_per_tab); let floored_length = count as f64 * (px_per_tab + gaps_between) - gaps_between; - let mut ones_left = ((length - floored_length) / pixel).max(0.).round() as usize; + let mut ones_left = ((length - floored_length) / pixel).round() as usize; let mut shader_loc = Point::from((-gap - width, round((side - length) / 2.))); match position { diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 25d2bb80..a402ad44 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -1474,6 +1474,10 @@ impl<W: LayoutElement> Workspace<W> { .start_close_animation_for_tile(renderer, snapshot, tile_size, tile_pos, blocker); } + pub fn start_open_animation(&mut self, id: &W::Id) -> bool { + self.scrolling.start_open_animation(id) || self.floating.start_open_animation(id) + } + pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<(&W, HitType)> { // This logic is consistent with tiles_with_render_positions(). if self.is_floating_visible() { |
