mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-10-31 09:09:25 +08:00 
			
		
		
		
	Streamline tree API a bit
This commit is contained in:
		 Patrik Svensson
					Patrik Svensson
				
			
				
					committed by
					
						 Patrik Svensson
						Patrik Svensson
					
				
			
			
				
	
			
			
			 Patrik Svensson
						Patrik Svensson
					
				
			
						parent
						
							b136d0299b
						
					
				
				
					commit
					4bfb24bfcb
				
			| @@ -1,90 +0,0 @@ | ||||
| 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,55 +0,0 @@ | ||||
| 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										128
									
								
								src/Spectre.Console.Tests/Unit/TreeTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/Spectre.Console.Tests/Unit/TreeTests.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| 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; | ||||
|  | ||||
| namespace Spectre.Console.Tests.Unit | ||||
| { | ||||
|     [UsesVerify] | ||||
|     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<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 | ||||
|             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() | ||||
|         { | ||||
|             // 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 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(root); | ||||
|  | ||||
|             // When | ||||
|             console.Render(tree); | ||||
|  | ||||
|             // Then | ||||
|             return Verifier.Verify(console.Output); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								src/Spectre.Console/Extensions/HasTreeNodeExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/Spectre.Console/Extensions/HasTreeNodeExtensions.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| namespace Spectre.Console | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Contains extension methods for <see cref="IHasCulture"/>. | ||||
|     /// </summary> | ||||
|     public static class HasTreeNodeExtensions | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Adds a child to the end of the node's list of children. | ||||
|         /// </summary> | ||||
|         /// <typeparam name="T">An object type with tree nodes.</typeparam> | ||||
|         /// <param name="obj">The object that has tree nodes.</param> | ||||
|         /// <param name="child">Child to be added.</param> | ||||
|         /// <returns>The same instance so that multiple calls can be chained.</returns> | ||||
|         public static T AddChild<T>(this T obj, TreeNode child) | ||||
|             where T : class, IHasTreeNodes | ||||
|         { | ||||
|             if (obj is null) | ||||
|             { | ||||
|                 throw new System.ArgumentNullException(nameof(obj)); | ||||
|             } | ||||
|  | ||||
|             obj.Children.Add(child); | ||||
|             return obj; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										15
									
								
								src/Spectre.Console/IHasTreeNodes.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/Spectre.Console/IHasTreeNodes.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| using System.Collections.Generic; | ||||
|  | ||||
| namespace Spectre.Console | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Represents something that has tree nodes. | ||||
|     /// </summary> | ||||
|     public interface IHasTreeNodes | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Gets the children of this node. | ||||
|         /// </summary> | ||||
|         public List<TreeNode> Children { get; } | ||||
|     } | ||||
| } | ||||
| @@ -5,10 +5,13 @@ namespace Spectre.Console.Rendering | ||||
|     /// <summary> | ||||
|     /// An ASCII rendering of a tree. | ||||
|     /// </summary> | ||||
|     public sealed class AsciiTreeRendering : ITreeRendering | ||||
|     public sealed class AsciiTreeAppearance : TreeAppearance | ||||
|     { | ||||
|         /// <inheritdoc/> | ||||
|         public string GetPart(TreePart part) | ||||
|         public override int PartSize => 4; | ||||
| 
 | ||||
|         /// <inheritdoc/> | ||||
|         public override string GetPart(TreePart part) | ||||
|         { | ||||
|             return part switch | ||||
|             { | ||||
| @@ -18,8 +21,5 @@ namespace Spectre.Console.Rendering | ||||
|                 _ => throw new ArgumentOutOfRangeException(nameof(part), part, "Unknown tree part."), | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         /// <inheritdoc/> | ||||
|         public int PartSize => 4; | ||||
|     } | ||||
| } | ||||
| @@ -1,13 +0,0 @@ | ||||
| namespace Spectre.Console.Rendering | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Selection of different renderings which can be used by <see cref="Tree"/>. | ||||
|     /// </summary> | ||||
|     public static class TreeRendering | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Gets ASCII rendering of a tree. | ||||
|         /// </summary> | ||||
|         public static ITreeRendering Ascii { get; } = new AsciiTreeRendering(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										15
									
								
								src/Spectre.Console/TreeAppearance.Known.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/Spectre.Console/TreeAppearance.Known.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| using Spectre.Console.Rendering; | ||||
|  | ||||
| namespace Spectre.Console | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Represents a tree appearance. | ||||
|     /// </summary> | ||||
|     public abstract partial class TreeAppearance | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Gets ASCII rendering of a tree. | ||||
|         /// </summary> | ||||
|         public static TreeAppearance Ascii { get; } = new AsciiTreeAppearance(); | ||||
|     } | ||||
| } | ||||
| @@ -1,20 +1,22 @@ | ||||
| namespace Spectre.Console.Rendering | ||||
| using Spectre.Console.Rendering; | ||||
| 
 | ||||
| namespace Spectre.Console | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Represents the characters used to render a tree. | ||||
|     /// Represents a tree appearance. | ||||
|     /// </summary> | ||||
|     public interface ITreeRendering | ||||
|     public abstract partial class TreeAppearance | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Gets the length of all tree part strings. | ||||
|         /// </summary> | ||||
|         public abstract int PartSize { get; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Get the set of characters used to render the corresponding <see cref="TreePart"/>. | ||||
|         /// </summary> | ||||
|         /// <param name="part">The part of the tree to get rendering string for.</param> | ||||
|         /// <returns>Rendering string for the tree part.</returns> | ||||
|         string GetPart(TreePart part); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the length of all tree part strings. | ||||
|         /// </summary> | ||||
|         int PartSize { get; } | ||||
|         public abstract string GetPart(TreePart part); | ||||
|     } | ||||
| } | ||||
| @@ -19,9 +19,9 @@ namespace Spectre.Console | ||||
|         public Style Style { get; set; } = Style.Plain; | ||||
|  | ||||
|         /// <summary> | ||||
|         ///  Gets or sets the rendering type used for the tree. | ||||
|         ///  Gets or sets the appearance of the tree. | ||||
|         /// </summary> | ||||
|         public ITreeRendering Rendering { get; set; } = TreeRendering.Ascii; | ||||
|         public TreeAppearance Appearance { get; set; } = TreeAppearance.Ascii; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="Tree"/> class. | ||||
| @@ -29,37 +29,37 @@ namespace Spectre.Console | ||||
|         /// <param name="rootNode">Root node of the tree to be rendered.</param> | ||||
|         public Tree(TreeNode rootNode) | ||||
|         { | ||||
|             _rootNode = rootNode; | ||||
|             _rootNode = rootNode ?? throw new ArgumentNullException(nameof(rootNode)); | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc /> | ||||
|         protected override Measurement Measure(RenderContext context, int maxWidth) | ||||
|         { | ||||
|             return MeasureAtDepth(context, maxWidth, _rootNode, depth: 0); | ||||
|         } | ||||
|  | ||||
|         private Measurement MeasureAtDepth(RenderContext context, int maxWidth, TreeNode node, int depth) | ||||
|         { | ||||
|             var rootMeasurement = node.Measure(context, maxWidth); | ||||
|             var treeIndentation = depth * Rendering.PartSize; | ||||
|             var currentMax = rootMeasurement.Max + treeIndentation; | ||||
|             var currentMin = rootMeasurement.Min + treeIndentation; | ||||
|  | ||||
|             foreach (var child in node.Children) | ||||
|             Measurement MeasureAtDepth(RenderContext context, int maxWidth, TreeNode node, int depth) | ||||
|             { | ||||
|                 var childMeasurement = MeasureAtDepth(context, maxWidth, child, depth + 1); | ||||
|                 if (childMeasurement.Min > currentMin) | ||||
|                 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) | ||||
|                 { | ||||
|                     currentMin = childMeasurement.Min; | ||||
|                     var childMeasurement = MeasureAtDepth(context, maxWidth, child, depth + 1); | ||||
|                     if (childMeasurement.Min > currentMin) | ||||
|                     { | ||||
|                         currentMin = childMeasurement.Min; | ||||
|                     } | ||||
|  | ||||
|                     if (childMeasurement.Max > currentMax) | ||||
|                     { | ||||
|                         currentMax = childMeasurement.Max; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (childMeasurement.Max > currentMax) | ||||
|                 { | ||||
|                     currentMax = childMeasurement.Max; | ||||
|                 } | ||||
|                 return new Measurement(currentMin, Math.Min(currentMax, maxWidth)); | ||||
|             } | ||||
|  | ||||
|             return new Measurement(currentMin, Math.Min(currentMax, maxWidth)); | ||||
|             return MeasureAtDepth(context, maxWidth, _rootNode, depth: 0); | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc /> | ||||
| @@ -68,27 +68,25 @@ namespace Spectre.Console | ||||
|             return _rootNode | ||||
|                 .Render(context, maxWidth) | ||||
|                 .Concat(new List<Segment> { Segment.LineBreak }) | ||||
|                 .Concat(RenderChildren(context, maxWidth - Rendering.PartSize, _rootNode, depth: 0)); | ||||
|                 .Concat(RenderChildren(context, maxWidth - Appearance.PartSize, _rootNode, depth: 0)); | ||||
|         } | ||||
|  | ||||
|         private IEnumerable<Segment> RenderChildren(RenderContext context, int maxWidth, TreeNode node, int depth, | ||||
|             int? trailingStarted = null) | ||||
|         { | ||||
|             var result = new List<Segment>(); | ||||
|  | ||||
|             foreach (var (index, firstChild, lastChild, childNode) in node.Children.Enumerate()) | ||||
|             foreach (var (_, _, lastChild, childNode) in node.Children.Enumerate()) | ||||
|             { | ||||
|                 var lines = Segment.SplitLines(context, childNode.Render(context, maxWidth)); | ||||
|  | ||||
|                 foreach (var (lineIndex, firstLine, lastLine, line) in lines.Enumerate()) | ||||
|                 foreach (var (_, isFirstLine, _, line) in lines.Enumerate()) | ||||
|                 { | ||||
|                     var siblingConnectorSegment = | ||||
|                         new Segment(Rendering.GetPart(TreePart.SiblingConnector), Style); | ||||
|                         new Segment(Appearance.GetPart(TreePart.SiblingConnector), Style); | ||||
|                     if (trailingStarted != null) | ||||
|                     { | ||||
|                         result.AddRange(Enumerable.Repeat(siblingConnectorSegment, trailingStarted.Value)); | ||||
|                         result.AddRange(Enumerable.Repeat( | ||||
|                             Segment.Padding(Rendering.PartSize), | ||||
|                             Segment.Padding(Appearance.PartSize), | ||||
|                             depth - trailingStarted.Value)); | ||||
|                     } | ||||
|                     else | ||||
| @@ -96,15 +94,15 @@ namespace Spectre.Console | ||||
|                         result.AddRange(Enumerable.Repeat(siblingConnectorSegment, depth)); | ||||
|                     } | ||||
|  | ||||
|                     if (firstLine) | ||||
|                     if (isFirstLine) | ||||
|                     { | ||||
|                         result.Add(lastChild | ||||
|                             ? new Segment(Rendering.GetPart(TreePart.BottomChildBranch), Style) | ||||
|                             : new Segment(Rendering.GetPart(TreePart.ChildBranch), Style)); | ||||
|                             ? new Segment(Appearance.GetPart(TreePart.BottomChildBranch), Style) | ||||
|                             : new Segment(Appearance.GetPart(TreePart.ChildBranch), Style)); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         result.Add(lastChild ? Segment.Padding(Rendering.PartSize) : siblingConnectorSegment); | ||||
|                         result.Add(lastChild ? Segment.Padding(Appearance.PartSize) : siblingConnectorSegment); | ||||
|                     } | ||||
|  | ||||
|                     result.AddRange(line); | ||||
| @@ -112,8 +110,12 @@ namespace Spectre.Console | ||||
|                 } | ||||
|  | ||||
|                 var childTrailingStarted = trailingStarted ?? (lastChild ? depth : null); | ||||
|                 result.AddRange(RenderChildren(context, maxWidth - Rendering.PartSize, childNode, depth + 1, | ||||
|                     childTrailingStarted)); | ||||
|  | ||||
|                 result.AddRange( | ||||
|                     RenderChildren( | ||||
|                         context, maxWidth - Appearance.PartSize, | ||||
|                         childNode, depth + 1, | ||||
|                         childTrailingStarted)); | ||||
|             } | ||||
|  | ||||
|             return result; | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using Spectre.Console.Rendering; | ||||
| @@ -7,10 +8,12 @@ namespace Spectre.Console | ||||
|     /// <summary> | ||||
|     /// Node of a tree. | ||||
|     /// </summary> | ||||
|     public sealed class TreeNode : IRenderable | ||||
|     public sealed class TreeNode : IHasTreeNodes, IRenderable | ||||
|     { | ||||
|         private readonly IRenderable _renderable; | ||||
|         private List<TreeNode> _children; | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public List<TreeNode> Children { get; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of the <see cref="TreeNode"/> class. | ||||
| @@ -19,25 +22,8 @@ namespace Spectre.Console | ||||
|         /// <param name="children">Any children that the node is declared with.</param> | ||||
|         public TreeNode(IRenderable renderable, IEnumerable<TreeNode>? children = null) | ||||
|         { | ||||
|             _renderable = renderable; | ||||
|             _children = new List<TreeNode>(children ?? Enumerable.Empty<TreeNode>()); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets the children of this node. | ||||
|         /// </summary> | ||||
|         public List<TreeNode> Children | ||||
|         { | ||||
|             get => _children; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Adds a child to the end of the node's list of children. | ||||
|         /// </summary> | ||||
|         /// <param name="child">Child to be added.</param> | ||||
|         public void AddChild(TreeNode child) | ||||
|         { | ||||
|             _children.Add(child); | ||||
|             _renderable = renderable ?? throw new ArgumentNullException(nameof(renderable)); | ||||
|             Children = new List<TreeNode>(children ?? Enumerable.Empty<TreeNode>()); | ||||
|         } | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user