Skip to content

Commit e02d576

Browse files
authored
Fixes #4009 - fix tree ordering (#4015)
1 parent 7ba9e53 commit e02d576

File tree

11 files changed

+356
-110
lines changed

11 files changed

+356
-110
lines changed

Terminal.Gui/Views/TableView/TreeTableSource.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ private string GetColumnZeroRepresentationFromTree (int row)
8787
Branch<T> branch = RowToBranch (row);
8888

8989
// Everything on line before the expansion run and branch text
90-
Rune [] prefix = branch.GetLinePrefix (Application.Driver).ToArray ();
91-
Rune expansion = branch.GetExpandableSymbol (Application.Driver);
90+
Rune [] prefix = branch.GetLinePrefix ().ToArray ();
91+
Rune expansion = branch.GetExpandableSymbol ();
9292
string lineBody = _tree.AspectGetter (branch.Model) ?? "";
9393

9494
var sb = new StringBuilder ();

Terminal.Gui/Views/TreeView/Branch.cs

Lines changed: 91 additions & 89 deletions
Large diffs are not rendered by default.

Terminal.Gui/Views/TreeView/TreeView.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ public IEnumerable<T> GetChildren (T o)
847847
return new T [0];
848848
}
849849

850-
return branch.ChildBranches?.Values?.Select (b => b.Model)?.ToArray () ?? new T [0];
850+
return branch.ChildBranches?.Select (b => b.Model)?.ToArray () ?? new T [0];
851851
}
852852

853853
/// <summary>Returns the maximum width line in the tree including prefix and expansion symbols.</summary>
@@ -879,10 +879,10 @@ public int GetContentWidth (bool visible)
879879
return 0;
880880
}
881881

882-
return map.Skip (ScrollOffsetVertical).Take (Viewport.Height).Max (b => b.GetWidth (Driver));
882+
return map.Skip (ScrollOffsetVertical).Take (Viewport.Height).Max (b => b.GetWidth ());
883883
}
884884

885-
return map.Max (b => b.GetWidth (Driver));
885+
return map.Max (b => b.GetWidth ());
886886
}
887887

888888
/// <summary>
@@ -1171,7 +1171,7 @@ protected override bool OnDrawingContent ()
11711171
if (idxToRender < map.Count)
11721172
{
11731173
// Render the line
1174-
map.ElementAt (idxToRender).Draw (Driver, ColorScheme, line, Viewport.Width);
1174+
map.ElementAt (idxToRender).Draw (line, Viewport.Width);
11751175
}
11761176
else
11771177
{
@@ -1488,7 +1488,7 @@ private IEnumerable<Branch<T>> AddToLineMap (Branch<T> currentBranch, bool paren
14881488

14891489
if (currentBranch.IsExpanded)
14901490
{
1491-
foreach (Branch<T> subBranch in currentBranch.ChildBranches.Values)
1491+
foreach (Branch<T> subBranch in currentBranch.ChildBranches)
14921492
{
14931493
foreach (Branch<T> sub in AddToLineMap (subBranch, weMatch, out bool childMatch))
14941494
{

Terminal.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests.Parallelizable",
6565
EndProject
6666
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerminalGuiFluentTesting", "TerminalGuiFluentTesting\TerminalGuiFluentTesting.csproj", "{2DBA7BDC-17AE-474B-A507-00807D087607}"
6767
EndProject
68+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TerminalGuiFluentTesting.Xunit", "TerminalGuiFluentTesting.Xunit\TerminalGuiFluentTesting.Xunit.csproj", "{231B9723-10F3-46DB-8EAE-50C0C0375AD3}"
69+
EndProject
6870
Global
6971
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7072
Debug|Any CPU = Debug|Any CPU
@@ -123,6 +125,10 @@ Global
123125
{2DBA7BDC-17AE-474B-A507-00807D087607}.Debug|Any CPU.Build.0 = Debug|Any CPU
124126
{2DBA7BDC-17AE-474B-A507-00807D087607}.Release|Any CPU.ActiveCfg = Release|Any CPU
125127
{2DBA7BDC-17AE-474B-A507-00807D087607}.Release|Any CPU.Build.0 = Release|Any CPU
128+
{231B9723-10F3-46DB-8EAE-50C0C0375AD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
129+
{231B9723-10F3-46DB-8EAE-50C0C0375AD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
130+
{231B9723-10F3-46DB-8EAE-50C0C0375AD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
131+
{231B9723-10F3-46DB-8EAE-50C0C0375AD3}.Release|Any CPU.Build.0 = Release|Any CPU
126132
EndGlobalSection
127133
GlobalSection(SolutionProperties) = preSolution
128134
HideSolutionNode = FALSE
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\TerminalGuiFluentTesting\TerminalGuiFluentTesting.csproj" />
11+
<PackageReference Include="xunit" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Xunit;
2+
3+
namespace TerminalGuiFluentTesting;
4+
5+
public static class XunitContextExtensions
6+
{
7+
public static GuiTestContext AssertTrue (this GuiTestContext context, bool? condition)
8+
{
9+
context.Then (
10+
() =>
11+
{
12+
Assert.True (condition);
13+
});
14+
return context;
15+
}
16+
public static GuiTestContext AssertEqual (this GuiTestContext context, object? expected, object? actual)
17+
{
18+
context.Then (
19+
() =>
20+
{
21+
Assert.Equal (expected,actual);
22+
});
23+
return context;
24+
}
25+
}

TerminalGuiFluentTesting/GuiTestContext.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,18 @@ public GuiTestContext WaitIteration (Action? a = null)
243243
/// <returns></returns>
244244
public GuiTestContext Then (Action doAction)
245245
{
246-
doAction ();
246+
try
247+
{
248+
doAction ();
249+
}
250+
catch(Exception)
251+
{
252+
Stop ();
253+
_hardStop.Cancel();
254+
255+
throw;
256+
257+
}
247258

248259
return this;
249260
}
@@ -360,6 +371,7 @@ public GuiTestContext Right ()
360371
{
361372
SendNetKey (k);
362373
}
374+
WaitIteration ();
363375
break;
364376
default:
365377
throw new ArgumentOutOfRangeException ();
@@ -550,4 +562,25 @@ private void SendWindowsKey (ConsoleKeyMapping.VK specialKey)
550562

551563
WaitIteration ();
552564
}
565+
566+
/// <summary>
567+
/// Sets the input focus to the given <see cref="View"/>.
568+
/// Throws <see cref="ArgumentException"/> if focus did not change due to system
569+
/// constraints e.g. <paramref name="toFocus"/>
570+
/// <see cref="View.CanFocus"/> is <see langword="false"/>
571+
/// </summary>
572+
/// <param name="toFocus"></param>
573+
/// <returns></returns>
574+
/// <exception cref="ArgumentException"></exception>
575+
public GuiTestContext Focus (View toFocus)
576+
{
577+
toFocus.FocusDeepest (NavigationDirection.Forward, TabBehavior.TabStop);
578+
579+
if (!toFocus.HasFocus)
580+
{
581+
throw new ArgumentException ("Failed to set focus, FocusDeepest did not result in HasFocus becoming true. Ensure view is added and focusable");
582+
}
583+
584+
return WaitIteration ();
585+
}
553586
}

Tests/IntegrationTests/FluentTests/BasicFluentAssertionTests.cs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System.Text;
2-
using Terminal.Gui;
1+
using Terminal.Gui;
32
using TerminalGuiFluentTesting;
43
using Xunit.Abstractions;
54

@@ -9,17 +8,6 @@ public class BasicFluentAssertionTests
98
{
109
private readonly TextWriter _out;
1110

12-
public class TestOutputWriter : TextWriter
13-
{
14-
private readonly ITestOutputHelper _output;
15-
16-
public TestOutputWriter (ITestOutputHelper output) { _output = output; }
17-
18-
public override void WriteLine (string? value) { _output.WriteLine (value ?? string.Empty); }
19-
20-
public override Encoding Encoding => Encoding.UTF8;
21-
}
22-
2311
public BasicFluentAssertionTests (ITestOutputHelper outputHelper) { _out = new TestOutputWriter (outputHelper); }
2412

2513
[Theory]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Text;
2+
using Xunit.Abstractions;
3+
4+
namespace IntegrationTests.FluentTests;
5+
6+
public class TestOutputWriter : TextWriter
7+
{
8+
private readonly ITestOutputHelper _output;
9+
10+
public TestOutputWriter (ITestOutputHelper output) { _output = output; }
11+
12+
public override void WriteLine (string? value) { _output.WriteLine (value ?? string.Empty); }
13+
14+
public override Encoding Encoding => Encoding.UTF8;
15+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
using Terminal.Gui;
2+
using TerminalGuiFluentTesting;
3+
using Xunit.Abstractions;
4+
5+
namespace IntegrationTests.FluentTests;
6+
7+
public class TreeViewFluentTests
8+
{
9+
private readonly TextWriter _out;
10+
11+
public TreeViewFluentTests (ITestOutputHelper outputHelper) { _out = new TestOutputWriter (outputHelper); }
12+
13+
[Theory]
14+
[ClassData (typeof (V2TestDrivers))]
15+
public void TreeView_AllowReOrdering (V2TestDriver d)
16+
{
17+
var tv = new TreeView
18+
{
19+
Width = Dim.Fill (),
20+
Height = Dim.Fill ()
21+
};
22+
23+
TreeNode car;
24+
TreeNode lorry;
25+
TreeNode bike;
26+
27+
var root = new TreeNode ("Root")
28+
{
29+
Children =
30+
[
31+
car = new ("Car"),
32+
lorry = new ("Lorry"),
33+
bike = new ("Bike")
34+
]
35+
};
36+
37+
tv.AddObject (root);
38+
39+
using GuiTestContext context =
40+
With.A<Window> (40, 10, d)
41+
.Add (tv)
42+
.Focus (tv)
43+
.WaitIteration ()
44+
.ScreenShot ("Before expanding", _out)
45+
.AssertEqual (root, tv.GetObjectOnRow (0))
46+
.Then (() => Assert.Null (tv.GetObjectOnRow (1)))
47+
.Right ()
48+
.ScreenShot ("After expanding", _out)
49+
.AssertEqual (root, tv.GetObjectOnRow (0))
50+
.AssertEqual (car, tv.GetObjectOnRow (1))
51+
.AssertEqual (lorry, tv.GetObjectOnRow (2))
52+
.AssertEqual (bike, tv.GetObjectOnRow (3))
53+
.Then (
54+
() =>
55+
{
56+
// Re order
57+
root.Children = [bike, car, lorry];
58+
tv.RefreshObject (root);
59+
})
60+
.WaitIteration ()
61+
.ScreenShot ("After re-order", _out)
62+
.AssertEqual (root, tv.GetObjectOnRow (0))
63+
.AssertEqual (bike, tv.GetObjectOnRow (1))
64+
.AssertEqual (car, tv.GetObjectOnRow (2))
65+
.AssertEqual (lorry, tv.GetObjectOnRow (3))
66+
.WriteOutLogs (_out);
67+
68+
context.Stop ();
69+
}
70+
71+
[Theory]
72+
[ClassData (typeof (V2TestDrivers))]
73+
public void TreeViewReOrder_PreservesExpansion (V2TestDriver d)
74+
{
75+
var tv = new TreeView
76+
{
77+
Width = Dim.Fill (),
78+
Height = Dim.Fill ()
79+
};
80+
81+
TreeNode car;
82+
TreeNode lorry;
83+
TreeNode bike;
84+
85+
TreeNode mrA;
86+
TreeNode mrB;
87+
88+
TreeNode mrC;
89+
90+
TreeNode mrD;
91+
TreeNode mrE;
92+
93+
var root = new TreeNode ("Root")
94+
{
95+
Children =
96+
[
97+
car = new ("Car")
98+
{
99+
Children =
100+
[
101+
mrA = new ("Mr A"),
102+
mrB = new ("Mr B")
103+
]
104+
},
105+
lorry = new ("Lorry")
106+
{
107+
Children =
108+
[
109+
mrC = new ("Mr C")
110+
]
111+
},
112+
bike = new ("Bike")
113+
{
114+
Children =
115+
[
116+
mrD = new ("Mr D"),
117+
mrE = new ("Mr E")
118+
]
119+
}
120+
]
121+
};
122+
123+
tv.AddObject (root);
124+
tv.ExpandAll ();
125+
126+
using GuiTestContext context =
127+
With.A<Window> (40, 13, d)
128+
.Add (tv)
129+
.WaitIteration ()
130+
.ScreenShot ("Initial State", _out)
131+
.AssertEqual (root, tv.GetObjectOnRow (0))
132+
.AssertEqual (car, tv.GetObjectOnRow (1))
133+
.AssertEqual (mrA, tv.GetObjectOnRow (2))
134+
.AssertEqual (mrB, tv.GetObjectOnRow (3))
135+
.AssertEqual (lorry, tv.GetObjectOnRow (4))
136+
.AssertEqual (mrC, tv.GetObjectOnRow (5))
137+
.AssertEqual (bike, tv.GetObjectOnRow (6))
138+
.AssertEqual (mrD, tv.GetObjectOnRow (7))
139+
.AssertEqual (mrE, tv.GetObjectOnRow (8))
140+
.Then (
141+
() =>
142+
{
143+
// Re order
144+
root.Children = [bike, car, lorry];
145+
tv.RefreshObject (root);
146+
})
147+
.WaitIteration ()
148+
.ScreenShot ("After re-order", _out)
149+
.AssertEqual (root, tv.GetObjectOnRow (0))
150+
.AssertEqual (bike, tv.GetObjectOnRow (1))
151+
.AssertEqual (mrD, tv.GetObjectOnRow (2))
152+
.AssertEqual (mrE, tv.GetObjectOnRow (3))
153+
.AssertEqual (car, tv.GetObjectOnRow (4))
154+
.AssertEqual (mrA, tv.GetObjectOnRow (5))
155+
.AssertEqual (mrB, tv.GetObjectOnRow (6))
156+
.AssertEqual (lorry, tv.GetObjectOnRow (7))
157+
.AssertEqual (mrC, tv.GetObjectOnRow (8))
158+
.WriteOutLogs (_out);
159+
160+
context.Stop ();
161+
}
162+
}

0 commit comments

Comments
 (0)