diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2025-08-27 09:57:56 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2025-08-27 10:43:57 +0300 |
| commit | da5e33775cbb7a4e64189360b4eb00eab6dbb1b3 (patch) | |
| tree | d01aff0c078f8f0ec8795daba28183d6808222f5 /niri-config/src/lib.rs | |
| parent | 1e3ab8a8808db07742825678ca38ed992bdf6767 (diff) | |
| download | niri-da5e33775cbb7a4e64189360b4eb00eab6dbb1b3.tar.gz niri-da5e33775cbb7a4e64189360b4eb00eab6dbb1b3.tar.bz2 niri-da5e33775cbb7a4e64189360b4eb00eab6dbb1b3.zip | |
config: Extract animations
Diffstat (limited to 'niri-config/src/lib.rs')
| -rw-r--r-- | niri-config/src/lib.rs | 668 |
1 files changed, 4 insertions, 664 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index f0b89a4a..f5aa3e1a 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -12,7 +12,6 @@ use std::time::Duration; use bitflags::bitflags; use knuffel::errors::DecodeError; -use knuffel::Decode as _; use miette::{miette, Context, IntoDiagnostic}; use niri_ipc::{ ColumnDisplay, ConfiguredMode, LayoutSwitchTarget, PositionChange, SizeChange, Transform, @@ -27,10 +26,14 @@ use smithay::reexports::input; pub const DEFAULT_BACKGROUND_COLOR: Color = Color::from_array_unpremul([0.25, 0.25, 0.25, 1.]); pub const DEFAULT_BACKDROP_COLOR: Color = Color::from_array_unpremul([0.15, 0.15, 0.15, 1.]); +pub mod animations; pub mod layer_rule; pub mod utils; pub mod window_rule; +pub use crate::animations::{ + Animation, AnimationCurve, AnimationKind, Animations, EasingParams, SpringParams, +}; pub use crate::layer_rule::LayerRule; pub use crate::window_rule::{FloatingPosition, RelativeTo, WindowRule}; @@ -1089,261 +1092,6 @@ pub struct Clipboard { pub disable_primary: bool, } -#[derive(knuffel::Decode, Debug, Clone, PartialEq)] -pub struct Animations { - #[knuffel(child)] - pub off: bool, - #[knuffel(child, unwrap(argument), default = FloatOrInt(1.))] - pub slowdown: FloatOrInt<0, { i32::MAX }>, - #[knuffel(child, default)] - pub workspace_switch: WorkspaceSwitchAnim, - #[knuffel(child, default)] - pub window_open: WindowOpenAnim, - #[knuffel(child, default)] - pub window_close: WindowCloseAnim, - #[knuffel(child, default)] - pub horizontal_view_movement: HorizontalViewMovementAnim, - #[knuffel(child, default)] - pub window_movement: WindowMovementAnim, - #[knuffel(child, default)] - pub window_resize: WindowResizeAnim, - #[knuffel(child, default)] - pub config_notification_open_close: ConfigNotificationOpenCloseAnim, - #[knuffel(child, default)] - pub exit_confirmation_open_close: ExitConfirmationOpenCloseAnim, - #[knuffel(child, default)] - pub screenshot_ui_open: ScreenshotUiOpenAnim, - #[knuffel(child, default)] - pub overview_open_close: OverviewOpenCloseAnim, -} - -impl Default for Animations { - fn default() -> Self { - Self { - off: false, - slowdown: FloatOrInt(1.), - 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(), - exit_confirmation_open_close: Default::default(), - screenshot_ui_open: Default::default(), - overview_open_close: Default::default(), - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct WorkspaceSwitchAnim(pub Animation); - -impl Default for WorkspaceSwitchAnim { - fn default() -> Self { - Self(Animation { - off: false, - kind: AnimationKind::Spring(SpringParams { - damping_ratio: 1., - stiffness: 1000, - epsilon: 0.0001, - }), - }) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct WindowOpenAnim { - pub anim: Animation, - pub custom_shader: Option<String>, -} - -impl Default for WindowOpenAnim { - fn default() -> Self { - Self { - anim: Animation { - off: false, - kind: AnimationKind::Easing(EasingParams { - duration_ms: 150, - curve: AnimationCurve::EaseOutExpo, - }), - }, - custom_shader: None, - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct WindowCloseAnim { - pub anim: Animation, - pub custom_shader: Option<String>, -} - -impl Default for WindowCloseAnim { - fn default() -> Self { - Self { - anim: Animation { - off: false, - kind: AnimationKind::Easing(EasingParams { - duration_ms: 150, - curve: AnimationCurve::EaseOutQuad, - }), - }, - custom_shader: None, - } - } -} - -#[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, - }), - }) - } -} - -#[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, - }), - }) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct WindowResizeAnim { - pub anim: Animation, - pub custom_shader: Option<String>, -} - -impl Default for WindowResizeAnim { - fn default() -> Self { - Self { - anim: Animation { - off: false, - kind: AnimationKind::Spring(SpringParams { - damping_ratio: 1., - stiffness: 800, - epsilon: 0.0001, - }), - }, - custom_shader: None, - } - } -} - -#[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, - }), - }) - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct ExitConfirmationOpenCloseAnim(pub Animation); - -impl Default for ExitConfirmationOpenCloseAnim { - fn default() -> Self { - Self(Animation { - off: false, - kind: AnimationKind::Spring(SpringParams { - damping_ratio: 0.6, - stiffness: 500, - epsilon: 0.01, - }), - }) - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct ScreenshotUiOpenAnim(pub Animation); - -impl Default for ScreenshotUiOpenAnim { - fn default() -> Self { - Self(Animation { - off: false, - kind: AnimationKind::Easing(EasingParams { - duration_ms: 200, - curve: AnimationCurve::EaseOutQuad, - }), - }) - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct OverviewOpenCloseAnim(pub Animation); - -impl Default for OverviewOpenCloseAnim { - 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), -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct EasingParams { - pub duration_ms: u32, - pub curve: AnimationCurve, -} - -#[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq)] -pub enum AnimationCurve { - Linear, - EaseOutQuad, - EaseOutCubic, - EaseOutExpo, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct SpringParams { - pub damping_ratio: f64, - pub stiffness: u32, - pub epsilon: f64, -} - #[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)] pub struct Gestures { #[knuffel(child, default)] @@ -3061,51 +2809,6 @@ fn parse_arg_node<S: knuffel::traits::ErrorSpan, T: knuffel::traits::DecodeScala Ok(value) } -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, |_, _| { - Ok(false) - })?)) - } -} - -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, |_, _| { - Ok(false) - })?)) - } -} - -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, |_, _| { - Ok(false) - })?)) - } -} - impl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceName { fn type_check( type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>, @@ -3157,369 +2860,6 @@ impl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceName { } } -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().anim; - let mut custom_shader = None; - let anim = Animation::decode_node(node, ctx, default, |child, ctx| { - if &**child.node_name == "custom-shader" { - custom_shader = parse_arg_node("custom-shader", child, ctx)?; - Ok(true) - } else { - Ok(false) - } - })?; - - Ok(Self { - anim, - custom_shader, - }) - } -} - -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().anim; - let mut custom_shader = None; - let anim = Animation::decode_node(node, ctx, default, |child, ctx| { - if &**child.node_name == "custom-shader" { - custom_shader = parse_arg_node("custom-shader", child, ctx)?; - Ok(true) - } else { - Ok(false) - } - })?; - - Ok(Self { - anim, - custom_shader, - }) - } -} - -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().anim; - let mut custom_shader = None; - let anim = Animation::decode_node(node, ctx, default, |child, ctx| { - if &**child.node_name == "custom-shader" { - custom_shader = parse_arg_node("custom-shader", child, ctx)?; - Ok(true) - } else { - Ok(false) - } - })?; - - Ok(Self { - anim, - custom_shader, - }) - } -} - -impl<S> knuffel::Decode<S> for ConfigNotificationOpenCloseAnim -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, |_, _| { - Ok(false) - })?)) - } -} - -impl<S> knuffel::Decode<S> for ExitConfirmationOpenCloseAnim -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, |_, _| { - Ok(false) - })?)) - } -} - -impl<S> knuffel::Decode<S> for ScreenshotUiOpenAnim -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, |_, _| { - Ok(false) - })?)) - } -} - -impl<S> knuffel::Decode<S> for OverviewOpenCloseAnim -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, |_, _| { - Ok(false) - })?)) - } -} - -impl Animation { - pub fn new_off() -> Self { - Self { - off: true, - kind: AnimationKind::Easing(EasingParams { - duration_ms: 0, - curve: AnimationCurve::Linear, - }), - } - } - - fn decode_node<S: knuffel::traits::ErrorSpan>( - node: &knuffel::ast::SpannedNode<S>, - ctx: &mut knuffel::decode::Context<S>, - default: Self, - mut process_children: impl FnMut( - &knuffel::ast::SpannedNode<S>, - &mut knuffel::decode::Context<S>, - ) -> Result<bool, DecodeError<S>>, - ) -> 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 = OptionalEasingParams::default(); - let mut spring_params = None; - - for child in node.children() { - match &**child.node_name { - "off" => { - knuffel::decode::check_flag_node(child, ctx); - if off { - ctx.emit_error(DecodeError::unexpected( - &child.node_name, - "node", - "duplicate node `off`, single node expected", - )); - } else { - off = true; - } - } - "spring" => { - if easing_params != OptionalEasingParams::default() { - ctx.emit_error(DecodeError::unexpected( - child, - "node", - "cannot set both spring and easing parameters at once", - )); - } - if spring_params.is_some() { - ctx.emit_error(DecodeError::unexpected( - &child.node_name, - "node", - "duplicate node `spring`, single node expected", - )); - } - - spring_params = Some(SpringParams::decode_node(child, ctx)?); - } - "duration-ms" => { - if spring_params.is_some() { - ctx.emit_error(DecodeError::unexpected( - child, - "node", - "cannot set both spring and easing parameters at once", - )); - } - if easing_params.duration_ms.is_some() { - ctx.emit_error(DecodeError::unexpected( - &child.node_name, - "node", - "duplicate node `duration-ms`, single node expected", - )); - } - - easing_params.duration_ms = Some(parse_arg_node("duration-ms", child, ctx)?); - } - "curve" => { - if spring_params.is_some() { - ctx.emit_error(DecodeError::unexpected( - child, - "node", - "cannot set both spring and easing parameters at once", - )); - } - if easing_params.curve.is_some() { - ctx.emit_error(DecodeError::unexpected( - &child.node_name, - "node", - "duplicate node `curve`, single node expected", - )); - } - - easing_params.curve = Some(parse_arg_node("curve", child, ctx)?); - } - name_str => { - if !process_children(child, ctx)? { - ctx.emit_error(DecodeError::unexpected( - child, - "node", - format!("unexpected node `{}`", name_str.escape_default()), - )); - } - } - } - } - - 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 { - // 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 }) - } -} - -impl<S> knuffel::Decode<S> for SpringParams -where - S: knuffel::traits::ErrorSpan, -{ - fn decode_node( - node: &knuffel::ast::SpannedNode<S>, - ctx: &mut knuffel::decode::Context<S>, - ) -> Result<Self, DecodeError<S>> { - if let Some(type_name) = &node.type_name { - ctx.emit_error(DecodeError::unexpected( - type_name, - "type name", - "no type name expected for this node", - )); - } - if let Some(val) = node.arguments.first() { - ctx.emit_error(DecodeError::unexpected( - &val.literal, - "argument", - "unexpected argument", - )); - } - for child in node.children() { - ctx.emit_error(DecodeError::unexpected( - child, - "node", - format!("unexpected node `{}`", child.node_name.escape_default()), - )); - } - - let mut damping_ratio = None; - let mut stiffness = None; - let mut epsilon = None; - for (name, val) in &node.properties { - match &***name { - "damping-ratio" => { - damping_ratio = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?); - } - "stiffness" => { - stiffness = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?); - } - "epsilon" => { - epsilon = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?); - } - name_str => { - ctx.emit_error(DecodeError::unexpected( - name, - "property", - format!("unexpected property `{}`", name_str.escape_default()), - )); - } - } - } - let damping_ratio = damping_ratio - .ok_or_else(|| DecodeError::missing(node, "property `damping-ratio` is required"))?; - let stiffness = stiffness - .ok_or_else(|| DecodeError::missing(node, "property `stiffness` is required"))?; - let epsilon = - epsilon.ok_or_else(|| DecodeError::missing(node, "property `epsilon` is required"))?; - - if !(0.1..=10.).contains(&damping_ratio) { - ctx.emit_error(DecodeError::conversion( - node, - "damping-ratio must be between 0.1 and 10.0", - )); - } - if stiffness < 1 { - ctx.emit_error(DecodeError::conversion(node, "stiffness must be >= 1")); - } - if !(0.00001..=0.1).contains(&epsilon) { - ctx.emit_error(DecodeError::conversion( - node, - "epsilon must be between 0.00001 and 0.1", - )); - } - - Ok(SpringParams { - damping_ratio, - stiffness, - epsilon, - }) - } -} - impl<S> knuffel::Decode<S> for CornerRadius where S: knuffel::traits::ErrorSpan, |
