mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
config: Extract animations
This commit is contained in:
@@ -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
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user