diff --git a/src/Spectre.Console.Tests/Expectations/TreeTests.Should_Render_Tree_With_Multiple_Roots_Correctly.verified.txt b/src/Spectre.Console.Tests/Expectations/TreeTests.Should_Render_Tree_With_Multiple_Roots_Correctly.verified.txt new file mode 100644 index 0000000..a8f06d1 --- /dev/null +++ b/src/Spectre.Console.Tests/Expectations/TreeTests.Should_Render_Tree_With_Multiple_Roots_Correctly.verified.txt @@ -0,0 +1,43 @@ +├── Root node +│ ├── child1 +│ │ ├── multiple +│ │ │ line 0 +│ │ ├── multiple +│ │ │ line 1 +│ │ ├── multiple +│ │ │ line 2 +│ │ ├── multiple +│ │ │ line 3 +│ │ ├── multiple +│ │ │ line 4 +│ │ ├── multiple +│ │ │ line 5 +│ │ ├── multiple +│ │ │ line 6 +│ │ ├── multiple +│ │ │ line 7 +│ │ ├── multiple +│ │ │ line 8 +│ │ └── multiple +│ │ line 9 +│ ├── child2 +│ │ └── child2Child +│ │ └── Child 2 child +│ │ child +│ └── child3 +│ └── single leaf +│ multiline +│ └── 2020 January +│ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐ +│ │ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ +│ ├─────┼─────┼─────┼─────┼─────┼─────┼─────┤ +│ │ │ │ │ 1 │ 2 │ 3 │ 4 │ +│ │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ +│ │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ +│ │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │ +│ │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ │ +│ │ │ │ │ │ │ │ │ +│ └─────┴─────┴─────┴─────┴─────┴─────┴─────┘ +└── child2Child + └── Child 2 child + child diff --git a/src/Spectre.Console.Tests/Expectations/TreeTests.Should_Render_Tree_Correctly.verified.txt b/src/Spectre.Console.Tests/Expectations/TreeTests.Should_Render_Tree_With_Single_Root_Correctly.verified.txt similarity index 100% rename from src/Spectre.Console.Tests/Expectations/TreeTests.Should_Render_Tree_Correctly.verified.txt rename to src/Spectre.Console.Tests/Expectations/TreeTests.Should_Render_Tree_With_Single_Root_Correctly.verified.txt diff --git a/src/Spectre.Console.Tests/Unit/TreeTests.cs b/src/Spectre.Console.Tests/Unit/TreeTests.cs index 1cb755b..c677119 100644 --- a/src/Spectre.Console.Tests/Unit/TreeTests.cs +++ b/src/Spectre.Console.Tests/Unit/TreeTests.cs @@ -1,9 +1,6 @@ using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; -using Shouldly; -using Spectre.Console.Rendering; using Spectre.Console.Testing; using VerifyXunit; using Xunit; @@ -14,77 +11,7 @@ namespace Spectre.Console.Tests.Unit public class TreeTests { [Fact] - public void Should_Measure_Tree_Correctly() - { - // Given - var nestedChildren = - Enumerable.Range(0, 10) - .Select(x => new TreeNode(new Text($"multiple \n line {x}"))); - var child3 = new TreeNode(new Text("child3")); - child3.AddChild(new TreeNode(new Text("single leaf\n multiline"))); - var children = new List - { - new(new Text("child1"), nestedChildren), new(new Text("child2")), child3, - }; - var root = new TreeNode(new Text("Root node"), children); - var tree = new Tree(root); - - // When - var measurement = ((IRenderable)tree).Measure(new RenderContext(Encoding.Unicode, false), 80); - - // Then - measurement.Min.ShouldBe(17); - measurement.Max.ShouldBe(19); - } - - [Fact] - public void Should_Measure_Tree_Correctly_With_Regard_To_Max_Width() - { - // Given - var root = new TreeNode(new Text("Root node")); - var currentNode = root; - foreach (var i in Enumerable.Range(0, 100)) - { - var newNode = new TreeNode(new Text(string.Empty)); - currentNode.AddChild(newNode); - currentNode = newNode; - } - - var tree = new Tree(root); - - // When - var measurement = ((IRenderable)tree).Measure(new RenderContext(Encoding.Unicode, false), 80); - - // Then - measurement.Min.ShouldBe(400); - measurement.Max.ShouldBe(80); - } - - [Fact] - public void Measure_Leaf_Dominated_Width() - { - // Given - var root = new TreeNode(new Text("Root node")); - var currentNode = root; - foreach (var i in Enumerable.Range(0, 10)) - { - var newNode = new TreeNode(new Text(i.ToString())); - currentNode.AddChild(newNode); - currentNode = newNode; - } - - var tree = new Tree(root); - - // When - var measurement = ((IRenderable)tree).Measure(new RenderContext(Encoding.Unicode, false), 80); - - // Then - measurement.Min.ShouldBe(41); - measurement.Max.ShouldBe(41); - } - - [Fact] - public Task Should_Render_Tree_Correctly() + public Task Should_Render_Tree_With_Single_Root_Correctly() { // Given var console = new FakeConsole(width: 80); @@ -101,7 +28,34 @@ namespace Spectre.Console.Tests.Unit child3.AddChild(child3Child); var children = new List { new(new Text("child1"), nestedChildren), child2, child3 }; var root = new TreeNode(new Text("Root node"), children); - var tree = new Tree(root); + var tree = new Tree().AddChild(root); + + // When + console.Render(tree); + + // Then + return Verifier.Verify(console.Output); + } + + [Fact] + public Task Should_Render_Tree_With_Multiple_Roots_Correctly() + { + // Given + var console = new FakeConsole(width: 80); + var nestedChildren = + Enumerable.Range(0, 10) + .Select(x => new TreeNode(new Text($"multiple \n line {x}"))); + var child2 = new TreeNode(new Text("child2")); + var child2Child = new TreeNode(new Text("child2Child")); + child2.AddChild(child2Child); + child2Child.AddChild(new TreeNode(new Text("Child 2 child\n child"))); + var child3 = new TreeNode(new Text("child3")); + var child3Child = new TreeNode(new Text("single leaf\n multiline")); + child3Child.AddChild(new TreeNode(new Calendar(2020, 01))); + child3.AddChild(child3Child); + var children = new List { new(new Text("child1"), nestedChildren), child2, child3 }; + var root = new TreeNode(new Text("Root node"), children); + var tree = new Tree().AddChild(root).AddChild(child2Child); // When console.Render(tree); @@ -116,7 +70,7 @@ namespace Spectre.Console.Tests.Unit // Given var console = new FakeConsole(width: 80); var root = new TreeNode(new Text("Root node"), Enumerable.Empty()); - var tree = new Tree(root); + var tree = new Tree().AddChild(root); // When console.Render(tree); diff --git a/src/Spectre.Console/Widgets/Tree.cs b/src/Spectre.Console/Widgets/Tree.cs index e613dc6..898cd01 100644 --- a/src/Spectre.Console/Widgets/Tree.cs +++ b/src/Spectre.Console/Widgets/Tree.cs @@ -9,10 +9,8 @@ namespace Spectre.Console /// /// Representation of tree data. /// - public sealed class Tree : Renderable + public sealed class Tree : Renderable, IHasTreeNodes { - private readonly TreeNode _rootNode; - /// /// Gets or sets the tree style. /// @@ -23,13 +21,20 @@ namespace Spectre.Console /// public TreeAppearance Appearance { get; set; } = TreeAppearance.Ascii; + /// + /// Gets the tree nodes. + /// + public List Nodes { get; } + + /// + List IHasTreeNodes.Children => Nodes; + /// /// Initializes a new instance of the class. /// - /// Root node of the tree to be rendered. - public Tree(TreeNode rootNode) + public Tree() { - _rootNode = rootNode ?? throw new ArgumentNullException(nameof(rootNode)); + Nodes = new List(); } /// @@ -59,20 +64,52 @@ namespace Spectre.Console return new Measurement(currentMin, Math.Min(currentMax, maxWidth)); } - return MeasureAtDepth(context, maxWidth, _rootNode, depth: 0); + if (Nodes.Count == 1) + { + return MeasureAtDepth(context, maxWidth, Nodes[0], depth: 0); + } + else + { + var root = new TreeNode(Text.Empty); + foreach (var node in Nodes) + { + root.AddChild(node); + } + + return MeasureAtDepth(context, maxWidth, root, depth: 0); + } } /// protected override IEnumerable Render(RenderContext context, int maxWidth) { - return _rootNode - .Render(context, maxWidth) - .Concat(new List { Segment.LineBreak }) - .Concat(RenderChildren(context, maxWidth - Appearance.PartSize, _rootNode, depth: 0)); + if (Nodes.Count == 1) + { + // Single root + return Nodes[0] + .Render(context, maxWidth) + .Concat(new List { Segment.LineBreak }) + .Concat(RenderChildren(context, maxWidth - Appearance.PartSize, Nodes[0], depth: 0)); + } + else + { + // Multiple roots + var root = new TreeNode(Text.Empty); + foreach (var node in Nodes) + { + root.AddChild(node); + } + + return Enumerable.Empty() + .Concat(RenderChildren( + context, maxWidth - Appearance.PartSize, root, + depth: 0)); + } } - private IEnumerable RenderChildren(RenderContext context, int maxWidth, TreeNode node, int depth, - int? trailingStarted = null) + private IEnumerable RenderChildren( + RenderContext context, int maxWidth, TreeNode node, + int depth, int? trailingStarted = null) { var result = new List(); foreach (var (_, _, lastChild, childNode) in node.Children.Enumerate())