1use std::{collections::HashMap, fmt, panic::Location, sync::Arc};
4
5use bumpalo::collections::Vec as BumpVec;
6use kurbo::{RoundedRect, Size, Vec2};
7
8use crate::{
9 hasher::IdentityBuildHasher,
10 interner::{StrId, StringInterner},
11 layout::TextCacheEntry,
12 prelude::*,
13};
14
15type EventCallback<S, H> = Box<dyn Fn(&mut S, &mut EventCtx<H>)>;
16type StyleCallback<S> = Box<dyn Fn(&S, &mut Style)>;
17type MeasureCallback<S> = Box<dyn Fn(&S, &MeasureCtx) -> Size>;
18type CanvasCallback<S> = Box<dyn Fn(&S, &mut CanvasCtx)>;
19type AccessibilityCallback<S> = Arc<dyn for<'a> Fn(&S, &mut AccessibilityCtx<'a>) + Send + Sync + 'static>;
20
21pub(crate) struct Node<S: 'static, H> {
22 pub nid: Option<NodeId>,
23 pub classes: Vec<StrId>,
24 pub text: Option<UIString>,
25 pub enabled: UIParam<bool>,
26 pub offset: Option<UIParam<Vec2>>,
27 pub event_callbacks: Vec<(On, EventCallback<S, H>)>,
28 pub style_sheet: Option<Stylesheet>,
29 pub style_callback: Option<StyleCallback<S>>,
30 pub measure_callback: Option<MeasureCallback<S>>,
31 pub canvas_callback: Option<CanvasCallback<S>>,
32 pub accessibility_callback: Option<AccessibilityCallback<S>>,
33 pub parent: usize,
34 pub num_children: usize,
35 pub subtree_size: usize,
36}
37
38impl<S, H> fmt::Debug for Node<S, H> {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 f.debug_struct("Node")
41 .field("id", &self.nid)
42 .field("classes", &self.classes)
43 .field("text", &self.text.is_some())
44 .field("enabled", &self.enabled.get())
45 .field("offset", &self.offset.as_ref().map(|p| p.get()))
46 .field("event_callbacks", &self.event_callbacks.len())
47 .field("style_sheet", &self.style_sheet.is_some())
48 .field("style_callback", &self.style_callback.is_some())
49 .field("canvas_callback", &self.canvas_callback.is_some())
50 .field("accessibility_callback", &self.accessibility_callback.is_some())
51 .field("parent", &self.parent)
52 .field("num_children", &self.num_children)
53 .field("subtree_size", &self.subtree_size)
54 .finish()
55 }
56}
57
58impl<S, H> Node<S, H> {
59 pub fn run_callbacks(&mut self, event_type: On, state: &mut S, ctx: &mut EventCtx<H>) -> bool {
60 let mut has_callback = false;
61 for (et, callback) in self.event_callbacks.iter_mut() {
62 if *et == event_type {
63 (callback)(state, ctx);
64 has_callback = true;
65 }
66 }
67 has_callback
68 }
69
70 pub fn has_callback(&self, event_type: On) -> bool {
71 for (et, _) in self.event_callbacks.iter() {
72 if *et == event_type {
73 return true;
74 }
75 }
76 false
77 }
78}
79
80pub struct Ui<S: 'static, H: 'static> {
82 pub(crate) nodes: Vec<Node<S, H>>,
84
85 pub(crate) on_anim_nodes: Vec<usize>,
87
88 pub(crate) on_style_nodes: Vec<usize>,
90
91 pub(crate) on_style_deps: HashMap<usize, DependencyMap, IdentityBuildHasher>,
93
94 pub(crate) dynamic_enabled_prev: Vec<(usize, bool)>,
96
97 pub(crate) fixed_nodes: Vec<usize>,
99
100 pub(crate) nid_map: HashMap<NodeId, usize, IdentityBuildHasher>,
102
103 pub(crate) nid_sorted: Vec<NodeId>,
105
106 pub(crate) style_cache: Vec<Style>,
108
109 pub(crate) style_flags: Vec<u8>,
111
112 pub(crate) dirty_roots: Vec<usize>,
114
115 pub(crate) var_scope_cache: Vec<Vec<(Arc<str>, Arc<str>)>>,
117
118 pub(crate) layout_cache: Vec<RoundedRect>,
120
121 pub(crate) text_cache: HashMap<usize, TextCacheEntry, IdentityBuildHasher>,
123
124 pub(crate) max_children: usize,
126
127 current_parent_idx: usize,
129
130 current_node_idx: usize,
132}
133
134impl<S: 'static, H: 'static> fmt::Debug for Ui<S, H> {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 f.debug_struct("Ui")
137 .field("nodes", &self.nodes)
138 .field("on_anim_nodes", &self.on_anim_nodes)
139 .field("on_style_nodes", &self.on_style_nodes)
140 .field("on_style_deps", &self.on_style_deps)
141 .field("dynamic_enabled_prev", &self.dynamic_enabled_prev)
142 .field("fixed_nodes", &self.fixed_nodes)
143 .field("nid_map", &self.nid_map)
144 .field("nid_sorted", &self.nid_sorted)
145 .field("style_cache", &self.style_cache)
146 .field("style_flags", &self.style_flags)
147 .field("dirty_roots", &self.dirty_roots)
148 .field("var_scope_cache", &self.var_scope_cache)
149 .field("layout_cache", &self.layout_cache)
150 .field("text_cache", &self.text_cache)
151 .field("max_children", &self.max_children)
152 .field("current_parent_idx", &self.current_parent_idx)
153 .field("current_node_idx", &self.current_node_idx)
154 .finish()
155 }
156}
157
158impl<S, H> Ui<S, H> {
159 pub(crate) fn new() -> Self {
160 Ui {
161 nodes: Vec::with_capacity(1000),
162 on_anim_nodes: Vec::new(),
163 on_style_nodes: Vec::new(),
164 on_style_deps: HashMap::with_hasher(IdentityBuildHasher),
165 dynamic_enabled_prev: Vec::new(),
166 fixed_nodes: Vec::new(),
167 nid_map: HashMap::with_hasher(IdentityBuildHasher),
168 nid_sorted: Vec::new(),
169 style_cache: Vec::new(),
170 style_flags: Vec::new(),
171 dirty_roots: Vec::new(),
172 var_scope_cache: Vec::new(),
173 layout_cache: Vec::new(),
174 text_cache: HashMap::with_hasher(IdentityBuildHasher),
175 max_children: 0,
176 current_parent_idx: usize::MAX,
177 current_node_idx: usize::MAX,
178 }
179 }
180
181 pub(crate) fn clear(&mut self) {
182 self.nodes.clear();
183 self.on_anim_nodes.clear();
184 self.on_style_nodes.clear();
185 self.on_style_deps.clear();
186 self.dynamic_enabled_prev.clear();
187 self.fixed_nodes.clear();
188 self.nid_map.clear();
189 self.nid_sorted.clear();
190 self.style_cache.clear();
191 self.style_flags.clear();
192 self.dirty_roots.clear();
193 self.var_scope_cache.clear();
194 self.layout_cache.clear();
195 self.text_cache.clear();
196 self.max_children = 0;
197 self.current_parent_idx = usize::MAX;
198 self.current_node_idx = usize::MAX;
199 }
200
201 pub(crate) fn finish(&mut self) {
202 self.nid_sorted.clear();
203 self.nid_sorted.reserve_exact(self.nid_map.len());
204 self.nid_sorted.extend(self.nid_map.keys().copied());
205 self.nid_sorted.sort_unstable();
206
207 self.on_anim_nodes.sort_unstable();
208 self.on_anim_nodes.dedup();
209
210 self.on_style_nodes.sort_unstable();
211 self.on_style_nodes.dedup();
212
213 let len = self.nodes.len();
214 self.style_cache.resize(len, Style::default());
215 self.style_flags.reserve_exact(len);
216 self.var_scope_cache.reserve_exact(len);
217
218 self.dynamic_enabled_prev.sort_unstable();
219 self.dynamic_enabled_prev.dedup();
220 for (idx, value) in &mut self.dynamic_enabled_prev {
221 *value = self.nodes[*idx].enabled.get_or(true);
222 }
223
224 self.dirty_roots.push(0);
225 }
226
227 pub(crate) fn child_indexes(&self, parent: usize, output: &mut BumpVec<usize>) {
229 output.clear();
230 let count = self.nodes[parent].num_children;
231 if count == 0 {
232 return;
233 }
234 output.reserve(count);
235
236 let mut idx = parent + 1;
238 let end = idx + self.nodes[parent].subtree_size;
239 while idx < end {
240 output.push(idx);
241 idx += self.nodes[idx].subtree_size + 1;
242 }
243 }
244
245 pub(crate) fn add_dirty_root_by_nid(&mut self, nid: Option<NodeId>, flag: u8) -> bool {
249 let Some(nid) = nid else {
250 return false;
251 };
252 let Some(&idx) = self.nid_map.get(&nid) else {
253 return false;
254 };
255 self.add_dirty_root_by_idx(idx, flag)
256 }
257
258 pub(crate) fn add_dirty_root_by_idx(&mut self, idx: usize, flag: u8) -> bool {
262 if (self.style_flags[idx] & flag) != 0 {
263 self.dirty_roots.push(idx);
264 true
265 } else {
266 false
267 }
268 }
269
270 pub(crate) fn merge_dirty_roots(&mut self) {
272 self.dirty_roots.sort_unstable();
273
274 let mut kept = 0usize; let mut dirty_until = 0usize;
276
277 for r in 0..self.dirty_roots.len() {
278 let idx = self.dirty_roots[r];
279
280 if kept != 0 && self.dirty_roots[kept - 1] == idx {
282 continue;
283 }
284
285 if idx < dirty_until {
287 continue;
288 }
289
290 self.dirty_roots[kept] = idx;
291 kept += 1;
292
293 dirty_until = idx + self.nodes[idx].subtree_size + 1;
294 }
295
296 self.dirty_roots.truncate(kept);
297 }
298
299 #[track_caller]
303 #[inline]
304 pub fn node(&mut self) -> &mut Self {
305 debug_assert!(self.current_parent_idx != usize::MAX || self.nodes.is_empty(), "There can only be one root node.");
306
307 if self.current_parent_idx != usize::MAX {
308 let parent = &mut self.nodes[self.current_parent_idx];
309 parent.num_children += 1;
310 }
311
312 self.current_node_idx = self.nodes.len();
313
314 self.nodes.push(Node {
315 nid: None,
316 classes: Vec::new(),
317 text: None,
318 enabled: UIParam::Static(true),
319 offset: None,
320 event_callbacks: Vec::new(),
321 style_sheet: None,
322 style_callback: None,
323 measure_callback: None,
324 canvas_callback: None,
325 accessibility_callback: None,
326 parent: self.current_parent_idx,
327 num_children: 0,
328 subtree_size: 0,
329 });
330
331 self
332 }
333
334 #[track_caller]
338 #[inline]
339 pub fn id(&mut self, id: NodeId) -> &mut Self {
340 debug_assert!(self.current_node_idx != usize::MAX, "You must call .node() before setting properties.");
341 let current_node = &mut self.nodes[self.current_node_idx];
342
343 if let Some(old_id) = current_node.nid {
344 self.nid_map.remove(&old_id);
345 }
346
347 current_node.nid = Some(id);
348
349 if self.nid_map.insert(id, self.current_node_idx).is_some() {
350 let location = Location::caller();
351 panic!("NodeId reused at {location}.");
352 }
353
354 self
355 }
356
357 #[track_caller]
361 #[inline]
362 pub fn classes<'a>(&mut self, classes: impl Into<Option<&'a str>>) -> &mut Self {
363 debug_assert!(self.current_node_idx != usize::MAX, "You must call .node() before setting properties.");
364 let current_node = &mut self.nodes[self.current_node_idx];
365 let classes = classes.into();
366 if let Some(classes) = classes {
367 let mut interner = StringInterner::global().write();
368 for class in classes.split_whitespace() {
369 let str_id = interner.intern(class);
370 if !current_node.classes.contains(&str_id) {
371 current_node.classes.push(str_id);
372 }
373 }
374 } else {
375 current_node.classes.clear();
376 }
377 self
378 }
379
380 #[track_caller]
382 #[inline]
383 pub fn text(&mut self, text: impl Into<UIString>) -> &mut Self {
384 debug_assert!(self.current_node_idx != usize::MAX, "You must call .node() before setting properties.");
385 let current_node = &mut self.nodes[self.current_node_idx];
386 current_node.text = Some(text.into());
387 self
388 }
389
390 #[track_caller]
394 #[inline]
395 pub fn enabled(&mut self, enabled: impl Into<UIParam<bool>>) -> &mut Self {
396 debug_assert!(self.current_node_idx != usize::MAX, "You must call .node() before setting properties.");
397 let enabled = enabled.into();
398 if let UIParam::Dynamic(_) = enabled {
399 self.dynamic_enabled_prev.push((self.current_node_idx, true));
401 }
402 self.nodes[self.current_node_idx].enabled = enabled;
403 self
404 }
405
406 #[track_caller]
410 #[inline]
411 pub fn offset(&mut self, offset: impl Into<UIParam<Vec2>>) -> &mut Self {
412 debug_assert!(self.current_node_idx != usize::MAX, "You must call .node() before setting properties.");
413 self.nodes[self.current_node_idx].offset = Some(offset.into());
414 self
415 }
416
417 #[track_caller]
421 #[inline]
422 pub fn style_sheet<'a>(&mut self, style_sheet: impl Into<Option<&'a Stylesheet>>) -> &mut Self {
423 debug_assert!(self.current_node_idx != usize::MAX, "You must call .node() before setting properties.");
424 self.nodes[self.current_node_idx].style_sheet = style_sheet.into().cloned();
425 self
426 }
427
428 #[track_caller]
430 #[inline]
431 pub fn event(&mut self, event_type: On, callback: impl Fn(&mut S, &mut EventCtx<H>) + 'static) -> &mut Self {
432 debug_assert!(self.current_node_idx != usize::MAX, "You must call .node() before setting properties.");
433 if event_type == On::AnimationFrame {
434 self.on_anim_nodes.push(self.current_node_idx);
435 }
436 self.nodes[self.current_node_idx].event_callbacks.push((event_type, Box::new(callback)));
437 self
438 }
439
440 #[track_caller]
445 #[inline]
446 pub fn on_style(&mut self, callback: impl Fn(&S, &mut Style) + 'static) -> &mut Self {
447 debug_assert!(self.current_node_idx != usize::MAX, "You must call .node() before setting properties.");
448 self.on_style_nodes.push(self.current_node_idx);
449 self.nodes[self.current_node_idx].style_callback = Some(Box::new(callback));
450 self
451 }
452
453 #[track_caller]
458 #[inline]
459 pub fn on_measure(&mut self, callback: impl Fn(&S, &MeasureCtx) -> Size + 'static) -> &mut Self {
460 debug_assert!(self.current_node_idx != usize::MAX, "You must call .node() before setting properties.");
461 self.nodes[self.current_node_idx].measure_callback = Some(Box::new(callback));
462 self
463 }
464
465 #[track_caller]
469 #[inline]
470 pub fn on_canvas(&mut self, callback: impl Fn(&S, &mut CanvasCtx) + 'static) -> &mut Self {
471 debug_assert!(self.current_node_idx != usize::MAX, "You must call .node() before setting properties.");
472 self.nodes[self.current_node_idx].canvas_callback = Some(Box::new(callback));
473 self
474 }
475
476 #[track_caller]
480 #[inline]
481 pub fn on_accessibility(&mut self, f: impl for<'a> Fn(&S, &mut AccessibilityCtx<'a>) + Send + Sync + 'static) -> &mut Self {
482 debug_assert!(self.current_node_idx != usize::MAX, "You must call .node() before setting properties.");
483 self.nodes[self.current_node_idx].accessibility_callback = Some(std::sync::Arc::new(f));
484 self
485 }
486
487 #[track_caller]
489 #[inline]
490 pub fn children(&mut self, func: impl FnOnce(&mut Ui<S, H>)) -> &mut Self {
491 debug_assert!(self.current_node_idx != usize::MAX, "You must call .node() before setting properties.");
492 let parent_index = self.current_parent_idx;
493 let node_index = self.current_node_idx;
494
495 self.current_parent_idx = node_index;
496 self.current_node_idx = usize::MAX;
497 func(self);
498
499 let subtree_size = self.nodes.len() - (1 + node_index);
500 self.nodes[node_index].subtree_size = subtree_size;
501
502 self.current_parent_idx = parent_index;
503 self.current_node_idx = node_index;
504 self.max_children = self.max_children.max(self.nodes[node_index].num_children);
505
506 self
507 }
508}