Skip to main content

ferroui_base/animation/
key_frame.rs

1use crate::animation::{Cue, IAnimationSetter, KeySpline, TimeSpan};
2use crate::collections::FerroList;
3use crate::{ferro_class, ferro_impl_classes, instantiate, FerroObject, FerroObjectImpl, Ref};
4use std::cell::{Cell, RefCell};
5use std::rc::Rc;
6
7/// How the position of a key frame within an iteration is given.
8#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
9#[repr(i32)]
10pub(crate) enum KeyFrameTimingMode {
11    #[default]
12    Unset = 0,
13    TimeSpan = 1,
14    Cue = 2,
15}
16
17/// Stores data regarding a specific key point and value in an animation.
18#[repr(C)]
19pub struct KeyFrame {
20    base: FerroObject,
21    k_time_span: Cell<TimeSpan>,
22    k_cue: Cell<Cue>,
23    k_key_spline: RefCell<Option<Ref<KeySpline>>>,
24    setters: FerroList<Rc<dyn IAnimationSetter>>,
25    timing_mode: Cell<KeyFrameTimingMode>,
26}
27
28ferro_class!(KeyFrame: FerroObject);
29crate::ferro_class_info!(KeyFrame { new: KeyFrame::new });
30ferro_impl_classes!(KeyFrame: FerroObjectImpl);
31
32impl KeyFrame {
33    /// Creates the class data; see [`FerroObject::construct`].
34    pub fn construct() -> Self {
35        Self {
36            base: FerroObject::construct(),
37            k_time_span: Cell::new(TimeSpan::ZERO),
38            k_cue: Cell::new(Cue::default()),
39            k_key_spline: RefCell::new(None),
40            setters: FerroList::new(),
41            timing_mode: Cell::new(KeyFrameTimingMode::Unset),
42        }
43    }
44
45    pub fn new() -> Ref<Self> {
46        instantiate(Self::construct())
47    }
48
49    /// Creates a key frame at `cue` with the given setters.
50    pub fn with_cue(cue: Cue, setters: impl IntoIterator<Item = Rc<dyn IAnimationSetter>>) -> Ref<Self> {
51        let result = Self::new();
52        result.set_cue(cue);
53        result.setters.add_range(setters);
54        result
55    }
56
57    /// Creates a key frame at `key_time` with the given setters.
58    pub fn with_key_time(key_time: TimeSpan, setters: impl IntoIterator<Item = Rc<dyn IAnimationSetter>>) -> Ref<Self> {
59        let result = Self::new();
60        result.set_key_time(key_time);
61        result.setters.add_range(setters);
62        result
63    }
64
65    /// The setters of the key frame: the values the animated properties
66    /// have at its position.
67    pub fn setters(&self) -> FerroList<Rc<dyn IAnimationSetter>> {
68        self.setters.clone()
69    }
70
71    pub(crate) fn timing_mode(&self) -> KeyFrameTimingMode {
72        self.timing_mode.get()
73    }
74
75    /// The key time of this key frame.
76    pub fn key_time(&self) -> TimeSpan {
77        self.k_time_span.get()
78    }
79
80    /// Sets the key time of this key frame. Panics when the cue has been
81    /// set: only one of them can be.
82    pub fn set_key_time(&self, value: TimeSpan) {
83        if self.timing_mode.get() == KeyFrameTimingMode::Cue {
84            panic!("You can only set either KeyTime or Cue.");
85        }
86        self.timing_mode.set(KeyFrameTimingMode::TimeSpan);
87        self.k_time_span.set(value);
88    }
89
90    /// The cue of this key frame.
91    pub fn cue(&self) -> Cue {
92        self.k_cue.get()
93    }
94
95    /// Sets the cue of this key frame. Panics when the key time has been
96    /// set: only one of them can be.
97    pub fn set_cue(&self, value: Cue) {
98        if self.timing_mode.get() == KeyFrameTimingMode::TimeSpan {
99            panic!("You can only set either KeyTime or Cue.");
100        }
101        self.timing_mode.set(KeyFrameTimingMode::Cue);
102        self.k_cue.set(value);
103    }
104
105    /// The key spline of this key frame.
106    pub fn key_spline(&self) -> Option<Ref<KeySpline>> {
107        self.k_key_spline.borrow().clone()
108    }
109
110    /// Sets the key spline of this key frame. Panics when the spline is not
111    /// valid; it is stored nevertheless.
112    pub fn set_key_spline(&self, value: Option<Ref<KeySpline>>) {
113        let valid = value.as_ref().is_none_or(|value| value.is_valid());
114        *self.k_key_spline.borrow_mut() = value;
115        if !valid {
116            panic!("KeySpline must have X coordinates >= 0.0 and <= 1.0.");
117        }
118    }
119}