Skip to main content

ferroui_base/animation/animators/
effect_animator.rs

1use crate::animation::animators::{apply_with_visual, Animator, AnimatorBase, ColorAnimator, DoubleAnimator};
2use crate::animation::{Animatable, Animation, AnimatorFactory, AnimatorKeyFrame, IAnimator, IClock};
3use crate::logging::{LogArea, LogEventLevel, Logger};
4use crate::media::effects::{
5    IBlurEffect, IDropShadowEffect, IEffect, ImmutableBlurEffect, ImmutableDropShadowDirectionEffect,
6    ImmutableDropShadowEffect,
7};
8use crate::reactive::{IDisposable, IObservable};
9use crate::Visual;
10use std::any::TypeId;
11use std::rc::Rc;
12
13type EffectValue = Option<Rc<dyn IEffect>>;
14
15fn effect_of(frame: &AnimatorKeyFrame) -> EffectValue {
16    frame.with_value(|value| value.and_then(|value| value.downcast_ref::<EffectValue>()).and_then(|effect| effect.clone()))
17}
18
19/// Animator that handles all animations on properties with an effect as
20/// their type: redirects them to the animator for the kind of effects in
21/// the key frames.
22#[derive(Default)]
23pub struct EffectAnimator {
24    base: AnimatorBase,
25}
26
27impl EffectAnimator {
28    pub fn new() -> Self {
29        Self::default()
30    }
31
32    fn try_create_animator<TAnimator: Animator + Default>(
33        &self,
34        is_kind: impl Fn(&dyn IEffect) -> bool,
35    ) -> Option<Rc<dyn IAnimator>> {
36        let mut created_animator: Option<Rc<TAnimator>> = None;
37        let factory: AnimatorFactory = Rc::new(|| Rc::new(TAnimator::default()));
38
39        for key_frame in self.base.to_vec() {
40            if !effect_of(&key_frame).is_some_and(|effect| is_kind(&*effect)) {
41                return None;
42            }
43
44            let animator = created_animator.get_or_insert_with(|| {
45                let animator = Rc::new(TAnimator::default());
46                animator.base().set_property(self.base.property());
47                animator
48            });
49            let copy = AnimatorKeyFrame::new(
50                Some(TypeId::of::<TAnimator>()),
51                Some(factory.clone()),
52                key_frame.cue(),
53                key_frame.key_spline().cloned(),
54            );
55            copy.set_value(key_frame.value());
56            copy.set_fill_before(key_frame.fill_before());
57            copy.set_fill_after(key_frame.fill_after());
58            IAnimator::add(&**animator, copy);
59        }
60
61        created_animator.map(|animator| animator as Rc<dyn IAnimator>)
62    }
63
64    /// The effect animator is part of the default animator registrations;
65    /// there is nothing to do to make it available.
66    pub fn ensure_registered() {}
67}
68
69impl Animator for EffectAnimator {
70    type Value = EffectValue;
71
72    fn base(&self) -> &AnimatorBase {
73        &self.base
74    }
75
76    fn apply(
77        this: &Rc<Self>,
78        animation: &Animation,
79        control: &Animatable,
80        clock: Option<Rc<dyn IClock>>,
81        match_: Rc<dyn IObservable<bool>>,
82        on_complete: Option<Rc<dyn Fn()>>,
83        should_pause_on_invisible: bool,
84    ) -> Option<Rc<dyn IDisposable>> {
85        let animator = this
86            .try_create_animator::<BlurEffectAnimator>(|effect| effect.as_blur_effect().is_some())
87            .or_else(|| this.try_create_animator::<DropShadowEffectAnimator>(|effect| effect.as_drop_shadow_effect().is_some()));
88        if let Some(animator) = animator {
89            return animator.apply(animation, control, clock, match_, on_complete, should_pause_on_invisible);
90        }
91
92        if let Some(logger) = Logger::try_get(LogEventLevel::Error, LogArea::ANIMATIONS) {
93            logger.log(None, "The animation's keyframe value types set is not supported.");
94        }
95
96        let visual_target = control.downcast_ref::<Visual>().map(Visual::to_ref);
97        Some(apply_with_visual(
98            this,
99            animation,
100            control,
101            clock,
102            match_,
103            on_complete,
104            should_pause_on_invisible,
105            visual_target,
106        ))
107    }
108
109    /// Fallback implementation of effect animation.
110    fn interpolate(&self, progress: f64, old_value: &EffectValue, new_value: &EffectValue) -> EffectValue {
111        if progress >= 0.5 {
112            new_value.clone()
113        } else {
114            old_value.clone()
115        }
116    }
117}
118
119/// Animator that interpolates blur effects. Values that are not blur
120/// effects are not interpolated.
121#[derive(Default)]
122pub struct BlurEffectAnimator {
123    base: AnimatorBase,
124}
125
126impl BlurEffectAnimator {
127    pub fn new() -> Self {
128        Self::default()
129    }
130
131    /// Interpolates between two blur effects using the specified progress.
132    pub fn interpolate_blur(progress: f64, old_value: &dyn IBlurEffect, new_value: &dyn IBlurEffect) -> ImmutableBlurEffect {
133        ImmutableBlurEffect::new(DoubleAnimator::interpolate_core(progress, &old_value.radius(), &new_value.radius()))
134    }
135
136    /// Interpolates between two effects using the specified progress.
137    pub fn interpolate_core(progress: f64, old_value: &EffectValue, new_value: &EffectValue) -> EffectValue {
138        let old = old_value.as_ref().and_then(|effect| effect.as_blur_effect());
139        let n = new_value.as_ref().and_then(|effect| effect.as_blur_effect());
140        match (old, n) {
141            (Some(old), Some(n)) => Some(Rc::new(Self::interpolate_blur(progress, old, n))),
142            _ => {
143                if progress >= 0.5 {
144                    new_value.clone()
145                } else {
146                    old_value.clone()
147                }
148            }
149        }
150    }
151}
152
153impl Animator for BlurEffectAnimator {
154    type Value = EffectValue;
155
156    fn base(&self) -> &AnimatorBase {
157        &self.base
158    }
159
160    fn interpolate(&self, progress: f64, old_value: &EffectValue, new_value: &EffectValue) -> EffectValue {
161        Self::interpolate_core(progress, old_value, new_value)
162    }
163}
164
165/// Animator that interpolates drop shadow effects. Values that are not drop
166/// shadow effects are not interpolated.
167#[derive(Default)]
168pub struct DropShadowEffectAnimator {
169    base: AnimatorBase,
170}
171
172impl DropShadowEffectAnimator {
173    pub fn new() -> Self {
174        Self::default()
175    }
176
177    /// Interpolates between two drop shadow effects using the specified
178    /// progress.
179    pub fn interpolate_drop_shadow(
180        progress: f64,
181        old_value: &dyn IDropShadowEffect,
182        new_value: &dyn IDropShadowEffect,
183    ) -> Rc<dyn IEffect> {
184        let blur = DoubleAnimator::interpolate_core(progress, &old_value.blur_radius(), &new_value.blur_radius());
185        let color = ColorAnimator::interpolate_core(progress, &old_value.color(), &new_value.color());
186        let opacity = DoubleAnimator::interpolate_core(progress, &old_value.opacity(), &new_value.opacity());
187
188        if let (Some(old_direction), Some(new_direction)) =
189            (old_value.as_direction_drop_shadow_effect(), new_value.as_direction_drop_shadow_effect())
190        {
191            return Rc::new(ImmutableDropShadowDirectionEffect::new(
192                DoubleAnimator::interpolate_core(progress, &old_direction.direction(), &new_direction.direction()),
193                DoubleAnimator::interpolate_core(progress, &old_direction.shadow_depth(), &new_direction.shadow_depth()),
194                blur,
195                color,
196                opacity,
197            ));
198        }
199
200        Rc::new(ImmutableDropShadowEffect::new(
201            DoubleAnimator::interpolate_core(progress, &old_value.offset_x(), &new_value.offset_x()),
202            DoubleAnimator::interpolate_core(progress, &old_value.offset_y(), &new_value.offset_y()),
203            blur,
204            color,
205            opacity,
206        ))
207    }
208
209    /// Interpolates between two effects using the specified progress.
210    pub fn interpolate_core(progress: f64, old_value: &EffectValue, new_value: &EffectValue) -> EffectValue {
211        let old = old_value.as_ref().and_then(|effect| effect.as_drop_shadow_effect());
212        let n = new_value.as_ref().and_then(|effect| effect.as_drop_shadow_effect());
213        match (old, n) {
214            (Some(old), Some(n)) => Some(Self::interpolate_drop_shadow(progress, old, n)),
215            _ => {
216                if progress >= 0.5 {
217                    new_value.clone()
218                } else {
219                    old_value.clone()
220                }
221            }
222        }
223    }
224}
225
226impl Animator for DropShadowEffectAnimator {
227    type Value = EffectValue;
228
229    fn base(&self) -> &AnimatorBase {
230        &self.base
231    }
232
233    fn interpolate(&self, progress: f64, old_value: &EffectValue, new_value: &EffectValue) -> EffectValue {
234        Self::interpolate_core(progress, old_value, new_value)
235    }
236}