1use crate::schema::{WindowHeightClass, WindowSizeClass};
10use std::cell::{Cell, RefCell};
11use std::sync::Arc;
12
13#[derive(Clone, Copy, Debug, PartialEq)]
20pub struct WindowSize {
21 pub width_dp: f32,
22 pub height_dp: f32,
23 pub class: WindowSizeClass,
24 pub height_class: WindowHeightClass,
25}
26
27impl WindowSize {
28 pub const fn new(
29 width_dp: f32,
30 height_dp: f32,
31 class: WindowSizeClass,
32 height_class: WindowHeightClass,
33 ) -> Self {
34 Self {
35 width_dp,
36 height_dp,
37 class,
38 height_class,
39 }
40 }
41
42 pub fn is_compact(&self) -> bool {
44 matches!(self.class, WindowSizeClass::Compact)
45 }
46
47 pub fn is_medium(&self) -> bool {
49 matches!(self.class, WindowSizeClass::Medium)
50 }
51
52 pub fn is_expanded(&self) -> bool {
54 matches!(self.class, WindowSizeClass::Expanded)
55 }
56
57 pub fn is_short(&self) -> bool {
60 matches!(self.height_class, WindowHeightClass::Compact)
61 }
62
63 pub fn is_medium_height(&self) -> bool {
65 matches!(self.height_class, WindowHeightClass::Medium)
66 }
67
68 pub fn is_tall(&self) -> bool {
70 matches!(self.height_class, WindowHeightClass::Expanded)
71 }
72}
73
74impl Default for WindowSize {
75 fn default() -> Self {
81 Self::new(
82 0.0,
83 0.0,
84 WindowSizeClass::Compact,
85 WindowHeightClass::Compact,
86 )
87 }
88}
89
90struct Subscriber {
91 id: u64,
92 notify: Arc<dyn Fn() + Send + Sync>,
93}
94
95thread_local! {
96 static NODES: RefCell<Vec<(u32, WindowSize)>> = const { RefCell::new(Vec::new()) };
101 static NODE_SUBSCRIBERS: RefCell<Vec<(u32, Subscriber)>> = const { RefCell::new(Vec::new()) };
102 static CURRENT: Cell<WindowSize> = const { Cell::new(WindowSize {
103 width_dp: 0.0,
104 height_dp: 0.0,
105 class: WindowSizeClass::Compact,
106 height_class: WindowHeightClass::Compact,
107 }) };
108 static SUBSCRIBERS: RefCell<Vec<Subscriber>> = const { RefCell::new(Vec::new()) };
109 static WINDOWS: RefCell<Vec<(u32, WindowSize)>> = const { RefCell::new(Vec::new()) };
113 static WINDOW_SUBSCRIBERS: RefCell<Vec<(u32, Subscriber)>> = const { RefCell::new(Vec::new()) };
114 static NEXT_SUBSCRIBER_ID: Cell<u64> = const { Cell::new(1) };
115}
116
117pub fn window_size() -> WindowSize {
124 match crate::windows::current_window_id() {
125 0 => CURRENT.with(Cell::get),
126 window => window_size_of(window),
127 }
128}
129
130pub fn window_size_of(window: u32) -> WindowSize {
133 WINDOWS.with_borrow(|windows| {
134 windows
135 .iter()
136 .find(|(id, _)| *id == window)
137 .map(|(_, size)| *size)
138 .unwrap_or_default()
139 })
140}
141
142pub(crate) fn publish_window(window: u32, size: WindowSize) -> bool {
145 let changed =
146 WINDOWS.with_borrow_mut(
147 |windows| match windows.iter_mut().find(|(id, _)| *id == window) {
148 Some(entry) => {
149 if entry.1 == size {
150 return false;
151 }
152 entry.1 = size;
153 true
154 }
155 None => {
156 windows.push((window, size));
157 true
158 }
159 },
160 );
161 if !changed {
162 return false;
163 }
164 WINDOW_SUBSCRIBERS.with_borrow(|subscribers| {
165 let mut woke = false;
166 for (id, subscriber) in subscribers {
167 if *id == window {
168 (subscriber.notify)();
169 woke = true;
170 }
171 }
172 woke
173 })
174}
175
176pub(crate) fn window_state_len() -> usize {
178 WINDOWS.with_borrow(Vec::len) + WINDOW_SUBSCRIBERS.with_borrow(Vec::len)
179}
180
181pub(crate) fn forget_window(window: u32) {
183 WINDOWS.with_borrow_mut(|windows| windows.retain(|(id, _)| *id != window));
184}
185
186pub struct WindowSubscription {
188 id: u64,
189}
190
191pub fn subscribe_window(window: u32, notify: Arc<dyn Fn() + Send + Sync>) -> WindowSubscription {
194 let id = next_subscriber_id();
195 WINDOW_SUBSCRIBERS.with_borrow_mut(|subscribers| {
196 subscribers.push((window, Subscriber { id, notify }));
197 });
198 WindowSubscription { id }
199}
200
201impl Drop for WindowSubscription {
202 fn drop(&mut self) {
203 WINDOW_SUBSCRIBERS.with_borrow_mut(|subscribers| {
204 subscribers.retain(|(_, subscriber)| subscriber.id != self.id);
205 });
206 }
207}
208
209pub(crate) fn publish(size: WindowSize) -> bool {
214 if CURRENT.with(Cell::get) == size {
215 return false;
216 }
217 CURRENT.with(|current| current.set(size));
218 SUBSCRIBERS.with_borrow(|subscribers| {
219 for subscriber in subscribers {
220 (subscriber.notify)();
221 }
222 });
223 true
224}
225
226pub fn node_size(token: u32) -> WindowSize {
228 NODES.with_borrow(|nodes| {
229 nodes
230 .iter()
231 .find(|(name, _)| *name == token)
232 .map(|(_, size)| *size)
233 .unwrap_or_default()
234 })
235}
236
237pub(crate) fn publish_node(token: u32, size: WindowSize) -> bool {
242 let changed =
243 NODES.with_borrow_mut(
244 |nodes| match nodes.iter_mut().find(|(name, _)| *name == token) {
245 Some(entry) => {
246 if entry.1 == size {
247 return false;
248 }
249 entry.1 = size;
250 true
251 }
252 None => {
253 nodes.push((token, size));
254 true
255 }
256 },
257 );
258 if !changed {
259 return false;
260 }
261 NODE_SUBSCRIBERS.with_borrow(|subscribers| {
262 let mut woke = false;
263 for (name, subscriber) in subscribers {
264 if *name == token {
265 (subscriber.notify)();
266 woke = true;
267 }
268 }
269 woke
270 })
271}
272
273#[doc(hidden)]
275pub fn reset_window_size() {
276 CURRENT.with(|current| current.set(WindowSize::default()));
277 SUBSCRIBERS.with_borrow_mut(Vec::clear);
278 NODES.with_borrow_mut(Vec::clear);
279 NODE_SUBSCRIBERS.with_borrow_mut(Vec::clear);
280 WINDOWS.with_borrow_mut(Vec::clear);
281 WINDOW_SUBSCRIBERS.with_borrow_mut(Vec::clear);
282}
283
284pub struct WindowSizeSubscription {
287 id: u64,
288}
289
290pub fn subscribe(notify: Arc<dyn Fn() + Send + Sync>) -> WindowSizeSubscription {
293 let id = next_subscriber_id();
294 SUBSCRIBERS.with_borrow_mut(|subscribers| subscribers.push(Subscriber { id, notify }));
295 WindowSizeSubscription { id }
296}
297
298fn next_subscriber_id() -> u64 {
299 NEXT_SUBSCRIBER_ID.with(|next| {
300 let id = next.get();
301 next.set(id + 1);
302 id
303 })
304}
305
306impl Drop for WindowSizeSubscription {
307 fn drop(&mut self) {
308 SUBSCRIBERS.with_borrow_mut(|subscribers| {
309 subscribers.retain(|subscriber| subscriber.id != self.id);
310 });
311 }
312}
313
314#[derive(Clone, Copy, Debug, PartialEq)]
334pub struct NodeSize {
335 token: u32,
336 size: WindowSize,
337}
338
339impl NodeSize {
340 pub fn of(token: u32) -> Self {
342 Self {
343 token,
344 size: node_size(token),
345 }
346 }
347
348 pub const fn token(&self) -> i64 {
350 self.token as i64
351 }
352
353 pub fn is_compact(&self) -> bool {
355 self.size.is_compact()
356 }
357
358 pub fn is_medium(&self) -> bool {
360 self.size.is_medium()
361 }
362
363 pub fn is_expanded(&self) -> bool {
365 self.size.is_expanded()
366 }
367
368 pub const fn measured(&self) -> WindowSize {
370 self.size
371 }
372}
373
374thread_local! {
375 static NEXT_TOKEN: Cell<u32> = const { Cell::new(1) };
376}
377
378pub fn next_node_token() -> u32 {
380 NEXT_TOKEN.with(|next| {
381 let token = next.get();
382 next.set(token + 1);
383 token
384 })
385}
386
387pub struct NodeSizeSubscription {
392 token: u32,
393 id: u64,
394}
395
396impl NodeSizeSubscription {
397 pub fn token(&self) -> u32 {
399 self.token
400 }
401}
402
403pub fn subscribe_node(token: u32, notify: Arc<dyn Fn() + Send + Sync>) -> NodeSizeSubscription {
406 let id = next_subscriber_id();
407 NODE_SUBSCRIBERS.with_borrow_mut(|subscribers| {
408 subscribers.push((token, Subscriber { id, notify }));
409 });
410 NodeSizeSubscription { token, id }
411}
412
413impl Drop for NodeSizeSubscription {
414 fn drop(&mut self) {
415 NODE_SUBSCRIBERS.with_borrow_mut(|subscribers| {
418 subscribers.retain(|(_, subscriber)| subscriber.id != self.id);
419 });
420 NODES.with_borrow_mut(|nodes| nodes.retain(|(token, _)| *token != self.token));
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427
428 #[test]
429 fn fr20_window_size_starts_compact_before_the_renderer_measures_anything() {
430 reset_window_size();
431 assert_eq!(window_size(), WindowSize::default());
432 assert!(window_size().is_compact());
433 }
434
435 #[test]
436 fn fr20_publishing_the_same_size_twice_wakes_nobody() {
437 reset_window_size();
438 let size = WindowSize::new(
439 700.0,
440 800.0,
441 WindowSizeClass::Medium,
442 WindowHeightClass::Medium,
443 );
444 assert!(publish(size));
445 assert!(!publish(size));
446 reset_window_size();
447 }
448}