1use std::any::Any;
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::rc::Rc;
5
6use crate::scope::Scope;
7use crate::{Rect, Scene, View, semantics::Role};
8
9thread_local! {
10 pub static COMPOSER: RefCell<Composer> = RefCell::new(Composer::default());
11 static ROOT_SCOPE: RefCell<Option<Scope>> = RefCell::new(None);
12}
13
14#[derive(Default)]
15pub struct Composer {
16 pub slots: Vec<Box<dyn Any>>,
17 pub cursor: usize,
18 pub keyed_slots: HashMap<String, Box<dyn Any>>,
19}
20
21pub struct ComposeGuard {
22 scope: Scope,
23}
24
25impl ComposeGuard {
26 pub fn begin() -> Self {
27 let scope = Scope::new();
28
29 COMPOSER.with(|c| {
30 let mut c = c.borrow_mut();
31 c.cursor = 0;
32 });
33
34 ROOT_SCOPE.with(|rs| {
35 *rs.borrow_mut() = Some(scope.clone());
36 });
37
38 ComposeGuard { scope }
39 }
40
41 pub fn scope(&self) -> &Scope {
42 &self.scope
43 }
44}
45
46impl Drop for ComposeGuard {
47 fn drop(&mut self) {
48 ROOT_SCOPE.with(|rs| {
49 *rs.borrow_mut() = None;
50 });
51 }
52}
53
54pub fn remember<T: 'static>(init: impl FnOnce() -> T) -> Rc<T> {
56 COMPOSER.with(|c| {
57 let mut c = c.borrow_mut();
58 if c.cursor >= c.slots.len() {
59 c.slots.push(Box::new(Rc::new(init())));
60 }
61 let cursor = c.cursor;
62 c.cursor += 1;
63 let boxed = &c.slots[cursor];
64 boxed.downcast_ref::<Rc<T>>().unwrap().clone()
65 })
66}
67
68pub fn remember_with_key<T: 'static>(key: impl Into<String>, init: impl FnOnce() -> T) -> Rc<T> {
70 COMPOSER.with(|c| {
71 let mut c = c.borrow_mut();
72 let key = key.into();
73
74 if !c.keyed_slots.contains_key(&key) {
75 c.keyed_slots.insert(key.clone(), Box::new(Rc::new(init())));
76 }
77
78 c.keyed_slots
79 .get(&key)
80 .unwrap()
81 .downcast_ref::<Rc<T>>()
82 .unwrap()
83 .clone()
84 })
85}
86
87pub fn remember_state<T: 'static>(init: impl FnOnce() -> T) -> Rc<RefCell<T>> {
88 remember(|| RefCell::new(init()))
89}
90
91pub fn remember_state_with_key<T: 'static>(
92 key: impl Into<String>,
93 init: impl FnOnce() -> T,
94) -> Rc<RefCell<T>> {
95 remember_with_key(key, || RefCell::new(init()))
96}
97
98pub struct Frame {
100 pub scene: Scene,
101 pub hit_regions: Vec<HitRegion>,
102 pub semantics_nodes: Vec<SemNode>,
103 pub focus_chain: Vec<u64>,
104}
105
106#[derive(Clone)]
107pub struct HitRegion {
108 pub id: u64,
109 pub rect: Rect,
110 pub on_click: Option<Rc<dyn Fn()>>,
111 pub on_scroll: Option<Rc<dyn Fn(f32) -> f32>>,
112 pub focusable: bool,
113 pub on_pointer_down: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
114 pub on_pointer_move: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
115 pub on_pointer_up: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
116 pub on_pointer_enter: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
117 pub on_pointer_leave: Option<Rc<dyn Fn(crate::input::PointerEvent)>>,
118 pub z_index: f32,
119 pub on_text_change: Option<Rc<dyn Fn(String)>>,
120}
121
122#[derive(Clone)]
123pub struct SemNode {
124 pub id: u64,
125 pub role: Role,
126 pub label: Option<String>,
127 pub rect: Rect,
128 pub focused: bool,
129 pub enabled: bool,
130}
131
132pub struct Scheduler {
133 next_id: u64,
134 pub focused: Option<u64>,
135 pub size: (u32, u32),
136}
137
138impl Scheduler {
139 pub fn new() -> Self {
140 Self {
141 next_id: 1,
142 focused: None,
143 size: (1280, 800),
144 }
145 }
146
147 pub fn id(&mut self) -> u64 {
148 let id = self.next_id;
149 self.next_id += 1;
150 id
151 }
152
153 pub fn repose<F>(
154 &mut self,
155 mut build_root: F,
156 layout_paint: impl Fn(&View, (u32, u32)) -> (Scene, Vec<HitRegion>, Vec<SemNode>),
157 ) -> Frame
158 where
159 F: FnMut(&mut Scheduler) -> View,
160 {
161 let guard = ComposeGuard::begin();
162 let root = guard.scope.run(|| build_root(self));
163 let (scene, hits, sem) = layout_paint(&root, self.size);
164
165 let focus_chain: Vec<u64> = hits.iter().filter(|h| h.focusable).map(|h| h.id).collect();
166
167 Frame {
168 scene,
169 hit_regions: hits,
170 semantics_nodes: sem,
171 focus_chain,
172 }
173 }
174}