1use std::borrow::Cow;
39use std::collections::{HashMap, HashSet};
40
41use kurbo::{Point, Rect, Size};
42use tree_arena::{ArenaMut, ArenaRef, TreeArena};
43
44use crate::view::{ChangeFlags, WidgetId};
45use crate::widget::{ChildPod, Widget};
46
47pub struct WidgetPod {
53 id: WidgetId,
54 widget: Box<dyn Widget>,
55 origin: Point,
56 size: Size,
57 flags: ChangeFlags,
58 type_name: &'static str,
61 debug_label: Option<Cow<'static, str>>,
64}
65
66impl WidgetPod {
67 pub const ERASED_TYPE_NAME: &'static str = "<erased dyn Widget>";
70
71 pub fn new(id: WidgetId, widget: Box<dyn Widget>) -> Self {
79 Self::with_type_name(id, widget, Self::ERASED_TYPE_NAME)
80 }
81
82 pub fn new_typed<W: Widget>(id: WidgetId, widget: W) -> Self {
91 Self::with_type_name(id, Box::new(widget), core::any::type_name::<W>())
92 }
93
94 fn with_type_name(id: WidgetId, widget: Box<dyn Widget>, type_name: &'static str) -> Self {
95 Self {
96 id,
97 widget,
98 origin: Point::ZERO,
99 size: Size::ZERO,
100 flags: ChangeFlags::LAYOUT | ChangeFlags::PAINT,
101 type_name,
102 debug_label: None,
103 }
104 }
105
106 pub fn id(&self) -> WidgetId {
108 self.id
109 }
110
111 pub fn type_name(&self) -> &'static str {
123 self.type_name
124 }
125
126 pub fn debug_label(&self) -> Option<&str> {
128 self.debug_label.as_deref()
129 }
130
131 pub fn set_debug_label(&mut self, label: impl Into<Cow<'static, str>>) {
135 self.debug_label = Some(label.into());
136 }
137
138 pub fn clear_debug_label(&mut self) {
140 self.debug_label = None;
141 }
142
143 pub fn widget(&self) -> &dyn Widget {
145 &*self.widget
146 }
147
148 pub fn widget_mut(&mut self) -> &mut dyn Widget {
150 &mut *self.widget
151 }
152
153 pub fn origin(&self) -> Point {
155 self.origin
156 }
157
158 pub fn size(&self) -> Size {
160 self.size
161 }
162
163 pub fn set_layout(&mut self, origin: Point, size: Size) {
165 self.origin = origin;
166 self.size = size;
167 }
168
169 pub fn flags(&self) -> ChangeFlags {
171 self.flags
172 }
173
174 pub fn merge_flags(&mut self, flags: ChangeFlags) {
176 self.flags |= flags;
177 }
178
179 pub fn clear_flags(&mut self) {
181 self.flags = ChangeFlags::NONE;
182 }
183}
184
185#[derive(Debug, Clone, PartialEq)]
193pub struct InspectNode {
194 pub id: WidgetId,
196 pub parent: Option<WidgetId>,
198 pub type_name: &'static str,
200 pub debug_label: Option<Cow<'static, str>>,
202 pub bounds: Rect,
206 pub children: Vec<WidgetId>,
209 pub depth: usize,
211}
212
213pub struct WidgetTree {
215 arena: TreeArena<WidgetPod>,
216 root_order: Vec<WidgetId>,
218 child_order: HashMap<WidgetId, Vec<WidgetId>>,
220}
221
222impl WidgetTree {
223 pub fn new() -> Self {
225 Self {
226 arena: TreeArena::new(),
227 root_order: Vec::new(),
228 child_order: HashMap::new(),
229 }
230 }
231
232 pub fn insert_root(&mut self, pod: WidgetPod) -> WidgetId {
234 let id = pod.id();
235 self.arena.roots_mut().insert(id.0, pod);
237 self.root_order.push(id);
238 id
239 }
240
241 pub fn insert_child(&mut self, parent: WidgetId, pod: WidgetPod) -> Option<WidgetId> {
248 let id = pod.id();
249 let mut parent_mut: ArenaMut<'_, WidgetPod> = self.arena.find_mut(parent.0)?;
250 parent_mut.children.insert(id.0, pod);
251 self.child_order.entry(parent).or_default().push(id);
252 Some(id)
253 }
254
255 pub fn pod(&self, id: WidgetId) -> Option<&WidgetPod> {
257 self.arena.find(id.0).map(|node| node.item)
258 }
259
260 pub fn pod_mut(&mut self, id: WidgetId) -> Option<&mut WidgetPod> {
262 self.arena.find_mut(id.0).map(|node| node.item)
263 }
264
265 pub fn roots(&self) -> Vec<WidgetId> {
269 let live: HashSet<u64> = self.arena.root_ids().collect();
270 order_against(&self.root_order, &live)
271 }
272
273 pub fn children(&self, id: WidgetId) -> Vec<WidgetId> {
279 match self.arena.find(id.0) {
280 Some(node) => self.ordered_children(id, node),
281 None => Vec::new(),
282 }
283 }
284
285 pub fn inspect(&self) -> Vec<InspectNode> {
301 let mut out = Vec::new();
302 let roots = self.arena.roots();
303 for id in self.roots() {
304 if let Some(node) = roots.into_item(id.0) {
305 self.inspect_node(node, id, None, Point::ZERO, 0, &mut out);
306 }
307 }
308 out
309 }
310
311 pub fn inspect_layout_dirty(&self) -> Vec<(WidgetId, bool)> {
323 let mut out = Vec::new();
324 let roots = self.arena.roots();
325 for id in self.roots() {
326 if let Some(node) = roots.into_item(id.0) {
327 self.layout_dirty_node(node, id, &mut out);
328 }
329 }
330 out
331 }
332
333 pub fn inspect_paint_dirty(&self) -> Vec<(WidgetId, bool)> {
337 let mut out = Vec::new();
338 let roots = self.arena.roots();
339 for id in self.roots() {
340 if let Some(node) = roots.into_item(id.0) {
341 self.pod_flag_node(node, id, &ChildPod::is_paint_dirty, &mut out);
342 }
343 }
344 out
345 }
346
347 fn pod_flag_node(
349 &self,
350 node: ArenaRef<'_, WidgetPod>,
351 id: WidgetId,
352 flag: &dyn Fn(&ChildPod) -> bool,
353 out: &mut Vec<(WidgetId, bool)>,
354 ) {
355 out.push((id, false));
356 for child_id in self.ordered_children(id, node) {
357 if let Some(child) = node.children.into_item(child_id.0) {
358 self.pod_flag_node(child, child_id, flag, out);
359 }
360 }
361 Self::pod_flag_pods(node.item.widget(), flag, out);
362 }
363
364 fn pod_flag_pods(
366 widget: &dyn Widget,
367 flag: &dyn Fn(&ChildPod) -> bool,
368 out: &mut Vec<(WidgetId, bool)>,
369 ) {
370 widget.visit_children(&mut |child| {
371 out.push((child.inspect_id(), flag(child)));
372 Self::pod_flag_pods(child.widget(), flag, out);
373 });
374 }
375
376 fn layout_dirty_node(
380 &self,
381 node: ArenaRef<'_, WidgetPod>,
382 id: WidgetId,
383 out: &mut Vec<(WidgetId, bool)>,
384 ) {
385 out.push((id, false));
386 for child_id in self.ordered_children(id, node) {
387 if let Some(child) = node.children.into_item(child_id.0) {
388 self.layout_dirty_node(child, child_id, out);
389 }
390 }
391 Self::layout_dirty_pods(node.item.widget(), out);
392 }
393
394 fn layout_dirty_pods(widget: &dyn Widget, out: &mut Vec<(WidgetId, bool)>) {
397 widget.visit_children(&mut |child| {
398 out.push((child.inspect_id(), child.is_layout_dirty()));
399 Self::layout_dirty_pods(child.widget(), out);
400 });
401 }
402
403 fn inspect_node(
407 &self,
408 node: ArenaRef<'_, WidgetPod>,
409 id: WidgetId,
410 parent: Option<WidgetId>,
411 parent_origin: Point,
412 depth: usize,
413 out: &mut Vec<InspectNode>,
414 ) {
415 let pod = node.item;
416 let origin = parent_origin + pod.origin().to_vec2();
417 let arena_children = self.ordered_children(id, node);
418 let slot = out.len();
421 out.push(InspectNode {
422 id,
423 parent,
424 type_name: pod.type_name(),
425 debug_label: pod.debug_label.clone(),
426 bounds: Rect::from_origin_size(origin, pod.size()),
427 children: Vec::new(),
428 depth,
429 });
430 let mut children = Vec::with_capacity(arena_children.len());
431 for child_id in arena_children {
432 if let Some(child) = node.children.into_item(child_id.0) {
433 children.push(child_id);
434 self.inspect_node(child, child_id, Some(id), origin, depth + 1, out);
435 }
436 }
437 Self::inspect_pods(pod.widget(), id, origin, depth, &mut children, out);
438 out[slot].children = children;
439 }
440
441 fn inspect_pods(
444 widget: &dyn Widget,
445 parent: WidgetId,
446 parent_origin: Point,
447 parent_depth: usize,
448 children: &mut Vec<WidgetId>,
449 out: &mut Vec<InspectNode>,
450 ) {
451 widget.visit_children(&mut |child| {
452 let child_id = child.inspect_id();
453 children.push(child_id);
454 Self::inspect_child(
455 child,
456 child_id,
457 parent,
458 parent_origin,
459 parent_depth + 1,
460 out,
461 );
462 });
463 }
464
465 fn inspect_child(
468 child: &ChildPod,
469 id: WidgetId,
470 parent: WidgetId,
471 parent_origin: Point,
472 depth: usize,
473 out: &mut Vec<InspectNode>,
474 ) {
475 let origin = parent_origin + child.origin().to_vec2();
476 let slot = out.len();
477 out.push(InspectNode {
478 id,
479 parent: Some(parent),
480 type_name: child.type_name(),
481 debug_label: child.debug_label_cow(),
482 bounds: Rect::from_origin_size(origin, child.size()),
483 children: Vec::new(),
484 depth,
485 });
486 let mut grandchildren = Vec::new();
487 Self::inspect_pods(child.widget(), id, origin, depth, &mut grandchildren, out);
488 out[slot].children = grandchildren;
489 }
490
491 fn ordered_children(&self, id: WidgetId, node: ArenaRef<'_, WidgetPod>) -> Vec<WidgetId> {
493 let live: HashSet<u64> = node.child_ids().into_iter().collect();
494 let recorded = self.child_order.get(&id).map_or(&[][..], Vec::as_slice);
495 order_against(recorded, &live)
496 }
497}
498
499fn order_against(recorded: &[WidgetId], live: &HashSet<u64>) -> Vec<WidgetId> {
507 let mut ordered: Vec<WidgetId> = recorded
508 .iter()
509 .copied()
510 .filter(|id| live.contains(&id.0))
511 .collect();
512 if ordered.len() != live.len() {
513 let seen: HashSet<u64> = ordered.iter().map(|id| id.0).collect();
514 let mut unrecorded: Vec<u64> = live.difference(&seen).copied().collect();
515 unrecorded.sort_unstable();
516 ordered.extend(unrecorded.into_iter().map(WidgetId));
517 }
518 ordered
519}
520
521impl Default for WidgetTree {
522 fn default() -> Self {
523 Self::new()
524 }
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530 use crate::layout::BoxConstraints;
531 use crate::widget::{LayoutCtx, PaintCtx, PaintScene};
532
533 struct Leaf;
534 impl Widget for Leaf {
535 fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
536 bc.max()
537 }
538 fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut dyn PaintScene) {}
539 }
540
541 struct Container {
544 children: Vec<ChildPod>,
545 }
546
547 impl Container {
548 fn new(children: Vec<(Point, Size, Box<dyn Widget>)>) -> Self {
551 Self {
552 children: children
553 .into_iter()
554 .map(|(origin, size, widget)| {
555 let mut pod = ChildPod::new(widget);
556 pod.set_origin(origin);
557 pod.layout_child(&mut LayoutCtx::new(), &BoxConstraints::new(size, size));
560 pod
561 })
562 .collect(),
563 }
564 }
565 }
566
567 impl Widget for Container {
568 fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
569 bc.max()
570 }
571 fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut dyn PaintScene) {}
572 fn visit_children(&self, visitor: &mut dyn FnMut(&ChildPod)) {
573 for child in &self.children {
574 visitor(child);
575 }
576 }
577 }
578
579 fn pod(id: u64) -> WidgetPod {
580 WidgetPod::new(WidgetId(id), Box::new(Leaf))
581 }
582
583 #[test]
584 fn insert_root_and_find() {
585 let mut tree = WidgetTree::new();
586 let id = tree.insert_root(pod(1));
587 assert_eq!(id, WidgetId(1));
588 assert!(tree.pod(id).is_some());
589 assert!(tree.pod_mut(id).is_some());
590 assert!(tree.pod(WidgetId(999)).is_none());
591 }
592
593 #[test]
594 fn insert_child_traverses_arena() {
595 let mut tree = WidgetTree::new();
596 let root = tree.insert_root(pod(1));
597 let child = tree.insert_child(root, pod(2)).expect("child inserted");
598 assert_eq!(child, WidgetId(2));
599 assert!(tree.pod(child).is_some());
601 assert!(tree.insert_child(WidgetId(42), pod(3)).is_none());
603 }
604
605 #[test]
606 fn roots_and_children_follow_insertion_order() {
607 let mut tree = WidgetTree::new();
608 let root_b = tree.insert_root(pod(20));
610 let root_a = tree.insert_root(pod(10));
611 assert_eq!(tree.roots(), vec![root_b, root_a]);
612
613 let c2 = tree.insert_child(root_b, pod(22)).unwrap();
614 let c1 = tree.insert_child(root_b, pod(21)).unwrap();
615 let grandchild = tree.insert_child(c2, pod(30)).unwrap();
616 assert_eq!(tree.children(root_b), vec![c2, c1]);
617 assert_eq!(tree.children(c2), vec![grandchild]);
618 assert!(tree.children(c1).is_empty());
620 assert!(tree.children(WidgetId(999)).is_empty());
621 }
622
623 #[test]
624 fn pod_records_type_name_and_optional_label() {
625 let mut tree = WidgetTree::new();
626 let erased = tree.insert_root(WidgetPod::new(WidgetId(1), Box::new(Leaf)));
628 assert_eq!(
629 tree.pod(erased).unwrap().type_name(),
630 WidgetPod::ERASED_TYPE_NAME
631 );
632
633 let typed = tree
634 .insert_child(erased, WidgetPod::new_typed(WidgetId(2), Leaf))
635 .unwrap();
636 let pod = tree.pod_mut(typed).unwrap();
637 assert!(pod.type_name().ends_with("Leaf"), "{}", pod.type_name());
638 assert_eq!(pod.debug_label(), None);
640 pod.set_debug_label("the-leaf");
641 assert_eq!(pod.debug_label(), Some("the-leaf"));
642 pod.clear_debug_label();
643 assert_eq!(pod.debug_label(), None);
644 }
645
646 #[test]
647 fn inspect_walks_pre_order_with_absolute_bounds() {
648 let mut tree = WidgetTree::new();
649 let root = tree.insert_root(WidgetPod::new_typed(WidgetId(1), Leaf));
650 let a = tree
651 .insert_child(root, WidgetPod::new_typed(WidgetId(2), Leaf))
652 .unwrap();
653 let a_child = tree
654 .insert_child(a, WidgetPod::new_typed(WidgetId(3), Leaf))
655 .unwrap();
656 let b = tree
657 .insert_child(root, WidgetPod::new_typed(WidgetId(4), Leaf))
658 .unwrap();
659
660 tree.pod_mut(root)
662 .unwrap()
663 .set_layout(Point::ZERO, Size::new(100.0, 100.0));
664 tree.pod_mut(a)
665 .unwrap()
666 .set_layout(Point::new(10.0, 5.0), Size::new(50.0, 40.0));
667 tree.pod_mut(a_child)
668 .unwrap()
669 .set_layout(Point::new(2.0, 3.0), Size::new(10.0, 10.0));
670 tree.pod_mut(b)
671 .unwrap()
672 .set_layout(Point::new(0.0, 60.0), Size::new(20.0, 20.0));
673 tree.pod_mut(a).unwrap().set_debug_label("branch-a");
674
675 let nodes = tree.inspect();
676 let ids: Vec<WidgetId> = nodes.iter().map(|n| n.id).collect();
678 assert_eq!(ids, vec![root, a, a_child, b]);
679 assert_eq!(
680 nodes.iter().map(|n| n.depth).collect::<Vec<_>>(),
681 vec![0, 1, 2, 1]
682 );
683 assert_eq!(
684 nodes.iter().map(|n| n.parent).collect::<Vec<_>>(),
685 vec![None, Some(root), Some(a), Some(root)]
686 );
687 assert_eq!(nodes[0].children, vec![a, b]);
688 assert_eq!(nodes[2].children, Vec::new());
689
690 assert_eq!(nodes[1].bounds, Rect::new(10.0, 5.0, 60.0, 45.0));
692 assert_eq!(nodes[2].bounds, Rect::new(12.0, 8.0, 22.0, 18.0));
693
694 assert!(nodes[0].type_name.ends_with("Leaf"));
696 assert_eq!(nodes[1].debug_label.as_deref(), Some("branch-a"));
697 assert!(nodes[0].debug_label.is_none());
698 assert!(nodes[3].debug_label.is_none());
699 }
700
701 #[test]
702 fn inspect_of_an_unlaid_out_tree_reports_zero_rects() {
703 let mut tree = WidgetTree::new();
704 let root = tree.insert_root(WidgetPod::new_typed(WidgetId(1), Leaf));
705 tree.insert_child(root, WidgetPod::new_typed(WidgetId(2), Leaf))
706 .unwrap();
707 let nodes = tree.inspect();
708 assert_eq!(nodes.len(), 2);
709 assert!(nodes.iter().all(|n| n.bounds == Rect::ZERO));
710 }
711
712 #[test]
713 fn traversal_follows_the_arena_not_the_order_index() {
714 let mut tree = WidgetTree::new();
718 let root = tree.insert_root(pod(1));
719 let kept = tree.insert_child(root, pod(2)).unwrap();
720
721 tree.child_order
723 .get_mut(&root)
724 .unwrap()
725 .push(WidgetId(1234));
726 assert_eq!(tree.children(root), vec![kept]);
727 assert_eq!(tree.inspect().len(), 2);
728
729 tree.child_order.remove(&root);
731 assert_eq!(tree.children(root), vec![kept]);
732 tree.root_order.clear();
733 assert_eq!(tree.roots(), vec![root]);
734 }
735
736 #[test]
737 fn inspect_descends_through_child_pods() {
738 let inner = Container::new(vec![(
742 Point::new(1.0, 1.0),
743 Size::new(5.0, 5.0),
744 Box::new(Leaf),
745 )]);
746 let outer = Container::new(vec![
747 (Point::new(10.0, 5.0), Size::new(50.0, 40.0), Box::new(Leaf)),
748 (
749 Point::new(0.0, 60.0),
750 Size::new(20.0, 20.0),
751 Box::new(inner) as Box<dyn Widget>,
752 ),
753 ]);
754
755 let mut tree = WidgetTree::new();
756 let root = tree.insert_root(WidgetPod::new_typed(WidgetId(1), outer));
757 tree.pod_mut(root)
758 .unwrap()
759 .set_layout(Point::new(2.0, 3.0), Size::new(100.0, 100.0));
760
761 let nodes = tree.inspect();
762 assert_eq!(nodes.len(), 4, "root + two pods + one grandchild pod");
763
764 assert_eq!(
766 nodes.iter().map(|n| n.depth).collect::<Vec<_>>(),
767 vec![0, 1, 1, 2],
768 "a pod's depth continues from its owning node's"
769 );
770 assert_eq!(nodes[0].parent, None);
771 assert_eq!(nodes[1].parent, Some(nodes[0].id));
772 assert_eq!(nodes[2].parent, Some(nodes[0].id));
773 assert_eq!(nodes[3].parent, Some(nodes[2].id));
774 assert_eq!(nodes[0].children, vec![nodes[1].id, nodes[2].id]);
775 assert_eq!(nodes[2].children, vec![nodes[3].id]);
776 assert!(nodes[1].children.is_empty(), "a leaf publishes no children");
777
778 assert_eq!(nodes[0].bounds, Rect::new(2.0, 3.0, 102.0, 103.0));
782 assert_eq!(nodes[1].bounds, Rect::new(12.0, 8.0, 62.0, 48.0));
783 assert_eq!(nodes[2].bounds, Rect::new(2.0, 63.0, 22.0, 83.0));
784 assert_eq!(nodes[3].bounds, Rect::new(3.0, 64.0, 8.0, 69.0));
785
786 assert!(
789 nodes[0].type_name.ends_with("Container"),
790 "{}",
791 nodes[0].type_name
792 );
793 assert!(
794 nodes[1..]
795 .iter()
796 .all(|n| n.type_name.ends_with("Leaf") || n.type_name.ends_with("Container"))
797 );
798
799 let ids: HashSet<u64> = nodes.iter().map(|n| n.id.0).collect();
802 assert_eq!(ids.len(), nodes.len(), "ids are unique across a snapshot");
803 assert!(
804 nodes[1..]
805 .iter()
806 .all(|n| n.id.0 >= crate::widget::ChildPod::INSPECT_ID_BASE)
807 );
808 }
809
810 #[test]
811 fn a_pods_tooling_id_and_label_survive_the_next_walk() {
812 let mut tree = WidgetTree::new();
815 let root = tree.insert_root(WidgetPod::new_typed(
816 WidgetId(1),
817 Container::new(vec![(Point::ZERO, Size::new(4.0, 4.0), Box::new(Leaf))]),
818 ));
819 let first = tree.inspect();
820 let second = tree.inspect();
821 assert_eq!(first[1].id, second[1].id);
822
823 let pod = tree.pod_mut(root).unwrap();
826 let container = pod
827 .widget_mut()
828 .downcast_mut::<Container>()
829 .expect("the root widget is the container");
830 container.children[0].set_debug_label("the-child");
831 assert_eq!(tree.inspect()[1].debug_label.as_deref(), Some("the-child"));
832 let container = tree
833 .pod_mut(root)
834 .unwrap()
835 .widget_mut()
836 .downcast_mut::<Container>()
837 .expect("the root widget is the container");
838 container.children[0].clear_debug_label();
839 assert_eq!(tree.inspect()[1].debug_label, None);
840 }
841
842 #[test]
843 fn a_widget_that_ignores_the_seam_reads_as_a_leaf() {
844 struct Opaque {
847 _child: ChildPod,
848 }
849 impl Widget for Opaque {
850 fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
851 bc.max()
852 }
853 fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut dyn PaintScene) {}
854 }
855
856 let mut tree = WidgetTree::new();
857 tree.insert_root(WidgetPod::new_typed(
858 WidgetId(1),
859 Opaque {
860 _child: ChildPod::new(Box::new(Leaf)),
861 },
862 ));
863 assert_eq!(tree.inspect().len(), 1);
864 }
865
866 #[test]
867 fn pod_stores_layout_geometry() {
868 let mut tree = WidgetTree::new();
869 let id = tree.insert_root(pod(1));
870 let p = tree.pod_mut(id).unwrap();
871 assert!(p.flags().needs_layout());
872 p.set_layout(Point::new(2.0, 3.0), Size::new(10.0, 20.0));
873 p.clear_flags();
874 assert_eq!(p.origin(), Point::new(2.0, 3.0));
875 assert_eq!(p.size(), Size::new(10.0, 20.0));
876 assert!(p.flags().is_empty());
877 }
878}