ferroui_base/media/
media_context_clock.rs1use super::media_context::MediaContext;
4use crate::animation::{IClock, IGlobalClock, PlayState, TimeSpan};
5use crate::reactive::{Disposable, IDisposable, IObservable, IObserver};
6use std::cell::{Cell, RefCell};
7use std::collections::VecDeque;
8use std::rc::{Rc, Weak};
9
10type AnimationFrame = Box<dyn FnOnce(TimeSpan)>;
11
12pub struct MediaContextClock {
18 this: Weak<MediaContextClock>,
19 parent: Weak<MediaContext>,
20 observers: RefCell<Vec<Rc<dyn IObserver<TimeSpan>>>>,
21 new_observers: RefCell<Vec<Rc<dyn IObserver<TimeSpan>>>>,
22 queued_animation_frames: RefCell<VecDeque<AnimationFrame>>,
23 current_animation_timestamp: Cell<TimeSpan>,
24}
25
26impl MediaContextClock {
27 pub(crate) fn new(parent: Weak<MediaContext>) -> Rc<Self> {
28 Rc::new_cyclic(|this| Self {
29 this: this.clone(),
30 parent,
31 observers: RefCell::new(Vec::new()),
32 new_observers: RefCell::new(Vec::new()),
33 queued_animation_frames: RefCell::new(VecDeque::new()),
34 current_animation_timestamp: Cell::new(TimeSpan::ZERO),
35 })
36 }
37
38 pub fn has_new_subscriptions(&self) -> bool {
40 !self.new_observers.borrow().is_empty()
41 }
42
43 pub fn has_subscriptions(&self) -> bool {
45 !self.observers.borrow().is_empty() || !self.queued_animation_frames.borrow().is_empty()
46 }
47
48 pub fn request_animation_frame(&self, action: impl FnOnce(TimeSpan) + 'static) {
50 if let Some(parent) = self.parent.upgrade() {
51 parent.schedule_render(false);
52 }
53 self.queued_animation_frames.borrow_mut().push_back(Box::new(action));
54 }
55
56 pub(crate) fn pulse(&self, now: TimeSpan) {
57 self.new_observers.borrow_mut().clear();
58 self.current_animation_timestamp.set(now);
59
60 let animation_frames = std::mem::take(&mut *self.queued_animation_frames.borrow_mut());
63 for callback in animation_frames {
64 callback(now);
65 }
66
67 let observers = self.observers.borrow().clone();
68 for observer in observers {
69 observer.on_next(self.current_animation_timestamp.get());
70 }
71 }
72
73 pub(crate) fn pulse_new_subscriptions(&self) {
74 let new_observers = self.new_observers.borrow().clone();
75 for observer in new_observers {
76 observer.on_next(self.current_animation_timestamp.get());
77 }
78 self.new_observers.borrow_mut().clear();
79 }
80}
81
82impl IObservable<TimeSpan> for MediaContextClock {
83 fn subscribe(&self, observer: Rc<dyn IObserver<TimeSpan>>) -> Rc<dyn IDisposable> {
84 if let Some(parent) = self.parent.upgrade() {
85 parent.schedule_render(false);
86 parent.verify_access();
87 }
88 self.observers.borrow_mut().push(observer.clone());
89 self.new_observers.borrow_mut().push(observer.clone());
90 let weak = self.this.clone();
91 Disposable::create(move || {
92 let Some(this) = weak.upgrade() else { return };
93 if let Some(parent) = this.parent.upgrade() {
94 parent.verify_access();
95 }
96 let mut observers = this.observers.borrow_mut();
97 if let Some(index) = observers.iter().position(|o| Rc::ptr_eq(o, &observer)) {
98 observers.remove(index);
99 }
100 })
101 }
102}
103
104impl IClock for MediaContextClock {
105 fn play_state(&self) -> PlayState {
106 PlayState::Run
107 }
108
109 fn set_play_state(&self, _value: PlayState) {
110 panic!("The play state of the global clock cannot be changed.");
111 }
112}
113
114impl IGlobalClock for MediaContextClock {}