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}