1use crate::animation::cancellation::register_local;
2use crate::animation::easings::Easing;
3use crate::animation::key_frame::KeyFrameTimingMode;
4use crate::animation::{
5 Animatable, AnimationError, AnimationTask, AnimatorFactory, AnimatorKeyFrame, Cue, FillMode, IAnimator, IClock,
6 IterationCount, KeyFrames, PlaybackBehavior, PlaybackDirection, TimeSpan,
7};
8use crate::data::BindingMode;
9use crate::reactive::{CompositeDisposable, IDisposable, IObservable, Observable};
10use crate::animation::i_page_transition::continuation_context;
11use crate::threading::CancellationToken;
12use crate::{
13 ferro_class, ferro_impl_classes, ferro_property, instantiate, DirectProperty, DirectPropertyMetadata, FerroObject,
14 FerroObjectImpl, FerroProperty, Ref, Visual,
15};
16use std::any::TypeId;
17use std::cell::{Cell, RefCell};
18use std::rc::Rc;
19
20#[repr(C)]
22pub struct Animation {
23 base: FerroObject,
24 duration: Cell<TimeSpan>,
25 iteration_count: Cell<IterationCount>,
26 playback_direction: Cell<PlaybackDirection>,
27 playback_behavior: Cell<PlaybackBehavior>,
28 fill_mode: Cell<FillMode>,
29 easing: RefCell<Easing>,
30 delay: Cell<TimeSpan>,
31 delay_between_iterations: Cell<TimeSpan>,
32 speed_ratio: Cell<f64>,
33 children: KeyFrames,
34}
35
36ferro_class!(Animation: FerroObject);
37crate::ferro_class_info!(Animation { new: Animation::new, interfaces: [Rc<dyn crate::animation::IAnimation>] });
38ferro_impl_classes!(Animation: FerroObjectImpl);
39
40macro_rules! direct_cell_property {
41 ($(#[$meta:meta])* $accessor:ident, $name:literal, $ty:ty, $getter:ident, $setter:ident, $field:ident, $unset:expr) => {
42 ferro_property!(for Animation;
43 $(#[$meta])*
44 pub fn $accessor() -> DirectProperty<Animation, $ty> {
45 FerroProperty::register_direct::<Animation, _>($name, |o| o.$getter(), Some(|o, v| o.$setter(v)), $unset)
46 }
47 );
48
49 pub fn $getter(&self) -> $ty {
50 self.$field.get()
51 }
52
53 pub fn $setter(&self, value: $ty) {
54 self.set_and_raise_cell(Self::$accessor(), &self.$field, value);
55 }
56 };
57}
58
59crate::ferro_properties! {
60 impl Animation, also [
61 Animation::duration_property,
62 Animation::iteration_count_property,
63 Animation::playback_direction_property,
64 Animation::playback_behavior_property,
65 Animation::fill_mode_property,
66 Animation::delay_property,
67 Animation::delay_between_iterations_property,
68 Animation::easing_property,
69 Animation::speed_ratio_property,
70 ] {}
71}
72
73impl Animation {
74 direct_cell_property!(
75 duration_property, "Duration", TimeSpan, duration, set_duration, duration, TimeSpan::ZERO
77 );
78
79 direct_cell_property!(
80 iteration_count_property, "IterationCount", IterationCount, iteration_count, set_iteration_count,
82 iteration_count, IterationCount::new(1)
83 );
84
85 direct_cell_property!(
86 playback_direction_property, "PlaybackDirection", PlaybackDirection, playback_direction,
88 set_playback_direction, playback_direction, PlaybackDirection::Normal
89 );
90
91 direct_cell_property!(
92 playback_behavior_property, "PlaybackBehavior", PlaybackBehavior, playback_behavior,
95 set_playback_behavior, playback_behavior, PlaybackBehavior::Auto
96 );
97
98 direct_cell_property!(
99 fill_mode_property, "FillMode", FillMode, fill_mode, set_fill_mode, fill_mode, FillMode::None
101 );
102
103 direct_cell_property!(
104 delay_property, "Delay", TimeSpan, delay, set_delay, delay, TimeSpan::ZERO
106 );
107
108 direct_cell_property!(
109 delay_between_iterations_property, "DelayBetweenIterations", TimeSpan, delay_between_iterations,
111 set_delay_between_iterations, delay_between_iterations, TimeSpan::ZERO
112 );
113
114 ferro_property!(for Animation;
115 pub fn easing_property() -> DirectProperty<Animation, Easing> {
117 FerroProperty::register_direct::<Animation, _>(
118 "Easing",
119 |o| o.easing(),
120 Some(|o, v| o.set_easing(v)),
121 Easing::default(),
122 )
123 }
124 );
125
126 ferro_property!(for Animation;
127 pub fn speed_ratio_property() -> DirectProperty<Animation, f64> {
129 FerroProperty::register_direct_with::<Animation, _>(
130 "SpeedRatio",
131 |o| o.speed_ratio(),
132 Some(|o, v| o.set_speed_ratio(v)),
133 DirectPropertyMetadata::new(Some(1.0)).with_default_binding_mode(BindingMode::TwoWay),
134 )
135 }
136 );
137
138 pub fn construct() -> Self {
140 Self {
141 base: FerroObject::construct(),
142 duration: Cell::new(TimeSpan::ZERO),
143 iteration_count: Cell::new(IterationCount::new(1)),
144 playback_direction: Cell::new(PlaybackDirection::Normal),
145 playback_behavior: Cell::new(PlaybackBehavior::Auto),
146 fill_mode: Cell::new(FillMode::None),
147 easing: RefCell::new(Easing::default()),
148 delay: Cell::new(TimeSpan::ZERO),
149 delay_between_iterations: Cell::new(TimeSpan::ZERO),
150 speed_ratio: Cell::new(1.0),
151 children: KeyFrames::new(),
152 }
153 }
154
155 pub fn new() -> Ref<Self> {
156 instantiate(Self::construct())
157 }
158
159 pub fn easing(&self) -> Easing {
161 self.easing.borrow().clone()
162 }
163
164 pub fn set_easing(&self, value: impl Into<Easing>) {
165 self.set_and_raise(Self::easing_property(), &self.easing, value.into());
166 }
167
168 pub fn speed_ratio(&self) -> f64 {
170 self.speed_ratio.get()
171 }
172
173 pub fn set_speed_ratio(&self, value: f64) {
174 self.set_and_raise_cell(Self::speed_ratio_property(), &self.speed_ratio, value);
175 }
176
177 pub fn children(&self) -> KeyFrames {
179 self.children.clone()
180 }
181
182 #[allow(clippy::type_complexity)]
183 fn interpret_keyframes(&self, control: &Animatable) -> (Vec<Rc<dyn IAnimator>>, Vec<Rc<dyn IDisposable>>, bool) {
184 let mut handler_list: Vec<((TypeId, &'static FerroProperty), AnimatorFactory)> = Vec::new();
185 let mut animator_key_frames: Vec<Ref<AnimatorKeyFrame>> = Vec::new();
186 let mut subscriptions: Vec<Rc<dyn IDisposable>> = Vec::new();
187 let mut animates_visibility = false;
188
189 for keyframe in self.children.snapshot().iter() {
190 for setter in keyframe.setters().snapshot().iter() {
191 let Some(property) = setter.property() else {
192 panic!("No Setter property assigned.");
193 };
194
195 if property == Visual::is_visible_property().as_property() {
196 animates_visibility = true;
197 }
198
199 let handler = Animation::get_animator(setter).or_else(|| Animation::get_animator_type(property));
200
201 let Some((type_, factory)) = handler else {
202 panic!(
203 "No animator registered for the property {property}. Add an animator to the Animation.Animators collection that matches this property to animate it."
204 );
205 };
206
207 if !handler_list.iter().any(|((t, p), _)| *t == type_ && *p == property) {
208 handler_list.push(((type_, property), factory.clone()));
209 }
210
211 let mut cue = keyframe.cue();
212
213 if keyframe.timing_mode() == KeyFrameTimingMode::TimeSpan {
214 cue = Cue::new(keyframe.key_time().total_seconds() / self.duration().total_seconds());
215 }
216
217 let new_kf = AnimatorKeyFrame::new(Some(type_), Some(factory), cue, keyframe.key_spline());
218
219 subscriptions.push(new_kf.bind_setter(&**setter, control));
220
221 animator_key_frames.push(new_kf);
222 }
223 }
224
225 animator_key_frames.sort_by(|x, y| x.cue().cue_value().total_cmp(&y.cue().cue_value()));
226
227 let mut new_animator_instances: Vec<Rc<dyn IAnimator>> = Vec::new();
228
229 for ((_, property), factory) in &handler_list {
230 let new_instance = factory();
231 new_instance.set_property(Some(property));
232 new_animator_instances.push(new_instance);
233 }
234
235 let fill_mode = self.fill_mode();
236 for keyframe in animator_key_frames {
237 let animator = new_animator_instances
238 .iter()
239 .find(|a| Some(a.animator_type()) == keyframe.animator_type() && a.property() == keyframe.property())
240 .expect("Sequence contains no matching element");
241
242 if animator.count() == 0 && matches!(fill_mode, FillMode::Backward | FillMode::Both) {
243 keyframe.set_fill_before(true);
244 }
245
246 animator.add(keyframe);
247 }
248
249 if matches!(fill_mode, FillMode::Forward | FillMode::Both) {
250 for new_animator_instance in &new_animator_instances {
251 let count = new_animator_instance.count();
252 if count > 0 {
253 new_animator_instance.get(count - 1).set_fill_after(true);
254 }
255 }
256 }
257
258 (new_animator_instances, subscriptions, animates_visibility)
259 }
260
261 pub fn apply(
267 &self,
268 control: &Animatable,
269 clock: Option<Rc<dyn IClock>>,
270 match_: Rc<dyn IObservable<bool>>,
271 on_complete: Option<Rc<dyn Fn()>>,
272 is_manually_started: bool,
273 ) -> Rc<dyn IDisposable> {
274 let (animators, mut subscriptions, animates_visibility) = self.interpret_keyframes(control);
275
276 let should_pause_on_invisible = match self.playback_behavior.get() {
277 PlaybackBehavior::Auto => !(animates_visibility || is_manually_started),
278 PlaybackBehavior::Always => false,
279 PlaybackBehavior::OnlyIfVisible => true,
280 };
281
282 if animators.len() == 1 {
283 let subscription = animators[0].clone().apply(
284 self,
285 control,
286 clock,
287 match_,
288 on_complete,
289 should_pause_on_invisible,
290 );
291
292 if let Some(subscription) = subscription {
293 subscriptions.push(subscription);
294 }
295 } else {
296 let remaining = Rc::new(Cell::new(animators.len()));
299 let schedule_on_complete = on_complete.map(|on_complete| {
300 let context = continuation_context();
301 Rc::new(move || {
302 let on_complete = on_complete.clone();
303 context.post_local(move || on_complete());
304 })
305 });
306
307 for animator in &animators {
308 let animator_on_complete: Option<Rc<dyn Fn()>> = schedule_on_complete.as_ref().map(|schedule| {
309 let schedule = schedule.clone();
310 let remaining = remaining.clone();
311 let completed = Cell::new(false);
312 let action: Rc<dyn Fn()> = Rc::new(move || {
313 if completed.replace(true) {
314 return;
315 }
316 remaining.set(remaining.get() - 1);
317 if remaining.get() == 0 {
318 schedule();
319 }
320 });
321 action
322 });
323
324 let subscription = animator.clone().apply(
325 self,
326 control,
327 clock.clone(),
328 match_.clone(),
329 animator_on_complete,
330 should_pause_on_invisible,
331 );
332
333 if let Some(subscription) = subscription {
334 subscriptions.push(subscription);
335 }
336 }
337
338 if animators.is_empty() {
339 if let Some(schedule) = &schedule_on_complete {
340 schedule();
341 }
342 }
343 }
344
345 Rc::new(CompositeDisposable::from_disposables(subscriptions))
346 }
347
348 pub fn run_async(&self, control: &Animatable, cancellation_token: CancellationToken) -> AnimationTask {
351 self.run_async_with_clock(control, None, cancellation_token)
352 }
353
354 pub fn run_async_with_clock(
356 &self,
357 control: &Animatable,
358 clock: Option<Rc<dyn IClock>>,
359 cancellation_token: CancellationToken,
360 ) -> AnimationTask {
361 if cancellation_token.is_cancellation_requested() {
362 return AnimationTask::completed();
363 }
364
365 let run = AnimationTask::pending();
366
367 if self.iteration_count() == IterationCount::INFINITE {
368 run.try_set(Err(AnimationError("Looping animations must not use the Run method.".to_string())));
369 }
370
371 let subscriptions: Rc<RefCell<Option<Rc<dyn IDisposable>>>> = Rc::new(RefCell::new(None));
372 let cancellation = Rc::new(RefCell::new(None));
373
374 let finish: Rc<dyn Fn()> = {
375 let run = run.clone();
376 let subscriptions = subscriptions.clone();
377 let cancellation = cancellation.clone();
378 Rc::new(move || {
379 run.try_set_result();
380 let subscriptions = subscriptions.borrow_mut().take();
381 if let Some(subscriptions) = subscriptions {
382 subscriptions.dispose();
383 }
384 let cancellation: Option<crate::animation::cancellation::LocalCancellationRegistration> =
385 cancellation.borrow_mut().take();
386 if let Some(cancellation) = cancellation {
387 cancellation.dispose();
388 }
389 })
390 };
391
392 let applied = self.apply(control, clock, Observable::return_(true), Some(finish.clone()), true);
393 *subscriptions.borrow_mut() = Some(applied);
396
397 let registration = register_local(&cancellation_token, move || finish());
398 if !run.is_completed() || !cancellation_token.is_cancellation_requested() {
399 *cancellation.borrow_mut() = Some(registration);
400 }
401
402 run
403 }
404}