Skip to main content

ferroui_base/animation/
transition_instance.rs

1use 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
7/// Handles the timing and lifetime of a transition: an observable of the
8/// progress of the transition, from 0 to 1.
9pub 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
19/// Holds the instance strongly: a running transition stays alive until it
20/// completes or is disposed, whether or not its subscription is kept.
21struct 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    /// Creates the progress of a transition that runs on `clock`, starts
39    /// after `delay` and takes `duration`.
40    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        // [<------------- normalizedTotalDur ------------------>]
54        // [<---- Delay ---->][<---------- Duration ------------>]
55        //                   ^- normalizedDelayEnd
56        //                    [<----   normalizedInterpVal   --->]
57
58        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        // Clamp interpolation value.
80        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}