Skip to main content

fui/
viewport.rs

1use crate::bindings::ui;
2use crate::signal::{Callback, Signal, SubscriptionGuard};
3use std::cell::RefCell;
4use std::rc::Rc;
5
6#[derive(Clone, Copy)]
7enum ViewportAxis {
8    Width,
9    Height,
10}
11
12#[derive(Clone, Copy)]
13pub struct ViewportSignalHandle {
14    axis: ViewportAxis,
15}
16
17thread_local! {
18    static VIEWPORT_WIDTH_SIGNAL: RefCell<Signal<f32>> = RefCell::new(Signal::new(ui::get_viewport_width()));
19    static VIEWPORT_HEIGHT_SIGNAL: RefCell<Signal<f32>> = RefCell::new(Signal::new(ui::get_viewport_height()));
20}
21
22fn update_signal(signal: &RefCell<Signal<f32>>, next: f32) {
23    let callbacks = signal.borrow_mut().set(next);
24    if let Some(callbacks) = callbacks {
25        for callback in callbacks {
26            callback();
27        }
28    }
29}
30
31impl ViewportSignalHandle {
32    pub fn value(&self) -> f32 {
33        match self.axis {
34            ViewportAxis::Width => VIEWPORT_WIDTH_SIGNAL.with(|slot| slot.borrow().get()),
35            ViewportAxis::Height => VIEWPORT_HEIGHT_SIGNAL.with(|slot| slot.borrow().get()),
36        }
37    }
38
39    pub fn subscribe(&self, handler: impl Fn(f32) + 'static) -> SubscriptionGuard {
40        handler(self.value());
41        match self.axis {
42            ViewportAxis::Width => VIEWPORT_WIDTH_SIGNAL.with(|slot| {
43                let callback: Callback = Rc::new(move || handler(viewport_width_signal().value()));
44                slot.borrow_mut().subscribe(callback)
45            }),
46            ViewportAxis::Height => VIEWPORT_HEIGHT_SIGNAL.with(|slot| {
47                let callback: Callback = Rc::new(move || handler(viewport_height_signal().value()));
48                slot.borrow_mut().subscribe(callback)
49            }),
50        }
51    }
52}
53
54pub fn viewport_width_signal() -> ViewportSignalHandle {
55    ViewportSignalHandle {
56        axis: ViewportAxis::Width,
57    }
58}
59
60pub fn viewport_height_signal() -> ViewportSignalHandle {
61    ViewportSignalHandle {
62        axis: ViewportAxis::Height,
63    }
64}
65
66pub(crate) fn set_viewport_size(width: f32, height: f32) {
67    VIEWPORT_WIDTH_SIGNAL.with(|slot| update_signal(slot, width));
68    VIEWPORT_HEIGHT_SIGNAL.with(|slot| update_signal(slot, height));
69}
70
71#[cfg(test)]
72mod tests {
73    use super::{set_viewport_size, viewport_height_signal, viewport_width_signal};
74    use std::cell::Cell;
75    use std::rc::Rc;
76
77    #[test]
78    fn viewport_signals_update_and_notify() {
79        let width_values = Rc::new(Cell::new(0));
80        let height_values = Rc::new(Cell::new(0));
81
82        let width_seen = Rc::new(Cell::new(0.0));
83        let height_seen = Rc::new(Cell::new(0.0));
84
85        let width_values_clone = width_values.clone();
86        let width_seen_clone = width_seen.clone();
87        let _width_guard = viewport_width_signal().subscribe(move |value| {
88            width_values_clone.set(width_values_clone.get() + 1);
89            width_seen_clone.set(value);
90        });
91
92        let height_values_clone = height_values.clone();
93        let height_seen_clone = height_seen.clone();
94        let _height_guard = viewport_height_signal().subscribe(move |value| {
95            height_values_clone.set(height_values_clone.get() + 1);
96            height_seen_clone.set(value);
97        });
98
99        set_viewport_size(777.0, 555.0);
100
101        assert_eq!(viewport_width_signal().value(), 777.0);
102        assert_eq!(viewport_height_signal().value(), 555.0);
103        assert_eq!(width_seen.get(), 777.0);
104        assert_eq!(height_seen.get(), 555.0);
105        assert!(width_values.get() >= 2);
106        assert!(height_values.get() >= 2);
107    }
108}