diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/animation/clock.rs | 41 | ||||
| -rw-r--r-- | src/animation/mod.rs | 80 | ||||
| -rw-r--r-- | src/input/mod.rs | 3 | ||||
| -rw-r--r-- | src/layout/closing_window.rs | 3 | ||||
| -rw-r--r-- | src/layout/mod.rs | 120 | ||||
| -rw-r--r-- | src/layout/monitor.rs | 20 | ||||
| -rw-r--r-- | src/layout/tile.rs | 25 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 46 | ||||
| -rw-r--r-- | src/niri.rs | 15 | ||||
| -rw-r--r-- | src/ui/config_error_notification.rs | 14 | ||||
| -rw-r--r-- | src/ui/screenshot_ui.rs | 17 |
11 files changed, 306 insertions, 78 deletions
diff --git a/src/animation/clock.rs b/src/animation/clock.rs new file mode 100644 index 00000000..3cfa727b --- /dev/null +++ b/src/animation/clock.rs @@ -0,0 +1,41 @@ +use std::cell::Cell; +use std::rc::Rc; +use std::time::Duration; + +use crate::utils::get_monotonic_time; + +/// Clock that can have its time value overridden. +/// +/// Can be cloned to share the same clock. +#[derive(Debug, Default, Clone)] +pub struct Clock { + time_override: Rc<Cell<Option<Duration>>>, +} + +impl Clock { + /// Creates a new [`Clock`] with time override in place. + pub fn with_override(time: Duration) -> Self { + Self { + time_override: Rc::new(Cell::new(Some(time))), + } + } + + /// Sets the current time override. + pub fn set_time_override(&mut self, time: Option<Duration>) { + self.time_override.set(time); + } + + /// Gets the current time. + #[inline] + pub fn now(&self) -> Duration { + self.time_override.get().unwrap_or_else(get_monotonic_time) + } +} + +impl PartialEq for Clock { + fn eq(&self, other: &Self) -> bool { + Rc::ptr_eq(&self.time_override, &other.time_override) + } +} + +impl Eq for Clock {} diff --git a/src/animation/mod.rs b/src/animation/mod.rs index 780cb3a8..5efb0f16 100644 --- a/src/animation/mod.rs +++ b/src/animation/mod.rs @@ -4,11 +4,12 @@ use keyframe::functions::{EaseOutCubic, EaseOutQuad}; use keyframe::EasingFunction; use portable_atomic::{AtomicF64, Ordering}; -use crate::utils::get_monotonic_time; - mod spring; pub use spring::{Spring, SpringParams}; +mod clock; +pub use clock::Clock; + pub static ANIMATION_SLOWDOWN: AtomicF64 = AtomicF64::new(1.); #[derive(Debug, Clone)] @@ -48,11 +49,24 @@ pub enum Curve { } impl Animation { - pub fn new(from: f64, to: f64, initial_velocity: f64, config: niri_config::Animation) -> Self { + pub fn new( + current_time: Duration, + from: f64, + to: f64, + initial_velocity: f64, + config: niri_config::Animation, + ) -> Self { // Scale the velocity by slowdown to keep the touchpad gestures feeling right. let initial_velocity = initial_velocity * ANIMATION_SLOWDOWN.load(Ordering::Relaxed); - let mut rv = Self::ease(from, to, initial_velocity, 0, Curve::EaseOutCubic); + let mut rv = Self::ease( + current_time, + from, + to, + initial_velocity, + 0, + Curve::EaseOutCubic, + ); if config.off { rv.is_off = true; return rv; @@ -83,10 +97,11 @@ impl Animation { initial_velocity: self.initial_velocity, params, }; - *self = Self::spring(spring); + *self = Self::spring(current_time, spring); } niri_config::AnimationKind::Easing(p) => { *self = Self::ease( + current_time, self.from, self.to, self.initial_velocity, @@ -101,7 +116,13 @@ impl Animation { } /// Restarts the animation using the previous config. - pub fn restarted(&self, from: f64, to: f64, initial_velocity: f64) -> Self { + pub fn restarted( + &self, + current_time: Duration, + from: f64, + to: f64, + initial_velocity: f64, + ) -> Self { if self.is_off { return self.clone(); } @@ -111,6 +132,7 @@ impl Animation { match self.kind { Kind::Easing { curve } => Self::ease( + current_time, from, to, initial_velocity, @@ -124,23 +146,32 @@ impl Animation { initial_velocity: self.initial_velocity, params: spring.params, }; - Self::spring(spring) + Self::spring(current_time, spring) } Kind::Deceleration { initial_velocity, deceleration_rate, } => { let threshold = 0.001; // FIXME - Self::decelerate(from, initial_velocity, deceleration_rate, threshold) + Self::decelerate( + current_time, + from, + initial_velocity, + deceleration_rate, + threshold, + ) } } } - pub fn ease(from: f64, to: f64, initial_velocity: f64, duration_ms: u64, curve: Curve) -> Self { - // FIXME: ideally we shouldn't use current time here because animations started within the - // same frame cycle should have the same start time to be synchronized. - let now = get_monotonic_time(); - + pub fn ease( + current_time: Duration, + from: f64, + to: f64, + initial_velocity: f64, + duration_ms: u64, + curve: Curve, + ) -> Self { let duration = Duration::from_millis(duration_ms); let kind = Kind::Easing { curve }; @@ -152,19 +183,15 @@ impl Animation { duration, // Our current curves never overshoot. clamped_duration: duration, - start_time: now, - current_time: now, + start_time: current_time, + current_time, kind, } } - pub fn spring(spring: Spring) -> Self { + pub fn spring(current_time: Duration, spring: Spring) -> Self { let _span = tracy_client::span!("Animation::spring"); - // FIXME: ideally we shouldn't use current time here because animations started within the - // same frame cycle should have the same start time to be synchronized. - let now = get_monotonic_time(); - let duration = spring.duration(); let clamped_duration = spring.clamped_duration().unwrap_or(duration); let kind = Kind::Spring(spring); @@ -176,22 +203,19 @@ impl Animation { is_off: false, duration, clamped_duration, - start_time: now, - current_time: now, + start_time: current_time, + current_time, kind, } } pub fn decelerate( + current_time: Duration, from: f64, initial_velocity: f64, deceleration_rate: f64, threshold: f64, ) -> Self { - // FIXME: ideally we shouldn't use current time here because animations started within the - // same frame cycle should have the same start time to be synchronized. - let now = get_monotonic_time(); - let duration_s = if initial_velocity == 0. { 0. } else { @@ -214,8 +238,8 @@ impl Animation { is_off: false, duration, clamped_duration: duration, - start_time: now, - current_time: now, + start_time: current_time, + current_time, kind, } } diff --git a/src/input/mod.rs b/src/input/mod.rs index 976f51db..eec8fbe4 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -3024,6 +3024,7 @@ pub fn mods_with_finger_scroll_binds(comp_mod: CompositorMod, binds: &Binds) -> #[cfg(test)] mod tests { use super::*; + use crate::animation::Clock; #[test] fn bindings_suppress_keys() { @@ -3042,7 +3043,7 @@ mod tests { let comp_mod = CompositorMod::Super; let mut suppressed_keys = HashSet::new(); - let screenshot_ui = ScreenshotUi::new(Default::default()); + let screenshot_ui = ScreenshotUi::new(Clock::default(), Default::default()); let disable_power_key_handling = false; // The key_code we pick is arbitrary, the only thing diff --git a/src/layout/closing_window.rs b/src/layout/closing_window.rs index 8bbf0258..744c099e 100644 --- a/src/layout/closing_window.rs +++ b/src/layout/closing_window.rs @@ -142,8 +142,7 @@ impl ClosingWindow { match &mut self.anim_state { AnimationState::Waiting { blocker, anim } => { if blocker.state() != BlockerState::Pending { - let mut anim = anim.restarted(0., 1., 0.); - anim.set_current_time(current_time); + let anim = anim.restarted(current_time, 0., 1., 0.); self.anim_state = AnimationState::Animating(anim); } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index e5cfea67..f4e59af3 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -52,6 +52,7 @@ use workspace::WorkspaceId; pub use self::monitor::MonitorRenderElement; use self::monitor::{Monitor, WorkspaceSwitch}; use self::workspace::{compute_working_area, Column, ColumnWidth, InsertHint, OutputId, Workspace}; +use crate::animation::Clock; use crate::layout::workspace::InsertPosition; use crate::niri_render_elements; use crate::render_helpers::renderer::NiriRenderer; @@ -216,6 +217,8 @@ pub struct Layout<W: LayoutElement> { last_active_workspace_id: HashMap<String, WorkspaceId>, /// Ongoing interactive move. interactive_move: Option<InteractiveMoveState<W>>, + /// Clock for driving animations. + clock: Clock, /// Configurable properties of the layout. options: Rc<Options>, } @@ -433,27 +436,30 @@ impl Options { } impl<W: LayoutElement> Layout<W> { - pub fn new(config: &Config) -> Self { - Self::with_options_and_workspaces(config, Options::from_config(config)) + pub fn new(clock: Clock, config: &Config) -> Self { + Self::with_options_and_workspaces(clock, config, Options::from_config(config)) } - pub fn with_options(options: Options) -> Self { + pub fn with_options(clock: Clock, options: Options) -> Self { Self { monitor_set: MonitorSet::NoOutputs { workspaces: vec![] }, is_active: true, last_active_workspace_id: HashMap::new(), interactive_move: None, + clock, options: Rc::new(options), } } - fn with_options_and_workspaces(config: &Config, options: Options) -> Self { + fn with_options_and_workspaces(clock: Clock, config: &Config, options: Options) -> Self { let opts = Rc::new(options); let workspaces = config .workspaces .iter() - .map(|ws| Workspace::new_with_config_no_outputs(Some(ws.clone()), opts.clone())) + .map(|ws| { + Workspace::new_with_config_no_outputs(Some(ws.clone()), clock.clone(), opts.clone()) + }) .collect(); Self { @@ -461,6 +467,7 @@ impl<W: LayoutElement> Layout<W> { is_active: true, last_active_workspace_id: HashMap::new(), interactive_move: None, + clock, options: opts, } } @@ -522,13 +529,18 @@ impl<W: LayoutElement> Layout<W> { } // Make sure there's always an empty workspace. - workspaces.push(Workspace::new(output.clone(), self.options.clone())); + workspaces.push(Workspace::new( + output.clone(), + self.clock.clone(), + self.options.clone(), + )); for ws in &mut workspaces { ws.set_output(Some(output.clone())); } - let mut monitor = Monitor::new(output, workspaces, self.options.clone()); + let mut monitor = + Monitor::new(output, workspaces, self.clock.clone(), self.options.clone()); monitor.active_workspace_idx = active_workspace_idx.unwrap_or(0); monitors.push(monitor); @@ -540,7 +552,11 @@ impl<W: LayoutElement> Layout<W> { } MonitorSet::NoOutputs { mut workspaces } => { // We know there are no empty workspaces there, so add one. - workspaces.push(Workspace::new(output.clone(), self.options.clone())); + workspaces.push(Workspace::new( + output.clone(), + self.clock.clone(), + self.options.clone(), + )); let ws_id_to_activate = self.last_active_workspace_id.remove(&output.name()); let mut active_workspace_idx = 0; @@ -553,7 +569,8 @@ impl<W: LayoutElement> Layout<W> { } } - let mut monitor = Monitor::new(output, workspaces, self.options.clone()); + let mut monitor = + Monitor::new(output, workspaces, self.clock.clone(), self.options.clone()); monitor.active_workspace_idx = active_workspace_idx; MonitorSet::Normal { @@ -785,7 +802,10 @@ impl<W: LayoutElement> Layout<W> { let ws = if let Some(ws) = workspaces.get_mut(0) { ws } else { - workspaces.push(Workspace::new_no_outputs(self.options.clone())); + workspaces.push(Workspace::new_no_outputs( + self.clock.clone(), + self.options.clone(), + )); &mut workspaces[0] }; ws.add_window(None, window, true, width, is_full_width); @@ -1992,6 +2012,8 @@ impl<W: LayoutElement> Layout<W> { move_win_id = Some(window_id.clone()); } InteractiveMoveState::Moving(move_) => { + assert_eq!(self.clock, move_.tile.clock); + let scale = move_.output.current_scale().fractional_scale(); let options = Options::clone(&self.options).adjusted_for_scale(scale); assert_eq!( @@ -2026,6 +2048,8 @@ impl<W: LayoutElement> Layout<W> { "with no outputs there cannot be empty unnamed workspaces" ); + assert_eq!(self.clock, workspace.clock); + assert_eq!( workspace.base_options, self.options, "workspace base options must be synchronized with layout" @@ -2070,6 +2094,7 @@ impl<W: LayoutElement> Layout<W> { ); assert!(monitor.active_workspace_idx < monitor.workspaces.len()); + assert_eq!(self.clock, monitor.clock); assert_eq!( monitor.options, self.options, "monitor options must be synchronized with layout" @@ -2135,6 +2160,8 @@ impl<W: LayoutElement> Layout<W> { // exists. for workspace in &monitor.workspaces { + assert_eq!(self.clock, workspace.clock); + assert_eq!( workspace.base_options, self.options, "workspace options must be synchronized with layout" @@ -2326,6 +2353,7 @@ impl<W: LayoutElement> Layout<W> { return; } + let clock = self.clock.clone(); let options = self.options.clone(); match &mut self.monitor_set { @@ -2349,6 +2377,7 @@ impl<W: LayoutElement> Layout<W> { let ws = Workspace::new_with_config( mon.output.clone(), Some(ws_config.clone()), + clock, options, ); mon.workspaces.insert(0, ws); @@ -2357,7 +2386,8 @@ impl<W: LayoutElement> Layout<W> { mon.clean_up_workspaces(); } MonitorSet::NoOutputs { workspaces } => { - let ws = Workspace::new_with_config_no_outputs(Some(ws_config.clone()), options); + let ws = + Workspace::new_with_config_no_outputs(Some(ws_config.clone()), clock, options); workspaces.insert(0, ws); } } @@ -3161,7 +3191,10 @@ impl<W: LayoutElement> Layout<W> { let ws = if let Some(ws) = workspaces.get_mut(0) { ws } else { - workspaces.push(Workspace::new_no_outputs(self.options.clone())); + workspaces.push(Workspace::new_no_outputs( + self.clock.clone(), + self.options.clone(), + )); &mut workspaces[0] }; @@ -3620,7 +3653,7 @@ mod tests { impl<W: LayoutElement> Default for Layout<W> { fn default() -> Self { - Self::with_options(Default::default()) + Self::with_options(Clock::with_override(Duration::ZERO), Default::default()) } } @@ -3854,6 +3887,16 @@ mod tests { prop_oneof![Just(1.), Just(1.5), Just(2.),] } + fn arbitrary_msec_delta() -> impl Strategy<Value = i32> { + prop_oneof![ + 1 => Just(-1000), + 2 => Just(-10), + 1 => Just(0), + 2 => Just(10), + 6 => Just(1000), + ] + } + #[derive(Debug, Clone, Copy, Arbitrary)] enum Op { AddOutput(#[proptest(strategy = "1..=5usize")] usize), @@ -3997,6 +4040,10 @@ mod tests { Refresh { is_active: bool, }, + AdvanceAnimations { + #[proptest(strategy = "arbitrary_msec_delta()")] + msec_delta: i32, + }, MoveWorkspaceToOutput(#[proptest(strategy = "1..=5u8")] u8), ViewOffsetGestureBegin { #[proptest(strategy = "1..=5usize")] @@ -4505,6 +4552,16 @@ mod tests { Op::Refresh { is_active } => { layout.refresh(is_active); } + Op::AdvanceAnimations { msec_delta } => { + let mut now = layout.clock.now(); + 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); + } Op::MoveWorkspaceToOutput(id) => { let name = format!("output{id}"); let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else { @@ -4617,7 +4674,7 @@ mod tests { #[track_caller] fn check_ops_with_options(options: Options, ops: &[Op]) { - let mut layout = Layout::with_options(options); + let mut layout = Layout::with_options(Clock::with_override(Duration::ZERO), options); for op in ops { op.apply(&mut layout); @@ -5440,7 +5497,7 @@ mod tests { config.layout.border.off = false; config.layout.border.width = FloatOrInt(2.); - let mut layout = Layout::new(&config); + let mut layout = Layout::new(Clock::default(), &config); Op::AddWindow { id: 1, @@ -5460,7 +5517,7 @@ mod tests { let mut config = Config::default(); config.layout.preset_window_heights = vec![PresetSize::Fixed(1), PresetSize::Fixed(2)]; - let mut layout = Layout::new(&config); + let mut layout = Layout::new(Clock::default(), &config); let ops = [ Op::AddOutput(1), @@ -5753,6 +5810,37 @@ mod tests { } #[test] + fn interactive_move_onto_last_workspace() { + let ops = [ + Op::AddOutput(1), + Op::AddWindow { + id: 0, + bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)), + min_max_size: Default::default(), + }, + Op::InteractiveMoveBegin { + window: 0, + output_idx: 1, + px: 0., + py: 0., + }, + Op::InteractiveMoveUpdate { + window: 0, + dx: 1000., + dy: 0., + output_idx: 1, + px: 0., + py: 0., + }, + Op::FocusWorkspaceDown, + Op::AdvanceAnimations { msec_delta: 1000 }, + Op::InteractiveMoveEnd { window: 0 }, + ]; + + check_ops(&ops); + } + + #[test] fn output_active_workspace_is_preserved() { let ops = [ Op::AddOutput(1), diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index aecad889..aad921bb 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -15,7 +15,7 @@ use super::workspace::{ WorkspaceRenderElement, }; use super::{LayoutElement, Options}; -use crate::animation::Animation; +use crate::animation::{Animation, Clock}; use crate::input::swipe_tracker::SwipeTracker; use crate::render_helpers::renderer::NiriRenderer; use crate::render_helpers::RenderTarget; @@ -45,6 +45,8 @@ pub struct Monitor<W: LayoutElement> { pub(super) previous_workspace_id: Option<WorkspaceId>, /// In-progress switch between workspaces. pub(super) workspace_switch: Option<WorkspaceSwitch>, + /// Clock for driving animations. + pub(super) clock: Clock, /// Configurable properties of the layout. pub(super) options: Rc<Options>, } @@ -94,7 +96,12 @@ impl WorkspaceSwitch { } impl<W: LayoutElement> Monitor<W> { - pub fn new(output: Output, workspaces: Vec<Workspace<W>>, options: Rc<Options>) -> Self { + pub fn new( + output: Output, + workspaces: Vec<Workspace<W>>, + clock: Clock, + options: Rc<Options>, + ) -> Self { Self { output_name: output.name(), output, @@ -102,6 +109,7 @@ impl<W: LayoutElement> Monitor<W> { active_workspace_idx: 0, previous_workspace_id: None, workspace_switch: None, + clock, options, } } @@ -151,7 +159,11 @@ impl<W: LayoutElement> Monitor<W> { } pub fn add_workspace_bottom(&mut self) { - let ws = Workspace::new(self.output.clone(), self.options.clone()); + let ws = Workspace::new( + self.output.clone(), + self.clock.clone(), + self.options.clone(), + ); self.workspaces.push(ws); } @@ -172,6 +184,7 @@ impl<W: LayoutElement> Monitor<W> { self.active_workspace_idx = idx; self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new( + self.clock.now(), current_idx, idx as f64, 0., @@ -1099,6 +1112,7 @@ impl<W: LayoutElement> Monitor<W> { self.active_workspace_idx = new_idx; self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new( + self.clock.now(), gesture.current_idx, new_idx as f64, velocity, diff --git a/src/layout/tile.rs b/src/layout/tile.rs index ddb9bd8c..dd3d50af 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -13,7 +13,7 @@ use super::{ LayoutElement, LayoutElementRenderElement, LayoutElementRenderSnapshot, Options, RESIZE_ANIMATION_THRESHOLD, }; -use crate::animation::Animation; +use crate::animation::{Animation, Clock}; use crate::niri_render_elements; use crate::render_helpers::border::BorderRenderElement; use crate::render_helpers::clipped_surface::{ClippedSurfaceRenderElement, RoundedCornerDamage}; @@ -76,6 +76,9 @@ pub struct Tile<W: LayoutElement> { /// Scale of the output the tile is on (and rounds its sizes to). scale: f64, + /// Clock for driving animations. + pub(super) clock: Clock, + /// Configurable properties of the layout. pub(super) options: Rc<Options>, } @@ -110,7 +113,7 @@ struct MoveAnimation { } impl<W: LayoutElement> Tile<W> { - pub fn new(window: W, scale: f64, options: Rc<Options>) -> Self { + pub fn new(window: W, scale: f64, clock: Clock, options: Rc<Options>) -> Self { let rules = window.rules(); let border_config = rules.border.resolve_against(options.border); let focus_ring_config = rules.focus_ring.resolve_against(options.focus_ring.into()); @@ -130,6 +133,7 @@ impl<W: LayoutElement> Tile<W> { unmap_snapshot: None, rounded_corner_damage: Default::default(), scale, + clock, options, } } @@ -180,7 +184,13 @@ impl<W: LayoutElement> Tile<W> { let change = self.window.size().to_f64().to_point() - size_from.to_point(); let change = f64::max(change.x.abs(), change.y.abs()); if change > RESIZE_ANIMATION_THRESHOLD { - let anim = Animation::new(0., 1., 0., self.options.animations.window_resize.anim); + let anim = Animation::new( + self.clock.now(), + 0., + 1., + 0., + self.options.animations.window_resize.anim, + ); self.resize_animation = Some(ResizeAnimation { anim, size_from, @@ -316,6 +326,7 @@ impl<W: LayoutElement> Tile<W> { pub fn start_open_animation(&mut self) { self.open_animation = Some(OpenAnimation::new(Animation::new( + self.clock.now(), 0., 1., 0., @@ -342,8 +353,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(1., 0., 0.)) - .unwrap_or_else(|| Animation::new(1., 0., 0., config)); + .map(|anim| anim.restarted(self.clock.now(), 1., 0., 0.)) + .unwrap_or_else(|| Animation::new(self.clock.now(), 1., 0., 0., config)); self.move_x_animation = Some(MoveAnimation { anim, @@ -361,8 +372,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(1., 0., 0.)) - .unwrap_or_else(|| Animation::new(1., 0., 0., config)); + .map(|anim| anim.restarted(self.clock.now(), 1., 0., 0.)) + .unwrap_or_else(|| Animation::new(self.clock.now(), 1., 0., 0., config)); self.move_y_animation = Some(MoveAnimation { anim, diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index b26dfcc1..6432ca8b 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -19,7 +19,7 @@ use super::closing_window::{ClosingWindow, ClosingWindowRenderElement}; use super::insert_hint_element::{InsertHintElement, InsertHintRenderElement}; use super::tile::{Tile, TileRenderElement, TileRenderSnapshot}; use super::{ConfigureIntent, InteractiveResizeData, LayoutElement, Options, RemovedTile}; -use crate::animation::Animation; +use crate::animation::{Animation, Clock}; use crate::input::swipe_tracker::SwipeTracker; use crate::niri_render_elements; use crate::render_helpers::renderer::NiriRenderer; @@ -113,6 +113,9 @@ pub struct Workspace<W: LayoutElement> { /// Insert hint element for rendering. insert_hint_element: InsertHintElement, + /// Clock for driving animations. + pub(super) clock: Clock, + /// Configurable properties of the layout as received from the parent monitor. pub(super) base_options: Rc<Options>, @@ -293,6 +296,9 @@ pub struct Column<W: LayoutElement> { /// Scale of the output the column is on (and rounds its sizes to). scale: f64, + /// Clock for driving animations. + clock: Clock, + /// Configurable properties of the layout. options: Rc<Options>, } @@ -403,13 +409,14 @@ impl TileData { } impl<W: LayoutElement> Workspace<W> { - pub fn new(output: Output, options: Rc<Options>) -> Self { - Self::new_with_config(output, None, options) + pub fn new(output: Output, clock: Clock, options: Rc<Options>) -> Self { + Self::new_with_config(output, None, clock, options) } pub fn new_with_config( output: Output, config: Option<WorkspaceConfig>, + clock: Clock, base_options: Rc<Options>, ) -> Self { let original_output = config @@ -442,6 +449,7 @@ impl<W: LayoutElement> Workspace<W> { closing_windows: vec![], insert_hint: None, insert_hint_element: InsertHintElement::new(options.insert_hint), + clock, base_options, options, name: config.map(|c| c.name.0), @@ -451,6 +459,7 @@ impl<W: LayoutElement> Workspace<W> { pub fn new_with_config_no_outputs( config: Option<WorkspaceConfig>, + clock: Clock, base_options: Rc<Options>, ) -> Self { let original_output = OutputId( @@ -482,6 +491,7 @@ impl<W: LayoutElement> Workspace<W> { closing_windows: vec![], insert_hint: None, insert_hint_element: InsertHintElement::new(options.insert_hint), + clock, base_options, options, name: config.map(|c| c.name.0), @@ -489,8 +499,8 @@ impl<W: LayoutElement> Workspace<W> { } } - pub fn new_no_outputs(options: Rc<Options>) -> Self { - Self::new_with_config_no_outputs(None, options) + pub fn new_no_outputs(clock: Clock, options: Rc<Options>) -> Self { + Self::new_with_config_no_outputs(None, clock, options) } pub fn id(&self) -> WorkspaceId { @@ -941,6 +951,7 @@ impl<W: LayoutElement> Workspace<W> { // FIXME: also compute and use current velocity. self.view_offset_adj = Some(ViewOffsetAdjustment::Animation(Animation::new( + self.clock.now(), self.view_offset, new_view_offset, 0., @@ -1100,7 +1111,12 @@ impl<W: LayoutElement> Workspace<W> { width: ColumnWidth, is_full_width: bool, ) { - let tile = Tile::new(window, self.scale.fractional_scale(), self.options.clone()); + let tile = Tile::new( + window, + self.scale.fractional_scale(), + self.clock.clone(), + self.options.clone(), + ); self.add_tile(col_idx, tile, activate, width, is_full_width, None); } @@ -1118,7 +1134,6 @@ impl<W: LayoutElement> Workspace<W> { self.view_size, self.working_area, self.scale.fractional_scale(), - self.options.clone(), width, is_full_width, true, @@ -1682,7 +1697,13 @@ impl<W: LayoutElement> Workspace<W> { ) { let output_scale = Scale::from(self.scale.fractional_scale()); - let anim = Animation::new(0., 1., 0., self.options.animations.window_close.anim); + let anim = Animation::new( + self.clock.now(), + 0., + 1., + 0., + self.options.animations.window_close.anim, + ); let blocker = if self.options.disable_transactions { TransactionBlocker::completed() @@ -1725,6 +1746,7 @@ impl<W: LayoutElement> Workspace<W> { for (column, data) in zip(&self.columns, &self.data) { assert!(Rc::ptr_eq(&self.options, &column.options)); + assert_eq!(self.clock, column.clock); assert_eq!(self.scale.fractional_scale(), column.scale); column.verify_invariants(); @@ -2619,7 +2641,6 @@ impl<W: LayoutElement> Workspace<W> { self.view_size, self.working_area, self.scale.fractional_scale(), - self.options.clone(), removed.width, removed.is_full_width, false, @@ -2969,6 +2990,7 @@ impl<W: LayoutElement> Workspace<W> { let target_view_offset = target_snap.view_pos - new_col_x; self.view_offset_adj = Some(ViewOffsetAdjustment::Animation(Animation::new( + self.clock.now(), current_view_offset + delta, target_view_offset, velocity, @@ -3174,7 +3196,6 @@ impl<W: LayoutElement> Column<W> { view_size: Size<f64, Logical>, working_area: Rectangle<f64, Logical>, scale: f64, - options: Rc<Options>, width: ColumnWidth, is_full_width: bool, animate_resize: bool, @@ -3190,7 +3211,8 @@ impl<W: LayoutElement> Column<W> { view_size, working_area, scale, - options, + clock: tile.clock.clone(), + options: tile.options.clone(), }; let is_pending_fullscreen = tile.window().is_pending_fullscreen(); @@ -3313,6 +3335,7 @@ impl<W: LayoutElement> Column<W> { let current_offset = self.move_animation.as_ref().map_or(0., Animation::value); self.move_animation = Some(Animation::new( + self.clock.now(), from_x_offset + current_offset, 0., 0., @@ -3716,6 +3739,7 @@ impl<W: LayoutElement> Column<W> { let mut total_min_height = 0.; for (tile, data) in zip(&self.tiles, &self.data) { assert!(Rc::ptr_eq(&self.options, &tile.options)); + assert_eq!(self.clock, tile.clock); assert_eq!(self.scale, tile.scale()); assert_eq!(self.is_fullscreen, tile.window().is_pending_fullscreen()); |
