use-ui-motion 0.1.0

Motion semantics primitives for RustUse UI
Documentation
#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]

/// Motion duration in milliseconds.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct MotionDuration(u32);

impl MotionDuration {
    pub fn from_millis(value: u32) -> Self {
        Self(value)
    }

    pub fn millis(self) -> u32 {
        self.0
    }

    pub fn is_instant(self) -> bool {
        self.0 == 0
    }
}

/// Semantic motion curve.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum MotionCurve {
    Linear,
    Ease,
    EaseIn,
    EaseOut,
    EaseInOut,
}

/// User motion preference.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum MotionPreference {
    NoPreference,
    Reduced,
}

impl MotionPreference {
    pub fn allows_motion(self) -> bool {
        matches!(self, Self::NoPreference)
    }
}

/// Easing metadata.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Easing {
    curve: MotionCurve,
    duration: MotionDuration,
}

impl Easing {
    pub fn new(curve: MotionCurve, duration: MotionDuration) -> Self {
        Self { curve, duration }
    }

    pub fn curve(self) -> MotionCurve {
        self.curve
    }

    pub fn duration(self) -> MotionDuration {
        self.duration
    }
}

/// Transition intent.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum TransitionKind {
    Enter,
    Exit,
    Move,
    Resize,
    Fade,
    Scale,
}

/// Runtime-independent animation state labels.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum AnimationState {
    Idle,
    Pending,
    Running,
    Paused,
    Finished,
    Cancelled,
}

impl AnimationState {
    pub fn is_terminal(self) -> bool {
        matches!(self, Self::Finished | Self::Cancelled)
    }
}

#[cfg(test)]
mod tests {
    use super::{AnimationState, Easing, MotionCurve, MotionDuration, MotionPreference};

    #[test]
    fn checks_motion_helpers() {
        let duration = MotionDuration::from_millis(150);
        let easing = Easing::new(MotionCurve::EaseOut, duration);

        assert_eq!(duration.millis(), 150);
        assert!(!duration.is_instant());
        assert!(MotionDuration::from_millis(0).is_instant());
        assert_eq!(easing.curve(), MotionCurve::EaseOut);
        assert_eq!(easing.duration(), duration);
    }

    #[test]
    fn checks_motion_preference_and_state() {
        assert!(MotionPreference::NoPreference.allows_motion());
        assert!(!MotionPreference::Reduced.allows_motion());
        assert!(AnimationState::Finished.is_terminal());
        assert!(!AnimationState::Running.is_terminal());
    }
}