config: Extract animations

This commit is contained in:
Ivan Molodetskikh
2025-08-27 09:57:56 +03:00
parent 1e3ab8a880
commit da5e33775c
2 changed files with 671 additions and 664 deletions
+667
View File
@@ -0,0 +1,667 @@
use knuffel::errors::DecodeError;
use knuffel::Decode as _;
use crate::{expect_only_children, parse_arg_node, FloatOrInt};
#[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 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(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,
}),
})
}
}
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::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,
})
}
}
+4 -664
View File
@@ -12,7 +12,6 @@ use std::time::Duration;
use bitflags::bitflags; use bitflags::bitflags;
use knuffel::errors::DecodeError; use knuffel::errors::DecodeError;
use knuffel::Decode as _;
use miette::{miette, Context, IntoDiagnostic}; use miette::{miette, Context, IntoDiagnostic};
use niri_ipc::{ use niri_ipc::{
ColumnDisplay, ConfiguredMode, LayoutSwitchTarget, PositionChange, SizeChange, Transform, 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_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 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 layer_rule;
pub mod utils; pub mod utils;
pub mod window_rule; pub mod window_rule;
pub use crate::animations::{
Animation, AnimationCurve, AnimationKind, Animations, EasingParams, SpringParams,
};
pub use crate::layer_rule::LayerRule; pub use crate::layer_rule::LayerRule;
pub use crate::window_rule::{FloatingPosition, RelativeTo, WindowRule}; pub use crate::window_rule::{FloatingPosition, RelativeTo, WindowRule};
@@ -1089,261 +1092,6 @@ pub struct Clipboard {
pub disable_primary: bool, 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)] #[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
pub struct Gestures { pub struct Gestures {
#[knuffel(child, default)] #[knuffel(child, default)]
@@ -3061,51 +2809,6 @@ fn parse_arg_node<S: knuffel::traits::ErrorSpan, T: knuffel::traits::DecodeScala
Ok(value) 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 { impl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceName {
fn type_check( fn type_check(
type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>, 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 impl<S> knuffel::Decode<S> for CornerRadius
where where
S: knuffel::traits::ErrorSpan, S: knuffel::traits::ErrorSpan,