1use crate::geometry::{Point, Rect};
21use std::collections::HashMap;
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
28pub struct WidgetId(pub u64);
29
30impl WidgetId {
31 pub const ROOT: WidgetId = WidgetId(0);
33}
34
35#[derive(Debug)]
39pub struct WidgetIdAllocator {
40 next: u64,
41}
42
43impl WidgetIdAllocator {
44 pub fn new() -> Self {
46 Self { next: 1 }
47 }
48
49 pub fn alloc(&mut self) -> WidgetId {
51 let id = WidgetId(self.next);
52 self.next += 1;
53 id
54 }
55
56 pub fn allocated(&self) -> u64 {
58 self.next - 1
59 }
60}
61
62impl Default for WidgetIdAllocator {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68#[derive(Clone, Debug)]
70pub struct WidgetNode {
71 pub id: WidgetId,
73 pub parent: Option<WidgetId>,
75 pub children: Vec<WidgetId>,
77 pub rect: Rect,
79 pub z_index: i32,
81 pub hit_testable: bool,
83 pub focusable: bool,
85 pub dirty: bool,
87 pub clip_rect: Option<Rect>,
91 pub label: Option<String>,
93}
94
95impl WidgetNode {
96 pub fn new(id: WidgetId, rect: Rect) -> Self {
99 Self {
100 id,
101 parent: None,
102 children: Vec::new(),
103 rect,
104 z_index: 0,
105 hit_testable: true,
106 focusable: false,
107 dirty: true,
108 clip_rect: None,
109 label: None,
110 }
111 }
112
113 pub fn with_clip(mut self, clip: Rect) -> Self {
115 self.clip_rect = Some(clip);
116 self
117 }
118}
119
120#[derive(Debug)]
128pub struct WidgetTree {
129 nodes: Vec<WidgetNode>,
131 index: HashMap<WidgetId, usize>,
133 alloc: WidgetIdAllocator,
134}
135
136impl WidgetTree {
137 pub fn new(root_rect: Rect) -> Self {
139 let mut root = WidgetNode::new(WidgetId::ROOT, root_rect);
140 root.label = Some("root".to_owned());
141 let mut index = HashMap::new();
142 index.insert(WidgetId::ROOT, 0usize);
143 Self {
144 nodes: vec![root],
145 index,
146 alloc: WidgetIdAllocator::new(),
147 }
148 }
149
150 pub fn len(&self) -> usize {
152 self.nodes.len()
153 }
154
155 pub fn is_empty(&self) -> bool {
157 self.nodes.len() <= 1
158 }
159
160 fn index_of(&self, id: WidgetId) -> Option<usize> {
161 self.index.get(&id).copied()
162 }
163
164 pub fn get(&self, id: WidgetId) -> Option<&WidgetNode> {
166 self.index_of(id).map(|i| &self.nodes[i])
167 }
168
169 pub fn get_mut(&mut self, id: WidgetId) -> Option<&mut WidgetNode> {
171 self.index_of(id).map(move |i| &mut self.nodes[i])
172 }
173
174 pub fn insert(&mut self, parent: WidgetId, rect: Rect) -> Option<WidgetId> {
177 self.index_of(parent)?;
178 let id = self.alloc.alloc();
179 let new_idx = self.nodes.len();
180 let mut node = WidgetNode::new(id, rect);
181 node.parent = Some(parent);
182 self.nodes.push(node);
183 self.index.insert(id, new_idx);
184 if let Some(pi) = self.index_of(parent) {
185 self.nodes[pi].children.push(id);
186 }
187 Some(id)
188 }
189
190 pub fn remove(&mut self, id: WidgetId) -> usize {
194 if id == WidgetId::ROOT {
195 return 0;
196 }
197 let mut to_remove = Vec::new();
199 let mut stack = vec![id];
200 while let Some(cur) = stack.pop() {
201 if let Some(node) = self.get(cur) {
202 stack.extend(node.children.iter().copied());
203 to_remove.push(cur);
204 }
205 }
206 if let Some(parent) = self.get(id).and_then(|n| n.parent) {
208 if let Some(pi) = self.index_of(parent) {
209 self.nodes[pi].children.retain(|&c| c != id);
210 }
211 }
212 let removed = to_remove.len();
213 self.nodes.retain(|n| !to_remove.contains(&n.id));
215 for rid in &to_remove {
217 self.index.remove(rid);
218 }
219 for (slot, node) in self.nodes.iter().enumerate() {
221 self.index.insert(node.id, slot);
222 }
223 removed
224 }
225
226 pub fn reparent(&mut self, id: WidgetId, new_parent: WidgetId) -> bool {
229 if id == WidgetId::ROOT || self.get(id).is_none() || self.get(new_parent).is_none() {
230 return false;
231 }
232 if self.is_descendant(new_parent, id) {
234 return false;
235 }
236 let old_parent = self.get(id).and_then(|n| n.parent);
237 if let Some(op) = old_parent {
238 if let Some(oi) = self.index_of(op) {
239 self.nodes[oi].children.retain(|&c| c != id);
240 }
241 }
242 if let Some(ni) = self.index_of(new_parent) {
243 self.nodes[ni].children.push(id);
244 }
245 if let Some(i) = self.index_of(id) {
246 self.nodes[i].parent = Some(new_parent);
247 }
248 true
249 }
250
251 pub fn is_descendant(&self, maybe_descendant: WidgetId, ancestor: WidgetId) -> bool {
253 let mut cur = maybe_descendant;
254 while let Some(node) = self.get(cur) {
255 match node.parent {
256 Some(p) if p == ancestor => return true,
257 Some(p) => cur = p,
258 None => return false,
259 }
260 }
261 false
262 }
263
264 pub fn depth(&self, id: WidgetId) -> Option<usize> {
266 let mut depth = 0;
267 let mut cur = self.get(id)?;
268 while let Some(parent) = cur.parent {
269 depth += 1;
270 cur = self.get(parent)?;
271 }
272 Some(depth)
273 }
274
275 pub fn walk_dfs(&self, mut visit: impl FnMut(&WidgetNode, usize)) {
277 let mut stack = vec![(WidgetId::ROOT, 0usize)];
278 while let Some((id, depth)) = stack.pop() {
279 if let Some(node) = self.get(id) {
280 visit(node, depth);
281 for &child in node.children.iter().rev() {
283 stack.push((child, depth + 1));
284 }
285 }
286 }
287 }
288
289 pub fn effective_clip(&self, id: WidgetId) -> Option<Rect> {
297 let mut acc: Option<Rect> = None;
298 let mut cur = self.get(id);
299 while let Some(node) = cur {
300 if let Some(clip) = node.clip_rect {
301 acc = Some(match acc {
302 None => clip,
303 Some(existing) => existing.intersection(&clip).unwrap_or(Rect::ZERO),
304 });
305 }
306 cur = node.parent.and_then(|p| self.get(p));
307 }
308 acc
309 }
310
311 pub fn hit_test(&self, point: Point) -> Option<WidgetId> {
320 let mut best: Option<(WidgetId, usize, i32)> = None;
321 self.walk_dfs(|node, depth| {
322 if !node.hit_testable || !node.rect.contains(point) {
323 return;
324 }
325 if let Some(clip) = self.effective_clip(node.id) {
328 if !clip.contains(point) {
329 return;
330 }
331 }
332 let key = (depth, node.z_index);
333 match best {
334 Some((_, bd, bz)) if (bd, bz) >= key => {}
335 _ => best = Some((node.id, depth, node.z_index)),
336 }
337 });
338 best.map(|(id, _, _)| id)
339 }
340
341 pub fn focus_order(&self) -> Vec<WidgetId> {
343 let mut order = Vec::new();
344 self.walk_dfs(|node, _| {
345 if node.focusable {
346 order.push(node.id);
347 }
348 });
349 order
350 }
351
352 pub fn paint_order(&self) -> Vec<WidgetId> {
360 let mut entries: Vec<(usize, i32, usize, WidgetId)> = Vec::new();
361 let mut seq = 0usize;
362 self.walk_dfs(|node, depth| {
363 entries.push((depth, node.z_index, seq, node.id));
364 seq += 1;
365 });
366 entries.sort_by_key(|&(depth, z, seq, _)| (depth, z, seq));
369 entries.into_iter().map(|(_, _, _, id)| id).collect()
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376 use crate::geometry::Rect;
377
378 fn sample_tree() -> WidgetTree {
379 let mut t = WidgetTree::new(Rect::new(0.0, 0.0, 200.0, 200.0));
380 let a = t
381 .insert(WidgetId::ROOT, Rect::new(0.0, 0.0, 100.0, 100.0))
382 .expect("root exists");
383 let _b = t
384 .insert(WidgetId::ROOT, Rect::new(100.0, 0.0, 100.0, 100.0))
385 .expect("root exists");
386 let _a1 = t
387 .insert(a, Rect::new(10.0, 10.0, 30.0, 30.0))
388 .expect("a exists");
389 t
390 }
391
392 #[test]
393 fn id_allocator_is_monotonic_and_unique() {
394 let mut alloc = WidgetIdAllocator::new();
395 let a = alloc.alloc();
396 let b = alloc.alloc();
397 assert_ne!(a, b);
398 assert_eq!(a, WidgetId(1));
399 assert_eq!(b, WidgetId(2));
400 assert_eq!(alloc.allocated(), 2);
401 }
402
403 #[test]
404 fn insert_and_structure() {
405 let t = sample_tree();
406 assert_eq!(t.len(), 4); let root = t.get(WidgetId::ROOT).expect("root");
408 assert_eq!(root.children.len(), 2);
409 assert_eq!(t.depth(WidgetId(3)), Some(2)); }
411
412 #[test]
413 fn remove_subtree() {
414 let mut t = sample_tree();
415 let removed = t.remove(WidgetId(1)); assert_eq!(removed, 2);
417 assert_eq!(t.len(), 2); assert!(t.get(WidgetId(1)).is_none());
419 assert!(t.get(WidgetId(3)).is_none());
420 assert_eq!(t.remove(WidgetId::ROOT), 0);
422 }
423
424 #[test]
425 fn reparent_rejects_cycles() {
426 let mut t = sample_tree();
427 assert!(!t.reparent(WidgetId(1), WidgetId(3)));
429 assert!(t.reparent(WidgetId(3), WidgetId(2)));
432 assert_eq!(t.depth(WidgetId(3)), Some(2));
433 assert_eq!(t.get(WidgetId(3)).and_then(|n| n.parent), Some(WidgetId(2)));
435 }
436
437 #[test]
438 fn hit_test_front_most_wins() {
439 let mut t = WidgetTree::new(Rect::new(0.0, 0.0, 100.0, 100.0));
440 let back = t
442 .insert(WidgetId::ROOT, Rect::new(0.0, 0.0, 50.0, 50.0))
443 .expect("root");
444 let front = t
445 .insert(back, Rect::new(10.0, 10.0, 20.0, 20.0))
446 .expect("back");
447 let hit = t.hit_test(Point::new(15.0, 15.0));
448 assert_eq!(hit, Some(front));
449 assert_eq!(t.hit_test(Point::new(45.0, 45.0)), Some(back));
451 assert_eq!(t.hit_test(Point::new(90.0, 90.0)), Some(WidgetId::ROOT));
453 }
454
455 #[test]
456 fn hit_test_skips_non_hit_testable() {
457 let mut t = WidgetTree::new(Rect::new(0.0, 0.0, 100.0, 100.0));
458 let overlay = t
459 .insert(WidgetId::ROOT, Rect::new(0.0, 0.0, 100.0, 100.0))
460 .expect("root");
461 if let Some(n) = t.get_mut(overlay) {
462 n.hit_testable = false; }
464 assert_eq!(t.hit_test(Point::new(50.0, 50.0)), Some(WidgetId::ROOT));
466 }
467
468 #[test]
469 fn focus_order_dfs() {
470 let mut t = WidgetTree::new(Rect::ZERO);
471 let a = t.insert(WidgetId::ROOT, Rect::ZERO).expect("root");
472 let b = t.insert(WidgetId::ROOT, Rect::ZERO).expect("root");
473 for id in [a, b] {
474 if let Some(n) = t.get_mut(id) {
475 n.focusable = true;
476 }
477 }
478 assert_eq!(t.focus_order(), vec![a, b]);
479 }
480
481 #[test]
482 fn hit_test_respects_clip_rect() {
483 let mut t = WidgetTree::new(Rect::new(0.0, 0.0, 100.0, 100.0));
484 let container = t
486 .insert(WidgetId::ROOT, Rect::new(0.0, 0.0, 100.0, 100.0))
487 .expect("root");
488 if let Some(n) = t.get_mut(container) {
489 n.clip_rect = Some(Rect::new(0.0, 0.0, 50.0, 100.0));
490 }
491 let child = t
493 .insert(container, Rect::new(40.0, 0.0, 40.0, 20.0))
494 .expect("container");
495 assert_eq!(t.hit_test(Point::new(45.0, 5.0)), Some(child));
497 assert_eq!(t.hit_test(Point::new(70.0, 5.0)), Some(WidgetId::ROOT));
501 }
502
503 #[test]
504 fn effective_clip_intersects_ancestors() {
505 let mut t = WidgetTree::new(Rect::new(0.0, 0.0, 100.0, 100.0));
506 let outer = t
507 .insert(WidgetId::ROOT, Rect::new(0.0, 0.0, 100.0, 100.0))
508 .expect("root");
509 if let Some(n) = t.get_mut(outer) {
510 n.clip_rect = Some(Rect::new(0.0, 0.0, 60.0, 100.0));
511 }
512 let inner = t
513 .insert(outer, Rect::new(0.0, 0.0, 100.0, 100.0))
514 .expect("outer");
515 if let Some(n) = t.get_mut(inner) {
516 n.clip_rect = Some(Rect::new(40.0, 0.0, 60.0, 100.0));
517 }
518 assert_eq!(
520 t.effective_clip(inner),
521 Some(Rect::new(40.0, 0.0, 20.0, 100.0))
522 );
523 assert_eq!(t.effective_clip(WidgetId::ROOT), None);
525 }
526
527 #[test]
528 fn paint_order_back_to_front() {
529 let mut t = WidgetTree::new(Rect::ZERO);
530 let a = t.insert(WidgetId::ROOT, Rect::ZERO).expect("root");
532 let b = t.insert(WidgetId::ROOT, Rect::ZERO).expect("root");
533 let a1 = t.insert(a, Rect::ZERO).expect("a"); if let Some(n) = t.get_mut(a) {
535 n.z_index = 5;
536 }
537 if let Some(n) = t.get_mut(b) {
538 n.z_index = 1;
539 }
540 let order = t.paint_order();
541 assert_eq!(order[0], WidgetId::ROOT);
543 let pos_a = order.iter().position(|&x| x == a).expect("a present");
544 let pos_b = order.iter().position(|&x| x == b).expect("b present");
545 let pos_a1 = order.iter().position(|&x| x == a1).expect("a1 present");
546 assert!(pos_b < pos_a, "lower z_index paints first");
547 assert!(pos_a1 > pos_a && pos_a1 > pos_b);
549 }
550}