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 fn inspect_node(
315 &self,
316 node: ArenaRef<'_, WidgetPod>,
317 id: WidgetId,
318 parent: Option<WidgetId>,
319 parent_origin: Point,
320 depth: usize,
321 out: &mut Vec<InspectNode>,
322 ) {
323 let pod = node.item;
324 let origin = parent_origin + pod.origin().to_vec2();
325 let arena_children = self.ordered_children(id, node);
326 let slot = out.len();
329 out.push(InspectNode {
330 id,
331 parent,
332 type_name: pod.type_name(),
333 debug_label: pod.debug_label.clone(),
334 bounds: Rect::from_origin_size(origin, pod.size()),
335 children: Vec::new(),
336 depth,
337 });
338 let mut children = Vec::with_capacity(arena_children.len());
339 for child_id in arena_children {
340 if let Some(child) = node.children.into_item(child_id.0) {
341 children.push(child_id);
342 self.inspect_node(child, child_id, Some(id), origin, depth + 1, out);
343 }
344 }
345 Self::inspect_pods(pod.widget(), id, origin, depth, &mut children, out);
346 out[slot].children = children;
347 }
348
349 fn inspect_pods(
352 widget: &dyn Widget,
353 parent: WidgetId,
354 parent_origin: Point,
355 parent_depth: usize,
356 children: &mut Vec<WidgetId>,
357 out: &mut Vec<InspectNode>,
358 ) {
359 widget.visit_children(&mut |child| {
360 let child_id = child.inspect_id();
361 children.push(child_id);
362 Self::inspect_child(
363 child,
364 child_id,
365 parent,
366 parent_origin,
367 parent_depth + 1,
368 out,
369 );
370 });
371 }
372
373 fn inspect_child(
376 child: &ChildPod,
377 id: WidgetId,
378 parent: WidgetId,
379 parent_origin: Point,
380 depth: usize,
381 out: &mut Vec<InspectNode>,
382 ) {
383 let origin = parent_origin + child.origin().to_vec2();
384 let slot = out.len();
385 out.push(InspectNode {
386 id,
387 parent: Some(parent),
388 type_name: child.type_name(),
389 debug_label: child.debug_label_cow(),
390 bounds: Rect::from_origin_size(origin, child.size()),
391 children: Vec::new(),
392 depth,
393 });
394 let mut grandchildren = Vec::new();
395 Self::inspect_pods(child.widget(), id, origin, depth, &mut grandchildren, out);
396 out[slot].children = grandchildren;
397 }
398
399 fn ordered_children(&self, id: WidgetId, node: ArenaRef<'_, WidgetPod>) -> Vec<WidgetId> {
401 let live: HashSet<u64> = node.child_ids().into_iter().collect();
402 let recorded = self.child_order.get(&id).map_or(&[][..], Vec::as_slice);
403 order_against(recorded, &live)
404 }
405}
406
407fn order_against(recorded: &[WidgetId], live: &HashSet<u64>) -> Vec<WidgetId> {
415 let mut ordered: Vec<WidgetId> = recorded
416 .iter()
417 .copied()
418 .filter(|id| live.contains(&id.0))
419 .collect();
420 if ordered.len() != live.len() {
421 let seen: HashSet<u64> = ordered.iter().map(|id| id.0).collect();
422 let mut unrecorded: Vec<u64> = live.difference(&seen).copied().collect();
423 unrecorded.sort_unstable();
424 ordered.extend(unrecorded.into_iter().map(WidgetId));
425 }
426 ordered
427}
428
429impl Default for WidgetTree {
430 fn default() -> Self {
431 Self::new()
432 }
433}
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438 use crate::layout::BoxConstraints;
439 use crate::widget::{LayoutCtx, PaintCtx, PaintScene};
440
441 struct Leaf;
442 impl Widget for Leaf {
443 fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
444 bc.max()
445 }
446 fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut dyn PaintScene) {}
447 }
448
449 struct Container {
452 children: Vec<ChildPod>,
453 }
454
455 impl Container {
456 fn new(children: Vec<(Point, Size, Box<dyn Widget>)>) -> Self {
459 Self {
460 children: children
461 .into_iter()
462 .map(|(origin, size, widget)| {
463 let mut pod = ChildPod::new(widget);
464 pod.set_origin(origin);
465 pod.layout_child(&mut LayoutCtx::new(), &BoxConstraints::new(size, size));
468 pod
469 })
470 .collect(),
471 }
472 }
473 }
474
475 impl Widget for Container {
476 fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
477 bc.max()
478 }
479 fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut dyn PaintScene) {}
480 fn visit_children(&self, visitor: &mut dyn FnMut(&ChildPod)) {
481 for child in &self.children {
482 visitor(child);
483 }
484 }
485 }
486
487 fn pod(id: u64) -> WidgetPod {
488 WidgetPod::new(WidgetId(id), Box::new(Leaf))
489 }
490
491 #[test]
492 fn insert_root_and_find() {
493 let mut tree = WidgetTree::new();
494 let id = tree.insert_root(pod(1));
495 assert_eq!(id, WidgetId(1));
496 assert!(tree.pod(id).is_some());
497 assert!(tree.pod_mut(id).is_some());
498 assert!(tree.pod(WidgetId(999)).is_none());
499 }
500
501 #[test]
502 fn insert_child_traverses_arena() {
503 let mut tree = WidgetTree::new();
504 let root = tree.insert_root(pod(1));
505 let child = tree.insert_child(root, pod(2)).expect("child inserted");
506 assert_eq!(child, WidgetId(2));
507 assert!(tree.pod(child).is_some());
509 assert!(tree.insert_child(WidgetId(42), pod(3)).is_none());
511 }
512
513 #[test]
514 fn roots_and_children_follow_insertion_order() {
515 let mut tree = WidgetTree::new();
516 let root_b = tree.insert_root(pod(20));
518 let root_a = tree.insert_root(pod(10));
519 assert_eq!(tree.roots(), vec![root_b, root_a]);
520
521 let c2 = tree.insert_child(root_b, pod(22)).unwrap();
522 let c1 = tree.insert_child(root_b, pod(21)).unwrap();
523 let grandchild = tree.insert_child(c2, pod(30)).unwrap();
524 assert_eq!(tree.children(root_b), vec![c2, c1]);
525 assert_eq!(tree.children(c2), vec![grandchild]);
526 assert!(tree.children(c1).is_empty());
528 assert!(tree.children(WidgetId(999)).is_empty());
529 }
530
531 #[test]
532 fn pod_records_type_name_and_optional_label() {
533 let mut tree = WidgetTree::new();
534 let erased = tree.insert_root(WidgetPod::new(WidgetId(1), Box::new(Leaf)));
536 assert_eq!(
537 tree.pod(erased).unwrap().type_name(),
538 WidgetPod::ERASED_TYPE_NAME
539 );
540
541 let typed = tree
542 .insert_child(erased, WidgetPod::new_typed(WidgetId(2), Leaf))
543 .unwrap();
544 let pod = tree.pod_mut(typed).unwrap();
545 assert!(pod.type_name().ends_with("Leaf"), "{}", pod.type_name());
546 assert_eq!(pod.debug_label(), None);
548 pod.set_debug_label("the-leaf");
549 assert_eq!(pod.debug_label(), Some("the-leaf"));
550 pod.clear_debug_label();
551 assert_eq!(pod.debug_label(), None);
552 }
553
554 #[test]
555 fn inspect_walks_pre_order_with_absolute_bounds() {
556 let mut tree = WidgetTree::new();
557 let root = tree.insert_root(WidgetPod::new_typed(WidgetId(1), Leaf));
558 let a = tree
559 .insert_child(root, WidgetPod::new_typed(WidgetId(2), Leaf))
560 .unwrap();
561 let a_child = tree
562 .insert_child(a, WidgetPod::new_typed(WidgetId(3), Leaf))
563 .unwrap();
564 let b = tree
565 .insert_child(root, WidgetPod::new_typed(WidgetId(4), Leaf))
566 .unwrap();
567
568 tree.pod_mut(root)
570 .unwrap()
571 .set_layout(Point::ZERO, Size::new(100.0, 100.0));
572 tree.pod_mut(a)
573 .unwrap()
574 .set_layout(Point::new(10.0, 5.0), Size::new(50.0, 40.0));
575 tree.pod_mut(a_child)
576 .unwrap()
577 .set_layout(Point::new(2.0, 3.0), Size::new(10.0, 10.0));
578 tree.pod_mut(b)
579 .unwrap()
580 .set_layout(Point::new(0.0, 60.0), Size::new(20.0, 20.0));
581 tree.pod_mut(a).unwrap().set_debug_label("branch-a");
582
583 let nodes = tree.inspect();
584 let ids: Vec<WidgetId> = nodes.iter().map(|n| n.id).collect();
586 assert_eq!(ids, vec![root, a, a_child, b]);
587 assert_eq!(
588 nodes.iter().map(|n| n.depth).collect::<Vec<_>>(),
589 vec![0, 1, 2, 1]
590 );
591 assert_eq!(
592 nodes.iter().map(|n| n.parent).collect::<Vec<_>>(),
593 vec![None, Some(root), Some(a), Some(root)]
594 );
595 assert_eq!(nodes[0].children, vec![a, b]);
596 assert_eq!(nodes[2].children, Vec::new());
597
598 assert_eq!(nodes[1].bounds, Rect::new(10.0, 5.0, 60.0, 45.0));
600 assert_eq!(nodes[2].bounds, Rect::new(12.0, 8.0, 22.0, 18.0));
601
602 assert!(nodes[0].type_name.ends_with("Leaf"));
604 assert_eq!(nodes[1].debug_label.as_deref(), Some("branch-a"));
605 assert!(nodes[0].debug_label.is_none());
606 assert!(nodes[3].debug_label.is_none());
607 }
608
609 #[test]
610 fn inspect_of_an_unlaid_out_tree_reports_zero_rects() {
611 let mut tree = WidgetTree::new();
612 let root = tree.insert_root(WidgetPod::new_typed(WidgetId(1), Leaf));
613 tree.insert_child(root, WidgetPod::new_typed(WidgetId(2), Leaf))
614 .unwrap();
615 let nodes = tree.inspect();
616 assert_eq!(nodes.len(), 2);
617 assert!(nodes.iter().all(|n| n.bounds == Rect::ZERO));
618 }
619
620 #[test]
621 fn traversal_follows_the_arena_not_the_order_index() {
622 let mut tree = WidgetTree::new();
626 let root = tree.insert_root(pod(1));
627 let kept = tree.insert_child(root, pod(2)).unwrap();
628
629 tree.child_order
631 .get_mut(&root)
632 .unwrap()
633 .push(WidgetId(1234));
634 assert_eq!(tree.children(root), vec![kept]);
635 assert_eq!(tree.inspect().len(), 2);
636
637 tree.child_order.remove(&root);
639 assert_eq!(tree.children(root), vec![kept]);
640 tree.root_order.clear();
641 assert_eq!(tree.roots(), vec![root]);
642 }
643
644 #[test]
645 fn inspect_descends_through_child_pods() {
646 let inner = Container::new(vec![(
650 Point::new(1.0, 1.0),
651 Size::new(5.0, 5.0),
652 Box::new(Leaf),
653 )]);
654 let outer = Container::new(vec![
655 (Point::new(10.0, 5.0), Size::new(50.0, 40.0), Box::new(Leaf)),
656 (
657 Point::new(0.0, 60.0),
658 Size::new(20.0, 20.0),
659 Box::new(inner) as Box<dyn Widget>,
660 ),
661 ]);
662
663 let mut tree = WidgetTree::new();
664 let root = tree.insert_root(WidgetPod::new_typed(WidgetId(1), outer));
665 tree.pod_mut(root)
666 .unwrap()
667 .set_layout(Point::new(2.0, 3.0), Size::new(100.0, 100.0));
668
669 let nodes = tree.inspect();
670 assert_eq!(nodes.len(), 4, "root + two pods + one grandchild pod");
671
672 assert_eq!(
674 nodes.iter().map(|n| n.depth).collect::<Vec<_>>(),
675 vec![0, 1, 1, 2],
676 "a pod's depth continues from its owning node's"
677 );
678 assert_eq!(nodes[0].parent, None);
679 assert_eq!(nodes[1].parent, Some(nodes[0].id));
680 assert_eq!(nodes[2].parent, Some(nodes[0].id));
681 assert_eq!(nodes[3].parent, Some(nodes[2].id));
682 assert_eq!(nodes[0].children, vec![nodes[1].id, nodes[2].id]);
683 assert_eq!(nodes[2].children, vec![nodes[3].id]);
684 assert!(nodes[1].children.is_empty(), "a leaf publishes no children");
685
686 assert_eq!(nodes[0].bounds, Rect::new(2.0, 3.0, 102.0, 103.0));
690 assert_eq!(nodes[1].bounds, Rect::new(12.0, 8.0, 62.0, 48.0));
691 assert_eq!(nodes[2].bounds, Rect::new(2.0, 63.0, 22.0, 83.0));
692 assert_eq!(nodes[3].bounds, Rect::new(3.0, 64.0, 8.0, 69.0));
693
694 assert!(
697 nodes[0].type_name.ends_with("Container"),
698 "{}",
699 nodes[0].type_name
700 );
701 assert!(
702 nodes[1..]
703 .iter()
704 .all(|n| n.type_name.ends_with("Leaf") || n.type_name.ends_with("Container"))
705 );
706
707 let ids: HashSet<u64> = nodes.iter().map(|n| n.id.0).collect();
710 assert_eq!(ids.len(), nodes.len(), "ids are unique across a snapshot");
711 assert!(
712 nodes[1..]
713 .iter()
714 .all(|n| n.id.0 >= crate::widget::ChildPod::INSPECT_ID_BASE)
715 );
716 }
717
718 #[test]
719 fn a_pods_tooling_id_and_label_survive_the_next_walk() {
720 let mut tree = WidgetTree::new();
723 let root = tree.insert_root(WidgetPod::new_typed(
724 WidgetId(1),
725 Container::new(vec![(Point::ZERO, Size::new(4.0, 4.0), Box::new(Leaf))]),
726 ));
727 let first = tree.inspect();
728 let second = tree.inspect();
729 assert_eq!(first[1].id, second[1].id);
730
731 let pod = tree.pod_mut(root).unwrap();
734 let container = pod
735 .widget_mut()
736 .downcast_mut::<Container>()
737 .expect("the root widget is the container");
738 container.children[0].set_debug_label("the-child");
739 assert_eq!(tree.inspect()[1].debug_label.as_deref(), Some("the-child"));
740 let container = tree
741 .pod_mut(root)
742 .unwrap()
743 .widget_mut()
744 .downcast_mut::<Container>()
745 .expect("the root widget is the container");
746 container.children[0].clear_debug_label();
747 assert_eq!(tree.inspect()[1].debug_label, None);
748 }
749
750 #[test]
751 fn a_widget_that_ignores_the_seam_reads_as_a_leaf() {
752 struct Opaque {
755 _child: ChildPod,
756 }
757 impl Widget for Opaque {
758 fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
759 bc.max()
760 }
761 fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut dyn PaintScene) {}
762 }
763
764 let mut tree = WidgetTree::new();
765 tree.insert_root(WidgetPod::new_typed(
766 WidgetId(1),
767 Opaque {
768 _child: ChildPod::new(Box::new(Leaf)),
769 },
770 ));
771 assert_eq!(tree.inspect().len(), 1);
772 }
773
774 #[test]
775 fn pod_stores_layout_geometry() {
776 let mut tree = WidgetTree::new();
777 let id = tree.insert_root(pod(1));
778 let p = tree.pod_mut(id).unwrap();
779 assert!(p.flags().needs_layout());
780 p.set_layout(Point::new(2.0, 3.0), Size::new(10.0, 20.0));
781 p.clear_flags();
782 assert_eq!(p.origin(), Point::new(2.0, 3.0));
783 assert_eq!(p.size(), Size::new(10.0, 20.0));
784 assert!(p.flags().is_empty());
785 }
786}