1use presentar_core::{
7 Brick, BrickAssertion, BrickBudget, BrickVerification, Canvas, Color, Constraints, Event,
8 LayoutResult, Point, Rect, Size, TextStyle, TypeId, Widget,
9};
10use std::any::Any;
11use std::collections::HashSet;
12use std::time::Duration;
13
14const BRANCH_PIPE: &str = "│ ";
16const BRANCH_TEE: &str = "├── ";
17const BRANCH_ELBOW: &str = "└── ";
18const BRANCH_SPACE: &str = " ";
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub struct NodeId(pub u64);
23
24impl NodeId {
25 #[must_use]
27 pub const fn new(id: u64) -> Self {
28 Self(id)
29 }
30}
31
32#[derive(Debug, Clone)]
34pub struct TreeNode {
35 pub id: NodeId,
37 pub label: String,
39 pub info: Option<String>,
41 pub children: Vec<Self>,
43 pub color: Option<Color>,
45}
46
47impl TreeNode {
48 #[must_use]
50 pub fn new(id: u64, label: impl Into<String>) -> Self {
51 Self {
52 id: NodeId::new(id),
53 label: label.into(),
54 info: None,
55 children: vec![],
56 color: None,
57 }
58 }
59
60 #[must_use]
62 pub fn with_info(mut self, info: impl Into<String>) -> Self {
63 self.info = Some(info.into());
64 self
65 }
66
67 #[must_use]
69 pub fn with_color(mut self, color: Color) -> Self {
70 self.color = Some(color);
71 self
72 }
73
74 #[must_use]
76 pub fn with_child(mut self, child: Self) -> Self {
77 self.children.push(child);
78 self
79 }
80
81 #[must_use]
83 pub fn with_children(mut self, children: Vec<Self>) -> Self {
84 self.children = children;
85 self
86 }
87
88 #[must_use]
90 pub fn count_nodes(&self) -> usize {
91 1 + self.children.iter().map(Self::count_nodes).sum::<usize>()
92 }
93
94 #[must_use]
96 pub fn depth(&self) -> usize {
97 if self.children.is_empty() {
98 1
99 } else {
100 1 + self.children.iter().map(Self::depth).max().unwrap_or(0)
101 }
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct Tree {
108 root: Option<TreeNode>,
110 expanded: HashSet<NodeId>,
112 default_color: Color,
114 show_info: bool,
116 #[allow(dead_code)]
118 indent_width: usize,
119 scroll_offset: usize,
121 selected: Option<NodeId>,
123 bounds: Rect,
125}
126
127impl Default for Tree {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133impl Tree {
134 #[must_use]
136 pub fn new() -> Self {
137 Self {
138 root: None,
139 expanded: HashSet::new(),
140 default_color: Color::new(0.8, 0.8, 0.8, 1.0),
141 show_info: true,
142 indent_width: 4,
143 scroll_offset: 0,
144 selected: None,
145 bounds: Rect::default(),
146 }
147 }
148
149 #[must_use]
151 pub fn with_root(mut self, root: TreeNode) -> Self {
152 self.expanded.insert(root.id);
154 self.root = Some(root);
155 self
156 }
157
158 #[must_use]
160 pub fn with_color(mut self, color: Color) -> Self {
161 self.default_color = color;
162 self
163 }
164
165 #[must_use]
167 pub fn with_info(mut self, show: bool) -> Self {
168 self.show_info = show;
169 self
170 }
171
172 #[must_use]
174 pub fn expand_all(mut self) -> Self {
175 if let Some(ref root) = self.root {
176 Self::collect_all_ids(root, &mut self.expanded);
177 }
178 self
179 }
180
181 #[must_use]
183 pub fn collapse_all(mut self) -> Self {
184 self.expanded.clear();
185 if let Some(ref root) = self.root {
186 self.expanded.insert(root.id);
187 }
188 self
189 }
190
191 pub fn toggle(&mut self, id: NodeId) {
193 if self.expanded.contains(&id) {
194 self.expanded.remove(&id);
195 } else {
196 self.expanded.insert(id);
197 }
198 }
199
200 pub fn expand(&mut self, id: NodeId) {
202 self.expanded.insert(id);
203 }
204
205 pub fn collapse(&mut self, id: NodeId) {
207 self.expanded.remove(&id);
208 }
209
210 #[must_use]
212 pub fn is_expanded(&self, id: NodeId) -> bool {
213 self.expanded.contains(&id)
214 }
215
216 pub fn set_scroll(&mut self, offset: usize) {
218 self.scroll_offset = offset;
219 }
220
221 pub fn select(&mut self, id: Option<NodeId>) {
223 self.selected = id;
224 }
225
226 #[must_use]
228 pub fn selected(&self) -> Option<NodeId> {
229 self.selected
230 }
231
232 pub fn set_root(&mut self, root: TreeNode) {
234 self.expanded.insert(root.id);
235 self.root = Some(root);
236 }
237
238 #[must_use]
240 pub fn visible_lines(&self) -> usize {
241 self.root
242 .as_ref()
243 .map_or(0, |r| self.count_visible_lines(r))
244 }
245
246 fn count_visible_lines(&self, node: &TreeNode) -> usize {
247 let mut count = 1;
248 if self.expanded.contains(&node.id) {
249 for child in &node.children {
250 count += self.count_visible_lines(child);
251 }
252 }
253 count
254 }
255
256 fn collect_all_ids(node: &TreeNode, ids: &mut HashSet<NodeId>) {
257 ids.insert(node.id);
258 for child in &node.children {
259 Self::collect_all_ids(child, ids);
260 }
261 }
262
263 #[allow(clippy::too_many_arguments)]
264 fn render_node(
265 &self,
266 canvas: &mut dyn Canvas,
267 node: &TreeNode,
268 x: f32,
269 y: &mut f32,
270 prefix: &str,
271 is_last: bool,
272 visible_height: f32,
273 ) {
274 if *y < self.bounds.y {
276 }
278
279 let branch = if prefix.is_empty() {
281 String::new()
282 } else if is_last {
283 format!("{prefix}{BRANCH_ELBOW}")
284 } else {
285 format!("{prefix}{BRANCH_TEE}")
286 };
287
288 if *y >= self.bounds.y && *y < self.bounds.y + visible_height {
290 let branch_style = TextStyle {
292 color: Color::new(0.5, 0.5, 0.5, 1.0),
293 ..Default::default()
294 };
295 canvas.draw_text(&branch, Point::new(x, *y), &branch_style);
296
297 let indicator = if node.children.is_empty() {
299 " "
300 } else if self.expanded.contains(&node.id) {
301 "▼ "
302 } else {
303 "▶ "
304 };
305
306 let indicator_x = x + branch.chars().count() as f32;
307 canvas.draw_text(indicator, Point::new(indicator_x, *y), &branch_style);
308
309 let label_x = indicator_x + 2.0;
311 let color = node.color.unwrap_or(self.default_color);
312 let is_selected = self.selected == Some(node.id);
313
314 let label_style = TextStyle {
315 color: if is_selected {
316 Color::new(0.0, 0.0, 0.0, 1.0)
317 } else {
318 color
319 },
320 ..Default::default()
321 };
322
323 if is_selected {
325 let label_len = node.label.len() as f32;
326 canvas.fill_rect(
327 Rect::new(label_x, *y, label_len, 1.0),
328 Color::new(0.3, 0.7, 1.0, 1.0),
329 );
330 }
331
332 canvas.draw_text(&node.label, Point::new(label_x, *y), &label_style);
333
334 if self.show_info {
336 if let Some(ref info) = node.info {
337 let info_x = label_x + node.label.len() as f32 + 2.0;
338 let info_style = TextStyle {
339 color: Color::new(0.6, 0.6, 0.6, 1.0),
340 ..Default::default()
341 };
342 canvas.draw_text(info, Point::new(info_x, *y), &info_style);
343 }
344 }
345 }
346
347 *y += 1.0;
348
349 if self.expanded.contains(&node.id) && !node.children.is_empty() {
351 let child_prefix = if prefix.is_empty() {
352 String::new()
353 } else if is_last {
354 format!("{prefix}{BRANCH_SPACE}")
355 } else {
356 format!("{prefix}{BRANCH_PIPE}")
357 };
358
359 let child_count = node.children.len();
360 for (i, child) in node.children.iter().enumerate() {
361 let child_is_last = i == child_count - 1;
362 self.render_node(
363 canvas,
364 child,
365 x,
366 y,
367 &child_prefix,
368 child_is_last,
369 visible_height,
370 );
371 }
372 }
373 }
374}
375
376impl Brick for Tree {
377 fn brick_name(&self) -> &'static str {
378 "tree"
379 }
380
381 fn assertions(&self) -> &[BrickAssertion] {
382 static ASSERTIONS: &[BrickAssertion] = &[BrickAssertion::max_latency_ms(16)];
383 ASSERTIONS
384 }
385
386 fn budget(&self) -> BrickBudget {
387 BrickBudget::uniform(16)
388 }
389
390 fn verify(&self) -> BrickVerification {
391 BrickVerification {
392 passed: self.assertions().to_vec(),
393 failed: vec![],
394 verification_time: Duration::from_micros(10),
395 }
396 }
397
398 fn to_html(&self) -> String {
399 String::new()
400 }
401
402 fn to_css(&self) -> String {
403 String::new()
404 }
405}
406
407impl Widget for Tree {
408 fn type_id(&self) -> TypeId {
409 TypeId::of::<Self>()
410 }
411
412 fn measure(&self, constraints: Constraints) -> Size {
413 let lines = self.visible_lines() as f32;
414 let width = constraints.max_width.min(80.0);
415 let height = lines.min(constraints.max_height);
416 constraints.constrain(Size::new(width, height.max(1.0)))
417 }
418
419 fn layout(&mut self, bounds: Rect) -> LayoutResult {
420 self.bounds = bounds;
421 LayoutResult {
422 size: Size::new(bounds.width, bounds.height),
423 }
424 }
425
426 fn paint(&self, canvas: &mut dyn Canvas) {
427 if self.root.is_none() || self.bounds.width < 1.0 {
428 return;
429 }
430
431 let mut y = self.bounds.y - self.scroll_offset as f32;
432 if let Some(ref root) = self.root {
433 self.render_node(
434 canvas,
435 root,
436 self.bounds.x,
437 &mut y,
438 "",
439 true,
440 self.bounds.height,
441 );
442 }
443 }
444
445 fn event(&mut self, _event: &Event) -> Option<Box<dyn Any + Send>> {
446 None
447 }
448
449 fn children(&self) -> &[Box<dyn Widget>] {
450 &[]
451 }
452
453 fn children_mut(&mut self) -> &mut [Box<dyn Widget>] {
454 &mut []
455 }
456}
457
458#[cfg(test)]
459mod tests {
460 use super::*;
461
462 struct MockCanvas {
463 texts: Vec<(String, Point)>,
464 rects: Vec<(Rect, Color)>,
465 }
466
467 impl MockCanvas {
468 fn new() -> Self {
469 Self {
470 texts: vec![],
471 rects: vec![],
472 }
473 }
474 }
475
476 impl Canvas for MockCanvas {
477 fn fill_rect(&mut self, rect: Rect, color: Color) {
478 self.rects.push((rect, color));
479 }
480 fn stroke_rect(&mut self, _rect: Rect, _color: Color, _width: f32) {}
481 fn draw_text(&mut self, text: &str, position: Point, _style: &TextStyle) {
482 self.texts.push((text.to_string(), position));
483 }
484 fn draw_line(&mut self, _from: Point, _to: Point, _color: Color, _width: f32) {}
485 fn fill_circle(&mut self, _center: Point, _radius: f32, _color: Color) {}
486 fn stroke_circle(&mut self, _center: Point, _radius: f32, _color: Color, _width: f32) {}
487 fn fill_arc(&mut self, _c: Point, _r: f32, _s: f32, _e: f32, _color: Color) {}
488 fn draw_path(&mut self, _points: &[Point], _color: Color, _width: f32) {}
489 fn fill_polygon(&mut self, _points: &[Point], _color: Color) {}
490 fn push_clip(&mut self, _rect: Rect) {}
491 fn pop_clip(&mut self) {}
492 fn push_transform(&mut self, _transform: presentar_core::Transform2D) {}
493 fn pop_transform(&mut self) {}
494 }
495
496 #[test]
497 fn test_tree_creation() {
498 let tree = Tree::new();
499 assert!(tree.root.is_none());
500 }
501
502 #[test]
503 fn test_tree_with_root() {
504 let root = TreeNode::new(1, "Root");
505 let tree = Tree::new().with_root(root);
506 assert!(tree.root.is_some());
507 assert!(tree.is_expanded(NodeId::new(1)));
508 }
509
510 #[test]
511 fn test_tree_node_builder() {
512 let node = TreeNode::new(1, "Parent")
513 .with_info("Info text")
514 .with_color(Color::RED)
515 .with_child(TreeNode::new(2, "Child"));
516 assert_eq!(node.children.len(), 1);
517 assert!(node.info.is_some());
518 assert!(node.color.is_some());
519 }
520
521 #[test]
522 fn test_tree_toggle() {
523 let root = TreeNode::new(1, "Root").with_child(TreeNode::new(2, "Child"));
524 let mut tree = Tree::new().with_root(root);
525
526 assert!(tree.is_expanded(NodeId::new(1)));
527 tree.toggle(NodeId::new(1));
528 assert!(!tree.is_expanded(NodeId::new(1)));
529 tree.toggle(NodeId::new(1));
530 assert!(tree.is_expanded(NodeId::new(1)));
531 }
532
533 #[test]
534 fn test_tree_expand_collapse() {
535 let root = TreeNode::new(1, "Root").with_child(TreeNode::new(2, "Child"));
536 let mut tree = Tree::new().with_root(root);
537
538 tree.expand(NodeId::new(2));
539 assert!(tree.is_expanded(NodeId::new(2)));
540 tree.collapse(NodeId::new(2));
541 assert!(!tree.is_expanded(NodeId::new(2)));
542 }
543
544 #[test]
545 fn test_tree_expand_all() {
546 let root = TreeNode::new(1, "Root")
547 .with_child(TreeNode::new(2, "Child1").with_child(TreeNode::new(3, "GrandChild")))
548 .with_child(TreeNode::new(4, "Child2"));
549 let tree = Tree::new().with_root(root).expand_all();
550
551 assert!(tree.is_expanded(NodeId::new(1)));
552 assert!(tree.is_expanded(NodeId::new(2)));
553 assert!(tree.is_expanded(NodeId::new(3)));
554 assert!(tree.is_expanded(NodeId::new(4)));
555 }
556
557 #[test]
558 fn test_tree_collapse_all() {
559 let root = TreeNode::new(1, "Root").with_child(TreeNode::new(2, "Child"));
560 let tree = Tree::new().with_root(root).expand_all().collapse_all();
561
562 assert!(tree.is_expanded(NodeId::new(1))); assert!(!tree.is_expanded(NodeId::new(2)));
564 }
565
566 #[test]
567 fn test_tree_visible_lines() {
568 let root = TreeNode::new(1, "Root")
569 .with_child(TreeNode::new(2, "Child1"))
570 .with_child(TreeNode::new(3, "Child2"));
571 let tree = Tree::new().with_root(root);
572
573 assert_eq!(tree.visible_lines(), 3);
575 }
576
577 #[test]
578 fn test_tree_visible_lines_collapsed() {
579 let root = TreeNode::new(1, "Root")
580 .with_child(TreeNode::new(2, "Child1"))
581 .with_child(TreeNode::new(3, "Child2"));
582 let mut tree = Tree::new().with_root(root);
583 tree.collapse(NodeId::new(1));
584
585 assert_eq!(tree.visible_lines(), 1);
587 }
588
589 #[test]
590 fn test_tree_node_count() {
591 let root = TreeNode::new(1, "Root")
592 .with_child(TreeNode::new(2, "Child1").with_child(TreeNode::new(3, "GrandChild")))
593 .with_child(TreeNode::new(4, "Child2"));
594
595 assert_eq!(root.count_nodes(), 4);
596 }
597
598 #[test]
599 fn test_tree_node_depth() {
600 let root = TreeNode::new(1, "Root")
601 .with_child(TreeNode::new(2, "Child1").with_child(TreeNode::new(3, "GrandChild")))
602 .with_child(TreeNode::new(4, "Child2"));
603
604 assert_eq!(root.depth(), 3);
605 }
606
607 #[test]
608 fn test_tree_selection() {
609 let root = TreeNode::new(1, "Root");
610 let mut tree = Tree::new().with_root(root);
611
612 assert!(tree.selected().is_none());
613 tree.select(Some(NodeId::new(1)));
614 assert_eq!(tree.selected(), Some(NodeId::new(1)));
615 tree.select(None);
616 assert!(tree.selected().is_none());
617 }
618
619 #[test]
620 fn test_tree_paint() {
621 let root = TreeNode::new(1, "Root")
622 .with_child(TreeNode::new(2, "Child1"))
623 .with_child(TreeNode::new(3, "Child2"));
624 let mut tree = Tree::new().with_root(root);
625 tree.bounds = Rect::new(0.0, 0.0, 40.0, 10.0);
626
627 let mut canvas = MockCanvas::new();
628 tree.paint(&mut canvas);
629
630 assert!(!canvas.texts.is_empty());
631 }
632
633 #[test]
634 fn test_tree_paint_empty() {
635 let tree = Tree::new();
636 let mut canvas = MockCanvas::new();
637 tree.paint(&mut canvas);
638 assert!(canvas.texts.is_empty());
639 }
640
641 #[test]
642 fn test_tree_measure() {
643 let root = TreeNode::new(1, "Root")
644 .with_child(TreeNode::new(2, "Child1"))
645 .with_child(TreeNode::new(3, "Child2"));
646 let tree = Tree::new().with_root(root);
647
648 let size = tree.measure(Constraints::loose(Size::new(100.0, 50.0)));
649 assert!(size.height >= 3.0); }
651
652 #[test]
653 fn test_tree_layout() {
654 let mut tree = Tree::new();
655 let bounds = Rect::new(5.0, 10.0, 30.0, 20.0);
656 let result = tree.layout(bounds);
657
658 assert_eq!(result.size.width, 30.0);
659 assert_eq!(result.size.height, 20.0);
660 assert_eq!(tree.bounds, bounds);
661 }
662
663 #[test]
664 fn test_tree_brick_name() {
665 let tree = Tree::new();
666 assert_eq!(tree.brick_name(), "tree");
667 }
668
669 #[test]
670 fn test_tree_assertions() {
671 let tree = Tree::new();
672 assert!(!tree.assertions().is_empty());
673 }
674
675 #[test]
676 fn test_tree_budget() {
677 let tree = Tree::new();
678 let budget = tree.budget();
679 assert!(budget.paint_ms > 0);
680 }
681
682 #[test]
683 fn test_tree_verify() {
684 let tree = Tree::new();
685 assert!(tree.verify().is_valid());
686 }
687
688 #[test]
689 fn test_tree_type_id() {
690 let tree = Tree::new();
691 assert_eq!(Widget::type_id(&tree), TypeId::of::<Tree>());
692 }
693
694 #[test]
695 fn test_tree_children() {
696 let tree = Tree::new();
697 assert!(tree.children().is_empty());
698 }
699
700 #[test]
701 fn test_tree_children_mut() {
702 let mut tree = Tree::new();
703 assert!(tree.children_mut().is_empty());
704 }
705
706 #[test]
707 fn test_tree_event() {
708 let mut tree = Tree::new();
709 let event = Event::KeyDown {
710 key: presentar_core::Key::Enter,
711 };
712 assert!(tree.event(&event).is_none());
713 }
714
715 #[test]
716 fn test_tree_default() {
717 let tree = Tree::default();
718 assert!(tree.root.is_none());
719 }
720
721 #[test]
722 fn test_tree_to_html() {
723 let tree = Tree::new();
724 assert!(tree.to_html().is_empty());
725 }
726
727 #[test]
728 fn test_tree_to_css() {
729 let tree = Tree::new();
730 assert!(tree.to_css().is_empty());
731 }
732
733 #[test]
734 fn test_tree_scroll() {
735 let mut tree = Tree::new();
736 tree.set_scroll(5);
737 assert_eq!(tree.scroll_offset, 5);
738 }
739
740 #[test]
741 fn test_tree_with_color() {
742 let tree = Tree::new().with_color(Color::RED);
743 assert_eq!(tree.default_color, Color::RED);
744 }
745
746 #[test]
747 fn test_tree_with_info() {
748 let tree = Tree::new().with_info(false);
749 assert!(!tree.show_info);
750 }
751
752 #[test]
753 fn test_tree_set_root() {
754 let mut tree = Tree::new();
755 tree.set_root(TreeNode::new(1, "New Root"));
756 assert!(tree.root.is_some());
757 }
758
759 #[test]
760 fn test_node_id() {
761 let id = NodeId::new(42);
762 assert_eq!(id.0, 42);
763 }
764
765 #[test]
766 fn test_tree_node_with_children() {
767 let children = vec![TreeNode::new(2, "A"), TreeNode::new(3, "B")];
768 let node = TreeNode::new(1, "Root").with_children(children);
769 assert_eq!(node.children.len(), 2);
770 }
771
772 #[test]
773 fn test_tree_paint_with_selection() {
774 let root = TreeNode::new(1, "Root");
775 let mut tree = Tree::new().with_root(root);
776 tree.select(Some(NodeId::new(1)));
777 tree.bounds = Rect::new(0.0, 0.0, 40.0, 10.0);
778
779 let mut canvas = MockCanvas::new();
780 tree.paint(&mut canvas);
781
782 assert!(!canvas.rects.is_empty());
784 }
785
786 #[test]
787 fn test_tree_leaf_node_depth() {
788 let leaf = TreeNode::new(1, "Leaf");
789 assert_eq!(leaf.depth(), 1);
790 }
791
792 #[test]
793 fn test_tree_paint_collapsed_node() {
794 let root = TreeNode::new(1, "Root").with_child(TreeNode::new(2, "Child"));
796 let mut tree = Tree::new().with_root(root);
797 tree.collapse(NodeId::new(1)); tree.bounds = Rect::new(0.0, 0.0, 40.0, 10.0);
799
800 let mut canvas = MockCanvas::new();
801 tree.paint(&mut canvas);
802
803 assert!(canvas.texts.iter().any(|(t, _)| t.contains("▶")));
805 }
806
807 #[test]
808 fn test_tree_paint_with_info() {
809 let root = TreeNode::new(1, "Root").with_info("Info text");
811 let mut tree = Tree::new().with_root(root).with_info(true);
812 tree.bounds = Rect::new(0.0, 0.0, 60.0, 10.0);
813
814 let mut canvas = MockCanvas::new();
815 tree.paint(&mut canvas);
816
817 assert!(canvas.texts.iter().any(|(t, _)| t.contains("Info text")));
819 }
820
821 #[test]
822 fn test_tree_paint_nested_expanded() {
823 let root = TreeNode::new(1, "Root")
825 .with_child(
826 TreeNode::new(2, "Child1")
827 .with_child(TreeNode::new(4, "GrandChild1"))
828 .with_child(TreeNode::new(5, "GrandChild2")),
829 )
830 .with_child(TreeNode::new(3, "Child2"));
831 let mut tree = Tree::new().with_root(root).expand_all();
832 tree.bounds = Rect::new(0.0, 0.0, 60.0, 20.0);
833
834 let mut canvas = MockCanvas::new();
835 tree.paint(&mut canvas);
836
837 assert!(canvas.texts.len() > 5);
839 }
840
841 #[test]
842 fn test_tree_paint_not_last_child() {
843 let root = TreeNode::new(1, "Root")
845 .with_child(TreeNode::new(2, "Child1"))
846 .with_child(TreeNode::new(3, "Child2"))
847 .with_child(TreeNode::new(4, "Child3"));
848 let mut tree = Tree::new().with_root(root);
849 tree.bounds = Rect::new(0.0, 0.0, 40.0, 10.0);
850
851 let mut canvas = MockCanvas::new();
852 tree.paint(&mut canvas);
853
854 assert!(canvas.texts.len() >= 4);
857 }
858
859 #[test]
860 fn test_tree_paint_scrolled() {
861 let root = TreeNode::new(1, "Root")
863 .with_child(TreeNode::new(2, "Child1"))
864 .with_child(TreeNode::new(3, "Child2"));
865 let mut tree = Tree::new().with_root(root);
866 tree.set_scroll(5);
867 tree.bounds = Rect::new(0.0, 0.0, 40.0, 10.0);
868
869 let mut canvas = MockCanvas::new();
870 tree.paint(&mut canvas);
871 }
873
874 #[test]
875 fn test_tree_paint_deep_nesting() {
876 let grandchild =
878 TreeNode::new(4, "GrandChild").with_child(TreeNode::new(5, "GreatGrandChild"));
879 let child1 = TreeNode::new(2, "Child1").with_child(grandchild);
880 let child2 = TreeNode::new(3, "Child2");
881 let root = TreeNode::new(1, "Root")
882 .with_child(child1)
883 .with_child(child2);
884 let mut tree = Tree::new().with_root(root).expand_all();
885 tree.bounds = Rect::new(0.0, 0.0, 80.0, 20.0);
886
887 let mut canvas = MockCanvas::new();
888 tree.paint(&mut canvas);
889
890 assert!(canvas.texts.len() >= 5);
892 }
893}