Fix tree rendering

Fixes some tree rendering problems where lines were not properly drawn
at some levels during some circumstances.

* Change the API back to only allow one root.
* Now uses a stack based approach to rendering instead of recursion.
* Removes the need for measuring the whole tree in advance.
  Leave this up to each child to render.
This commit is contained in:
Patrik Svensson
2021-01-09 18:34:07 +01:00
committed by Patrik Svensson
parent 0e0f4b4220
commit 8261b25e5c
34 changed files with 697 additions and 446 deletions

View File

@ -1,43 +0,0 @@
├── 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

View File

@ -0,0 +1,41 @@
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
║ ╚══ child2-1
║ ╚══ Child2-1-1
║ child
╠══ child3
║ ╚══ single leaf
║ multiline
║ ╚══ 2021 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 │ │ │ │ │ │ │
║ └─────┴─────┴─────┴─────┴─────┴─────┴─────┘
╚══ child4

View File

@ -1,40 +0,0 @@
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 │ │
│ │ │ │ │ │ │ │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘

View File

@ -33,4 +33,8 @@
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Expectations\Widgets\Tree\" />
</ItemGroup>
</Project>

View File

@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Spectre.Console.Testing;
@ -13,25 +12,28 @@ namespace Spectre.Console.Tests.Unit
public class TreeTests
{
[Fact]
[Expectation("SingleRoot")]
public Task Should_Render_Tree_With_Single_Root_Correctly()
[Expectation("Render")]
public Task Should_Render_Tree_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 tree = new Tree(new Text("Root node")).Guide(TreeGuide.DoubleLine);
var nestedChildren = Enumerable.Range(0, 10).Select(x => new Text($"multiple\nline {x}"));
var child2 = new TreeNode(new Text("child2"));
var child2Child = new TreeNode(new Text("child2Child"));
var child2Child = new TreeNode(new Text("child2-1"));
child2.AddNode(child2Child);
child2Child.AddNode(new TreeNode(new Text("Child 2 child\n child")));
child2Child.AddNode(new TreeNode(new Text("Child2-1-1\nchild")));
var child3 = new TreeNode(new Text("child3"));
var child3Child = new TreeNode(new Text("single leaf\n multiline"));
child3Child.AddNode(new TreeNode(new Calendar(2020, 01)));
var child3Child = new TreeNode(new Text("single leaf\nmultiline"));
child3Child.AddNode(new TreeNode(new Calendar(2021, 01)));
child3.AddNode(child3Child);
var children = new List<TreeNode> { new(new Text("child1"), nestedChildren), child2, child3 };
var root = new TreeNode(new Text("Root node"), children);
var tree = new Tree().AddNode(root);
tree.AddNode("child1").AddNodes(nestedChildren);
tree.AddNode(child2);
tree.AddNode(child3);
tree.AddNode("child4");
// When
console.Render(tree);
@ -41,41 +43,12 @@ namespace Spectre.Console.Tests.Unit
}
[Fact]
[Expectation("MultipleRoots")]
public Task Should_Render_Tree_With_Multiple_Roots_Correctly()
[Expectation("Render_NoChildren")]
public Task Should_Render_Tree_With_No_Child_Nodes_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.AddNode(child2Child);
child2Child.AddNode(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.AddNode(new TreeNode(new Calendar(2020, 01)));
child3.AddNode(child3Child);
var children = new List<TreeNode> { new(new Text("child1"), nestedChildren), child2, child3 };
var root = new TreeNode(new Text("Root node"), children);
var tree = new Tree().AddNode(root).AddNode(child2Child);
// When
console.Render(tree);
// Then
return Verifier.Verify(console.Output);
}
[Fact]
[Expectation("OnlyRoot")]
public Task Should_Render_Tree_With_Only_Root_Node_Correctly()
{
// Given
var console = new FakeConsole(width: 80);
var root = new TreeNode(new Text("Root node"), Enumerable.Empty<TreeNode>());
var tree = new Tree().AddNode(root);
var tree = new Tree(new Text("Root node"));
// When
console.Render(tree);