1use crate::animation::{Clock, IClock, ITransition, TransitionInstance, Transitions};
2use crate::collections::{CollectionChangedHandler, NotifyCollectionChangedAction, NotifyCollectionChangedEventArgs};
3use crate::data::BindingPriority;
4use crate::reactive::IDisposable;
5use crate::{
6 ferro_class, ferro_property, instantiate, BoxedValue, FerroObject, FerroObjectExtensions, FerroObjectImpl,
7 FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Ref, StyledProperty, StyledPropertyOptions,
8 UnsetValueType,
9};
10use std::any::Any;
11use std::cell::{Cell, RefCell};
12use std::rc::Rc;
13
14#[repr(C)]
16pub struct Animatable {
17 base: FerroObject,
18 transitions_enabled: Cell<bool>,
19 is_subscribed_to_transitions_collection: Cell<bool>,
20 transition_state: RefCell<Option<Vec<(Rc<dyn ITransition>, Rc<TransitionState>)>>>,
21 collection_subscriptions: RefCell<Vec<(Transitions, u64)>>,
24 applying_transition_instance: RefCell<Option<Rc<TransitionInstance>>>,
27 transition_context: RefCell<TransitionContextState>,
30}
31
32struct TransitionState {
33 instance: RefCell<Option<Rc<dyn IDisposable>>>,
34 progress: RefCell<Option<Rc<TransitionInstance>>>,
36 base_value: RefCell<BoxedValue>,
37 interruption_state: RefCell<Option<Rc<dyn Any>>>,
38}
39
40#[derive(Default)]
41struct TransitionContextState {
42 applying: bool,
43 interruption: Option<Rc<dyn Any>>,
44}
45
46struct TransitionContext<'a> {
48 context: &'a RefCell<TransitionContextState>,
49 previous: Option<TransitionContextState>,
50}
51
52impl<'a> TransitionContext<'a> {
53 fn enter(context: &'a RefCell<TransitionContextState>, state: Option<Rc<dyn Any>>) -> Self {
54 let previous = context.replace(TransitionContextState { applying: true, interruption: state });
55 Self { context, previous: Some(previous) }
56 }
57
58 fn finish(mut self) -> Option<Rc<dyn Any>> {
59 self.context.replace(self.previous.take().expect("active transition context")).interruption
60 }
61}
62
63impl Drop for TransitionContext<'_> {
64 fn drop(&mut self) {
65 if let Some(previous) = self.previous.take() {
66 self.context.replace(previous);
67 }
68 }
69}
70
71ferro_class!(Animatable: FerroObject);
72crate::ferro_class_info!(Animatable { new: Animatable::new });
73
74crate::ferro_overrides! { impl FerroObjectImpl for Animatable {
75 fn on_property_changed_core(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
81 if change.property() == Self::transitions_property().as_property() && change.is_effective_value_change() {
82 let (old_transitions, new_transitions) = change.get_old_and_new_value::<Option<Transitions>>();
83
84 if let Some(new_transitions) = &new_transitions {
90 let mut to_add = new_transitions.snapshot();
91
92 if let Some(old_transitions) = &old_transitions {
93 if new_transitions.count() > 0 && old_transitions.count() > 0 {
94 to_add = Rc::new(Self::except(&to_add, &old_transitions.snapshot()));
95 }
96 }
97
98 if this.transitions_enabled.get() {
101 this.subscribe_to_collection(new_transitions);
102 this.is_subscribed_to_transitions_collection.set(true);
103 }
104
105 this.add_transitions(&to_add);
106 }
107
108 if let Some(old_transitions) = &old_transitions {
109 let mut to_remove = old_transitions.snapshot();
110
111 if let Some(new_transitions) = &new_transitions {
112 if old_transitions.count() > 0 && new_transitions.count() > 0 {
113 to_remove = Rc::new(Self::except(&to_remove, &new_transitions.snapshot()));
114 }
115 }
116
117 this.unsubscribe_from_collection(old_transitions);
118 this.remove_transitions(&to_remove);
119 }
120 } else if this.transitions_enabled.get()
121 && !change.property().is_direct()
122 && change.priority() > BindingPriority::Animation
123 && this.transition_state.borrow().is_some()
124 {
125 if let Some(transitions) = this.transitions() {
126 let transitions = transitions.snapshot();
127 for transition in transitions.iter().rev() {
128 let property = transition.property();
129 if property != change.property() {
130 continue;
131 }
132 let Some(state) = this.try_get_state(transition) else { continue };
133
134 let mut old_value = state.base_value.borrow().clone();
135 let new_value = this.get_animation_base_value(property);
136
137 if *old_value != *new_value {
138 *state.base_value.borrow_mut() = new_value.clone();
139
140 let animated_value = this.get_value_untyped(property);
143
144 if *new_value != *animated_value {
145 old_value = animated_value;
146 }
147
148 let clock = this.clock().unwrap_or_else(Clock::global_clock);
149 let interruption_state = state.interruption_state.borrow_mut().take();
150 let instance = state.instance.borrow_mut().take();
151 *state.progress.borrow_mut() = None;
152 if let Some(instance) = instance {
153 instance.dispose();
154 }
155 *this.applying_transition_instance.borrow_mut() = None;
156 let context = TransitionContext::enter(&this.transition_context, interruption_state);
157 let instance = transition.apply(this, clock, &old_value, &new_value);
158 *state.interruption_state.borrow_mut() = context.finish();
159 *state.progress.borrow_mut() = this.applying_transition_instance.borrow_mut().take();
160 *state.instance.borrow_mut() = Some(instance);
161 return;
162 }
163 }
164 }
165 }
166
167 Self::parent_on_property_changed_core(this, change);
168 }
169} }
170
171crate::ferro_properties! { impl Animatable {
172 ferro_property!(
173 pub fn clock_property() -> StyledProperty<Option<Rc<dyn IClock>>> {
175 FerroProperty::register_with::<Animatable, _>("Clock", StyledPropertyOptions::new(None).inherits(true))
176 }
177 );
178
179 ferro_property!(
180 pub fn transitions_property() -> StyledProperty<Option<Transitions>> {
182 FerroProperty::register::<Animatable, _>("Transitions", None)
183 }
184 );
185} }
186
187impl Animatable {
188 pub fn construct() -> Self {
190 Self {
191 base: FerroObject::construct(),
192 transitions_enabled: Cell::new(true),
193 is_subscribed_to_transitions_collection: Cell::new(false),
194 transition_state: RefCell::new(None),
195 collection_subscriptions: RefCell::new(Vec::new()),
196 applying_transition_instance: RefCell::new(None),
197 transition_context: RefCell::new(TransitionContextState::default()),
198 }
199 }
200
201 pub fn new() -> Ref<Self> {
202 instantiate(Self::construct())
203 }
204
205 pub fn clock(&self) -> Option<Rc<dyn IClock>> {
209 self.get_value(Self::clock_property())
210 }
211
212 pub fn set_clock(&self, value: Option<Rc<dyn IClock>>) {
213 self.set_value(Self::clock_property(), value)
214 }
215
216 pub fn transitions(&self) -> Option<Transitions> {
218 self.get_value(Self::transitions_property())
219 }
220
221 pub fn set_transitions(&self, value: Option<Transitions>) {
222 self.set_value(Self::transitions_property(), value)
223 }
224
225 pub fn enable_transitions(&self) {
230 if !self.transitions_enabled.get() {
231 self.transitions_enabled.set(true);
232
233 if let Some(transitions) = self.transitions() {
234 if !self.is_subscribed_to_transitions_collection.get() {
235 self.is_subscribed_to_transitions_collection.set(true);
236 self.subscribe_to_collection(&transitions);
237 }
238
239 self.add_transitions(&transitions.snapshot());
240 }
241 }
242 }
243
244 pub fn disable_transitions(&self) {
248 if self.transitions_enabled.get() {
249 self.transitions_enabled.set(false);
250
251 if let Some(transitions) = self.transitions() {
252 if self.is_subscribed_to_transitions_collection.get() {
253 self.is_subscribed_to_transitions_collection.set(false);
254 self.unsubscribe_from_collection(&transitions);
255 }
256
257 self.remove_transitions(&transitions.snapshot());
258 }
259 }
260 }
261
262 fn except(first: &[Rc<dyn ITransition>], second: &[Rc<dyn ITransition>]) -> Vec<Rc<dyn ITransition>> {
264 let mut result: Vec<Rc<dyn ITransition>> = Vec::new();
265 for item in first {
266 if !second.contains(item) && !result.contains(item) {
267 result.push(item.clone());
268 }
269 }
270 result
271 }
272
273 fn subscribe_to_collection(&self, transitions: &Transitions) {
274 let weak = self.to_ref().downgrade();
277 let handler: Rc<CollectionChangedHandler<Rc<dyn ITransition>>> =
278 Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, Rc<dyn ITransition>>| {
279 if let Some(this) = weak.upgrade() {
280 this.transitions_collection_changed(e);
281 }
282 });
283 let token = transitions.add_collection_changed(handler);
284 self.collection_subscriptions.borrow_mut().push((transitions.clone(), token));
285 }
286
287 fn unsubscribe_from_collection(&self, transitions: &Transitions) {
288 let token = {
289 let mut subscriptions = self.collection_subscriptions.borrow_mut();
290 subscriptions.iter().rposition(|(t, _)| t.ptr_eq(transitions)).map(|index| subscriptions.remove(index).1)
291 };
292 if let Some(token) = token {
293 transitions.remove_collection_changed(token);
294 }
295 }
296
297 fn transitions_collection_changed(&self, e: &NotifyCollectionChangedEventArgs<'_, Rc<dyn ITransition>>) {
298 if !self.transitions_enabled.get() {
299 return;
300 }
301
302 match e.action {
303 NotifyCollectionChangedAction::Add => self.add_transitions(e.new_items),
304 NotifyCollectionChangedAction::Remove => self.remove_transitions(e.old_items),
305 NotifyCollectionChangedAction::Replace => {
306 self.remove_transitions(e.old_items);
307 self.add_transitions(e.new_items);
308 }
309 NotifyCollectionChangedAction::Reset => panic!("Transitions collection cannot be reset."),
310 NotifyCollectionChangedAction::Move => {}
311 }
312 }
313
314 fn add_transitions(&self, items: &[Rc<dyn ITransition>]) {
315 if !self.transitions_enabled.get() {
316 return;
317 }
318
319 for t in items {
320 let base_value = self.get_animation_base_value(t.property());
321 let mut transition_state = self.transition_state.borrow_mut();
322 let transition_state = transition_state.get_or_insert_with(Vec::new);
323 if transition_state.iter().any(|(key, _)| key == t) {
324 panic!("An item with the same key has already been added.");
325 }
326 transition_state.push((
327 t.clone(),
328 Rc::new(TransitionState {
329 instance: RefCell::new(None),
330 progress: RefCell::new(None),
331 base_value: RefCell::new(base_value),
332 interruption_state: RefCell::new(None),
333 }),
334 ));
335 }
336 if self.transition_state.borrow().is_none() {
337 *self.transition_state.borrow_mut() = Some(Vec::new());
338 }
339 }
340
341 fn remove_transitions(&self, items: &[Rc<dyn ITransition>]) {
342 if self.transition_state.borrow().is_none() {
343 return;
344 }
345
346 for t in items {
347 let Some(state) = self.try_get_state(t) else { continue };
348 let instance = state.instance.borrow_mut().take();
349 *state.progress.borrow_mut() = None;
350 *state.interruption_state.borrow_mut() = None;
351 if let Some(instance) = instance {
352 instance.dispose();
353 }
354 if let Some(transition_state) = &mut *self.transition_state.borrow_mut() {
355 transition_state.retain(|(key, _)| key != t);
356 }
357 }
358 }
359
360 pub fn try_get_transition_instance(&self, transition: &Rc<dyn ITransition>) -> Option<Rc<TransitionInstance>> {
365 self.try_get_state(transition).and_then(|state| state.progress.borrow().clone())
366 }
367
368 pub(crate) fn report_transition_instance(&self, instance: &Rc<TransitionInstance>) {
371 *self.applying_transition_instance.borrow_mut() = Some(instance.clone());
372 }
373
374 pub fn take_transition_interruption_state<T: Any>(&self) -> Option<Rc<T>> {
381 self.transition_context.borrow_mut().interruption.take()?.downcast().ok()
382 }
383
384 pub fn report_transition_interruption_state<T: Any>(&self, state: Rc<T>) {
390 let mut context = self.transition_context.borrow_mut();
391 if context.applying {
392 context.interruption = Some(state);
393 }
394 }
395
396 fn try_get_state(&self, transition: &Rc<dyn ITransition>) -> Option<Rc<TransitionState>> {
397 let transition_state = self.transition_state.borrow();
398 transition_state
399 .as_ref()
400 .and_then(|states| states.iter().find(|(key, _)| key == transition).map(|(_, state)| state.clone()))
401 }
402
403 fn get_animation_base_value(&self, property: &'static FerroProperty) -> BoxedValue {
404 let value = self.get_base_value_untyped(property);
405
406 if value.is::<UnsetValueType>() {
407 self.get_value_untyped(property)
408 } else {
409 value
410 }
411 }
412}