Skip to main content

ferroui_base/rendering/composition/animations/
key_frame_animation.rs

1use super::{CompositionAnimation, CompositionColorSpace, IKeyFrames};
2use crate::animation::easings::{Easing, IEasing};
3use crate::animation::PlaybackDirection;
4use crate::rendering::composition::Compositor;
5use std::cell::{Cell, RefCell};
6use std::ops::Deref;
7use std::rc::Rc;
8use std::time::Duration;
9
10/// A time-based animation with one or more key frames.
11/// These frames are markers, allowing developers to specify values at specific times for the animating property.
12/// KeyFrame animations can be further customized by specifying how the animation interpolates between keyframes.
13///
14/// The classes for the value types (`ScalarKeyFrameAnimation`,
15/// `Vector3KeyFrameAnimation`, ...) embed it and dereference to it; they are
16/// expanded from the key frame animations of the composition schema at the
17/// end of this module.
18pub struct KeyFrameAnimation {
19    base: CompositionAnimation,
20    duration: Cell<Duration>,
21    delay_behavior: Cell<AnimationDelayBehavior>,
22    delay_time: Cell<Duration>,
23    direction: Cell<PlaybackDirection>,
24    iteration_behavior: Cell<AnimationIterationBehavior>,
25    iteration_count: Cell<i32>,
26    stop_behavior: Cell<AnimationStopBehavior>,
27    /// `ColorKeyFrameAnimation.InterpolationColorSpace`: held here, as the
28    /// classes of the value types are expanded from one macro; only the colour
29    /// animation exposes it.
30    interpolation_color_space: Cell<CompositionColorSpace>,
31    /// `private protected abstract IKeyFrames KeyFrames`: the key frames of
32    /// the class for the value type.
33    key_frames: RefCell<Box<dyn IKeyFrames>>,
34}
35
36impl Deref for KeyFrameAnimation {
37    type Target = CompositionAnimation;
38
39    fn deref(&self) -> &CompositionAnimation {
40        &self.base
41    }
42}
43
44impl KeyFrameAnimation {
45    pub(crate) fn new(compositor: &Rc<Compositor>, key_frames: Box<dyn IKeyFrames>) -> Self {
46        Self {
47            base: CompositionAnimation::new(compositor),
48            duration: Cell::new(Duration::from_millis(1)),
49            delay_behavior: Cell::new(AnimationDelayBehavior::default()),
50            delay_time: Cell::new(Duration::ZERO),
51            direction: Cell::new(PlaybackDirection::default()),
52            iteration_behavior: Cell::new(AnimationIterationBehavior::default()),
53            iteration_count: Cell::new(1),
54            stop_behavior: Cell::new(AnimationStopBehavior::default()),
55            interpolation_color_space: Cell::new(CompositionColorSpace::default()),
56            key_frames: RefCell::new(key_frames),
57        }
58    }
59
60    /// The delay behavior of the key frame animation.
61    pub fn delay_behavior(&self) -> AnimationDelayBehavior {
62        self.delay_behavior.get()
63    }
64
65    pub fn set_delay_behavior(&self, value: AnimationDelayBehavior) {
66        self.delay_behavior.set(value)
67    }
68
69    /// Delay before the animation starts after `start_animation` is called.
70    pub fn delay_time(&self) -> Duration {
71        self.delay_time.get()
72    }
73
74    pub fn set_delay_time(&self, value: Duration) {
75        self.delay_time.set(value)
76    }
77
78    /// The direction the animation is playing.
79    /// The Direction property allows you to drive your animation from start to end or end to start or alternate
80    /// between start and end or end to start if animation has an [`iteration_count`](Self::iteration_count) greater than one.
81    /// This gives an easy way for customizing animation definitions.
82    pub fn direction(&self) -> PlaybackDirection {
83        self.direction.get()
84    }
85
86    pub fn set_direction(&self, value: PlaybackDirection) {
87        self.direction.set(value)
88    }
89
90    /// The duration of the animation.
91    /// Minimum allowed value is 1ms and maximum allowed value is 24 days.
92    pub fn duration(&self) -> Duration {
93        self.duration.get()
94    }
95
96    /// Sets the duration. As upstream, the range check is made on the
97    /// current value rather than on the new one.
98    pub fn set_duration(&self, value: Duration) {
99        let duration = self.duration.get();
100        if duration < Duration::from_millis(1) || duration > Duration::from_secs(24 * 60 * 60) {
101            panic!("Minimum allowed value is 1ms and maximum allowed value is 24 days.");
102        }
103        self.duration.set(value)
104    }
105
106    /// The iteration behavior for the key frame animation.
107    pub fn iteration_behavior(&self) -> AnimationIterationBehavior {
108        self.iteration_behavior.get()
109    }
110
111    pub fn set_iteration_behavior(&self, value: AnimationIterationBehavior) {
112        self.iteration_behavior.set(value)
113    }
114
115    /// The number of times to repeat the key frame animation.
116    pub fn iteration_count(&self) -> i32 {
117        self.iteration_count.get()
118    }
119
120    pub fn set_iteration_count(&self, value: i32) {
121        self.iteration_count.set(value)
122    }
123
124    /// Specifies how to set the property value when animation is stopped
125    pub fn stop_behavior(&self) -> AnimationStopBehavior {
126        self.stop_behavior.get()
127    }
128
129    pub fn set_stop_behavior(&self, value: AnimationStopBehavior) {
130        self.stop_behavior.set(value)
131    }
132
133    /// The colour space the key frames are interpolated in; used by the
134    /// colour animation only.
135    pub(crate) fn interpolation_color_space_core(&self) -> CompositionColorSpace {
136        self.interpolation_color_space.get()
137    }
138
139    pub(crate) fn set_interpolation_color_space_core(&self, value: CompositionColorSpace) {
140        self.interpolation_color_space.set(value)
141    }
142
143    /// The number of key frames in the animation (WinUI `KeyFrameCount`).
144    pub fn key_frame_count(&self) -> i32 {
145        self.key_frames.borrow().key_frame_count() as i32
146    }
147
148    /// Inserts an expression keyframe.
149    ///
150    /// `normalized_progress_key` is the time the key frame should occur at,
151    /// expressed as a percentage of the animation Duration. Allowed value
152    /// is from 0.0 to 1.0. `value` is the expression used to calculate the
153    /// value of the key frame. `easing_function` is the easing function to
154    /// use when interpolating between frames; the default easing of the
155    /// compositor when `None`.
156    pub fn insert_expression_key_frame(&self, normalized_progress_key: f32, value: &str, easing_function: Option<Easing>) {
157        let easing = match easing_function {
158            Some(easing) => easing.as_rc().clone(),
159            None => self.compositor().default_easing(),
160        };
161        self.key_frames.borrow_mut().insert_expression_key_frame(normalized_progress_key, value, easing)
162    }
163
164    /// The key frames of the class for value type `T`.
165    pub(crate) fn with_key_frames<T: Copy + Default + 'static, R>(&self, f: impl FnOnce(&mut super::KeyFrames<T>) -> R) -> R {
166        let mut key_frames = self.key_frames.borrow_mut();
167        match key_frames.as_any_mut().downcast_mut::<super::KeyFrames<T>>() {
168            Some(key_frames) => f(key_frames),
169            None => unreachable!("the key frames of a key frame animation are of its value type"),
170        }
171    }
172}
173
174/// Specifies the animation delay behavior.
175#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
176pub enum AnimationDelayBehavior {
177    /// If a DelayTime is specified, it delays starting the animation according to delay time and after delay
178    /// has expired it applies animation to the object property.
179    #[default]
180    SetInitialValueAfterDelay,
181    /// Applies the initial value of the animation (i.e. the value at Keyframe 0) to the object before the delay time
182    /// is elapsed (when there is a DelayTime specified), it then delays starting the animation according to the DelayTime.
183    SetInitialValueBeforeDelay,
184}
185
186/// Specifies if the animation should loop.
187#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
188pub enum AnimationIterationBehavior {
189    /// The animation should loop the specified number of times.
190    #[default]
191    Count,
192    /// The animation should loop forever.
193    Forever,
194}
195
196/// Specifies the behavior of an animation when it stops.
197#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
198pub enum AnimationStopBehavior {
199    /// Leave the animation at its current value.
200    #[default]
201    LeaveCurrentValue,
202    /// Reset the animation to its initial value.
203    SetToInitialValue,
204    /// Set the animation to its final value.
205    SetToFinalValue,
206}
207
208/// Expands the key frame animation classes of the composition schema (see
209/// [`for_each_composition_key_frame_animation`](crate::rendering::composition::generated::for_each_composition_key_frame_animation)):
210/// upstream's generated `{Name}KeyFrameAnimation` classes and their
211/// `Compositor.Create{Name}KeyFrameAnimation` factories.
212macro_rules! key_frame_animation_classes {
213    ($(($name:ident, $create:ident, $ty:ty, $interpolator:ty)),* $(,)?) => {$(
214        #[doc = concat!("A key frame animation of `", stringify!($ty), "` values.")]
215        pub struct $name {
216            base: KeyFrameAnimation,
217        }
218
219        impl Deref for $name {
220            type Target = KeyFrameAnimation;
221
222            fn deref(&self) -> &KeyFrameAnimation {
223                &self.base
224            }
225        }
226
227        impl $name {
228            pub fn new(compositor: &Rc<Compositor>) -> Rc<$name> {
229                Rc::new($name {
230                    base: KeyFrameAnimation::new(compositor, Box::new(super::KeyFrames::<$ty>::new())),
231                })
232            }
233
234            pub fn insert_key_frame_with_easing(
235                &self,
236                normalized_progress_key: f32,
237                value: $ty,
238                easing_function: Rc<dyn IEasing>,
239            ) {
240                self.base.with_key_frames::<$ty, _>(|key_frames| {
241                    key_frames.insert(normalized_progress_key, value, easing_function)
242                })
243            }
244
245            pub fn insert_key_frame(&self, normalized_progress_key: f32, value: $ty) {
246                let easing = self.compositor().default_easing();
247                self.base.with_key_frames::<$ty, _>(|key_frames| {
248                    key_frames.insert(normalized_progress_key, value, easing)
249                })
250            }
251        }
252
253        impl super::ICompositionAnimationBase for $name {
254            fn as_composition_animation(&self) -> Option<&dyn super::ICompositionAnimation> {
255                Some(self)
256            }
257        }
258
259        impl super::ICompositionAnimation for $name {
260            fn target(&self) -> Option<String> {
261                self.base.target()
262            }
263
264            fn composition_animation(&self) -> Option<&super::CompositionAnimation> {
265                Some(&*self.base)
266            }
267
268            fn create_instance(
269                &self,
270                target_object: crate::rendering::composition::server::ServerObjectId,
271                final_value: Option<crate::rendering::composition::expressions::ExpressionVariant>,
272            ) -> super::AnimationInstanceFactory {
273                // The colour animation interpolates in its colour space.
274                let interpolator: &'static (dyn super::IInterpolator<$ty> + Sync) =
275                    super::interpolators::interpolator_for_color_space::<$ty>(
276                        <$interpolator>::instance(),
277                        self.base.interpolation_color_space_core(),
278                    );
279                // Parsed now: an expression that cannot be parsed fails the
280                // start, as in WinUI.
281                let initial_values = match self.initial_value_expressions().parse_all() {
282                    Ok(initial_values) => initial_values,
283                    Err(error) => panic!("{error}"),
284                };
285                let key_frames = self.base.with_key_frames::<$ty, _>(|key_frames| key_frames.snapshot());
286                let parameters = self.create_snapshot_source();
287                let final_value = final_value.map(|value| {
288                    crate::rendering::composition::expressions::ExpressionVariant::create(
289                        value.cast_or_default::<$ty>(),
290                    )
291                });
292                let delay_behavior = self.delay_behavior();
293                let delay_time = self.delay_time();
294                let direction = self.direction();
295                let duration = self.duration();
296                let iteration_behavior = self.iteration_behavior();
297                let iteration_count = self.iteration_count();
298                let stop_behavior = self.stop_behavior();
299                // The animation belongs to the batches that are open now; an
300                // animation that loops forever counts as infinite.
301                let completion = self.compositor().acquire_animation_completion(
302                    iteration_behavior == AnimationIterationBehavior::Forever,
303                );
304                super::AnimationInstanceFactory::new(move || {
305                    let instance = super::KeyFrameAnimationInstance::<$ty>::new(
306                        interpolator,
307                        key_frames,
308                        Rc::new(parameters.build()),
309                        final_value,
310                        target_object,
311                        delay_behavior,
312                        delay_time,
313                        direction,
314                        duration,
315                        iteration_behavior,
316                        iteration_count,
317                        stop_behavior,
318                    );
319                    instance.set_completion(completion);
320                    instance.set_initial_value_expressions(initial_values);
321                    instance
322                })
323            }
324        }
325
326        impl crate::rendering::composition::AsCompositionObject for $name {
327            fn as_composition_object(&self) -> &crate::rendering::composition::CompositionObject {
328                self.base.object()
329            }
330
331            fn composition_type_name(&self) -> &'static str {
332                stringify!($name)
333            }
334        }
335
336        impl Compositor {
337            pub fn $create(&self) -> Rc<$name> {
338                $name::new(&self.this_handle())
339            }
340        }
341    )*};
342}
343
344crate::rendering::composition::generated::for_each_composition_key_frame_animation!(key_frame_animation_classes);
345
346impl ColorKeyFrameAnimation {
347    /// The colour space the key frames are interpolated in (WinUI
348    /// `InterpolationColorSpace`); `Auto` interpolates in RGB.
349    pub fn interpolation_color_space(&self) -> CompositionColorSpace {
350        self.base.interpolation_color_space_core()
351    }
352
353    pub fn set_interpolation_color_space(&self, value: CompositionColorSpace) {
354        self.base.set_interpolation_color_space_core(value)
355    }
356}