diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-04-17 14:06:32 +0400 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-04-17 14:06:32 +0400 |
| commit | 73e9ef5fe20825ed12f1bb05ba10e7eb69bc7662 (patch) | |
| tree | cdb4d408a50494fd86695ce0807602869c047f93 | |
| parent | c40d4f3268318ac295f21bfce38b9809c5e48f0d (diff) | |
| download | niri-73e9ef5fe20825ed12f1bb05ba10e7eb69bc7662.tar.gz niri-73e9ef5fe20825ed12f1bb05ba10e7eb69bc7662.tar.bz2 niri-73e9ef5fe20825ed12f1bb05ba10e7eb69bc7662.zip | |
Resolve animation defaults during parsing
| -rw-r--r-- | niri-config/src/lib.rs | 310 | ||||
| -rw-r--r-- | src/animation/mod.rs | 52 | ||||
| -rw-r--r-- | src/layout/monitor.rs | 6 | ||||
| -rw-r--r-- | src/layout/tile.rs | 20 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 36 | ||||
| -rw-r--r-- | src/ui/config_error_notification.rs | 8 |
6 files changed, 242 insertions, 190 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index d0d517e1..d8e8bfd5 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -9,6 +9,7 @@ use std::time::Duration; use bitflags::bitflags; use knuffel::errors::DecodeError; +use knuffel::Decode as _; use miette::{miette, Context, IntoDiagnostic, NarratableReportHandler}; use niri_ipc::{LayoutSwitchTarget, SizeChange, Transform}; use regex::Regex; @@ -482,20 +483,20 @@ pub struct Animations { pub off: bool, #[knuffel(child, unwrap(argument), default = 1.)] pub slowdown: f64, - #[knuffel(child, default = Animation::default_workspace_switch())] - pub workspace_switch: Animation, - #[knuffel(child, default = Animation::default_horizontal_view_movement())] - pub horizontal_view_movement: Animation, - #[knuffel(child, default = Animation::default_window_movement())] - pub window_movement: Animation, - #[knuffel(child, default = Animation::default_window_open())] - pub window_open: Animation, - #[knuffel(child, default = Animation::default_window_close())] - pub window_close: Animation, - #[knuffel(child, default = Animation::default_window_resize())] - pub window_resize: Animation, - #[knuffel(child, default = Animation::default_config_notification_open_close())] - pub config_notification_open_close: Animation, + #[knuffel(child, default)] + pub workspace_switch: WorkspaceSwitchAnim, + #[knuffel(child, default)] + pub horizontal_view_movement: HorizontalViewMovementAnim, + #[knuffel(child, default)] + pub window_movement: WindowMovementAnim, + #[knuffel(child, default)] + pub window_open: WindowOpenAnim, + #[knuffel(child, default)] + pub window_close: WindowCloseAnim, + #[knuffel(child, default)] + pub window_resize: WindowResizeAnim, + #[knuffel(child, default)] + pub config_notification_open_close: ConfigNotificationOpenCloseAnim, } impl Default for Animations { @@ -503,115 +504,134 @@ impl Default for Animations { Self { off: false, slowdown: 1., - workspace_switch: Animation::default_workspace_switch(), - horizontal_view_movement: Animation::default_horizontal_view_movement(), - window_movement: Animation::default_window_movement(), - window_open: Animation::default_window_open(), - window_close: Animation::default_window_close(), - window_resize: Animation::default_window_resize(), - config_notification_open_close: Animation::default_config_notification_open_close(), + workspace_switch: Default::default(), + horizontal_view_movement: Default::default(), + window_movement: Default::default(), + window_open: Default::default(), + window_close: Default::default(), + window_resize: Default::default(), + config_notification_open_close: Default::default(), } } } #[derive(Debug, Clone, Copy, PartialEq)] -pub struct Animation { - pub off: bool, - pub kind: AnimationKind, -} +pub struct WorkspaceSwitchAnim(pub Animation); -impl Animation { - pub const fn unfilled() -> Self { - Self { - off: false, - kind: AnimationKind::Easing(EasingParams::unfilled()), - } - } - - pub const fn default() -> Self { - Self { - off: false, - kind: AnimationKind::Easing(EasingParams::default()), - } - } - - pub const fn default_workspace_switch() -> Self { - Self { +impl Default for WorkspaceSwitchAnim { + fn default() -> Self { + Self(Animation { off: false, kind: AnimationKind::Spring(SpringParams { damping_ratio: 1., stiffness: 1000, epsilon: 0.0001, }), - } + }) } +} - pub const fn default_horizontal_view_movement() -> Self { - Self { +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct HorizontalViewMovementAnim(pub Animation); + +impl Default for HorizontalViewMovementAnim { + fn default() -> Self { + Self(Animation { off: false, kind: AnimationKind::Spring(SpringParams { damping_ratio: 1., stiffness: 800, epsilon: 0.0001, }), - } + }) } +} - pub const fn default_window_movement() -> Self { - Self { +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct WindowMovementAnim(pub Animation); + +impl Default for WindowMovementAnim { + fn default() -> Self { + Self(Animation { off: false, kind: AnimationKind::Spring(SpringParams { damping_ratio: 1., stiffness: 800, epsilon: 0.0001, }), - } + }) } +} - pub const fn default_config_notification_open_close() -> Self { - Self { +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ConfigNotificationOpenCloseAnim(pub Animation); + +impl Default for ConfigNotificationOpenCloseAnim { + fn default() -> Self { + Self(Animation { off: false, kind: AnimationKind::Spring(SpringParams { damping_ratio: 0.6, stiffness: 1000, epsilon: 0.001, }), - } + }) } +} - pub const fn default_window_open() -> Self { - Self { +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct WindowOpenAnim(pub Animation); + +impl Default for WindowOpenAnim { + fn default() -> Self { + Self(Animation { off: false, kind: AnimationKind::Easing(EasingParams { - duration_ms: Some(150), - curve: Some(AnimationCurve::EaseOutExpo), + duration_ms: 150, + curve: AnimationCurve::EaseOutExpo, }), - } + }) } +} - pub const fn default_window_close() -> Self { - Self { +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct WindowCloseAnim(pub Animation); + +impl Default for WindowCloseAnim { + fn default() -> Self { + Self(Animation { off: false, kind: AnimationKind::Easing(EasingParams { - duration_ms: Some(150), - curve: Some(AnimationCurve::EaseOutQuad), + duration_ms: 150, + curve: AnimationCurve::EaseOutQuad, }), - } + }) } +} - pub const fn default_window_resize() -> Self { - Self { +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct WindowResizeAnim(pub Animation); + +impl Default for WindowResizeAnim { + fn default() -> Self { + Self(Animation { off: false, kind: AnimationKind::Spring(SpringParams { damping_ratio: 1., stiffness: 800, epsilon: 0.0001, }), - } + }) } } #[derive(Debug, Clone, Copy, PartialEq)] +pub struct Animation { + pub off: bool, + pub kind: AnimationKind, +} + +#[derive(Debug, Clone, Copy, PartialEq)] pub enum AnimationKind { Easing(EasingParams), Spring(SpringParams), @@ -619,24 +639,8 @@ pub enum AnimationKind { #[derive(Debug, Clone, Copy, PartialEq)] pub struct EasingParams { - pub duration_ms: Option<u32>, - pub curve: Option<AnimationCurve>, -} - -impl EasingParams { - pub const fn unfilled() -> Self { - Self { - duration_ms: None, - curve: None, - } - } - - pub const fn default() -> Self { - Self { - duration_ms: Some(250), - curve: Some(AnimationCurve::EaseOutCubic), - } - } + pub duration_ms: u32, + pub curve: AnimationCurve, } #[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq)] @@ -1177,7 +1181,85 @@ fn parse_arg_node<S: knuffel::traits::ErrorSpan, T: knuffel::traits::DecodeScala Ok(value) } -impl<S> knuffel::Decode<S> for Animation +impl<S> knuffel::Decode<S> for WorkspaceSwitchAnim +where + S: knuffel::traits::ErrorSpan, +{ + fn decode_node( + node: &knuffel::ast::SpannedNode<S>, + ctx: &mut knuffel::decode::Context<S>, + ) -> Result<Self, DecodeError<S>> { + let default = Self::default().0; + Ok(Self(Animation::decode_node(node, ctx, default)?)) + } +} + +impl<S> knuffel::Decode<S> for HorizontalViewMovementAnim +where + S: knuffel::traits::ErrorSpan, +{ + fn decode_node( + node: &knuffel::ast::SpannedNode<S>, + ctx: &mut knuffel::decode::Context<S>, + ) -> Result<Self, DecodeError<S>> { + let default = Self::default().0; + Ok(Self(Animation::decode_node(node, ctx, default)?)) + } +} + +impl<S> knuffel::Decode<S> for WindowMovementAnim +where + S: knuffel::traits::ErrorSpan, +{ + fn decode_node( + node: &knuffel::ast::SpannedNode<S>, + ctx: &mut knuffel::decode::Context<S>, + ) -> Result<Self, DecodeError<S>> { + let default = Self::default().0; + Ok(Self(Animation::decode_node(node, ctx, default)?)) + } +} + +impl<S> knuffel::Decode<S> for WindowOpenAnim +where + S: knuffel::traits::ErrorSpan, +{ + fn decode_node( + node: &knuffel::ast::SpannedNode<S>, + ctx: &mut knuffel::decode::Context<S>, + ) -> Result<Self, DecodeError<S>> { + let default = Self::default().0; + Ok(Self(Animation::decode_node(node, ctx, default)?)) + } +} + +impl<S> knuffel::Decode<S> for WindowCloseAnim +where + S: knuffel::traits::ErrorSpan, +{ + fn decode_node( + node: &knuffel::ast::SpannedNode<S>, + ctx: &mut knuffel::decode::Context<S>, + ) -> Result<Self, DecodeError<S>> { + let default = Self::default().0; + Ok(Self(Animation::decode_node(node, ctx, default)?)) + } +} + +impl<S> knuffel::Decode<S> for WindowResizeAnim +where + S: knuffel::traits::ErrorSpan, +{ + fn decode_node( + node: &knuffel::ast::SpannedNode<S>, + ctx: &mut knuffel::decode::Context<S>, + ) -> Result<Self, DecodeError<S>> { + let default = Self::default().0; + Ok(Self(Animation::decode_node(node, ctx, default)?)) + } +} + +impl<S> knuffel::Decode<S> for ConfigNotificationOpenCloseAnim where S: knuffel::traits::ErrorSpan, { @@ -1185,10 +1267,27 @@ where node: &knuffel::ast::SpannedNode<S>, ctx: &mut knuffel::decode::Context<S>, ) -> Result<Self, DecodeError<S>> { + let default = Self::default().0; + Ok(Self(Animation::decode_node(node, ctx, default)?)) + } +} + +impl Animation { + fn decode_node<S: knuffel::traits::ErrorSpan>( + node: &knuffel::ast::SpannedNode<S>, + ctx: &mut knuffel::decode::Context<S>, + default: Self, + ) -> Result<Self, DecodeError<S>> { + #[derive(Default, PartialEq)] + struct OptionalEasingParams { + duration_ms: Option<u32>, + curve: Option<AnimationCurve>, + } + expect_only_children(node, ctx); let mut off = false; - let mut easing_params = EasingParams::unfilled(); + let mut easing_params = OptionalEasingParams::default(); let mut spring_params = None; for child in node.children() { @@ -1206,7 +1305,7 @@ where } } "spring" => { - if easing_params != EasingParams::unfilled() { + if easing_params != OptionalEasingParams::default() { ctx.emit_error(DecodeError::unexpected( child, "node", @@ -1270,9 +1369,28 @@ where } let kind = if let Some(spring_params) = spring_params { + // Configured spring. AnimationKind::Spring(spring_params) + } else if easing_params == OptionalEasingParams::default() { + // Did not configure anything. + default.kind } else { - AnimationKind::Easing(easing_params) + // Configured easing. + let default = if let AnimationKind::Easing(easing) = default.kind { + easing + } else { + // Generic fallback values for when the default animation is spring, but the user + // configured an easing animation. + EasingParams { + duration_ms: 250, + curve: AnimationCurve::EaseOutCubic, + } + }; + + AnimationKind::Easing(EasingParams { + duration_ms: easing_params.duration_ms.unwrap_or(default.duration_ms), + curve: easing_params.curve.unwrap_or(default.curve), + }) }; Ok(Self { off, kind }) @@ -1948,25 +2066,25 @@ mod tests { }, animations: Animations { slowdown: 2., - workspace_switch: Animation { + workspace_switch: WorkspaceSwitchAnim(Animation { off: false, kind: AnimationKind::Spring(SpringParams { damping_ratio: 1., stiffness: 1000, epsilon: 0.0001, }), - }, - horizontal_view_movement: Animation { + }), + horizontal_view_movement: HorizontalViewMovementAnim(Animation { off: false, kind: AnimationKind::Easing(EasingParams { - duration_ms: Some(100), - curve: Some(AnimationCurve::EaseOutExpo), + duration_ms: 100, + curve: AnimationCurve::EaseOutExpo, }), - }, - window_open: Animation { + }), + window_open: WindowOpenAnim(Animation { off: true, - ..Animation::unfilled() - }, + ..WindowOpenAnim::default().0 + }), ..Default::default() }, environment: Environment(vec![ diff --git a/src/animation/mod.rs b/src/animation/mod.rs index 9831f666..e22ff1f1 100644 --- a/src/animation/mod.rs +++ b/src/animation/mod.rs @@ -46,52 +46,27 @@ pub enum Curve { } impl Animation { - pub fn new( - from: f64, - to: f64, - initial_velocity: f64, - config: niri_config::Animation, - default: niri_config::Animation, - ) -> Self { + pub fn new(from: f64, to: f64, initial_velocity: f64, config: niri_config::Animation) -> Self { let mut rv = Self::ease(from, to, initial_velocity, 0, Curve::EaseOutCubic); if config.off { return rv; } - rv.replace_config(config, default); + rv.replace_config(config); rv } - pub fn replace_config( - &mut self, - config: niri_config::Animation, - default: niri_config::Animation, - ) { + pub fn replace_config(&mut self, config: niri_config::Animation) { + if config.off { + self.duration = Duration::ZERO; + self.clamped_duration = Duration::ZERO; + return; + } + let start_time = self.start_time; let current_time = self.current_time; - // Resolve defaults. - let (kind, easing_defaults) = match (config.kind, default.kind) { - // Configured spring. - (configured @ niri_config::AnimationKind::Spring(_), _) => (configured, None), - // Configured nothing, defaults spring. - ( - niri_config::AnimationKind::Easing(easing), - defaults @ niri_config::AnimationKind::Spring(_), - ) if easing == niri_config::EasingParams::unfilled() => (defaults, None), - // Configured easing or nothing, defaults easing. - ( - configured @ niri_config::AnimationKind::Easing(_), - niri_config::AnimationKind::Easing(defaults), - ) => (configured, Some(defaults)), - // Configured easing, defaults spring. - ( - configured @ niri_config::AnimationKind::Easing(_), - niri_config::AnimationKind::Spring(_), - ) => (configured, None), - }; - - match kind { + match config.kind { niri_config::AnimationKind::Spring(p) => { let params = SpringParams::new(p.damping_ratio, f64::from(p.stiffness), p.epsilon); @@ -104,15 +79,12 @@ impl Animation { *self = Self::spring(spring); } niri_config::AnimationKind::Easing(p) => { - let defaults = easing_defaults.unwrap_or(niri_config::EasingParams::default()); - let duration_ms = p.duration_ms.or(defaults.duration_ms).unwrap(); - let curve = Curve::from(p.curve.or(defaults.curve).unwrap()); *self = Self::ease( self.from, self.to, self.initial_velocity, - u64::from(duration_ms), - curve, + u64::from(p.duration_ms), + Curve::from(p.curve), ); } } diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs index 7a19673e..604dcab3 100644 --- a/src/layout/monitor.rs +++ b/src/layout/monitor.rs @@ -127,8 +127,7 @@ impl<W: LayoutElement> Monitor<W> { current_idx, idx as f64, 0., - self.options.animations.workspace_switch, - niri_config::Animation::default_workspace_switch(), + self.options.animations.workspace_switch.0, ))); } @@ -882,8 +881,7 @@ impl<W: LayoutElement> Monitor<W> { gesture.current_idx, new_idx as f64, velocity, - self.options.animations.workspace_switch, - niri_config::Animation::default_workspace_switch(), + self.options.animations.workspace_switch.0, ))); true diff --git a/src/layout/tile.rs b/src/layout/tile.rs index 87c168b0..16779c10 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -150,13 +150,7 @@ impl<W: LayoutElement> Tile<W> { let change = self.window.size().to_point() - size_from.to_point(); let change = max(change.x.abs(), change.y.abs()); if change > RESIZE_ANIMATION_THRESHOLD { - let anim = Animation::new( - 0., - 1., - 0., - self.options.animations.window_resize, - niri_config::Animation::default_window_resize(), - ); + let anim = Animation::new(0., 1., 0., self.options.animations.window_resize.0); self.resize_animation = Some(ResizeAnimation { anim, size_from, @@ -230,8 +224,7 @@ impl<W: LayoutElement> Tile<W> { 0., 1., 0., - self.options.animations.window_open, - niri_config::Animation::default_window_open(), + self.options.animations.window_open.0, )); } @@ -244,23 +237,18 @@ impl<W: LayoutElement> Tile<W> { } pub fn animate_move_from(&mut self, from: Point<i32, Logical>) { - self.animate_move_from_with_config( - from, - self.options.animations.window_movement, - niri_config::Animation::default_window_movement(), - ); + self.animate_move_from_with_config(from, self.options.animations.window_movement.0); } pub fn animate_move_from_with_config( &mut self, from: Point<i32, Logical>, config: niri_config::Animation, - default: niri_config::Animation, ) { let current_offset = self.render_offset(); self.move_animation = Some(MoveAnimation { - anim: Animation::new(1., 0., 0., config, default), + anim: Animation::new(1., 0., 0., config), from: from + current_offset, }); } diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 271f6639..7474d650 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -525,8 +525,7 @@ impl<W: LayoutElement> Workspace<W> { self.view_offset as f64, new_view_offset as f64, 0., - self.options.animations.horizontal_view_movement, - niri_config::Animation::default_horizontal_view_movement(), + self.options.animations.horizontal_view_movement.0, ))); } @@ -857,8 +856,7 @@ impl<W: LayoutElement> Workspace<W> { for tile in &mut column.tiles[window_idx + 1..] { tile.animate_move_from_with_config( Point::from((0, offset_y)), - self.options.animations.window_resize, - niri_config::Animation::default_window_resize(), + self.options.animations.window_resize.0, ); } @@ -1034,16 +1032,14 @@ impl<W: LayoutElement> Workspace<W> { for col in &mut self.columns[col_idx + 1..] { col.animate_move_from_with_config( offset, - self.options.animations.window_resize, - niri_config::Animation::default_window_resize(), + self.options.animations.window_resize.0, ); } } else { for col in &mut self.columns[..=col_idx] { col.animate_move_from_with_config( -offset, - self.options.animations.window_resize, - niri_config::Animation::default_window_resize(), + self.options.animations.window_resize.0, ); } } @@ -1074,10 +1070,7 @@ impl<W: LayoutElement> Workspace<W> { // offset animation if the target was the same; maybe we shouldn't replace in this // case? if started_animation { - anim.replace_config( - self.options.animations.window_resize, - niri_config::Animation::default_window_resize(), - ); + anim.replace_config(self.options.animations.window_resize.0); } } } @@ -1149,13 +1142,7 @@ impl<W: LayoutElement> Workspace<W> { (1., 1.) }; - let anim = Animation::new( - 1., - 0., - 0., - self.options.animations.window_close, - niri_config::Animation::default_window_close(), - ); + let anim = Animation::new(1., 0., 0., self.options.animations.window_close.0); let res = ClosingWindow::new( renderer, @@ -2021,8 +2008,7 @@ impl<W: LayoutElement> Workspace<W> { current_view_offset + delta, target_view_offset as f64, velocity, - self.options.animations.horizontal_view_movement, - niri_config::Animation::default_horizontal_view_movement(), + self.options.animations.horizontal_view_movement.0, ))); // HACK: deal with things like snapping to the right edge of a larger-than-view window. @@ -2188,8 +2174,7 @@ impl<W: LayoutElement> Column<W> { pub fn animate_move_from(&mut self, from_x_offset: i32) { self.animate_move_from_with_config( from_x_offset, - self.options.animations.window_movement, - niri_config::Animation::default_window_movement(), + self.options.animations.window_movement.0, ); } @@ -2197,7 +2182,6 @@ impl<W: LayoutElement> Column<W> { &mut self, from_x_offset: i32, config: niri_config::Animation, - default: niri_config::Animation, ) { let current_offset = self.move_animation.as_ref().map_or(0., Animation::value); @@ -2206,7 +2190,6 @@ impl<W: LayoutElement> Column<W> { 0., 0., config, - default, )); } @@ -2257,8 +2240,7 @@ impl<W: LayoutElement> Column<W> { for tile in &mut self.tiles[tile_idx + 1..] { tile.animate_move_from_with_config( Point::from((0, offset)), - self.options.animations.window_resize, - niri_config::Animation::default_window_resize(), + self.options.animations.window_resize.0, ); } } diff --git a/src/ui/config_error_notification.rs b/src/ui/config_error_notification.rs index d49ab4f0..1331b912 100644 --- a/src/ui/config_error_notification.rs +++ b/src/ui/config_error_notification.rs @@ -59,13 +59,7 @@ impl ConfigErrorNotification { fn animation(&self, from: f64, to: f64) -> Animation { let c = self.config.borrow(); - Animation::new( - from, - to, - 0., - c.animations.config_notification_open_close, - niri_config::Animation::default_config_notification_open_close(), - ) + Animation::new(from, to, 0., c.animations.config_notification_open_close.0) } pub fn show_created(&mut self, created_path: Option<PathBuf>) { |
