1use std::borrow::Cow;
23use std::collections::{HashMap, HashSet};
24
25use kurbo::{Point, Rect, Size};
26use tree_arena::{ArenaMut, ArenaRef, TreeArena};
27
28use crate::view::{ChangeFlags, WidgetId};
29use crate::widget::{ChildPod, Widget};
30
31pub struct WidgetPod {
34 id: WidgetId,
35 widget: Box<dyn Widget>,
36 origin: Point,
37 size: Size,
38 flags: ChangeFlags,
39 type_name: &'static str,
41 debug_label: Option<Cow<'static, str>>,
43}
44
45impl WidgetPod {
46 pub const ERASED_TYPE_NAME: &'static str = "<erased dyn Widget>";
48
49 pub fn new(id: WidgetId, widget: Box<dyn Widget>) -> Self {
54 Self::with_type_name(id, widget, Self::ERASED_TYPE_NAME)
55 }
56
57 pub fn new_typed<W: Widget>(id: WidgetId, widget: W) -> Self {
64 Self::with_type_name(id, Box::new(widget), core::any::type_name::<W>())
65 }
66
67 fn with_type_name(id: WidgetId, widget: Box<dyn Widget>, type_name: &'static str) -> Self {
68 Self {
69 id,
70 widget,
71 origin: Point::ZERO,
72 size: Size::ZERO,
73 flags: ChangeFlags::LAYOUT | ChangeFlags::PAINT,
74 type_name,
75 debug_label: None,
76 }
77 }
78
79 pub fn id(&self) -> WidgetId {
81 self.id
82 }
83
84 pub fn type_name(&self) -> &'static str {
90 self.type_name
91 }
92
93 pub fn debug_label(&self) -> Option<&str> {
95 self.debug_label.as_deref()
96 }
97
98 pub fn set_debug_label(&mut self, label: impl Into<Cow<'static, str>>) {
100 self.debug_label = Some(label.into());
101 }
102
103 pub fn clear_debug_label(&mut self) {
105 self.debug_label = None;
106 }
107
108 pub fn widget(&self) -> &dyn Widget {
110 &*self.widget
111 }
112
113 pub fn widget_mut(&mut self) -> &mut dyn Widget {
115 &mut *self.widget
116 }
117
118 pub fn origin(&self) -> Point {
120 self.origin
121 }
122
123 pub fn size(&self) -> Size {
125 self.size
126 }
127
128 pub fn set_layout(&mut self, origin: Point, size: Size) {
130 self.origin = origin;
131 self.size = size;
132 }
133
134 pub fn flags(&self) -> ChangeFlags {
136 self.flags
137 }
138
139 pub fn merge_flags(&mut self, flags: ChangeFlags) {
141 self.flags |= flags;
142 }
143
144 pub fn clear_flags(&mut self) {
146 self.flags = ChangeFlags::NONE;
147 }
148}
149
150#[derive(Debug, Clone, PartialEq)]
155pub struct InspectNode {
156 pub id: WidgetId,
158 pub parent: Option<WidgetId>,
160 pub type_name: &'static str,
162 pub debug_label: Option<Cow<'static, str>>,
164 pub bounds: Rect,
167 pub children: Vec<WidgetId>,
169 pub depth: usize,
171}
172
173pub struct WidgetTree {
175 arena: TreeArena<WidgetPod>,
176 root_order: Vec<WidgetId>,
178 child_order: HashMap<WidgetId, Vec<WidgetId>>,
180}
181
182impl WidgetTree {
183 pub fn new() -> Self {
185 Self {
186 arena: TreeArena::new(),
187 root_order: Vec::new(),
188 child_order: HashMap::new(),
189 }
190 }
191
192 pub fn insert_root(&mut self, pod: WidgetPod) -> WidgetId {
194 let id = pod.id();
195 self.arena.roots_mut().insert(id.0, pod);
196 self.root_order.push(id);
197 id
198 }
199
200 pub fn insert_child(&mut self, parent: WidgetId, pod: WidgetPod) -> Option<WidgetId> {
202 let id = pod.id();
203 let mut parent_mut: ArenaMut<'_, WidgetPod> = self.arena.find_mut(parent.0)?;
204 parent_mut.children.insert(id.0, pod);
205 self.child_order.entry(parent).or_default().push(id);
206 Some(id)
207 }
208
209 pub fn pod(&self, id: WidgetId) -> Option<&WidgetPod> {
211 self.arena.find(id.0).map(|node| node.item)
212 }
213
214 pub fn pod_mut(&mut self, id: WidgetId) -> Option<&mut WidgetPod> {
216 self.arena.find_mut(id.0).map(|node| node.item)
217 }
218
219 pub fn roots(&self) -> Vec<WidgetId> {
221 let live: HashSet<u64> = self.arena.root_ids().collect();
222 order_against(&self.root_order, &live)
223 }
224
225 pub fn children(&self, id: WidgetId) -> Vec<WidgetId> {
227 match self.arena.find(id.0) {
228 Some(node) => self.ordered_children(id, node),
229 None => Vec::new(),
230 }
231 }
232
233 pub fn inspect(&self) -> Vec<InspectNode> {
241 let mut out = Vec::new();
242 let roots = self.arena.roots();
243 for id in self.roots() {
244 if let Some(node) = roots.into_item(id.0) {
245 self.inspect_node(node, id, None, Point::ZERO, 0, &mut out);
246 }
247 }
248 out
249 }
250
251 pub fn inspect_layout_dirty(&self) -> Vec<(WidgetId, bool)> {
258 let mut out = Vec::new();
259 let roots = self.arena.roots();
260 for id in self.roots() {
261 if let Some(node) = roots.into_item(id.0) {
262 self.layout_dirty_node(node, id, &mut out);
263 }
264 }
265 out
266 }
267
268 pub fn inspect_paint_dirty(&self) -> Vec<(WidgetId, bool)> {
271 let mut out = Vec::new();
272 let roots = self.arena.roots();
273 for id in self.roots() {
274 if let Some(node) = roots.into_item(id.0) {
275 self.pod_flag_node(node, id, &ChildPod::is_paint_dirty, &mut out);
276 }
277 }
278 out
279 }
280
281 fn pod_flag_node(
283 &self,
284 node: ArenaRef<'_, WidgetPod>,
285 id: WidgetId,
286 flag: &dyn Fn(&ChildPod) -> bool,
287 out: &mut Vec<(WidgetId, bool)>,
288 ) {
289 out.push((id, false));
290 for child_id in self.ordered_children(id, node) {
291 if let Some(child) = node.children.into_item(child_id.0) {
292 self.pod_flag_node(child, child_id, flag, out);
293 }
294 }
295 Self::pod_flag_pods(node.item.widget(), flag, out);
296 }
297
298 fn pod_flag_pods(
300 widget: &dyn Widget,
301 flag: &dyn Fn(&ChildPod) -> bool,
302 out: &mut Vec<(WidgetId, bool)>,
303 ) {
304 widget.visit_children(&mut |child| {
305 out.push((child.inspect_id(), flag(child)));
306 Self::pod_flag_pods(child.widget(), flag, out);
307 });
308 }
309
310 fn layout_dirty_node(
312 &self,
313 node: ArenaRef<'_, WidgetPod>,
314 id: WidgetId,
315 out: &mut Vec<(WidgetId, bool)>,
316 ) {
317 out.push((id, false));
318 for child_id in self.ordered_children(id, node) {
319 if let Some(child) = node.children.into_item(child_id.0) {
320 self.layout_dirty_node(child, child_id, out);
321 }
322 }
323 Self::layout_dirty_pods(node.item.widget(), out);
324 }
325
326 fn layout_dirty_pods(widget: &dyn Widget, out: &mut Vec<(WidgetId, bool)>) {
328 widget.visit_children(&mut |child| {
329 out.push((child.inspect_id(), child.is_layout_dirty()));
330 Self::layout_dirty_pods(child.widget(), out);
331 });
332 }
333
334 fn inspect_node(
337 &self,
338 node: ArenaRef<'_, WidgetPod>,
339 id: WidgetId,
340 parent: Option<WidgetId>,
341 parent_origin: Point,
342 depth: usize,
343 out: &mut Vec<InspectNode>,
344 ) {
345 let pod = node.item;
346 let origin = parent_origin + pod.origin().to_vec2();
347 let arena_children = self.ordered_children(id, node);
348 let slot = out.len();
350 out.push(InspectNode {
351 id,
352 parent,
353 type_name: pod.type_name(),
354 debug_label: pod.debug_label.clone(),
355 bounds: Rect::from_origin_size(origin, pod.size()),
356 children: Vec::new(),
357 depth,
358 });
359 let mut children = Vec::with_capacity(arena_children.len());
360 for child_id in arena_children {
361 if let Some(child) = node.children.into_item(child_id.0) {
362 children.push(child_id);
363 self.inspect_node(child, child_id, Some(id), origin, depth + 1, out);
364 }
365 }
366 Self::inspect_pods(pod.widget(), id, origin, depth, &mut children, out);
367 out[slot].children = children;
368 }
369
370 fn inspect_pods(
372 widget: &dyn Widget,
373 parent: WidgetId,
374 parent_origin: Point,
375 parent_depth: usize,
376 children: &mut Vec<WidgetId>,
377 out: &mut Vec<InspectNode>,
378 ) {
379 widget.visit_children(&mut |child| {
380 let child_id = child.inspect_id();
381 children.push(child_id);
382 Self::inspect_child(
383 child,
384 child_id,
385 parent,
386 parent_origin,
387 parent_depth + 1,
388 out,
389 );
390 });
391 }
392
393 fn inspect_child(
395 child: &ChildPod,
396 id: WidgetId,
397 parent: WidgetId,
398 parent_origin: Point,
399 depth: usize,
400 out: &mut Vec<InspectNode>,
401 ) {
402 let origin = parent_origin + child.origin().to_vec2();
403 let slot = out.len();
404 out.push(InspectNode {
405 id,
406 parent: Some(parent),
407 type_name: child.type_name(),
408 debug_label: child.debug_label_cow(),
409 bounds: Rect::from_origin_size(origin, child.size()),
410 children: Vec::new(),
411 depth,
412 });
413 let mut grandchildren = Vec::new();
414 Self::inspect_pods(child.widget(), id, origin, depth, &mut grandchildren, out);
415 out[slot].children = grandchildren;
416 }
417
418 fn ordered_children(&self, id: WidgetId, node: ArenaRef<'_, WidgetPod>) -> Vec<WidgetId> {
420 let live: HashSet<u64> = node.child_ids().into_iter().collect();
421 let recorded = self.child_order.get(&id).map_or(&[][..], Vec::as_slice);
422 order_against(recorded, &live)
423 }
424}
425
426fn order_against(recorded: &[WidgetId], live: &HashSet<u64>) -> Vec<WidgetId> {
431 let mut ordered: Vec<WidgetId> = recorded
432 .iter()
433 .copied()
434 .filter(|id| live.contains(&id.0))
435 .collect();
436 if ordered.len() != live.len() {
437 let seen: HashSet<u64> = ordered.iter().map(|id| id.0).collect();
438 let mut unrecorded: Vec<u64> = live.difference(&seen).copied().collect();
439 unrecorded.sort_unstable();
440 ordered.extend(unrecorded.into_iter().map(WidgetId));
441 }
442 ordered
443}
444
445impl Default for WidgetTree {
446 fn default() -> Self {
447 Self::new()
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454 use crate::layout::BoxConstraints;
455 use crate::widget::{LayoutCtx, PaintCtx, PaintScene};
456
457 struct Leaf;
458 impl Widget for Leaf {
459 fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
460 bc.max()
461 }
462 fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut dyn PaintScene) {}
463 }
464
465 struct Container {
468 children: Vec<ChildPod>,
469 }
470
471 impl Container {
472 fn new(children: Vec<(Point, Size, Box<dyn Widget>)>) -> Self {
475 Self {
476 children: children
477 .into_iter()
478 .map(|(origin, size, widget)| {
479 let mut pod = ChildPod::new(widget);
480 pod.set_origin(origin);
481 pod.layout_child(&mut LayoutCtx::new(), &BoxConstraints::new(size, size));
484 pod
485 })
486 .collect(),
487 }
488 }
489 }
490
491 impl Widget for Container {
492 fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
493 bc.max()
494 }
495 fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut dyn PaintScene) {}
496 fn visit_children(&self, visitor: &mut dyn FnMut(&ChildPod)) {
497 for child in &self.children {
498 visitor(child);
499 }
500 }
501 }
502
503 fn pod(id: u64) -> WidgetPod {
504 WidgetPod::new(WidgetId(id), Box::new(Leaf))
505 }
506
507 #[test]
508 fn insert_root_and_find() {
509 let mut tree = WidgetTree::new();
510 let id = tree.insert_root(pod(1));
511 assert_eq!(id, WidgetId(1));
512 assert!(tree.pod(id).is_some());
513 assert!(tree.pod_mut(id).is_some());
514 assert!(tree.pod(WidgetId(999)).is_none());
515 }
516
517 #[test]
518 fn insert_child_traverses_arena() {
519 let mut tree = WidgetTree::new();
520 let root = tree.insert_root(pod(1));
521 let child = tree.insert_child(root, pod(2)).expect("child inserted");
522 assert_eq!(child, WidgetId(2));
523 assert!(tree.pod(child).is_some());
525 assert!(tree.insert_child(WidgetId(42), pod(3)).is_none());
527 }
528
529 #[test]
530 fn roots_and_children_follow_insertion_order() {
531 let mut tree = WidgetTree::new();
532 let root_b = tree.insert_root(pod(20));
534 let root_a = tree.insert_root(pod(10));
535 assert_eq!(tree.roots(), vec![root_b, root_a]);
536
537 let c2 = tree.insert_child(root_b, pod(22)).unwrap();
538 let c1 = tree.insert_child(root_b, pod(21)).unwrap();
539 let grandchild = tree.insert_child(c2, pod(30)).unwrap();
540 assert_eq!(tree.children(root_b), vec![c2, c1]);
541 assert_eq!(tree.children(c2), vec![grandchild]);
542 assert!(tree.children(c1).is_empty());
544 assert!(tree.children(WidgetId(999)).is_empty());
545 }
546
547 #[test]
548 fn pod_records_type_name_and_optional_label() {
549 let mut tree = WidgetTree::new();
550 let erased = tree.insert_root(WidgetPod::new(WidgetId(1), Box::new(Leaf)));
552 assert_eq!(
553 tree.pod(erased).unwrap().type_name(),
554 WidgetPod::ERASED_TYPE_NAME
555 );
556
557 let typed = tree
558 .insert_child(erased, WidgetPod::new_typed(WidgetId(2), Leaf))
559 .unwrap();
560 let pod = tree.pod_mut(typed).unwrap();
561 assert!(pod.type_name().ends_with("Leaf"), "{}", pod.type_name());
562 assert_eq!(pod.debug_label(), None);
564 pod.set_debug_label("the-leaf");
565 assert_eq!(pod.debug_label(), Some("the-leaf"));
566 pod.clear_debug_label();
567 assert_eq!(pod.debug_label(), None);
568 }
569
570 #[test]
571 fn inspect_walks_pre_order_with_absolute_bounds() {
572 let mut tree = WidgetTree::new();
573 let root = tree.insert_root(WidgetPod::new_typed(WidgetId(1), Leaf));
574 let a = tree
575 .insert_child(root, WidgetPod::new_typed(WidgetId(2), Leaf))
576 .unwrap();
577 let a_child = tree
578 .insert_child(a, WidgetPod::new_typed(WidgetId(3), Leaf))
579 .unwrap();
580 let b = tree
581 .insert_child(root, WidgetPod::new_typed(WidgetId(4), Leaf))
582 .unwrap();
583
584 tree.pod_mut(root)
586 .unwrap()
587 .set_layout(Point::ZERO, Size::new(100.0, 100.0));
588 tree.pod_mut(a)
589 .unwrap()
590 .set_layout(Point::new(10.0, 5.0), Size::new(50.0, 40.0));
591 tree.pod_mut(a_child)
592 .unwrap()
593 .set_layout(Point::new(2.0, 3.0), Size::new(10.0, 10.0));
594 tree.pod_mut(b)
595 .unwrap()
596 .set_layout(Point::new(0.0, 60.0), Size::new(20.0, 20.0));
597 tree.pod_mut(a).unwrap().set_debug_label("branch-a");
598
599 let nodes = tree.inspect();
600 let ids: Vec<WidgetId> = nodes.iter().map(|n| n.id).collect();
602 assert_eq!(ids, vec![root, a, a_child, b]);
603 assert_eq!(
604 nodes.iter().map(|n| n.depth).collect::<Vec<_>>(),
605 vec![0, 1, 2, 1]
606 );
607 assert_eq!(
608 nodes.iter().map(|n| n.parent).collect::<Vec<_>>(),
609 vec![None, Some(root), Some(a), Some(root)]
610 );
611 assert_eq!(nodes[0].children, vec![a, b]);
612 assert_eq!(nodes[2].children, Vec::new());
613
614 assert_eq!(nodes[1].bounds, Rect::new(10.0, 5.0, 60.0, 45.0));
616 assert_eq!(nodes[2].bounds, Rect::new(12.0, 8.0, 22.0, 18.0));
617
618 assert!(nodes[0].type_name.ends_with("Leaf"));
620 assert_eq!(nodes[1].debug_label.as_deref(), Some("branch-a"));
621 assert!(nodes[0].debug_label.is_none());
622 assert!(nodes[3].debug_label.is_none());
623 }
624
625 #[test]
626 fn inspect_of_an_unlaid_out_tree_reports_zero_rects() {
627 let mut tree = WidgetTree::new();
628 let root = tree.insert_root(WidgetPod::new_typed(WidgetId(1), Leaf));
629 tree.insert_child(root, WidgetPod::new_typed(WidgetId(2), Leaf))
630 .unwrap();
631 let nodes = tree.inspect();
632 assert_eq!(nodes.len(), 2);
633 assert!(nodes.iter().all(|n| n.bounds == Rect::ZERO));
634 }
635
636 #[test]
637 fn traversal_follows_the_arena_not_the_order_index() {
638 let mut tree = WidgetTree::new();
642 let root = tree.insert_root(pod(1));
643 let kept = tree.insert_child(root, pod(2)).unwrap();
644
645 tree.child_order
647 .get_mut(&root)
648 .unwrap()
649 .push(WidgetId(1234));
650 assert_eq!(tree.children(root), vec![kept]);
651 assert_eq!(tree.inspect().len(), 2);
652
653 tree.child_order.remove(&root);
655 assert_eq!(tree.children(root), vec![kept]);
656 tree.root_order.clear();
657 assert_eq!(tree.roots(), vec![root]);
658 }
659
660 #[test]
661 fn inspect_descends_through_child_pods() {
662 let inner = Container::new(vec![(
666 Point::new(1.0, 1.0),
667 Size::new(5.0, 5.0),
668 Box::new(Leaf),
669 )]);
670 let outer = Container::new(vec![
671 (Point::new(10.0, 5.0), Size::new(50.0, 40.0), Box::new(Leaf)),
672 (
673 Point::new(0.0, 60.0),
674 Size::new(20.0, 20.0),
675 Box::new(inner) as Box<dyn Widget>,
676 ),
677 ]);
678
679 let mut tree = WidgetTree::new();
680 let root = tree.insert_root(WidgetPod::new_typed(WidgetId(1), outer));
681 tree.pod_mut(root)
682 .unwrap()
683 .set_layout(Point::new(2.0, 3.0), Size::new(100.0, 100.0));
684
685 let nodes = tree.inspect();
686 assert_eq!(nodes.len(), 4, "root + two pods + one grandchild pod");
687
688 assert_eq!(
690 nodes.iter().map(|n| n.depth).collect::<Vec<_>>(),
691 vec![0, 1, 1, 2],
692 "a pod's depth continues from its owning node's"
693 );
694 assert_eq!(nodes[0].parent, None);
695 assert_eq!(nodes[1].parent, Some(nodes[0].id));
696 assert_eq!(nodes[2].parent, Some(nodes[0].id));
697 assert_eq!(nodes[3].parent, Some(nodes[2].id));
698 assert_eq!(nodes[0].children, vec![nodes[1].id, nodes[2].id]);
699 assert_eq!(nodes[2].children, vec![nodes[3].id]);
700 assert!(nodes[1].children.is_empty(), "a leaf publishes no children");
701
702 assert_eq!(nodes[0].bounds, Rect::new(2.0, 3.0, 102.0, 103.0));
706 assert_eq!(nodes[1].bounds, Rect::new(12.0, 8.0, 62.0, 48.0));
707 assert_eq!(nodes[2].bounds, Rect::new(2.0, 63.0, 22.0, 83.0));
708 assert_eq!(nodes[3].bounds, Rect::new(3.0, 64.0, 8.0, 69.0));
709
710 assert!(
713 nodes[0].type_name.ends_with("Container"),
714 "{}",
715 nodes[0].type_name
716 );
717 assert!(
718 nodes[1..]
719 .iter()
720 .all(|n| n.type_name.ends_with("Leaf") || n.type_name.ends_with("Container"))
721 );
722
723 let ids: HashSet<u64> = nodes.iter().map(|n| n.id.0).collect();
726 assert_eq!(ids.len(), nodes.len(), "ids are unique across a snapshot");
727 assert!(
728 nodes[1..]
729 .iter()
730 .all(|n| n.id.0 >= crate::widget::ChildPod::INSPECT_ID_BASE)
731 );
732 }
733
734 #[test]
735 fn a_pods_tooling_id_and_label_survive_the_next_walk() {
736 let mut tree = WidgetTree::new();
739 let root = tree.insert_root(WidgetPod::new_typed(
740 WidgetId(1),
741 Container::new(vec![(Point::ZERO, Size::new(4.0, 4.0), Box::new(Leaf))]),
742 ));
743 let first = tree.inspect();
744 let second = tree.inspect();
745 assert_eq!(first[1].id, second[1].id);
746
747 let pod = tree.pod_mut(root).unwrap();
750 let container = pod
751 .widget_mut()
752 .downcast_mut::<Container>()
753 .expect("the root widget is the container");
754 container.children[0].set_debug_label("the-child");
755 assert_eq!(tree.inspect()[1].debug_label.as_deref(), Some("the-child"));
756 let container = tree
757 .pod_mut(root)
758 .unwrap()
759 .widget_mut()
760 .downcast_mut::<Container>()
761 .expect("the root widget is the container");
762 container.children[0].clear_debug_label();
763 assert_eq!(tree.inspect()[1].debug_label, None);
764 }
765
766 #[test]
767 fn a_widget_that_ignores_the_seam_reads_as_a_leaf() {
768 struct Opaque {
771 _child: ChildPod,
772 }
773 impl Widget for Opaque {
774 fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
775 bc.max()
776 }
777 fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut dyn PaintScene) {}
778 }
779
780 let mut tree = WidgetTree::new();
781 tree.insert_root(WidgetPod::new_typed(
782 WidgetId(1),
783 Opaque {
784 _child: ChildPod::new(Box::new(Leaf)),
785 },
786 ));
787 assert_eq!(tree.inspect().len(), 1);
788 }
789
790 #[test]
791 fn pod_stores_layout_geometry() {
792 let mut tree = WidgetTree::new();
793 let id = tree.insert_root(pod(1));
794 let p = tree.pod_mut(id).unwrap();
795 assert!(p.flags().needs_layout());
796 p.set_layout(Point::new(2.0, 3.0), Size::new(10.0, 20.0));
797 p.clear_flags();
798 assert_eq!(p.origin(), Point::new(2.0, 3.0));
799 assert_eq!(p.size(), Size::new(10.0, 20.0));
800 assert!(p.flags().is_empty());
801 }
802}