ferroui_base/animation/animators/
animator.rs1use crate::animation::{
2 Animatable, Animation, AnimationInstance, AnimatorKeyFrame, Clock, DisposeAnimationInstanceSubject, IAnimator,
3 IClock, KeySpline,
4};
5use crate::data::BindingPriority;
6use crate::reactive::{CompositeDisposable, IDisposable, IObservable};
7use crate::{AnyValue, FerroProperty, PropertyValue, Ref, StyledProperty, Visual};
8use std::any::TypeId;
9use std::cell::{Cell, RefCell};
10use std::rc::Rc;
11
12#[derive(Default)]
14pub struct AnimatorBase {
15 key_frames: RefCell<Vec<Ref<AnimatorKeyFrame>>>,
16 property: Cell<Option<&'static FerroProperty>>,
17}
18
19impl AnimatorBase {
20 pub fn new() -> Self {
21 Self::default()
22 }
23
24 #[inline]
26 pub fn property(&self) -> Option<&'static FerroProperty> {
27 self.property.get()
28 }
29
30 pub fn set_property(&self, value: Option<&'static FerroProperty>) {
31 self.property.set(value)
32 }
33
34 #[inline]
36 pub fn count(&self) -> usize {
37 self.key_frames.borrow().len()
38 }
39
40 pub fn get(&self, index: usize) -> Ref<AnimatorKeyFrame> {
42 self.key_frames.borrow()[index].clone()
43 }
44
45 pub fn to_vec(&self) -> Vec<Ref<AnimatorKeyFrame>> {
47 self.key_frames.borrow().clone()
48 }
49
50 pub(crate) fn push(&self, item: Ref<AnimatorKeyFrame>) {
51 self.key_frames.borrow_mut().push(item)
52 }
53
54 pub fn styled_property<T: PropertyValue>(&self) -> &'static StyledProperty<T> {
57 let Some(property) = self.property.get() else {
58 panic!("Animator has no property specified.");
59 };
60 match property.as_styled::<T>() {
61 Some(property) => property,
62 None => panic!(
63 "Unable to animate property '{}' of type {} as {}.",
64 property,
65 property.property_type_name(),
66 std::any::type_name::<T>()
67 ),
68 }
69 }
70}
71
72pub trait Animator: Sized + 'static {
79 type Value: PropertyValue + Default;
82
83 fn base(&self) -> &AnimatorBase;
85
86 fn interpolate(&self, progress: f64, old_value: &Self::Value, new_value: &Self::Value) -> Self::Value;
88
89 #[inline]
92 fn typed_value(&self, untyped: &dyn AnyValue) -> Option<Self::Value> {
93 untyped.downcast_ref::<Self::Value>().cloned()
94 }
95
96 fn validate(&self, _item: &Ref<AnimatorKeyFrame>) {}
98
99 fn bind_animation(&self, control: &Animatable, instance: Rc<dyn IObservable<Self::Value>>) -> Rc<dyn IDisposable> {
102 control.bind(self.base().styled_property::<Self::Value>(), instance, BindingPriority::Animation)
103 }
104
105 fn apply(
107 this: &Rc<Self>,
108 animation: &Animation,
109 control: &Animatable,
110 clock: Option<Rc<dyn IClock>>,
111 match_: Rc<dyn IObservable<bool>>,
112 on_complete: Option<Rc<dyn Fn()>>,
113 should_pause_on_invisible: bool,
114 ) -> Option<Rc<dyn IDisposable>> {
115 let visual_target = control.downcast_ref::<Visual>().map(Visual::to_ref);
116 Some(apply_with_visual(
117 this,
118 animation,
119 control,
120 clock,
121 match_,
122 on_complete,
123 should_pause_on_invisible,
124 visual_target,
125 ))
126 }
127}
128
129#[allow(clippy::too_many_arguments)]
132pub(crate) fn apply_with_visual<A: Animator>(
133 animator: &Rc<A>,
134 animation: &Animation,
135 control: &Animatable,
136 clock: Option<Rc<dyn IClock>>,
137 match_: Rc<dyn IObservable<bool>>,
138 on_complete: Option<Rc<dyn Fn()>>,
139 should_pause_on_invisible: bool,
140 visual_target: Option<Ref<Visual>>,
141) -> Rc<dyn IDisposable> {
142 let subject = Rc::new(DisposeAnimationInstanceSubject::new(
143 animator.clone(),
144 animation.to_ref(),
145 control.to_ref(),
146 clock,
147 on_complete,
148 should_pause_on_invisible,
149 visual_target,
150 ));
151 let subscription = match_.subscribe(subject.clone());
152 let subject: Rc<dyn IDisposable> = subject;
153 Rc::new(CompositeDisposable::from_disposables([subscription, subject]))
154}
155
156pub(crate) fn run<A: Animator>(
159 animator: &Rc<A>,
160 animation: &Ref<Animation>,
161 control: &Ref<Animatable>,
162 clock: Option<Rc<dyn IClock>>,
163 on_complete: Option<Rc<dyn Fn()>>,
164 should_pause_on_invisible: bool,
165 visual_target: Option<&Ref<Visual>>,
166) -> Rc<dyn IDisposable> {
167 let base_clock = clock.or_else(|| control.clock()).unwrap_or_else(Clock::global_clock);
168 let instance = AnimationInstance::new(
169 animation.clone(),
170 control,
171 animator.clone(),
172 base_clock,
173 on_complete,
174 should_pause_on_invisible,
175 visual_target,
176 );
177 animator.bind_animation(control, instance)
178}
179
180struct KeyFrameInfo<T> {
181 time: f64,
182 value: T,
183 key_spline: Option<Ref<KeySpline>>,
184}
185
186impl<T: Clone> KeyFrameInfo<T> {
187 fn from_key_frame<A: Animator<Value = T>>(animator: &A, source: &AnimatorKeyFrame, neutral_value: &T) -> Self {
188 Self {
189 time: source.cue().cue_value(),
190 value: get_typed_value(animator, source, neutral_value),
191 key_spline: source.key_spline().cloned(),
192 }
193 }
194}
195
196#[inline]
197fn get_typed_value<A: Animator>(animator: &A, frame: &AnimatorKeyFrame, neutral_value: &A::Value) -> A::Value {
198 frame
199 .with_value(|value| value.and_then(|value| animator.typed_value(value)))
200 .unwrap_or_else(|| neutral_value.clone())
201}
202
203pub(crate) fn interpolation_handler<A: Animator>(animator: &A, animation_time: f64, neutral_value: &A::Value) -> A::Value {
207 let (from, to) = {
208 let key_frames = animator.base().key_frames.borrow();
209 if key_frames.is_empty() {
210 return neutral_value.clone();
211 }
212 get_key_frames(animator, &key_frames, animation_time, neutral_value)
213 };
214
215 let mut progress = (animation_time - from.time) / (to.time - from.time);
216
217 if let Some(key_spline) = &to.key_spline {
218 progress = key_spline.get_spline_progress(progress);
219 }
220
221 animator.interpolate(progress, &from.value, &to.value)
222}
223
224fn get_key_frames<A: Animator>(
225 animator: &A,
226 key_frames: &[Ref<AnimatorKeyFrame>],
227 time: f64,
228 neutral_value: &A::Value,
229) -> (KeyFrameInfo<A::Value>, KeyFrameInfo<A::Value>) {
230 debug_assert!(!key_frames.is_empty());
231 let count = key_frames.len();
232
233 let first_frame = &key_frames[0];
235 let first_time = first_frame.cue().cue_value();
236 if time <= first_time && first_time > 0.0 {
237 let before_value = if first_frame.fill_before() {
238 get_typed_value(animator, first_frame, neutral_value)
239 } else {
240 neutral_value.clone()
241 };
242 return (
243 KeyFrameInfo { time: 0.0, value: before_value, key_spline: first_frame.key_spline().cloned() },
244 KeyFrameInfo::from_key_frame(animator, first_frame, neutral_value),
245 );
246 }
247
248 for i in 1..count {
250 let frame = &key_frames[i];
251 if time <= frame.cue().cue_value() {
252 return (
253 KeyFrameInfo::from_key_frame(animator, &key_frames[i - 1], neutral_value),
254 KeyFrameInfo::from_key_frame(animator, frame, neutral_value),
255 );
256 }
257 }
258
259 let last_frame = &key_frames[count - 1];
261 if last_frame.cue().cue_value() >= 1.0 {
262 if count == 1 {
263 let before_value = if last_frame.fill_before() {
264 get_typed_value(animator, last_frame, neutral_value)
265 } else {
266 neutral_value.clone()
267 };
268 return (
269 KeyFrameInfo { time: 0.0, value: before_value, key_spline: last_frame.key_spline().cloned() },
270 KeyFrameInfo::from_key_frame(animator, last_frame, neutral_value),
271 );
272 }
273
274 return (
275 KeyFrameInfo::from_key_frame(animator, &key_frames[count - 2], neutral_value),
276 KeyFrameInfo::from_key_frame(animator, last_frame, neutral_value),
277 );
278 }
279
280 let after_value = if last_frame.fill_after() {
282 get_typed_value(animator, last_frame, neutral_value)
283 } else {
284 neutral_value.clone()
285 };
286 (
287 KeyFrameInfo::from_key_frame(animator, last_frame, neutral_value),
288 KeyFrameInfo { time: 1.0, value: after_value, key_spline: last_frame.key_spline().cloned() },
289 )
290}
291
292impl<A: Animator> IAnimator for A {
293 fn property(&self) -> Option<&'static FerroProperty> {
294 self.base().property()
295 }
296
297 fn set_property(&self, value: Option<&'static FerroProperty>) {
298 self.base().set_property(value)
299 }
300
301 fn count(&self) -> usize {
302 self.base().count()
303 }
304
305 fn get(&self, index: usize) -> Ref<AnimatorKeyFrame> {
306 self.base().get(index)
307 }
308
309 fn add(&self, item: Ref<AnimatorKeyFrame>) {
310 self.validate(&item);
311 self.base().push(item)
312 }
313
314 fn animator_type(&self) -> TypeId {
315 TypeId::of::<A>()
316 }
317
318 fn apply(
319 self: Rc<Self>,
320 animation: &Animation,
321 control: &Animatable,
322 clock: Option<Rc<dyn IClock>>,
323 match_: Rc<dyn IObservable<bool>>,
324 on_complete: Option<Rc<dyn Fn()>>,
325 should_pause_on_invisible: bool,
326 ) -> Option<Rc<dyn IDisposable>> {
327 <A as Animator>::apply(&self, animation, control, clock, match_, on_complete, should_pause_on_invisible)
328 }
329}