ferroui_base/animation/animators/
transform_animator.rs1use crate::animation::animators::{apply_with_visual, Animator, AnimatorBase, DoubleAnimator};
2use crate::animation::{Animatable, Animation, IAnimator, IClock};
3use crate::logging::{LogArea, LogEventLevel, Logger};
4use crate::media::transformation::TransformOperations;
5use crate::media::{
6 ITransform, RotateTransform, Rotate3DTransform, ScaleTransform, SkewTransform, Transform, TransformGroup,
7 TranslateTransform,
8};
9use crate::reactive::{Disposable, IDisposable, IObservable};
10use crate::{Ref, Visual};
11use std::cell::RefCell;
12use std::rc::Rc;
13
14#[derive(Default)]
18pub struct TransformAnimator {
19 base: AnimatorBase,
20 double_animator: RefCell<Option<Rc<DoubleAnimator>>>,
21}
22
23impl TransformAnimator {
24 pub fn new() -> Self {
25 Self::default()
26 }
27}
28
29impl Animator for TransformAnimator {
30 type Value = f64;
31
32 fn base(&self) -> &AnimatorBase {
33 &self.base
34 }
35
36 fn apply(
37 this: &Rc<Self>,
38 animation: &Animation,
39 control: &Animatable,
40 clock: Option<Rc<dyn IClock>>,
41 obs_match: Rc<dyn IObservable<bool>>,
42 on_complete: Option<Rc<dyn Fn()>>,
43 should_pause_on_invisible: bool,
44 ) -> Option<Rc<dyn IDisposable>> {
45 let Some(ctrl) = control.downcast_ref::<Visual>() else {
46 panic!("Unable to cast object of type '{}' to type 'Visual'.", control.get_type());
47 };
48
49 let Some(property) = this.base.property() else {
50 panic!("Animator has no property specified.");
51 };
52
53 if Transform::TYPE.is_assignable_from(property.owner_type()) {
55 if ctrl.render_transform().is_some_and(|t| t.as_any().is::<TransformOperations>()) {
56 return Some(Disposable::empty());
58 }
59
60 let render_transform: Rc<dyn ITransform> = match ctrl.render_transform() {
61 Some(render_transform) => render_transform,
62 None => {
63 let normal_transform = TransformGroup::new();
64
65 let children = normal_transform.children();
68 children.add(ScaleTransform::new().upcast());
69 children.add(SkewTransform::new().upcast());
70 children.add(RotateTransform::new().upcast());
71 children.add(TranslateTransform::new().upcast());
72 children.add(Rotate3DTransform::new().upcast());
73
74 let render_transform: Rc<dyn ITransform> = normal_transform.into();
75 ctrl.set_render_transform(Some(render_transform.clone()));
76 render_transform
77 }
78 };
79
80 let double_animator = {
81 let mut double_animator = this.double_animator.borrow_mut();
82 double_animator
83 .get_or_insert_with(|| {
84 let double_animator = Rc::new(DoubleAnimator::new());
85 for keyframe in this.base.to_vec() {
86 IAnimator::add(&*double_animator, keyframe);
87 }
88 double_animator.base().set_property(Some(property));
89 double_animator
90 })
91 .clone()
92 };
93
94 let visual_target = Some(ctrl.to_ref());
95 let clock = clock.or_else(|| control.clock());
96
97 if let Some(render_transform) = render_transform.as_object() {
98 if render_transform.get_type() == property.owner_type() {
100 let target: Ref<Animatable> =
101 render_transform.to_ref().cast().expect("a transform is an animatable");
102 return Some(apply_with_visual(
103 &double_animator,
104 animation,
105 &target,
106 clock,
107 obs_match,
108 on_complete,
109 should_pause_on_invisible,
110 visual_target,
111 ));
112 }
113 else if let Some(group) = render_transform.downcast_ref::<TransformGroup>() {
115 if render_transform.get_type() == TransformGroup::TYPE {
116 for transform in group.children().iter() {
117 if transform.get_type() == property.owner_type() {
118 let target: Ref<Animatable> = transform.upcast();
119 return Some(apply_with_visual(
120 &double_animator,
121 animation,
122 &target,
123 clock,
124 obs_match,
125 on_complete,
126 should_pause_on_invisible,
127 visual_target,
128 ));
129 }
130 }
131 }
132 }
133 }
134
135 if let Some(logger) = Logger::try_get(LogEventLevel::Warning, LogArea::ANIMATIONS) {
136 logger.log(
137 None,
138 &format!(
139 "Cannot find the appropriate transform: \"{}\" in {}.",
140 property.owner_type(),
141 control.get_type()
142 ),
143 );
144 }
145 } else if let Some(logger) = Logger::try_get(LogEventLevel::Error, LogArea::ANIMATIONS) {
146 logger.log(
147 None,
148 &format!(
149 "Cannot apply animation: Target property owner {} is not a Transform object.",
150 property.owner_type()
151 ),
152 );
153 }
154
155 None
156 }
157
158 fn interpolate(&self, _progress: f64, _old_value: &f64, _new_value: &f64) -> f64 {
159 0.0
160 }
161}