1use crate::geometry::{Point, Rect};
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
15pub struct WidgetId(pub u64);
16
17impl WidgetId {
18 pub const ROOT: WidgetId = WidgetId(0);
20}
21
22#[derive(Debug)]
26pub struct WidgetIdAllocator {
27 next: u64,
28}
29
30impl WidgetIdAllocator {
31 pub fn new() -> Self {
33 Self { next: 1 }
34 }
35
36 pub fn alloc(&mut self) -> WidgetId {
38 let id = WidgetId(self.next);
39 self.next += 1;
40 id
41 }
42
43 pub fn allocated(&self) -> u64 {
45 self.next - 1
46 }
47}
48
49impl Default for WidgetIdAllocator {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55#[derive(Clone, Debug)]
57pub struct WidgetNode {
58 pub id: WidgetId,
60 pub parent: Option<WidgetId>,
62 pub children: Vec<WidgetId>,
64 pub rect: Rect,
66 pub z_index: i32,
68 pub hit_testable: bool,
70 pub focusable: bool,
72 pub dirty: bool,
74 pub clip_rect: Option<Rect>,
78 pub label: Option<String>,
80}
81
82impl WidgetNode {
83 pub fn new(id: WidgetId, rect: Rect) -> Self {
86 Self {
87 id,
88 parent: None,
89 children: Vec::new(),
90 rect,
91 z_index: 0,
92 hit_testable: true,
93 focusable: false,
94 dirty: true,
95 clip_rect: None,
96 label: None,
97 }
98 }
99
100 pub fn with_clip(mut self, clip: Rect) -> Self {
102 self.clip_rect = Some(clip);
103 self
104 }
105}
106
107#[derive(Debug)]
112pub struct WidgetTree {
113 nodes: Vec<WidgetNode>,
114 alloc: WidgetIdAllocator,
115}
116
117impl WidgetTree {
118 pub fn new(root_rect: Rect) -> Self {
120 let mut root = WidgetNode::new(WidgetId::ROOT, root_rect);
121 root.label = Some("root".to_owned());
122 Self {
123 nodes: vec![root],
124 alloc: WidgetIdAllocator::new(),
125 }
126 }
127
128 pub fn len(&self) -> usize {
130 self.nodes.len()
131 }
132
133 pub fn is_empty(&self) -> bool {
135 self.nodes.len() <= 1
136 }
137
138 fn index_of(&self, id: WidgetId) -> Option<usize> {
139 self.nodes.iter().position(|n| n.id == id)
140 }
141
142 pub fn get(&self, id: WidgetId) -> Option<&WidgetNode> {
144 self.index_of(id).map(|i| &self.nodes[i])
145 }
146
147 pub fn get_mut(&mut self, id: WidgetId) -> Option<&mut WidgetNode> {
149 self.index_of(id).map(move |i| &mut self.nodes[i])
150 }
151
152 pub fn insert(&mut self, parent: WidgetId, rect: Rect) -> Option<WidgetId> {
155 self.index_of(parent)?;
156 let id = self.alloc.alloc();
157 let mut node = WidgetNode::new(id, rect);
158 node.parent = Some(parent);
159 self.nodes.push(node);
160 if let Some(pi) = self.index_of(parent) {
161 self.nodes[pi].children.push(id);
162 }
163 Some(id)
164 }
165
166 pub fn remove(&mut self, id: WidgetId) -> usize {
170 if id == WidgetId::ROOT {
171 return 0;
172 }
173 let mut to_remove = Vec::new();
175 let mut stack = vec![id];
176 while let Some(cur) = stack.pop() {
177 if let Some(node) = self.get(cur) {
178 stack.extend(node.children.iter().copied());
179 to_remove.push(cur);
180 }
181 }
182 if let Some(parent) = self.get(id).and_then(|n| n.parent) {
184 if let Some(pi) = self.index_of(parent) {
185 self.nodes[pi].children.retain(|&c| c != id);
186 }
187 }
188 let removed = to_remove.len();
189 self.nodes.retain(|n| !to_remove.contains(&n.id));
190 removed
191 }
192
193 pub fn reparent(&mut self, id: WidgetId, new_parent: WidgetId) -> bool {
196 if id == WidgetId::ROOT || self.get(id).is_none() || self.get(new_parent).is_none() {
197 return false;
198 }
199 if self.is_descendant(new_parent, id) {
201 return false;
202 }
203 let old_parent = self.get(id).and_then(|n| n.parent);
204 if let Some(op) = old_parent {
205 if let Some(oi) = self.index_of(op) {
206 self.nodes[oi].children.retain(|&c| c != id);
207 }
208 }
209 if let Some(ni) = self.index_of(new_parent) {
210 self.nodes[ni].children.push(id);
211 }
212 if let Some(i) = self.index_of(id) {
213 self.nodes[i].parent = Some(new_parent);
214 }
215 true
216 }
217
218 pub fn is_descendant(&self, maybe_descendant: WidgetId, ancestor: WidgetId) -> bool {
220 let mut cur = maybe_descendant;
221 while let Some(node) = self.get(cur) {
222 match node.parent {
223 Some(p) if p == ancestor => return true,
224 Some(p) => cur = p,
225 None => return false,
226 }
227 }
228 false
229 }
230
231 pub fn depth(&self, id: WidgetId) -> Option<usize> {
233 let mut depth = 0;
234 let mut cur = self.get(id)?;
235 while let Some(parent) = cur.parent {
236 depth += 1;
237 cur = self.get(parent)?;
238 }
239 Some(depth)
240 }
241
242 pub fn walk_dfs(&self, mut visit: impl FnMut(&WidgetNode, usize)) {
244 let mut stack = vec![(WidgetId::ROOT, 0usize)];
245 while let Some((id, depth)) = stack.pop() {
246 if let Some(node) = self.get(id) {
247 visit(node, depth);
248 for &child in node.children.iter().rev() {
250 stack.push((child, depth + 1));
251 }
252 }
253 }
254 }
255
256 pub fn effective_clip(&self, id: WidgetId) -> Option<Rect> {
264 let mut acc: Option<Rect> = None;
265 let mut cur = self.get(id);
266 while let Some(node) = cur {
267 if let Some(clip) = node.clip_rect {
268 acc = Some(match acc {
269 None => clip,
270 Some(existing) => existing.intersection(&clip).unwrap_or(Rect::ZERO),
271 });
272 }
273 cur = node.parent.and_then(|p| self.get(p));
274 }
275 acc
276 }
277
278 pub fn hit_test(&self, point: Point) -> Option<WidgetId> {
287 let mut best: Option<(WidgetId, usize, i32)> = None;
288 self.walk_dfs(|node, depth| {
289 if !node.hit_testable || !node.rect.contains(point) {
290 return;
291 }
292 if let Some(clip) = self.effective_clip(node.id) {
295 if !clip.contains(point) {
296 return;
297 }
298 }
299 let key = (depth, node.z_index);
300 match best {
301 Some((_, bd, bz)) if (bd, bz) >= key => {}
302 _ => best = Some((node.id, depth, node.z_index)),
303 }
304 });
305 best.map(|(id, _, _)| id)
306 }
307
308 pub fn focus_order(&self) -> Vec<WidgetId> {
310 let mut order = Vec::new();
311 self.walk_dfs(|node, _| {
312 if node.focusable {
313 order.push(node.id);
314 }
315 });
316 order
317 }
318
319 pub fn paint_order(&self) -> Vec<WidgetId> {
327 let mut entries: Vec<(usize, i32, usize, WidgetId)> = Vec::new();
328 let mut seq = 0usize;
329 self.walk_dfs(|node, depth| {
330 entries.push((depth, node.z_index, seq, node.id));
331 seq += 1;
332 });
333 entries.sort_by_key(|&(depth, z, seq, _)| (depth, z, seq));
336 entries.into_iter().map(|(_, _, _, id)| id).collect()
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343 use crate::geometry::Rect;
344
345 fn sample_tree() -> WidgetTree {
346 let mut t = WidgetTree::new(Rect::new(0.0, 0.0, 200.0, 200.0));
347 let a = t
348 .insert(WidgetId::ROOT, Rect::new(0.0, 0.0, 100.0, 100.0))
349 .expect("root exists");
350 let _b = t
351 .insert(WidgetId::ROOT, Rect::new(100.0, 0.0, 100.0, 100.0))
352 .expect("root exists");
353 let _a1 = t
354 .insert(a, Rect::new(10.0, 10.0, 30.0, 30.0))
355 .expect("a exists");
356 t
357 }
358
359 #[test]
360 fn id_allocator_is_monotonic_and_unique() {
361 let mut alloc = WidgetIdAllocator::new();
362 let a = alloc.alloc();
363 let b = alloc.alloc();
364 assert_ne!(a, b);
365 assert_eq!(a, WidgetId(1));
366 assert_eq!(b, WidgetId(2));
367 assert_eq!(alloc.allocated(), 2);
368 }
369
370 #[test]
371 fn insert_and_structure() {
372 let t = sample_tree();
373 assert_eq!(t.len(), 4); let root = t.get(WidgetId::ROOT).expect("root");
375 assert_eq!(root.children.len(), 2);
376 assert_eq!(t.depth(WidgetId(3)), Some(2)); }
378
379 #[test]
380 fn remove_subtree() {
381 let mut t = sample_tree();
382 let removed = t.remove(WidgetId(1)); assert_eq!(removed, 2);
384 assert_eq!(t.len(), 2); assert!(t.get(WidgetId(1)).is_none());
386 assert!(t.get(WidgetId(3)).is_none());
387 assert_eq!(t.remove(WidgetId::ROOT), 0);
389 }
390
391 #[test]
392 fn reparent_rejects_cycles() {
393 let mut t = sample_tree();
394 assert!(!t.reparent(WidgetId(1), WidgetId(3)));
396 assert!(t.reparent(WidgetId(3), WidgetId(2)));
399 assert_eq!(t.depth(WidgetId(3)), Some(2));
400 assert_eq!(t.get(WidgetId(3)).and_then(|n| n.parent), Some(WidgetId(2)));
402 }
403
404 #[test]
405 fn hit_test_front_most_wins() {
406 let mut t = WidgetTree::new(Rect::new(0.0, 0.0, 100.0, 100.0));
407 let back = t
409 .insert(WidgetId::ROOT, Rect::new(0.0, 0.0, 50.0, 50.0))
410 .expect("root");
411 let front = t
412 .insert(back, Rect::new(10.0, 10.0, 20.0, 20.0))
413 .expect("back");
414 let hit = t.hit_test(Point::new(15.0, 15.0));
415 assert_eq!(hit, Some(front));
416 assert_eq!(t.hit_test(Point::new(45.0, 45.0)), Some(back));
418 assert_eq!(t.hit_test(Point::new(90.0, 90.0)), Some(WidgetId::ROOT));
420 }
421
422 #[test]
423 fn hit_test_skips_non_hit_testable() {
424 let mut t = WidgetTree::new(Rect::new(0.0, 0.0, 100.0, 100.0));
425 let overlay = t
426 .insert(WidgetId::ROOT, Rect::new(0.0, 0.0, 100.0, 100.0))
427 .expect("root");
428 if let Some(n) = t.get_mut(overlay) {
429 n.hit_testable = false; }
431 assert_eq!(t.hit_test(Point::new(50.0, 50.0)), Some(WidgetId::ROOT));
433 }
434
435 #[test]
436 fn focus_order_dfs() {
437 let mut t = WidgetTree::new(Rect::ZERO);
438 let a = t.insert(WidgetId::ROOT, Rect::ZERO).expect("root");
439 let b = t.insert(WidgetId::ROOT, Rect::ZERO).expect("root");
440 for id in [a, b] {
441 if let Some(n) = t.get_mut(id) {
442 n.focusable = true;
443 }
444 }
445 assert_eq!(t.focus_order(), vec![a, b]);
446 }
447
448 #[test]
449 fn hit_test_respects_clip_rect() {
450 let mut t = WidgetTree::new(Rect::new(0.0, 0.0, 100.0, 100.0));
451 let container = t
453 .insert(WidgetId::ROOT, Rect::new(0.0, 0.0, 100.0, 100.0))
454 .expect("root");
455 if let Some(n) = t.get_mut(container) {
456 n.clip_rect = Some(Rect::new(0.0, 0.0, 50.0, 100.0));
457 }
458 let child = t
460 .insert(container, Rect::new(40.0, 0.0, 40.0, 20.0))
461 .expect("container");
462 assert_eq!(t.hit_test(Point::new(45.0, 5.0)), Some(child));
464 assert_eq!(t.hit_test(Point::new(70.0, 5.0)), Some(WidgetId::ROOT));
468 }
469
470 #[test]
471 fn effective_clip_intersects_ancestors() {
472 let mut t = WidgetTree::new(Rect::new(0.0, 0.0, 100.0, 100.0));
473 let outer = t
474 .insert(WidgetId::ROOT, Rect::new(0.0, 0.0, 100.0, 100.0))
475 .expect("root");
476 if let Some(n) = t.get_mut(outer) {
477 n.clip_rect = Some(Rect::new(0.0, 0.0, 60.0, 100.0));
478 }
479 let inner = t
480 .insert(outer, Rect::new(0.0, 0.0, 100.0, 100.0))
481 .expect("outer");
482 if let Some(n) = t.get_mut(inner) {
483 n.clip_rect = Some(Rect::new(40.0, 0.0, 60.0, 100.0));
484 }
485 assert_eq!(
487 t.effective_clip(inner),
488 Some(Rect::new(40.0, 0.0, 20.0, 100.0))
489 );
490 assert_eq!(t.effective_clip(WidgetId::ROOT), None);
492 }
493
494 #[test]
495 fn paint_order_back_to_front() {
496 let mut t = WidgetTree::new(Rect::ZERO);
497 let a = t.insert(WidgetId::ROOT, Rect::ZERO).expect("root");
499 let b = t.insert(WidgetId::ROOT, Rect::ZERO).expect("root");
500 let a1 = t.insert(a, Rect::ZERO).expect("a"); if let Some(n) = t.get_mut(a) {
502 n.z_index = 5;
503 }
504 if let Some(n) = t.get_mut(b) {
505 n.z_index = 1;
506 }
507 let order = t.paint_order();
508 assert_eq!(order[0], WidgetId::ROOT);
510 let pos_a = order.iter().position(|&x| x == a).expect("a present");
511 let pos_b = order.iter().position(|&x| x == b).expect("b present");
512 let pos_a1 = order.iter().position(|&x| x == a1).expect("a1 present");
513 assert!(pos_b < pos_a, "lower z_index paints first");
514 assert!(pos_a1 > pos_a && pos_a1 > pos_b);
516 }
517}