Skip to main content

ferroui_base/animation/
transition_observable_base.rs

1use crate::animation::easings::Easing;
2use crate::reactive::{SingleSubscriberObservableBase, IDisposable, IObservable, IObserver, ObservableError};
3use std::cell::RefCell;
4use std::rc::{Rc, Weak};
5
6/// Provides the base for observables implementing transitions: eases the
7/// progress and produces the value for it.
8pub struct TransitionObservableBase<T: 'static, F: Fn(f64) -> T + 'static> {
9    this: Weak<Self>,
10    core: SingleSubscriberObservableBase<T>,
11    easing: Easing,
12    progress: Rc<dyn IObservable<f64>>,
13    progress_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
14    produce_value: F,
15}
16
17impl<T: 'static, F: Fn(f64) -> T + 'static> TransitionObservableBase<T, F> {
18    /// Creates the observable. `produce_value` produces the value at the
19    /// given eased progress.
20    pub fn new(progress: Rc<dyn IObservable<f64>>, easing: Easing, produce_value: F) -> Rc<Self> {
21        Rc::new_cyclic(|this| Self {
22            this: this.clone(),
23            core: SingleSubscriberObservableBase::new(),
24            easing,
25            progress,
26            progress_subscription: RefCell::new(None),
27            produce_value,
28        })
29    }
30
31    /// The progress the transition is driven by.
32    pub fn progress(&self) -> &Rc<dyn IObservable<f64>> {
33        &self.progress
34    }
35
36    fn subscribed(&self) {
37        let Some(this) = self.this.upgrade() else { return };
38        let subscription = self.progress.subscribe(this);
39        // The progress may have completed while subscribing.
40        if self.core.has_observer() {
41            *self.progress_subscription.borrow_mut() = Some(subscription);
42        } else {
43            subscription.dispose();
44        }
45    }
46
47    fn unsubscribed(&self) {
48        let subscription = self.progress_subscription.borrow_mut().take();
49        if let Some(subscription) = subscription {
50            subscription.dispose();
51        }
52    }
53}
54
55impl<T: 'static, F: Fn(f64) -> T + 'static> IObservable<T> for TransitionObservableBase<T, F> {
56    fn subscribe(&self, observer: Rc<dyn IObserver<T>>) -> Rc<dyn IDisposable> {
57        self.core.subscribe(observer, || self.subscribed());
58        self.this.upgrade().expect("the observable is alive while it is being subscribed to")
59    }
60}
61
62impl<T: 'static, F: Fn(f64) -> T + 'static> IDisposable for TransitionObservableBase<T, F> {
63    fn dispose(&self) {
64        self.core.dispose(|| self.unsubscribed());
65    }
66}
67
68impl<T: 'static, F: Fn(f64) -> T + 'static> IObserver<f64> for TransitionObservableBase<T, F> {
69    fn on_completed(&self) {
70        self.core.publish_completed(|| self.unsubscribed());
71    }
72
73    fn on_error(&self, error: ObservableError) {
74        self.core.publish_error(error, || self.unsubscribed());
75    }
76
77    fn on_next(&self, value: f64) {
78        let progress = self.easing.ease(value);
79        self.core.publish_next((self.produce_value)(progress));
80    }
81}