ferroui_base/animation/
transition_instance.rs1use crate::animation::{Clock, IClock, PlayState, TimeSpan};
2use crate::reactive::{SingleSubscriberObservableBase, IDisposable, IObservable, IObserver, ObservableError};
3use crate::utilities::MathUtilities;
4use std::cell::RefCell;
5use std::rc::{Rc, Weak};
6
7pub struct TransitionInstance {
10 this: Weak<TransitionInstance>,
11 core: SingleSubscriberObservableBase<f64>,
12 timer_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
13 delay: TimeSpan,
14 duration: TimeSpan,
15 base_clock: Rc<dyn IClock>,
16 clock: RefCell<Option<Rc<Clock>>>,
17}
18
19struct TimerObserver(Rc<TransitionInstance>);
22
23impl IObserver<TimeSpan> for TimerObserver {
24 fn on_next(&self, value: TimeSpan) {
25 self.0.timer_tick(value);
26 }
27
28 fn on_error(&self, error: ObservableError) {
29 self.0.core.publish_error(error, || self.0.unsubscribed());
30 }
31
32 fn on_completed(&self) {
33 self.0.core.publish_completed(|| self.0.unsubscribed());
34 }
35}
36
37impl TransitionInstance {
38 pub fn new(clock: Rc<dyn IClock>, delay: TimeSpan, duration: TimeSpan) -> Rc<Self> {
41 Rc::new_cyclic(|this| Self {
42 this: this.clone(),
43 core: SingleSubscriberObservableBase::new(),
44 timer_subscription: RefCell::new(None),
45 delay,
46 duration,
47 base_clock: clock,
48 clock: RefCell::new(None),
49 })
50 }
51
52 fn timer_tick(&self, t: TimeSpan) {
53 let normalized_interp_val;
59
60 if t < self.delay {
61 normalized_interp_val = 0.0;
62 } else if MathUtilities::are_close(self.duration.total_seconds(), 0.0) {
63 normalized_interp_val = 1.0;
64 } else {
65 let normalized_total_dur = self.delay + self.duration;
66 let normalized_delay_end = self.delay.total_seconds() / normalized_total_dur.total_seconds();
67 let normalized_presentation_time = t.total_seconds() / normalized_total_dur.total_seconds();
68
69 if normalized_presentation_time < normalized_delay_end
70 || MathUtilities::are_close(normalized_presentation_time, normalized_delay_end)
71 {
72 normalized_interp_val = 0.0;
73 } else {
74 normalized_interp_val =
75 (t.total_seconds() - self.delay.total_seconds()) / self.duration.total_seconds();
76 }
77 }
78
79 if normalized_interp_val >= 1.0 || normalized_interp_val < 0.0 {
81 self.core.publish_next(1.0);
82 self.core.publish_completed(|| self.unsubscribed());
83 } else {
84 self.core.publish_next(normalized_interp_val);
85 }
86 }
87
88 fn unsubscribed(&self) {
89 let subscription = self.timer_subscription.borrow_mut().take();
90 if let Some(subscription) = subscription {
91 subscription.dispose();
92 }
93 let clock = self.clock.borrow().clone();
94 if let Some(clock) = clock {
95 clock.set_play_state(PlayState::Stop);
96 }
97 }
98
99 fn subscribed(&self) {
100 let clock = Clock::with_parent(&self.base_clock);
101 *self.clock.borrow_mut() = Some(clock.clone());
102 let Some(this) = self.this.upgrade() else { return };
103 let subscription = clock.subscribe(Rc::new(TimerObserver(this)));
104 *self.timer_subscription.borrow_mut() = Some(subscription);
105 self.core.publish_next(0.0);
106 }
107}
108
109impl IObservable<f64> for TransitionInstance {
110 fn subscribe(&self, observer: Rc<dyn IObserver<f64>>) -> Rc<dyn IDisposable> {
111 self.core.subscribe(observer, || self.subscribed());
112 self.this.upgrade().expect("the instance is alive while it is being subscribed to")
113 }
114}
115
116impl IDisposable for TransitionInstance {
117 fn dispose(&self) {
118 self.core.dispose(|| self.unsubscribed());
119 }
120}