mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-07-02 10:58:17 +08:00
Add tree widget
This commit is contained in:
@ -0,0 +1,40 @@
|
||||
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 │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
|
@ -0,0 +1 @@
|
||||
Root node
|
90
src/Spectre.Console.Tests/Unit/TreeMeasureTests.cs
Normal file
90
src/Spectre.Console.Tests/Unit/TreeMeasureTests.cs
Normal file
@ -0,0 +1,90 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Spectre.Console.Rendering;
|
||||
using Xunit;
|
||||
|
||||
namespace Spectre.Console.Tests.Unit
|
||||
{
|
||||
public class TreeMeasureTests
|
||||
{
|
||||
[Fact]
|
||||
public void Measure_Tree_Dominated_Width()
|
||||
{
|
||||
// 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<TreeNode>
|
||||
{
|
||||
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
|
||||
// Corresponds to "│ └── multiple"
|
||||
Assert.Equal(17, measurement.Min);
|
||||
|
||||
// Corresponds to " └── single leaf" when untrimmed
|
||||
Assert.Equal(19, measurement.Max);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Measure_Max_Width_Bound()
|
||||
{
|
||||
// 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
|
||||
// Each node depth contributes 4 characters, so 100 node depth -> 400 character min width
|
||||
Assert.Equal(400, measurement.Min);
|
||||
|
||||
// Successfully capped at 80 terminal width
|
||||
Assert.Equal(80, measurement.Max);
|
||||
}
|
||||
|
||||
[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(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
|
||||
// Corresponds to "│ │ │ │ │ │ │ │ │ └── "
|
||||
Assert.Equal(40, measurement.Min);
|
||||
|
||||
// Corresponds to "│ │ │ │ │ │ │ │ │ └── "
|
||||
Assert.Equal(40, measurement.Max);
|
||||
}
|
||||
}
|
||||
}
|
55
src/Spectre.Console.Tests/Unit/TreeRenderingTests.cs
Normal file
55
src/Spectre.Console.Tests/Unit/TreeRenderingTests.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Spectre.Console.Testing;
|
||||
using VerifyXunit;
|
||||
using Xunit;
|
||||
|
||||
namespace Spectre.Console.Tests.Unit
|
||||
{
|
||||
[UsesVerify]
|
||||
public sealed class TreeRenderingTests
|
||||
{
|
||||
[Fact]
|
||||
public Task Representative_Tree()
|
||||
{
|
||||
// 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<TreeNode> { new(new Text("child1"), nestedChildren), child2, child3 };
|
||||
var root = new TreeNode(new Text("Root node"), children);
|
||||
var tree = new Tree(root);
|
||||
|
||||
// When
|
||||
console.Render(tree);
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task Root_Node_Only()
|
||||
{
|
||||
// Given
|
||||
var console = new FakeConsole(width: 80);
|
||||
var root = new TreeNode(new Text("Root node"), Enumerable.Empty<TreeNode>());
|
||||
var tree = new Tree(root);
|
||||
|
||||
// When
|
||||
console.Render(tree);
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(console.Output);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user