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::key_down(presentar_core::Key::Enter);
710 assert!(tree.event(&event).is_none());
711 }
712
713 #[test]
714 fn test_tree_default() {
715 let tree = Tree::default();
716 assert!(tree.root.is_none());
717 }
718
719 #[test]
720 fn test_tree_to_html() {
721 let tree = Tree::new();
722 assert!(tree.to_html().is_empty());
723 }
724
725 #[test]
726 fn test_tree_to_css() {
727 let tree = Tree::new();
728 assert!(tree.to_css().is_empty());
729 }
730
731 #[test]
732 fn test_tree_scroll() {
733 let mut tree = Tree::new();
734 tree.set_scroll(5);
735 assert_eq!(tree.scroll_offset, 5);
736 }
737
738 #[test]
739 fn test_tree_with_color() {
740 let tree = Tree::new().with_color(Color::RED);
741 assert_eq!(tree.default_color, Color::RED);
742 }
743
744 #[test]
745 fn test_tree_with_info() {
746 let tree = Tree::new().with_info(false);
747 assert!(!tree.show_info);
748 }
749
750 #[test]
751 fn test_tree_set_root() {
752 let mut tree = Tree::new();
753 tree.set_root(TreeNode::new(1, "New Root"));
754 assert!(tree.root.is_some());
755 }
756
757 #[test]
758 fn test_node_id() {
759 let id = NodeId::new(42);
760 assert_eq!(id.0, 42);
761 }
762
763 #[test]
764 fn test_tree_node_with_children() {
765 let children = vec![TreeNode::new(2, "A"), TreeNode::new(3, "B")];
766 let node = TreeNode::new(1, "Root").with_children(children);
767 assert_eq!(node.children.len(), 2);
768 }
769
770 #[test]
771 fn test_tree_paint_with_selection() {
772 let root = TreeNode::new(1, "Root");
773 let mut tree = Tree::new().with_root(root);
774 tree.select(Some(NodeId::new(1)));
775 tree.bounds = Rect::new(0.0, 0.0, 40.0, 10.0);
776
777 let mut canvas = MockCanvas::new();
778 tree.paint(&mut canvas);
779
780 assert!(!canvas.rects.is_empty());
782 }
783
784 #[test]
785 fn test_tree_leaf_node_depth() {
786 let leaf = TreeNode::new(1, "Leaf");
787 assert_eq!(leaf.depth(), 1);
788 }
789
790 #[test]
791 fn test_tree_paint_collapsed_node() {
792 let root = TreeNode::new(1, "Root").with_child(TreeNode::new(2, "Child"));
794 let mut tree = Tree::new().with_root(root);
795 tree.collapse(NodeId::new(1)); tree.bounds = Rect::new(0.0, 0.0, 40.0, 10.0);
797
798 let mut canvas = MockCanvas::new();
799 tree.paint(&mut canvas);
800
801 assert!(canvas.texts.iter().any(|(t, _)| t.contains("▶")));
803 }
804
805 #[test]
806 fn test_tree_paint_with_info() {
807 let root = TreeNode::new(1, "Root").with_info("Info text");
809 let mut tree = Tree::new().with_root(root).with_info(true);
810 tree.bounds = Rect::new(0.0, 0.0, 60.0, 10.0);
811
812 let mut canvas = MockCanvas::new();
813 tree.paint(&mut canvas);
814
815 assert!(canvas.texts.iter().any(|(t, _)| t.contains("Info text")));
817 }
818
819 #[test]
820 fn test_tree_paint_nested_expanded() {
821 let root = TreeNode::new(1, "Root")
823 .with_child(
824 TreeNode::new(2, "Child1")
825 .with_child(TreeNode::new(4, "GrandChild1"))
826 .with_child(TreeNode::new(5, "GrandChild2")),
827 )
828 .with_child(TreeNode::new(3, "Child2"));
829 let mut tree = Tree::new().with_root(root).expand_all();
830 tree.bounds = Rect::new(0.0, 0.0, 60.0, 20.0);
831
832 let mut canvas = MockCanvas::new();
833 tree.paint(&mut canvas);
834
835 assert!(canvas.texts.len() > 5);
837 }
838
839 #[test]
840 fn test_tree_paint_not_last_child() {
841 let root = TreeNode::new(1, "Root")
843 .with_child(TreeNode::new(2, "Child1"))
844 .with_child(TreeNode::new(3, "Child2"))
845 .with_child(TreeNode::new(4, "Child3"));
846 let mut tree = Tree::new().with_root(root);
847 tree.bounds = Rect::new(0.0, 0.0, 40.0, 10.0);
848
849 let mut canvas = MockCanvas::new();
850 tree.paint(&mut canvas);
851
852 assert!(canvas.texts.len() >= 4);
855 }
856
857 #[test]
858 fn test_tree_paint_scrolled() {
859 let root = TreeNode::new(1, "Root")
861 .with_child(TreeNode::new(2, "Child1"))
862 .with_child(TreeNode::new(3, "Child2"));
863 let mut tree = Tree::new().with_root(root);
864 tree.set_scroll(5);
865 tree.bounds = Rect::new(0.0, 0.0, 40.0, 10.0);
866
867 let mut canvas = MockCanvas::new();
868 tree.paint(&mut canvas);
869 }
871
872 #[test]
873 fn test_tree_paint_deep_nesting() {
874 let grandchild =
876 TreeNode::new(4, "GrandChild").with_child(TreeNode::new(5, "GreatGrandChild"));
877 let child1 = TreeNode::new(2, "Child1").with_child(grandchild);
878 let child2 = TreeNode::new(3, "Child2");
879 let root = TreeNode::new(1, "Root")
880 .with_child(child1)
881 .with_child(child2);
882 let mut tree = Tree::new().with_root(root).expand_all();
883 tree.bounds = Rect::new(0.0, 0.0, 80.0, 20.0);
884
885 let mut canvas = MockCanvas::new();
886 tree.paint(&mut canvas);
887
888 assert!(canvas.texts.len() >= 5);
890 }
891}