Skip to main content

ferroui_base/animation/
clock.rs

1use crate::animation::{ClockBase, IClock, IGlobalClock, PlayState, TimeSpan};
2use crate::reactive::{IDisposable, IObservable, IObserver, ObservableError};
3use crate::{FerroLocator, LocatorExtensions};
4use std::cell::RefCell;
5use std::rc::Rc;
6
7/// A clock that runs on another clock: its time starts at zero on the first
8/// tick of the parent after it was created, and stands still while it is
9/// paused.
10///
11/// The clock subscribes to its parent when it is created, whether or not
12/// anything is subscribed to it, and unsubscribes on the first tick after
13/// its play state was set to [`PlayState::Stop`].
14pub struct Clock {
15    base: ClockBase,
16    parent_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
17}
18
19struct ParentObserver(Rc<Clock>);
20
21impl IObserver<TimeSpan> for ParentObserver {
22    fn on_next(&self, value: TimeSpan) {
23        let clock = &self.0;
24        clock.base.pulse(value, || clock.stop());
25    }
26
27    fn on_error(&self, _error: ObservableError) {}
28
29    fn on_completed(&self) {}
30}
31
32impl Clock {
33    /// The global clock: the `dyn IGlobalClock` service.
34    ///
35    /// Panics when no global clock is registered.
36    pub fn global_clock() -> Rc<dyn IClock> {
37        FerroLocator::current().get_required_service::<dyn IGlobalClock>()
38    }
39
40    /// Creates a clock that runs on the global clock.
41    pub fn new() -> Rc<Clock> {
42        Self::with_parent(&Self::global_clock())
43    }
44
45    /// Creates a clock that runs on `parent`.
46    pub fn with_parent(parent: &Rc<dyn IClock>) -> Rc<Clock> {
47        let clock = Rc::new(Clock { base: ClockBase::new(), parent_subscription: RefCell::new(None) });
48        let subscription = parent.subscribe(Rc::new(ParentObserver(clock.clone())));
49        *clock.parent_subscription.borrow_mut() = Some(subscription);
50        clock
51    }
52
53    fn stop(&self) {
54        let subscription = self.parent_subscription.borrow_mut().take();
55        if let Some(subscription) = subscription {
56            subscription.dispose();
57        }
58    }
59}
60
61impl IObservable<TimeSpan> for Clock {
62    fn subscribe(&self, observer: Rc<dyn IObserver<TimeSpan>>) -> Rc<dyn IDisposable> {
63        self.base.subscribe(observer)
64    }
65}
66
67impl IClock for Clock {
68    fn play_state(&self) -> PlayState {
69        self.base.play_state()
70    }
71
72    fn set_play_state(&self, value: PlayState) {
73        self.base.set_play_state(value)
74    }
75}