diff --git a/docs/input/assets/images/tree.png b/docs/input/assets/images/tree.png
new file mode 100644
index 0000000..fe435fc
Binary files /dev/null and b/docs/input/assets/images/tree.png differ
diff --git a/docs/input/widgets/barchart.md b/docs/input/widgets/barchart.md
index 6e1a993..e76c188 100644
--- a/docs/input/widgets/barchart.md
+++ b/docs/input/widgets/barchart.md
@@ -1,5 +1,5 @@
Title: Bar Chart
-Order: 1
+Order: 20
---
Use `BarChart` to render bar charts to the console.
diff --git a/docs/input/widgets/calendar.md b/docs/input/widgets/calendar.md
index 3f3ad77..de92f5d 100644
--- a/docs/input/widgets/calendar.md
+++ b/docs/input/widgets/calendar.md
@@ -1,5 +1,5 @@
Title: Calendar
-Order: 3
+Order: 40
RedirectFrom: calendar
---
diff --git a/docs/input/widgets/canvas-image.md b/docs/input/widgets/canvas-image.md
index 24fd8d4..3e953c4 100644
--- a/docs/input/widgets/canvas-image.md
+++ b/docs/input/widgets/canvas-image.md
@@ -1,5 +1,5 @@
Title: Canvas Image
-Order: 6
+Order: 70
---
To add [ImageSharp](https://github.com/SixLabors/ImageSharp) superpowers to
diff --git a/docs/input/widgets/canvas.md b/docs/input/widgets/canvas.md
index 847f50e..b362743 100644
--- a/docs/input/widgets/canvas.md
+++ b/docs/input/widgets/canvas.md
@@ -1,5 +1,5 @@
Title: Canvas
-Order: 5
+Order: 60
---
`Canvas` is a widget that allows you to render arbitrary "pixels"
diff --git a/docs/input/widgets/figlet.md b/docs/input/widgets/figlet.md
index 762ea0d..2518d24 100644
--- a/docs/input/widgets/figlet.md
+++ b/docs/input/widgets/figlet.md
@@ -1,5 +1,5 @@
Title: Figlet
-Order: 4
+Order: 50
RedirectFrom: figlet
---
diff --git a/docs/input/widgets/rule.md b/docs/input/widgets/rule.md
index ff6ac55..c547f23 100644
--- a/docs/input/widgets/rule.md
+++ b/docs/input/widgets/rule.md
@@ -1,5 +1,5 @@
Title: Rule
-Order: 2
+Order: 30
RedirectFrom: rule
---
diff --git a/docs/input/widgets/tree.md b/docs/input/widgets/tree.md
new file mode 100644
index 0000000..b2cd873
--- /dev/null
+++ b/docs/input/widgets/tree.md
@@ -0,0 +1,70 @@
+Title: Tree
+Order: 10
+---
+
+The `Tree` widget can be used to render hierarchical data.
+
+
+
+# Usage
+
+```csharp
+// Create the tree
+var tree = new Tree("Root");
+
+// Add some nodes
+var foo = tree.AddNode("[yellow]Foo[/]");
+var table = foo.AddNode(new Table()
+ .RoundedBorder()
+ .AddColumn("First")
+ .AddColumn("Second")
+ .AddRow("1", "2")
+ .AddRow("3", "4")
+ .AddRow("5", "6"));
+
+table.AddNode("[blue]Baz[/]");
+foo.AddNode("Qux");
+
+var bar = tree.AddNode("[yellow]Bar[/]");
+bar.AddNode(new Calendar(2020, 12)
+ .AddCalendarEvent(2020, 12, 12)
+ .HideHeader());
+
+// Render the tree
+AnsiConsole.Render(root);
+```
+
+# Collapsing nodes
+
+```csharp
+root.AddNode("Label").Collapsed();
+```
+
+# Appearance
+
+## Style
+
+```csharp
+var root = new Tree("Root")
+ .Style("white on red");
+```
+
+## Guide lines
+
+```csharp
+// ASCII guide lines
+var root = new Tree("Root")
+ .Guide(TreeGuide.Ascii);
+
+// Default guide lines
+var root = new Tree("Root")
+ .Guide(TreeGuide.Line);
+
+// Double guide lines
+var root = new Tree("Root")
+ .Guide(TreeGuide.DoubleLine);
+
+// Bold guide lines
+var root = new Tree("Root")
+ .Guide(TreeGuide.BoldLine);
+```
\ No newline at end of file
diff --git a/examples/Console/Prompt/Program.cs b/examples/Console/Prompt/Program.cs
index edd83de..40874e6 100644
--- a/examples/Console/Prompt/Program.cs
+++ b/examples/Console/Prompt/Program.cs
@@ -1,4 +1,3 @@
-using System.Collections.Generic;
using Spectre.Console;
namespace Cursor
diff --git a/examples/Console/Trees/Program.cs b/examples/Console/Trees/Program.cs
index 5e46818..33747cd 100644
--- a/examples/Console/Trees/Program.cs
+++ b/examples/Console/Trees/Program.cs
@@ -6,42 +6,40 @@ namespace TableExample
{
public static void Main()
{
- var tree = new Tree();
-
- tree.AddNode(new FigletText("Dec 2020"));
- tree.AddNode("[link]Click to go to summary[/]");
-
- // Add the calendar nodes
- tree.AddNode("[blue]Calendar[/]",
- node => node.AddNode(
- new Calendar(2020, 12)
- .AddCalendarEvent(2020, 12, 12)
- .HideHeader()));
-
- // Add video games node
- tree.AddNode("[red]Played video games[/]",
- node => node.AddNode(
- new Table()
- .RoundedBorder()
- .AddColumn("Title")
- .AddColumn("Console")
- .AddRow("The Witcher 3", "XBox One X")
- .AddRow("Cyberpunk 2077", "PC")
- .AddRow("Animal Crossing", "Nintendo Switch")));
-
-
- // Add the fruit nodes
- tree.AddNode("[green]Fruits[/]", fruits =>
- fruits.AddNode("Eaten",
- node => node.AddNode(
- new BarChart().Width(40)
- .AddItem("Apple", 12, Color.Red)
- .AddItem("Kiwi", 3, Color.Green)
- .AddItem("Banana", 21, Color.Yellow))));
-
AnsiConsole.WriteLine();
- AnsiConsole.MarkupLine("[yellow]Monthly summary[/]");
+
+ // Render the tree
+ var tree = BuildTree();
AnsiConsole.Render(tree);
}
+
+ private static Tree BuildTree()
+ {
+ // Create the tree
+ var tree = new Tree("Root")
+ .Style(Style.Parse("red"))
+ .Guide(TreeGuide.BoldLine);
+
+ // Add some nodes
+ var foo = tree.AddNode("[yellow]Foo[/]");
+ var table = foo.AddNode(new Table()
+ .RoundedBorder()
+ .AddColumn("First")
+ .AddColumn("Second")
+ .AddRow("1", "2")
+ .AddRow("3", "4")
+ .AddRow("5", "6"));
+
+ table.AddNode("[blue]Baz[/]");
+ foo.AddNode("Qux");
+
+ var bar = tree.AddNode("[yellow]Bar[/]");
+ bar.AddNode(new Calendar(2020, 12)
+ .AddCalendarEvent(2020, 12, 12)
+ .HideHeader());
+
+ // Return the tree
+ return tree;
+ }
}
}
diff --git a/src/Spectre.Console.Tests/Expectations/Widgets/Tree/MultipleRoots.Output.verified.txt b/src/Spectre.Console.Tests/Expectations/Widgets/Tree/MultipleRoots.Output.verified.txt
deleted file mode 100644
index a8f06d1..0000000
--- a/src/Spectre.Console.Tests/Expectations/Widgets/Tree/MultipleRoots.Output.verified.txt
+++ /dev/null
@@ -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
diff --git a/src/Spectre.Console.Tests/Expectations/Widgets/Tree/Render.Output.verified.txt b/src/Spectre.Console.Tests/Expectations/Widgets/Tree/Render.Output.verified.txt
new file mode 100644
index 0000000..1d3ba98
--- /dev/null
+++ b/src/Spectre.Console.Tests/Expectations/Widgets/Tree/Render.Output.verified.txt
@@ -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
diff --git a/src/Spectre.Console.Tests/Expectations/Widgets/Tree/OnlyRoot.Output.verified.txt b/src/Spectre.Console.Tests/Expectations/Widgets/Tree/Render_NoChildren.Output.verified.txt
similarity index 100%
rename from src/Spectre.Console.Tests/Expectations/Widgets/Tree/OnlyRoot.Output.verified.txt
rename to src/Spectre.Console.Tests/Expectations/Widgets/Tree/Render_NoChildren.Output.verified.txt
diff --git a/src/Spectre.Console.Tests/Expectations/Widgets/Tree/SingleRoot.Output.verified.txt b/src/Spectre.Console.Tests/Expectations/Widgets/Tree/SingleRoot.Output.verified.txt
deleted file mode 100644
index 9d1ef68..0000000
--- a/src/Spectre.Console.Tests/Expectations/Widgets/Tree/SingleRoot.Output.verified.txt
+++ /dev/null
@@ -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 │ │
- │ │ │ │ │ │ │ │
- └─────┴─────┴─────┴─────┴─────┴─────┴─────┘
diff --git a/src/Spectre.Console.Tests/Spectre.Console.Tests.csproj b/src/Spectre.Console.Tests/Spectre.Console.Tests.csproj
index a687627..34cda0a 100644
--- a/src/Spectre.Console.Tests/Spectre.Console.Tests.csproj
+++ b/src/Spectre.Console.Tests/Spectre.Console.Tests.csproj
@@ -33,4 +33,8 @@
+
+
+
+
diff --git a/src/Spectre.Console.Tests/Unit/TreeTests.cs b/src/Spectre.Console.Tests/Unit/TreeTests.cs
index 672583e..eb5001d 100644
--- a/src/Spectre.Console.Tests/Unit/TreeTests.cs
+++ b/src/Spectre.Console.Tests/Unit/TreeTests.cs
@@ -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 { 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 { 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());
- var tree = new Tree().AddNode(root);
+ var tree = new Tree(new Text("Root node"));
// When
console.Render(tree);
diff --git a/src/Spectre.Console/Extensions/HasTreeNodeExtensions.cs b/src/Spectre.Console/Extensions/HasTreeNodeExtensions.cs
index a45d949..0417c2f 100644
--- a/src/Spectre.Console/Extensions/HasTreeNodeExtensions.cs
+++ b/src/Spectre.Console/Extensions/HasTreeNodeExtensions.cs
@@ -1,23 +1,24 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
///
- /// Contains extension methods for .
+ /// Contains extension methods for .
///
public static class HasTreeNodeExtensions
{
///
/// Adds a tree node.
///
- /// An object type with tree nodes.
- /// The object that has tree nodes.
+ /// An object with tree nodes.
+ /// The object to add the tree node to.
/// The node's markup text.
- /// The same instance so that multiple calls can be chained.
- public static T AddNode(this T obj, string markup)
- where T : class, IHasTreeNodes
+ /// The added tree node.
+ public static TreeNode AddNode(this T obj, string markup)
+ where T : IHasTreeNodes
{
if (obj is null)
{
@@ -35,31 +36,12 @@ namespace Spectre.Console
///
/// Adds a tree node.
///
- /// An object type with tree nodes.
- /// The object that has tree nodes.
- /// The node's markup text.
- /// An action that can be used to configure the created node further.
- /// The same instance so that multiple calls can be chained.
- public static T AddNode(this T obj, string markup, Action action)
- where T : class, IHasTreeNodes
- {
- if (markup is null)
- {
- throw new ArgumentNullException(nameof(markup));
- }
-
- return AddNode(obj, new Markup(markup), action);
- }
-
- ///
- /// Adds a tree node.
- ///
- /// An object type with tree nodes.
- /// The object that has tree nodes.
+ /// An object with tree nodes.
+ /// The object to add the tree node to.
/// The renderable to add.
- /// The same instance so that multiple calls can be chained.
- public static T AddNode(this T obj, IRenderable renderable)
- where T : class, IHasTreeNodes
+ /// The added tree node.
+ public static TreeNode AddNode(this T obj, IRenderable renderable)
+ where T : IHasTreeNodes
{
if (obj is null)
{
@@ -71,52 +53,20 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(renderable));
}
- obj.Children.Add(new TreeNode(renderable));
- return obj;
- }
-
- ///
- /// Adds a tree node.
- ///
- /// An object type with tree nodes.
- /// The object that has tree nodes.
- /// The renderable to add.
- /// An action that can be used to configure the created node further.
- /// The same instance so that multiple calls can be chained.
- public static T AddNode(this T obj, IRenderable renderable, Action action)
- where T : class, IHasTreeNodes
- {
- if (obj is null)
- {
- throw new ArgumentNullException(nameof(obj));
- }
-
- if (renderable is null)
- {
- throw new ArgumentNullException(nameof(renderable));
- }
-
- if (action is null)
- {
- throw new ArgumentNullException(nameof(action));
- }
-
var node = new TreeNode(renderable);
- action(node);
-
- obj.Children.Add(node);
- return obj;
+ obj.Nodes.Add(node);
+ return node;
}
///
/// Adds a tree node.
///
- /// An object type with tree nodes.
- /// The object that has tree nodes.
+ /// An object with tree nodes.
+ /// The object to add the tree node to.
/// The tree node to add.
- /// The same instance so that multiple calls can be chained.
- public static T AddNode(this T obj, TreeNode node)
- where T : class, IHasTreeNodes
+ /// The added tree node.
+ public static TreeNode AddNode(this T obj, TreeNode node)
+ where T : IHasTreeNodes
{
if (obj is null)
{
@@ -128,19 +78,18 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(node));
}
- obj.Children.Add(node);
- return obj;
+ obj.Nodes.Add(node);
+ return node;
}
///
/// Add multiple tree nodes.
///
- /// An object type with tree nodes.
- /// The object that has tree nodes.
+ /// An object with tree nodes.
+ /// The object to add the tree nodes to.
/// The tree nodes to add.
- /// The same instance so that multiple calls can be chained.
- public static T AddNodes(this T obj, params string[] nodes)
- where T : class, IHasTreeNodes
+ public static void AddNodes(this T obj, params string[] nodes)
+ where T : IHasTreeNodes
{
if (obj is null)
{
@@ -152,19 +101,17 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(nodes));
}
- obj.Children.AddRange(nodes.Select(node => new TreeNode(new Markup(node))));
- return obj;
+ obj.Nodes.AddRange(nodes.Select(node => new TreeNode(new Markup(node))));
}
///
/// Add multiple tree nodes.
///
- /// An object type with tree nodes.
- /// The object that has tree nodes.
+ /// An object with tree nodes.
+ /// The object to add the tree nodes to.
/// The tree nodes to add.
- /// The same instance so that multiple calls can be chained.
- public static T AddNodes(this T obj, params TreeNode[] nodes)
- where T : class, IHasTreeNodes
+ public static void AddNodes(this T obj, IEnumerable nodes)
+ where T : IHasTreeNodes
{
if (obj is null)
{
@@ -176,8 +123,95 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(nodes));
}
- obj.Children.AddRange(nodes);
- return obj;
+ obj.Nodes.AddRange(nodes.Select(node => new TreeNode(new Markup(node))));
+ }
+
+ ///
+ /// Add multiple tree nodes.
+ ///
+ /// An object with tree nodes.
+ /// The object to add the tree nodes to.
+ /// The tree nodes to add.
+ public static void AddNodes(this T obj, params IRenderable[] nodes)
+ where T : IHasTreeNodes
+ {
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+
+ if (nodes is null)
+ {
+ throw new ArgumentNullException(nameof(nodes));
+ }
+
+ obj.Nodes.AddRange(nodes.Select(node => new TreeNode(node)));
+ }
+
+ ///
+ /// Add multiple tree nodes.
+ ///
+ /// An object with tree nodes.
+ /// The object to add the tree nodes to.
+ /// The tree nodes to add.
+ public static void AddNodes(this T obj, IEnumerable nodes)
+ where T : IHasTreeNodes
+ {
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+
+ if (nodes is null)
+ {
+ throw new ArgumentNullException(nameof(nodes));
+ }
+
+ obj.Nodes.AddRange(nodes.Select(node => new TreeNode(node)));
+ }
+
+ ///
+ /// Add multiple tree nodes.
+ ///
+ /// An object with tree nodes.
+ /// The object to add the tree nodes to.
+ /// The tree nodes to add.
+ public static void AddNodes(this T obj, params TreeNode[] nodes)
+ where T : IHasTreeNodes
+ {
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+
+ if (nodes is null)
+ {
+ throw new ArgumentNullException(nameof(nodes));
+ }
+
+ obj.Nodes.AddRange(nodes);
+ }
+
+ ///
+ /// Add multiple tree nodes.
+ ///
+ /// An object with tree nodes.
+ /// The object to add the tree nodes to.
+ /// The tree nodes to add.
+ public static void AddNodes(this T obj, IEnumerable nodes)
+ where T : IHasTreeNodes
+ {
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+
+ if (nodes is null)
+ {
+ throw new ArgumentNullException(nameof(nodes));
+ }
+
+ obj.Nodes.AddRange(nodes);
}
}
}
diff --git a/src/Spectre.Console/Extensions/ListExtensions.cs b/src/Spectre.Console/Extensions/ListExtensions.cs
new file mode 100644
index 0000000..ecc66d2
--- /dev/null
+++ b/src/Spectre.Console/Extensions/ListExtensions.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+
+namespace Spectre.Console
+{
+ internal static class ListExtensions
+ {
+ public static void RemoveLast(this List list)
+ {
+ if (list is null)
+ {
+ throw new ArgumentNullException(nameof(list));
+ }
+
+ if (list.Count > 0)
+ {
+ list.RemoveAt(list.Count - 1);
+ }
+ }
+
+ public static void AddOrReplaceLast(this List list, T item)
+ {
+ if (list is null)
+ {
+ throw new ArgumentNullException(nameof(list));
+ }
+
+ if (list.Count == 0)
+ {
+ list.Add(item);
+ }
+ else
+ {
+ list[list.Count - 1] = item;
+ }
+ }
+ }
+}
diff --git a/src/Spectre.Console/Extensions/TreeExtensions.cs b/src/Spectre.Console/Extensions/TreeExtensions.cs
new file mode 100644
index 0000000..959a640
--- /dev/null
+++ b/src/Spectre.Console/Extensions/TreeExtensions.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace Spectre.Console
+{
+ ///
+ /// Contains extension methods for .
+ ///
+ public static class TreeExtensions
+ {
+ ///
+ /// Sets the tree style.
+ ///
+ /// The tree.
+ /// The tree style.
+ /// The same instance so that multiple calls can be chained.
+ public static Tree Style(this Tree tree, Style? style)
+ {
+ if (tree is null)
+ {
+ throw new ArgumentNullException(nameof(tree));
+ }
+
+ tree.Style = style;
+ return tree;
+ }
+
+ ///
+ /// Sets the tree guide line appearance.
+ ///
+ /// The tree.
+ /// The tree guide lines to use.
+ /// The same instance so that multiple calls can be chained.
+ public static Tree Guide(this Tree tree, TreeGuide guide)
+ {
+ if (tree is null)
+ {
+ throw new ArgumentNullException(nameof(tree));
+ }
+
+ tree.Guide = guide;
+ return tree;
+ }
+ }
+}
diff --git a/src/Spectre.Console/Extensions/TreeGuideExtensions.cs b/src/Spectre.Console/Extensions/TreeGuideExtensions.cs
new file mode 100644
index 0000000..2a7ef4e
--- /dev/null
+++ b/src/Spectre.Console/Extensions/TreeGuideExtensions.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace Spectre.Console.Rendering
+{
+ ///
+ /// Contains extension methods for .
+ ///
+ public static class TreeGuideExtensions
+ {
+ ///
+ /// Gets the safe border for a border.
+ ///
+ /// The tree guide to get the safe version for.
+ /// Whether or not to return the safe border.
+ /// The safe border if one exist, otherwise the original border.
+ public static TreeGuide GetSafeTreeGuide(this TreeGuide guide, bool safe)
+ {
+ if (guide is null)
+ {
+ throw new ArgumentNullException(nameof(guide));
+ }
+
+ if (safe && guide.SafeTreeGuide != null)
+ {
+ return guide.SafeTreeGuide;
+ }
+
+ return guide;
+ }
+ }
+}
diff --git a/src/Spectre.Console/Extensions/TreeNodeExtensions.cs b/src/Spectre.Console/Extensions/TreeNodeExtensions.cs
new file mode 100644
index 0000000..a58a8cd
--- /dev/null
+++ b/src/Spectre.Console/Extensions/TreeNodeExtensions.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace Spectre.Console
+{
+ ///
+ /// Contains extension methods for .
+ ///
+ public static class TreeNodeExtensions
+ {
+ ///
+ /// Expands the tree.
+ ///
+ /// The tree node.
+ /// The same instance so that multiple calls can be chained.
+ public static TreeNode Expand(this TreeNode node)
+ {
+ return Expand(node, true);
+ }
+
+ ///
+ /// Collapses the tree.
+ ///
+ /// The tree node.
+ /// The same instance so that multiple calls can be chained.
+ public static TreeNode Collapse(this TreeNode node)
+ {
+ return Expand(node, false);
+ }
+
+ ///
+ /// Sets whether or not the tree node should be expanded.
+ ///
+ /// The tree node.
+ /// Whether or not the tree node should be expanded.
+ /// The same instance so that multiple calls can be chained.
+ public static TreeNode Expand(this TreeNode node, bool expand)
+ {
+ if (node is null)
+ {
+ throw new ArgumentNullException(nameof(node));
+ }
+
+ node.Expanded = expand;
+ return node;
+ }
+ }
+}
diff --git a/src/Spectre.Console/IHasTreeNodes.cs b/src/Spectre.Console/IHasTreeNodes.cs
index bbed370..5b16994 100644
--- a/src/Spectre.Console/IHasTreeNodes.cs
+++ b/src/Spectre.Console/IHasTreeNodes.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
namespace Spectre.Console
{
@@ -8,8 +8,8 @@ namespace Spectre.Console
public interface IHasTreeNodes
{
///
- /// Gets the children of this node.
+ /// Gets the tree's child nodes.
///
- public List Children { get; }
+ List Nodes { get; }
}
-}
+}
\ No newline at end of file
diff --git a/src/Spectre.Console/Rendering/Tree/AsciiTreeAppearance.cs b/src/Spectre.Console/Rendering/Tree/AsciiTreeAppearance.cs
deleted file mode 100644
index 2e4d236..0000000
--- a/src/Spectre.Console/Rendering/Tree/AsciiTreeAppearance.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-
-namespace Spectre.Console.Rendering
-{
- ///
- /// An ASCII rendering of a tree.
- ///
- public sealed class AsciiTreeAppearance : TreeAppearance
- {
- ///
- public override int PartSize => 4;
-
- ///
- public override string GetPart(TreePart part)
- {
- return part switch
- {
- TreePart.SiblingConnector => "│ ",
- TreePart.ChildBranch => "├── ",
- TreePart.BottomChildBranch => "└── ",
- _ => throw new ArgumentOutOfRangeException(nameof(part), part, "Unknown tree part."),
- };
- }
- }
-}
\ No newline at end of file
diff --git a/src/Spectre.Console/Rendering/Tree/AsciiTreeGuide.cs b/src/Spectre.Console/Rendering/Tree/AsciiTreeGuide.cs
new file mode 100644
index 0000000..50dc083
--- /dev/null
+++ b/src/Spectre.Console/Rendering/Tree/AsciiTreeGuide.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace Spectre.Console.Rendering
+{
+ ///
+ /// An ASCII tree guide.
+ ///
+ public sealed class AsciiTreeGuide : TreeGuide
+ {
+ ///
+ public override string GetPart(TreeGuidePart part)
+ {
+ return part switch
+ {
+ TreeGuidePart.Space => " ",
+ TreeGuidePart.Continue => "| ",
+ TreeGuidePart.Fork => "|-- ",
+ TreeGuidePart.End => "`-- ",
+ _ => throw new ArgumentOutOfRangeException(nameof(part), part, "Unknown tree part."),
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spectre.Console/Rendering/Tree/BoldLineTreeGuide.cs b/src/Spectre.Console/Rendering/Tree/BoldLineTreeGuide.cs
new file mode 100644
index 0000000..164f0b7
--- /dev/null
+++ b/src/Spectre.Console/Rendering/Tree/BoldLineTreeGuide.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Spectre.Console.Rendering
+{
+ ///
+ /// A tree guide made up of bold lines.
+ ///
+ public sealed class BoldLineTreeGuide : TreeGuide
+ {
+ ///
+ public override TreeGuide? SafeTreeGuide => Ascii;
+
+ ///
+ public override string GetPart(TreeGuidePart part)
+ {
+ return part switch
+ {
+ TreeGuidePart.Space => " ",
+ TreeGuidePart.Continue => "┃ ",
+ TreeGuidePart.Fork => "┣━━ ",
+ TreeGuidePart.End => "┗━━ ",
+ _ => throw new ArgumentOutOfRangeException(nameof(part), part, "Unknown tree part."),
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spectre.Console/Rendering/Tree/DoubleLineTreeGuide.cs b/src/Spectre.Console/Rendering/Tree/DoubleLineTreeGuide.cs
new file mode 100644
index 0000000..73fb164
--- /dev/null
+++ b/src/Spectre.Console/Rendering/Tree/DoubleLineTreeGuide.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Spectre.Console.Rendering
+{
+ ///
+ /// A tree guide made up of double lines.
+ ///
+ public sealed class DoubleLineTreeGuide : TreeGuide
+ {
+ ///
+ public override TreeGuide? SafeTreeGuide => Ascii;
+
+ ///
+ public override string GetPart(TreeGuidePart part)
+ {
+ return part switch
+ {
+ TreeGuidePart.Space => " ",
+ TreeGuidePart.Continue => "║ ",
+ TreeGuidePart.Fork => "╠══ ",
+ TreeGuidePart.End => "╚══ ",
+ _ => throw new ArgumentOutOfRangeException(nameof(part), part, "Unknown tree part."),
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spectre.Console/Rendering/Tree/LineTreeGuide.cs b/src/Spectre.Console/Rendering/Tree/LineTreeGuide.cs
new file mode 100644
index 0000000..f3e73e0
--- /dev/null
+++ b/src/Spectre.Console/Rendering/Tree/LineTreeGuide.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Spectre.Console.Rendering
+{
+ ///
+ /// A tree guide made up of lines.
+ ///
+ public sealed class LineTreeGuide : TreeGuide
+ {
+ ///
+ public override TreeGuide? SafeTreeGuide => Ascii;
+
+ ///
+ public override string GetPart(TreeGuidePart part)
+ {
+ return part switch
+ {
+ TreeGuidePart.Space => " ",
+ TreeGuidePart.Continue => "│ ",
+ TreeGuidePart.Fork => "├── ",
+ TreeGuidePart.End => "└── ",
+ _ => throw new ArgumentOutOfRangeException(nameof(part), part, "Unknown tree part."),
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Spectre.Console/Rendering/TreePart.cs b/src/Spectre.Console/Rendering/TreeGuidePart.cs
similarity index 72%
rename from src/Spectre.Console/Rendering/TreePart.cs
rename to src/Spectre.Console/Rendering/TreeGuidePart.cs
index ea74582..282a8bc 100644
--- a/src/Spectre.Console/Rendering/TreePart.cs
+++ b/src/Spectre.Console/Rendering/TreeGuidePart.cs
@@ -3,21 +3,26 @@ namespace Spectre.Console.Rendering
///
/// Defines the different rendering parts of a .
///
- public enum TreePart
+ public enum TreeGuidePart
{
+ ///
+ /// Represents a space.
+ ///
+ Space,
+
///
/// Connection between siblings.
///
- SiblingConnector,
+ Continue,
///
/// Branch from parent to child.
///
- ChildBranch,
+ Fork,
///
/// Branch from parent to child for the last child in a set.
///
- BottomChildBranch,
+ End,
}
}
\ No newline at end of file
diff --git a/src/Spectre.Console/TreeAppearance.Known.cs b/src/Spectre.Console/TreeAppearance.Known.cs
deleted file mode 100644
index 4cbb85c..0000000
--- a/src/Spectre.Console/TreeAppearance.Known.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Spectre.Console.Rendering;
-
-namespace Spectre.Console
-{
- ///
- /// Represents a tree appearance.
- ///
- public abstract partial class TreeAppearance
- {
- ///
- /// Gets ASCII rendering of a tree.
- ///
- public static TreeAppearance Ascii { get; } = new AsciiTreeAppearance();
- }
-}
diff --git a/src/Spectre.Console/TreeGuide.Known.cs b/src/Spectre.Console/TreeGuide.Known.cs
new file mode 100644
index 0000000..f25387b
--- /dev/null
+++ b/src/Spectre.Console/TreeGuide.Known.cs
@@ -0,0 +1,30 @@
+using Spectre.Console.Rendering;
+
+namespace Spectre.Console
+{
+ ///
+ /// Represents tree guide lines.
+ ///
+ public abstract partial class TreeGuide
+ {
+ ///
+ /// Gets an instance.
+ ///
+ public static TreeGuide Ascii { get; } = new AsciiTreeGuide();
+
+ ///
+ /// Gets a instance.
+ ///
+ public static TreeGuide Line { get; } = new LineTreeGuide();
+
+ ///
+ /// Gets a instance.
+ ///
+ public static TreeGuide DoubleLine { get; } = new DoubleLineTreeGuide();
+
+ ///
+ /// Gets a instance.
+ ///
+ public static TreeGuide BoldLine { get; } = new BoldLineTreeGuide();
+ }
+}
diff --git a/src/Spectre.Console/TreeAppearance.cs b/src/Spectre.Console/TreeGuide.cs
similarity index 58%
rename from src/Spectre.Console/TreeAppearance.cs
rename to src/Spectre.Console/TreeGuide.cs
index 077c745..1a25522 100644
--- a/src/Spectre.Console/TreeAppearance.cs
+++ b/src/Spectre.Console/TreeGuide.cs
@@ -3,20 +3,20 @@ using Spectre.Console.Rendering;
namespace Spectre.Console
{
///
- /// Represents a tree appearance.
+ /// Represents tree guide lines.
///
- public abstract partial class TreeAppearance
+ public abstract partial class TreeGuide
{
///
- /// Gets the length of all tree part strings.
+ /// Gets the safe guide lines or null if none exist.
///
- public abstract int PartSize { get; }
+ public virtual TreeGuide? SafeTreeGuide { get; }
///
- /// Get the set of characters used to render the corresponding .
+ /// Get the set of characters used to render the corresponding .
///
/// The part of the tree to get rendering string for.
/// Rendering string for the tree part.
- public abstract string GetPart(TreePart part);
+ public abstract string GetPart(TreeGuidePart part);
}
}
\ No newline at end of file
diff --git a/src/Spectre.Console/Widgets/BarChart.cs b/src/Spectre.Console/Widgets/BarChart.cs
index 3541311..098f863 100644
--- a/src/Spectre.Console/Widgets/BarChart.cs
+++ b/src/Spectre.Console/Widgets/BarChart.cs
@@ -48,20 +48,20 @@ namespace Spectre.Console
{
var maxValue = Data.Max(item => item.Value);
- var table = new Grid();
- table.Collapse();
- table.AddColumn(new GridColumn().PadRight(2).RightAligned());
- table.AddColumn(new GridColumn().PadLeft(0));
- table.Width = Width;
+ var grid = new Grid();
+ grid.Collapse();
+ grid.AddColumn(new GridColumn().PadRight(2).RightAligned());
+ grid.AddColumn(new GridColumn().PadLeft(0));
+ grid.Width = Width;
if (!string.IsNullOrWhiteSpace(Label))
{
- table.AddRow(Text.Empty, new Markup(Label).Alignment(LabelAlignment));
+ grid.AddRow(Text.Empty, new Markup(Label).Alignment(LabelAlignment));
}
foreach (var item in Data)
{
- table.AddRow(
+ grid.AddRow(
new Markup(item.Label),
new ProgressBar()
{
@@ -76,7 +76,7 @@ namespace Spectre.Console
});
}
- return ((IRenderable)table).Render(context, maxWidth);
+ return ((IRenderable)grid).Render(context, maxWidth);
}
}
}
diff --git a/src/Spectre.Console/Widgets/Tree.cs b/src/Spectre.Console/Widgets/Tree.cs
index dd9a5c5..b613831 100644
--- a/src/Spectre.Console/Widgets/Tree.cs
+++ b/src/Spectre.Console/Widgets/Tree.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.Linq;
using Spectre.Console.Internal;
@@ -7,155 +6,124 @@ using Spectre.Console.Rendering;
namespace Spectre.Console
{
///
- /// Representation of tree data.
+ /// A renderable tree.
///
public sealed class Tree : Renderable, IHasTreeNodes
{
+ private readonly TreeNode _root;
+
///
/// Gets or sets the tree style.
///
- public Style Style { get; set; } = Style.Plain;
+ public Style? Style { get; set; }
///
- /// Gets or sets the appearance of the tree.
+ /// Gets or sets the tree guide lines.
///
- public TreeAppearance Appearance { get; set; } = TreeAppearance.Ascii;
+ public TreeGuide Guide { get; set; } = TreeGuide.Line;
///
- /// Gets the tree nodes.
+ /// Gets the tree's child nodes.
///
- public List Nodes { get; }
+ public List Nodes { get; } = new List();
+
+ ///
+ /// Gets or sets a value indicating whether or not the tree is expanded or not.
+ ///
+ public bool Expanded { get; set; } = true;
///
- List IHasTreeNodes.Children => Nodes;
+ List IHasTreeNodes.Nodes => _root.Nodes;
///
/// Initializes a new instance of the class.
///
- public Tree()
+ /// The tree label.
+ public Tree(IRenderable renderable)
{
- Nodes = new List();
+ _root = new TreeNode(renderable);
}
- ///
- protected override Measurement Measure(RenderContext context, int maxWidth)
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The tree label.
+ public Tree(string label)
{
- Measurement MeasureAtDepth(RenderContext context, int maxWidth, TreeNode node, int depth)
- {
- var rootMeasurement = node.Measure(context, maxWidth);
- var treeIndentation = depth * Appearance.PartSize;
- var currentMax = rootMeasurement.Max + treeIndentation;
- var currentMin = rootMeasurement.Min + treeIndentation;
-
- foreach (var child in node.Children)
- {
- var childMeasurement = MeasureAtDepth(context, maxWidth, child, depth + 1);
- if (childMeasurement.Min > currentMin)
- {
- currentMin = childMeasurement.Min;
- }
-
- if (childMeasurement.Max > currentMax)
- {
- currentMax = childMeasurement.Max;
- }
- }
-
- return new Measurement(currentMin, Math.Min(currentMax, maxWidth));
- }
-
- 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.AddNode(node);
- }
-
- return MeasureAtDepth(context, maxWidth, root, depth: 0);
- }
+ _root = new TreeNode(new Markup(label));
}
///
protected override IEnumerable Render(RenderContext context, int maxWidth)
{
- if (Nodes.Count == 1)
+ var result = new List();
+
+ var stack = new Stack>();
+ stack.Push(new Queue(new[] { _root }));
+
+ var levels = new List();
+ levels.Add(GetGuide(context, TreeGuidePart.Continue));
+
+ while (stack.Count > 0)
{
- // 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)
+ var stackNode = stack.Pop();
+ if (stackNode.Count == 0)
{
- root.AddNode(node);
+ levels.RemoveLast();
+ if (levels.Count > 0)
+ {
+ levels.AddOrReplaceLast(GetGuide(context, TreeGuidePart.Fork));
+ }
+
+ continue;
}
- return Enumerable.Empty()
- .Concat(RenderChildren(
- context, maxWidth - Appearance.PartSize, root,
- depth: 0));
- }
- }
+ var isLastChild = stackNode.Count == 1;
+ var current = stackNode.Dequeue();
- 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())
- {
- var lines = Segment.SplitLines(context, childNode.Render(context, maxWidth));
- foreach (var (_, isFirstLine, _, line) in lines.Enumerate())
+ stack.Push(stackNode);
+
+ if (isLastChild)
{
- var siblingConnectorSegment =
- new Segment(Appearance.GetPart(TreePart.SiblingConnector), Style);
- if (trailingStarted != null)
- {
- result.AddRange(Enumerable.Repeat(siblingConnectorSegment, trailingStarted.Value));
- result.AddRange(Enumerable.Repeat(
- Segment.Padding(Appearance.PartSize),
- depth - trailingStarted.Value));
- }
- else
- {
- result.AddRange(Enumerable.Repeat(siblingConnectorSegment, depth));
- }
+ levels.AddOrReplaceLast(GetGuide(context, TreeGuidePart.End));
+ }
- if (isFirstLine)
+ var prefix = levels.Skip(1).ToList();
+ var renderableLines = Segment.SplitLines(context, current.Renderable.Render(context, maxWidth - Segment.CellCount(context, prefix)));
+
+ foreach (var (_, isFirstLine, _, line) in renderableLines.Enumerate())
+ {
+ if (prefix.Count > 0)
{
- result.Add(lastChild
- ? new Segment(Appearance.GetPart(TreePart.BottomChildBranch), Style)
- : new Segment(Appearance.GetPart(TreePart.ChildBranch), Style));
- }
- else
- {
- result.Add(lastChild ? Segment.Padding(Appearance.PartSize) : siblingConnectorSegment);
+ result.AddRange(prefix.ToList());
}
result.AddRange(line);
result.Add(Segment.LineBreak);
+
+ if (isFirstLine && prefix.Count > 0)
+ {
+ var part = isLastChild ? TreeGuidePart.Space : TreeGuidePart.Continue;
+ prefix.AddOrReplaceLast(GetGuide(context, part));
+ }
}
- var childTrailingStarted = trailingStarted ?? (lastChild ? depth : null);
+ if (current.Expanded && current.Nodes.Count > 0)
+ {
+ levels.AddOrReplaceLast(GetGuide(context, isLastChild ? TreeGuidePart.Space : TreeGuidePart.Continue));
+ levels.Add(GetGuide(context, current.Nodes.Count == 1 ? TreeGuidePart.End : TreeGuidePart.Fork));
- result.AddRange(
- RenderChildren(
- context, maxWidth - Appearance.PartSize,
- childNode, depth + 1,
- childTrailingStarted));
+ stack.Push(new Queue(current.Nodes));
+ }
}
return result;
}
+
+ private Segment GetGuide(RenderContext context, TreeGuidePart part)
+ {
+ var guide = Guide.GetSafeTreeGuide(context.LegacyConsole || !context.Unicode);
+ return new Segment(guide.GetPart(part), Style ?? Style.Plain);
+ }
}
}
\ No newline at end of file
diff --git a/src/Spectre.Console/Widgets/TreeNode.cs b/src/Spectre.Console/Widgets/TreeNode.cs
index d7c19d2..188504f 100644
--- a/src/Spectre.Console/Widgets/TreeNode.cs
+++ b/src/Spectre.Console/Widgets/TreeNode.cs
@@ -1,41 +1,32 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
+using System.Collections.Generic;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
///
- /// Node of a tree.
+ /// Represents a tree node.
///
- public sealed class TreeNode : IHasTreeNodes, IRenderable
+ public sealed class TreeNode : IHasTreeNodes
{
- private readonly IRenderable _renderable;
+ internal IRenderable Renderable { get; }
- ///
- public List Children { get; }
+ ///
+ /// Gets the tree node's child nodes.
+ ///
+ public List Nodes { get; } = new List();
+
+ ///
+ /// Gets or sets a value indicating whether or not the tree node is expanded or not.
+ ///
+ public bool Expanded { get; set; } = true;
///
/// Initializes a new instance of the class.
///
- /// The which this node wraps.
- /// Any children that the node is declared with.
- public TreeNode(IRenderable renderable, IEnumerable? children = null)
+ /// The tree node label.
+ public TreeNode(IRenderable renderable)
{
- _renderable = renderable ?? throw new ArgumentNullException(nameof(renderable));
- Children = new List(children ?? Enumerable.Empty());
- }
-
- ///
- public Measurement Measure(RenderContext context, int maxWidth)
- {
- return _renderable.Measure(context, maxWidth);
- }
-
- ///
- public IEnumerable Render(RenderContext context, int maxWidth)
- {
- return _renderable.Render(context, maxWidth);
+ Renderable = renderable;
}
}
}
\ No newline at end of file