diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-11-24 09:41:43 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-11-25 04:07:59 -0800 |
| commit | 4c22c3285d8b10fbcef1c45a0788c3ddca03ec62 (patch) | |
| tree | 506174fe9962a91598ac25d4d2dee1cdaa3d5292 /src/layout | |
| parent | 93cee2994ab9ccf59a09f61d5b8acf6cd937d654 (diff) | |
| download | niri-4c22c3285d8b10fbcef1c45a0788c3ddca03ec62.tar.gz niri-4c22c3285d8b10fbcef1c45a0788c3ddca03ec62.tar.bz2 niri-4c22c3285d8b10fbcef1c45a0788c3ddca03ec62.zip | |
Refactor animation timing to use lazy clocks
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/closing_window.rs | 7 | ||||
| -rw-r--r-- | src/layout/mod.rs | 28 | ||||
| -rw-r--r-- | src/layout/monitor.rs | 9 | ||||
| -rw-r--r-- | src/layout/opening_window.rs | 5 | ||||
| -rw-r--r-- | src/layout/tile.rs | 19 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 339 |
6 files changed, 204 insertions, 203 deletions
diff --git a/src/layout/closing_window.rs b/src/layout/closing_window.rs index 744c099e..12a77f92 100644 --- a/src/layout/closing_window.rs +++ b/src/layout/closing_window.rs @@ -1,5 +1,4 @@ use std::collections::HashMap; -use std::time::Duration; use anyhow::Context as _; use glam::{Mat3, Vec2}; @@ -138,15 +137,15 @@ impl ClosingWindow { }) } - pub fn advance_animations(&mut self, current_time: Duration) { + pub fn advance_animations(&mut self) { match &mut self.anim_state { AnimationState::Waiting { blocker, anim } => { if blocker.state() != BlockerState::Pending { - let anim = anim.restarted(current_time, 0., 1., 0.); + let anim = anim.restarted(0., 1., 0.); self.anim_state = AnimationState::Animating(anim); } } - AnimationState::Animating(anim) => anim.set_current_time(current_time), + AnimationState::Animating(_anim) => (), } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index f4e59af3..45228038 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -219,6 +219,8 @@ pub struct Layout<W: LayoutElement> { interactive_move: Option<InteractiveMoveState<W>>, /// Clock for driving animations. clock: Clock, + /// Time that we last updated render elements for. + update_render_elements_time: Duration, /// Configurable properties of the layout. options: Rc<Options>, } @@ -447,6 +449,7 @@ impl<W: LayoutElement> Layout<W> { last_active_workspace_id: HashMap::new(), interactive_move: None, clock, + update_render_elements_time: Duration::ZERO, options: Rc::new(options), } } @@ -468,6 +471,7 @@ impl<W: LayoutElement> Layout<W> { last_active_workspace_id: HashMap::new(), interactive_move: None, clock, + update_render_elements_time: Duration::ZERO, options: opts, } } @@ -2194,22 +2198,22 @@ impl<W: LayoutElement> Layout<W> { } } - pub fn advance_animations(&mut self, current_time: Duration) { + pub fn advance_animations(&mut self) { let _span = tracy_client::span!("Layout::advance_animations"); if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move { - move_.tile.advance_animations(current_time); + move_.tile.advance_animations(); } match &mut self.monitor_set { MonitorSet::Normal { monitors, .. } => { for mon in monitors { - mon.advance_animations(current_time); + mon.advance_animations(); } } MonitorSet::NoOutputs { workspaces, .. } => { for ws in workspaces { - ws.advance_animations(current_time); + ws.advance_animations(); } } } @@ -2242,6 +2246,8 @@ impl<W: LayoutElement> Layout<W> { pub fn update_render_elements(&mut self, output: Option<&Output>) { let _span = tracy_client::span!("Layout::update_render_elements"); + self.update_render_elements_time = self.clock.now(); + if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move { if output.map_or(true, |output| move_.output == *output) { let pos_within_output = move_.tile_render_location(); @@ -3475,6 +3481,10 @@ impl<W: LayoutElement> Layout<W> { output: &Output, target: RenderTarget, ) -> impl Iterator<Item = TileRenderElement<R>> { + if self.update_render_elements_time != self.clock.now() { + error!("clock moved between updating render elements and rendering"); + } + let mut rv = None; if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move { @@ -3653,7 +3663,7 @@ mod tests { impl<W: LayoutElement> Default for Layout<W> { fn default() -> Self { - Self::with_options(Clock::with_override(Duration::ZERO), Default::default()) + Self::with_options(Clock::with_time(Duration::ZERO), Default::default()) } } @@ -4553,14 +4563,14 @@ mod tests { layout.refresh(is_active); } Op::AdvanceAnimations { msec_delta } => { - let mut now = layout.clock.now(); + let mut now = layout.clock.now_unadjusted(); if msec_delta >= 0 { now = now.saturating_add(Duration::from_millis(msec_delta as u64)); } else { now = now.saturating_sub(Duration::from_millis(-msec_delta as u64)); } - layout.clock.set_time_override(Some(now)); - layout.advance_animations(now); + layout.clock.set_unadjusted(now); + layout.advance_animations(); } Op::MoveWorkspaceToOutput(id) => { let name = format!("output{id}"); @@ -4674,7 +4684,7 @@ mod tests { #[track_caller] fn check_ops_with_options(options: Options, ops: &[Op]) { - let mut layout = Layout::with_options(Clock::with_override(Duration::ZERO), options); + let mut layout = Layout::with_options(Clock::with_time(Duration::ZERO), options); for op in ops { op.apply(&mut layout); diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index aad921bb..52e73cce 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -184,7 +184,7 @@ impl<W: LayoutElement> Monitor<W> { self.active_workspace_idx = idx; self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new( - self.clock.now(), + self.clock.clone(), current_idx, idx as f64, 0., @@ -734,9 +734,8 @@ impl<W: LayoutElement> Monitor<W> { Some(column.tiles[column.active_tile_idx].window()) } - pub fn advance_animations(&mut self, current_time: Duration) { + pub fn advance_animations(&mut self) { if let Some(WorkspaceSwitch::Animation(anim)) = &mut self.workspace_switch { - anim.set_current_time(current_time); if anim.is_done() { self.workspace_switch = None; self.clean_up_workspaces(); @@ -744,7 +743,7 @@ impl<W: LayoutElement> Monitor<W> { } for ws in &mut self.workspaces { - ws.advance_animations(current_time); + ws.advance_animations(); } } @@ -1112,7 +1111,7 @@ impl<W: LayoutElement> Monitor<W> { self.active_workspace_idx = new_idx; self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new( - self.clock.now(), + self.clock.clone(), gesture.current_idx, new_idx as f64, velocity, diff --git a/src/layout/opening_window.rs b/src/layout/opening_window.rs index 90e50581..0a3d4973 100644 --- a/src/layout/opening_window.rs +++ b/src/layout/opening_window.rs @@ -1,5 +1,4 @@ use std::collections::HashMap; -use std::time::Duration; use anyhow::Context as _; use glam::{Mat3, Vec2}; @@ -41,9 +40,7 @@ impl OpenAnimation { } } - pub fn advance_animations(&mut self, current_time: Duration) { - self.anim.set_current_time(current_time); - } + pub fn advance_animations(&mut self) {} pub fn is_done(&self) -> bool { self.anim.is_done() diff --git a/src/layout/tile.rs b/src/layout/tile.rs index dd3d50af..6646999f 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -1,5 +1,4 @@ use std::rc::Rc; -use std::time::Duration; use niri_config::{Color, CornerRadius, GradientInterpolation}; use smithay::backend::allocator::Fourcc; @@ -185,7 +184,7 @@ impl<W: LayoutElement> Tile<W> { let change = f64::max(change.x.abs(), change.y.abs()); if change > RESIZE_ANIMATION_THRESHOLD { let anim = Animation::new( - self.clock.now(), + self.clock.clone(), 0., 1., 0., @@ -218,29 +217,25 @@ impl<W: LayoutElement> Tile<W> { self.rounded_corner_damage.set_size(window_size); } - pub fn advance_animations(&mut self, current_time: Duration) { + pub fn advance_animations(&mut self) { if let Some(open) = &mut self.open_animation { - open.advance_animations(current_time); if open.is_done() { self.open_animation = None; } } if let Some(resize) = &mut self.resize_animation { - resize.anim.set_current_time(current_time); if resize.anim.is_done() { self.resize_animation = None; } } if let Some(move_) = &mut self.move_x_animation { - move_.anim.set_current_time(current_time); if move_.anim.is_done() { self.move_x_animation = None; } } if let Some(move_) = &mut self.move_y_animation { - move_.anim.set_current_time(current_time); if move_.anim.is_done() { self.move_y_animation = None; } @@ -326,7 +321,7 @@ impl<W: LayoutElement> Tile<W> { pub fn start_open_animation(&mut self) { self.open_animation = Some(OpenAnimation::new(Animation::new( - self.clock.now(), + self.clock.clone(), 0., 1., 0., @@ -353,8 +348,8 @@ impl<W: LayoutElement> Tile<W> { // Preserve the previous config if ongoing. let anim = self.move_x_animation.take().map(|move_| move_.anim); let anim = anim - .map(|anim| anim.restarted(self.clock.now(), 1., 0., 0.)) - .unwrap_or_else(|| Animation::new(self.clock.now(), 1., 0., 0., config)); + .map(|anim| anim.restarted(1., 0., 0.)) + .unwrap_or_else(|| Animation::new(self.clock.clone(), 1., 0., 0., config)); self.move_x_animation = Some(MoveAnimation { anim, @@ -372,8 +367,8 @@ impl<W: LayoutElement> Tile<W> { // Preserve the previous config if ongoing. let anim = self.move_y_animation.take().map(|move_| move_.anim); let anim = anim - .map(|anim| anim.restarted(self.clock.now(), 1., 0., 0.)) - .unwrap_or_else(|| Animation::new(self.clock.now(), 1., 0., 0., config)); + .map(|anim| anim.restarted(1., 0., 0.)) + .unwrap_or_else(|| Animation::new(self.clock.clone(), 1., 0., 0., config)); self.move_y_animation = Some(MoveAnimation { anim, diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 6432ca8b..2b3a462c 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -84,10 +84,7 @@ pub struct Workspace<W: LayoutElement> { /// Any gaps, including left padding from work area left exclusive zone, is handled /// with this view offset (rather than added as a constant elsewhere in the code). This allows /// for natural handling of fullscreen windows, which must ignore work area padding. - view_offset: f64, - - /// Adjustment of the view offset, if one is currently ongoing. - view_offset_adj: Option<ViewOffsetAdjustment>, + view_offset: ViewOffset, /// Whether to activate the previous, rather than the next, column upon column removal. /// @@ -188,8 +185,12 @@ struct ColumnData { } #[derive(Debug)] -enum ViewOffsetAdjustment { +enum ViewOffset { + /// The view offset is static. + Static(f64), + /// The view offset is animating. Animation(Animation), + /// The view offset is controlled by the ongoing gesture. Gesture(ViewGesture), } @@ -199,7 +200,7 @@ struct ViewGesture { tracker: SwipeTracker, delta_from_tracker: f64, // The view offset we'll use if needed for activate_prev_column_on_removal. - static_view_offset: f64, + stationary_view_offset: f64, /// Whether the gesture is controlled by the touchpad. is_touchpad: bool, } @@ -325,17 +326,70 @@ impl OutputId { } } -impl ViewOffsetAdjustment { +impl ViewOffset { + /// Returns the current view offset. + pub fn current(&self) -> f64 { + match self { + ViewOffset::Static(offset) => *offset, + ViewOffset::Animation(anim) => anim.value(), + ViewOffset::Gesture(gesture) => gesture.current_view_offset, + } + } + + /// Returns the target view offset suitable for computing the new view offset. + pub fn target(&self) -> f64 { + match self { + ViewOffset::Static(offset) => *offset, + ViewOffset::Animation(anim) => anim.to(), + // This can be used for example if a gesture is interrupted. + ViewOffset::Gesture(gesture) => gesture.current_view_offset, + } + } + + /// Returns a view offset value suitable for saving and later restoration. + /// + /// This means that it shouldn't return an in-progress animation or gesture value. + fn stationary(&self) -> f64 { + match self { + ViewOffset::Static(offset) => *offset, + // For animations we can return the final value. + ViewOffset::Animation(anim) => anim.to(), + ViewOffset::Gesture(gesture) => gesture.stationary_view_offset, + } + } + + pub fn is_static(&self) -> bool { + matches!(self, Self::Static(_)) + } + pub fn is_animation(&self) -> bool { matches!(self, Self::Animation(_)) } - pub fn target_view_offset(&self) -> f64 { + pub fn is_gesture(&self) -> bool { + matches!(self, Self::Gesture(_)) + } + + pub fn offset(&mut self, delta: f64) { match self { - ViewOffsetAdjustment::Animation(anim) => anim.to(), - ViewOffsetAdjustment::Gesture(gesture) => gesture.current_view_offset, + ViewOffset::Static(offset) => *offset += delta, + ViewOffset::Animation(anim) => anim.offset(delta), + ViewOffset::Gesture(_gesture) => { + // Is this needed? + error!("cancel gesture before offsetting"); + } } } + + pub fn cancel_gesture(&mut self) { + if let ViewOffset::Gesture(gesture) = self { + *self = ViewOffset::Static(gesture.current_view_offset); + } + } + + pub fn stop_anim_and_gesture(&mut self) { + *self = ViewOffset::Static(self.current()); + } } impl ColumnData { @@ -442,8 +496,7 @@ impl<W: LayoutElement> Workspace<W> { data: vec![], active_column_idx: 0, interactive_resize: None, - view_offset: 0., - view_offset_adj: None, + view_offset: ViewOffset::Static(0.), activate_prev_column_on_removal: None, view_offset_before_fullscreen: None, closing_windows: vec![], @@ -484,8 +537,7 @@ impl<W: LayoutElement> Workspace<W> { data: vec![], active_column_idx: 0, interactive_resize: None, - view_offset: 0., - view_offset_adj: None, + view_offset: ViewOffset::Static(0.), activate_prev_column_on_removal: None, view_offset_before_fullscreen: None, closing_windows: vec![], @@ -524,37 +576,31 @@ impl<W: LayoutElement> Workspace<W> { || (self.options.always_center_single_column && self.columns.len() <= 1) } - pub fn advance_animations(&mut self, current_time: Duration) { - if let Some(ViewOffsetAdjustment::Animation(anim)) = &mut self.view_offset_adj { - anim.set_current_time(current_time); - self.view_offset = anim.value(); + pub fn advance_animations(&mut self) { + if let ViewOffset::Animation(anim) = &self.view_offset { if anim.is_done() { - self.view_offset_adj = None; + self.view_offset = ViewOffset::Static(anim.to()); } - } else if let Some(ViewOffsetAdjustment::Gesture(gesture)) = &self.view_offset_adj { - self.view_offset = gesture.current_view_offset; } for col in &mut self.columns { - col.advance_animations(current_time); + col.advance_animations(); } self.closing_windows.retain_mut(|closing| { - closing.advance_animations(current_time); + closing.advance_animations(); closing.are_animations_ongoing() }); } pub fn are_animations_ongoing(&self) -> bool { - self.view_offset_adj - .as_ref() - .is_some_and(|s| s.is_animation()) + self.view_offset.is_animation() || self.columns.iter().any(Column::are_animations_ongoing) || !self.closing_windows.is_empty() } pub fn are_transitions_ongoing(&self) -> bool { - self.view_offset_adj.is_some() + !self.view_offset.is_static() || self.columns.iter().any(Column::are_animations_ongoing) || !self.closing_windows.is_empty() } @@ -786,7 +832,7 @@ impl<W: LayoutElement> Workspace<W> { fn compute_new_view_offset_fit( &self, - current_x: f64, + target_x: Option<f64>, col_x: f64, width: f64, is_fullscreen: bool, @@ -795,14 +841,10 @@ impl<W: LayoutElement> Workspace<W> { return 0.; } - let final_x = if let Some(ViewOffsetAdjustment::Animation(anim)) = &self.view_offset_adj { - current_x - self.view_offset + anim.to() - } else { - current_x - }; + let target_x = target_x.unwrap_or_else(|| self.target_view_pos()); let new_offset = compute_new_view_offset( - final_x + self.working_area.loc.x, + target_x + self.working_area.loc.x, self.working_area.size.w, col_x, width, @@ -815,37 +857,41 @@ impl<W: LayoutElement> Workspace<W> { fn compute_new_view_offset_centered( &self, - current_x: f64, + target_x: Option<f64>, col_x: f64, width: f64, is_fullscreen: bool, ) -> f64 { if is_fullscreen { - return self.compute_new_view_offset_fit(current_x, col_x, width, is_fullscreen); + return self.compute_new_view_offset_fit(target_x, col_x, width, is_fullscreen); } // Columns wider than the view are left-aligned (the fit code can deal with that). if self.working_area.size.w <= width { - return self.compute_new_view_offset_fit(current_x, col_x, width, is_fullscreen); + return self.compute_new_view_offset_fit(target_x, col_x, width, is_fullscreen); } -(self.working_area.size.w - width) / 2. - self.working_area.loc.x } - fn compute_new_view_offset_for_column_fit(&self, current_x: f64, idx: usize) -> f64 { + fn compute_new_view_offset_for_column_fit(&self, target_x: Option<f64>, idx: usize) -> f64 { let col = &self.columns[idx]; self.compute_new_view_offset_fit( - current_x, + target_x, self.column_x(idx), col.width(), col.is_fullscreen, ) } - fn compute_new_view_offset_for_column_centered(&self, current_x: f64, idx: usize) -> f64 { + fn compute_new_view_offset_for_column_centered( + &self, + target_x: Option<f64>, + idx: usize, + ) -> f64 { let col = &self.columns[idx]; self.compute_new_view_offset_centered( - current_x, + target_x, self.column_x(idx), col.width(), col.is_fullscreen, @@ -854,21 +900,21 @@ impl<W: LayoutElement> Workspace<W> { fn compute_new_view_offset_for_column( &self, - current_x: f64, + target_x: Option<f64>, idx: usize, prev_idx: Option<usize>, ) -> f64 { if self.is_centering_focused_column() { - return self.compute_new_view_offset_for_column_centered(current_x, idx); + return self.compute_new_view_offset_for_column_centered(target_x, idx); } match self.options.center_focused_column { CenterFocusedColumn::Always => { - self.compute_new_view_offset_for_column_centered(current_x, idx) + self.compute_new_view_offset_for_column_centered(target_x, idx) } CenterFocusedColumn::OnOverflow => { let Some(prev_idx) = prev_idx else { - return self.compute_new_view_offset_for_column_fit(current_x, idx); + return self.compute_new_view_offset_for_column_fit(target_x, idx); }; // Always take the left or right neighbor of the target as the source. @@ -878,36 +924,35 @@ impl<W: LayoutElement> Workspace<W> { idx.saturating_sub(1) }; - let source_x = self.column_x(source_idx); - let source_width = self.columns[source_idx].width(); + let source_col_x = self.column_x(source_idx); + let source_col_width = self.columns[source_idx].width(); - let target_x = self.column_x(idx); - let target_width = self.columns[idx].width(); + let target_col_x = self.column_x(idx); + let target_col_width = self.columns[idx].width(); - let total_width = if source_x < target_x { + let total_width = if source_col_x < target_col_x { // Source is left from target. - target_x - source_x + target_width + target_col_x - source_col_x + target_col_width } else { // Source is right from target. - source_x - target_x + source_width + source_col_x - target_col_x + source_col_width } + self.options.gaps * 2.; // If it fits together, do a normal animation, otherwise center the new column. if total_width <= self.working_area.size.w { - self.compute_new_view_offset_for_column_fit(current_x, idx) + self.compute_new_view_offset_for_column_fit(target_x, idx) } else { - self.compute_new_view_offset_for_column_centered(current_x, idx) + self.compute_new_view_offset_for_column_centered(target_x, idx) } } CenterFocusedColumn::Never => { - self.compute_new_view_offset_for_column_fit(current_x, idx) + self.compute_new_view_offset_for_column_fit(target_x, idx) } } } - fn animate_view_offset(&mut self, current_x: f64, idx: usize, new_view_offset: f64) { + fn animate_view_offset(&mut self, idx: usize, new_view_offset: f64) { self.animate_view_offset_with_config( - current_x, idx, new_view_offset, self.options.animations.horizontal_view_movement.0, @@ -916,78 +961,67 @@ impl<W: LayoutElement> Workspace<W> { fn animate_view_offset_with_config( &mut self, - current_x: f64, idx: usize, new_view_offset: f64, config: niri_config::Animation, ) { + self.view_offset.cancel_gesture(); + let new_col_x = self.column_x(idx); - let old_col_x = current_x - self.view_offset; + let old_col_x = self.column_x(self.active_column_idx); let offset_delta = old_col_x - new_col_x; - self.view_offset += offset_delta; + self.view_offset.offset(offset_delta); let pixel = 1. / self.scale.fractional_scale(); - // If we're already animating towards that, don't restart it. - if let Some(ViewOffsetAdjustment::Animation(anim)) = &mut self.view_offset_adj { - // Offset the animation for the active column change. - anim.offset(offset_delta); - - let to_diff = new_view_offset - anim.to(); - if (anim.value() - self.view_offset).abs() < pixel && to_diff.abs() < pixel { - // Correct for any inaccuracy. - anim.offset(to_diff); - return; - } - } - - // If our view offset is already this, we don't need to do anything. - if (self.view_offset - new_view_offset).abs() < pixel { + // If our view offset is already this or animating towards this, we don't need to do + // anything. + let to_diff = new_view_offset - self.view_offset.target(); + if to_diff.abs() < pixel { // Correct for any inaccuracy. - self.view_offset = new_view_offset; - self.view_offset_adj = None; + self.view_offset.offset(to_diff); return; } // FIXME: also compute and use current velocity. - self.view_offset_adj = Some(ViewOffsetAdjustment::Animation(Animation::new( - self.clock.now(), - self.view_offset, + self.view_offset = ViewOffset::Animation(Animation::new( + self.clock.clone(), + self.view_offset.current(), new_view_offset, 0., config, - ))); + )); } fn animate_view_offset_to_column_centered( &mut self, - current_x: f64, + target_x: Option<f64>, idx: usize, config: niri_config::Animation, ) { - let new_view_offset = self.compute_new_view_offset_for_column_centered(current_x, idx); - self.animate_view_offset_with_config(current_x, idx, new_view_offset, config); + let new_view_offset = self.compute_new_view_offset_for_column_centered(target_x, idx); + self.animate_view_offset_with_config(idx, new_view_offset, config); } fn animate_view_offset_to_column_with_config( &mut self, - current_x: f64, + target_x: Option<f64>, idx: usize, prev_idx: Option<usize>, config: niri_config::Animation, ) { - let new_view_offset = self.compute_new_view_offset_for_column(current_x, idx, prev_idx); - self.animate_view_offset_with_config(current_x, idx, new_view_offset, config); + let new_view_offset = self.compute_new_view_offset_for_column(target_x, idx, prev_idx); + self.animate_view_offset_with_config(idx, new_view_offset, config); } fn animate_view_offset_to_column( &mut self, - current_x: f64, + target_x: Option<f64>, idx: usize, prev_idx: Option<usize>, ) { self.animate_view_offset_to_column_with_config( - current_x, + target_x, idx, prev_idx, self.options.animations.horizontal_view_movement.0, @@ -1006,9 +1040,8 @@ impl<W: LayoutElement> Workspace<W> { return; } - let current_x = self.view_pos(); self.animate_view_offset_to_column_with_config( - current_x, + None, idx, Some(self.active_column_idx), config, @@ -1240,14 +1273,13 @@ impl<W: LayoutElement> Workspace<W> { // If this is the first window on an empty workspace, remove the effect of whatever // view_offset was left over and skip the animation. if was_empty { - self.view_offset = 0.; - self.view_offset_adj = None; + self.view_offset = ViewOffset::Static(0.); self.view_offset = - self.compute_new_view_offset_for_column(self.view_pos(), idx, None); + ViewOffset::Static(self.compute_new_view_offset_for_column(None, idx, None)); } let prev_offset = (!was_empty && idx == self.active_column_idx + 1) - .then(|| self.static_view_offset()); + .then(|| self.view_offset.stationary()); let anim_config = anim_config.unwrap_or(self.options.animations.horizontal_view_movement.0); @@ -1416,15 +1448,13 @@ impl<W: LayoutElement> Workspace<W> { // Restore the view offset but make sure to scroll the view in case the // previous window had resized. - let current_x = self.view_pos(); self.animate_view_offset_with_config( - current_x, self.active_column_idx, prev_offset, view_config, ); self.animate_view_offset_to_column_with_config( - current_x, + None, self.active_column_idx, None, view_config, @@ -1509,6 +1539,9 @@ impl<W: LayoutElement> Workspace<W> { // the Resizing state, which can trigger this code path for a while. let resize = if offset != 0. { resize } else { None }; if let Some(resize) = resize { + // Don't bother with the gesture. + self.view_offset.cancel_gesture(); + // If this is an interactive resize commit of an active window, then we need to // either preserve the view offset or adjust it accordingly. let centered = self.is_centering_focused_column(); @@ -1518,33 +1551,24 @@ impl<W: LayoutElement> Workspace<W> { // FIXME: when view_offset becomes fractional, this can be made additive too. let new_offset = -(self.working_area.size.w - width) / 2. - self.working_area.loc.x; - new_offset - self.view_offset + new_offset - self.view_offset.target() } else if resize.edges.contains(ResizeEdge::LEFT) { -offset } else { 0. }; - self.view_offset += offset; - if let Some(ViewOffsetAdjustment::Animation(anim)) = &mut self.view_offset_adj { - anim.offset(offset); - } else { - // Don't bother with the gesture. - self.view_offset_adj = None; - } + self.view_offset.offset(offset); } - if self.interactive_resize.is_none() - && !matches!(self.view_offset_adj, Some(ViewOffsetAdjustment::Gesture(_))) - { + if self.interactive_resize.is_none() && !self.view_offset.is_gesture() { // We might need to move the view to ensure the resized window is still visible. - let current_x = self.view_pos(); // Upon unfullscreening, restore the view offset. let is_fullscreen = self.columns[col_idx].tiles[tile_idx].is_fullscreen(); if was_fullscreen && !is_fullscreen { if let Some(prev_offset) = self.view_offset_before_fullscreen.take() { - self.animate_view_offset(current_x, col_idx, prev_offset); + self.animate_view_offset(col_idx, prev_offset); } } @@ -1558,7 +1582,7 @@ impl<W: LayoutElement> Workspace<W> { // FIXME: we will want to skip the animation in some cases here to make continuously // resizing windows not look janky. - self.animate_view_offset_to_column_with_config(current_x, col_idx, None, config); + self.animate_view_offset_to_column_with_config(None, col_idx, None, config); } } } @@ -1574,22 +1598,16 @@ impl<W: LayoutElement> Workspace<W> { return 0.; } - let current_x = self.view_pos(); + // Consider the end of an ongoing animation because that's what compute to fit does too. + let target_x = self.target_view_pos(); let new_view_offset = self.compute_new_view_offset_for_column( - current_x, + Some(target_x), column_idx, Some(self.active_column_idx), ); - // Consider the end of an ongoing animation because that's what compute to fit does too. - let final_x = if let Some(ViewOffsetAdjustment::Animation(anim)) = &self.view_offset_adj { - current_x - self.view_offset + anim.to() - } else { - current_x - }; - let new_col_x = self.column_x(column_idx); - let from_view_offset = final_x - new_col_x; + let from_view_offset = target_x - new_col_x; (from_view_offset - new_view_offset).abs() / self.working_area.size.w } @@ -1698,7 +1716,7 @@ impl<W: LayoutElement> Workspace<W> { let output_scale = Scale::from(self.scale.fractional_scale()); let anim = Animation::new( - self.clock.now(), + self.clock.clone(), 0., 1., 0., @@ -1870,10 +1888,8 @@ impl<W: LayoutElement> Workspace<W> { // Preserve the camera position when moving to the left. let view_offset_delta = -self.column_x(self.active_column_idx) + current_col_x; - self.view_offset += view_offset_delta; - if let Some(ViewOffsetAdjustment::Animation(anim)) = &mut self.view_offset_adj { - anim.offset(view_offset_delta); - } + self.view_offset.cancel_gesture(); + self.view_offset.offset(view_offset_delta); // The column we just moved is offset by the difference between its new and old position. let new_col_x = self.column_x(new_idx); @@ -1987,7 +2003,8 @@ impl<W: LayoutElement> Workspace<W> { if source_tile_was_active { // Make sure the previous (target) column is activated so the animation looks right. - self.activate_prev_column_on_removal = Some(self.static_view_offset() + offset.x); + self.activate_prev_column_on_removal = + Some(self.view_offset.stationary() + offset.x); } offset.x += self.columns[source_col_idx].render_offset().x; @@ -2204,9 +2221,8 @@ impl<W: LayoutElement> Workspace<W> { return; } - let center_x = self.view_pos(); self.animate_view_offset_to_column_centered( - center_x, + None, self.active_column_idx, self.options.animations.horizontal_view_movement.0, ); @@ -2216,19 +2232,11 |
