using System.ComponentModel;
using System.Text;
using Xunit.Abstractions;
namespace Terminal.Gui.ViewsTests;
public class TreeViewTests
{
private readonly ITestOutputHelper _output;
public TreeViewTests (ITestOutputHelper output) { _output = output; }
/// Tests that results in a correct content height
[Fact]
public void ContentHeight_BiggerAfterExpand ()
{
TreeView tree = CreateTree (out Factory f, out _, out _);
Assert.Equal (1, tree.ContentHeight);
tree.Expand (f);
Assert.Equal (3, tree.ContentHeight);
tree.Collapse (f);
Assert.Equal (1, tree.ContentHeight);
}
[Fact]
public void ContentWidth_BiggerAfterExpand ()
{
TreeView tree = CreateTree (out Factory f, out Car car1, out _);
tree.BeginInit ();
tree.EndInit ();
tree.Viewport = new Rectangle (0, 0, 10, 10);
//-+Factory
Assert.Equal (9, tree.GetContentWidth (true));
car1.Name = "123456789";
tree.Expand (f);
//..├-123456789
Assert.Equal (13, tree.GetContentWidth (true));
tree.Collapse (f);
//-+Factory
Assert.Equal (9, tree.GetContentWidth (true));
}
[Fact]
public void ContentWidth_VisibleVsAll ()
{
TreeView tree = CreateTree (out Factory f, out Car car1, out Car car2);
tree.BeginInit ();
tree.EndInit ();
// control only allows 1 row to be viewed at once
tree.Viewport = new Rectangle (0, 0, 20, 1);
//-+Factory
Assert.Equal (9, tree.GetContentWidth (true));
Assert.Equal (9, tree.GetContentWidth (false));
car1.Name = "123456789";
car2.Name = "12345678";
tree.Expand (f);
// Although expanded the bigger (longer) child node is not in the rendered area of the control
Assert.Equal (9, tree.GetContentWidth (true));
Assert.Equal (
13,
tree.GetContentWidth (false)
); // If you ask for the global max width it includes the longer child
// Now that we have scrolled down 1 row we should see the big child
tree.ScrollOffsetVertical = 1;
Assert.Equal (13, tree.GetContentWidth (true));
Assert.Equal (13, tree.GetContentWidth (false));
// Scroll down so only car2 is visible
tree.ScrollOffsetVertical = 2;
Assert.Equal (12, tree.GetContentWidth (true));
Assert.Equal (13, tree.GetContentWidth (false));
// Scroll way down (off bottom of control even)
tree.ScrollOffsetVertical = 5;
Assert.Equal (0, tree.GetContentWidth (true));
Assert.Equal (13, tree.GetContentWidth (false));
}
[Fact]
[AutoInitShutdown]
public void CursorVisibility_MultiSelect ()
{
var tv = new TreeView { Width = 20, Height = 10 };
var n1 = new TreeNode ("normal");
var n2 = new TreeNode ("pink");
tv.AddObject (n1);
tv.AddObject (n2);
var top = new Toplevel ();
top.Add (tv);
Application.Begin (top);
Assert.True (tv.MultiSelect);
Assert.True (tv.HasFocus);
Assert.Equal (CursorVisibility.Invisible, tv.CursorVisibility);
tv.SelectAll ();
tv.CursorVisibility = CursorVisibility.Default;
Application.PositionCursor ();
Application.Driver!.GetCursorVisibility (out CursorVisibility visibility);
Assert.Equal (CursorVisibility.Default, tv.CursorVisibility);
Assert.Equal (CursorVisibility.Default, visibility);
top.Dispose ();
}
[Fact]
public void EmptyTreeView_ContentSizes ()
{
var emptyTree = new TreeView ();
Assert.Equal (0, emptyTree.ContentHeight);
Assert.Equal (0, emptyTree.GetContentWidth (true));
Assert.Equal (0, emptyTree.GetContentWidth (false));
}
[Fact]
public void EmptyTreeViewGeneric_ContentSizes ()
{
TreeView emptyTree = new ();
Assert.Equal (0, emptyTree.ContentHeight);
Assert.Equal (0, emptyTree.GetContentWidth (true));
Assert.Equal (0, emptyTree.GetContentWidth (false));
}
///
/// Tests that returns the child objects for the factory. Note that
/// the method only works once the parent branch (Factory) is expanded to expose the child (Car)
///
[Fact]
public void GetChildren_ReturnsChildrenOnlyWhenExpanded ()
{
TreeView tree = CreateTree (out Factory f, out Car c1, out Car c2);
Assert.Empty (tree.GetChildren (f));
Assert.Empty (tree.GetChildren (c1));
Assert.Empty (tree.GetChildren (c2));
// now when we expand the factory we discover the cars
tree.Expand (f);
Assert.Contains (c1, tree.GetChildren (f));
Assert.Contains (c2, tree.GetChildren (f));
Assert.Empty (tree.GetChildren (c1));
Assert.Empty (tree.GetChildren (c2));
tree.Collapse (f);
Assert.Empty (tree.GetChildren (f));
Assert.Empty (tree.GetChildren (c1));
Assert.Empty (tree.GetChildren (c2));
}
///
/// Tests that returns the parent object for Cars (Factories). Note that
/// the method only works once the parent branch (Factory) is expanded to expose the child (Car)
///
[Fact]
public void GetParent_ReturnsParentOnlyWhenExpanded ()
{
TreeView tree = CreateTree (out Factory f, out Car c1, out Car c2);
Assert.Null (tree.GetParent (f));
Assert.Null (tree.GetParent (c1));
Assert.Null (tree.GetParent (c2));
// now when we expand the factory we discover the cars
tree.Expand (f);
Assert.Null (tree.GetParent (f));
Assert.Equal (f, tree.GetParent (c1));
Assert.Equal (f, tree.GetParent (c2));
tree.Collapse (f);
Assert.Null (tree.GetParent (f));
Assert.Null (tree.GetParent (c1));
Assert.Null (tree.GetParent (c2));
}
/// Tests for objects that are as yet undiscovered by the tree
[Fact]
public void GetScrollOffsetOf_MinusOneForUnRevealed ()
{
TreeView tree = CreateTree (out Factory f, out Car c1, out Car c2);
// to start with the tree is collapsed and only knows about the root object
Assert.Equal (0, tree.GetScrollOffsetOf (f));
Assert.Equal (-1, tree.GetScrollOffsetOf (c1));
Assert.Equal (-1, tree.GetScrollOffsetOf (c2));
// reveal it by expanding the root object
tree.Expand (f);
// tree now knows about children
Assert.Equal (0, tree.GetScrollOffsetOf (f));
Assert.Equal (1, tree.GetScrollOffsetOf (c1));
Assert.Equal (2, tree.GetScrollOffsetOf (c2));
// after collapsing the root node again
tree.Collapse (f);
// tree no longer knows about the locations of these objects
Assert.Equal (0, tree.GetScrollOffsetOf (f));
Assert.Equal (-1, tree.GetScrollOffsetOf (c1));
Assert.Equal (-1, tree.GetScrollOffsetOf (c2));
}
[Fact]
public void GoTo_OnlyAppliesToExposedObjects ()
{
TreeView tree = CreateTree (out Factory f, out Car car1, out _);
tree.BeginInit ();
tree.EndInit ();
// Make tree bounds 1 in height so that EnsureVisible always requires updating scroll offset
tree.Viewport = new Rectangle (0, 0, 50, 1);
Assert.Null (tree.SelectedObject);
Assert.Equal (0, tree.ScrollOffsetVertical);
// car 1 is not yet exposed
tree.GoTo (car1);
Assert.Null (tree.SelectedObject);
Assert.Equal (0, tree.ScrollOffsetVertical);
tree.Expand (f);
// Car1 is now exposed by expanding the factory
tree.GoTo (car1);
Assert.Equal (car1, tree.SelectedObject);
Assert.Equal (1, tree.ScrollOffsetVertical);
}
[Fact]
public void GoToEnd_ShouldNotFailOnEmptyTreeView ()
{
var tree = new TreeView ();
Exception exception = Record.Exception (() => tree.GoToEnd ());
Assert.Null (exception);
}
///
/// Tests that and behaves
/// correctly when an object cannot be expanded (because it has no children)
///
[Fact]
public void IsExpanded_FalseIfCannotExpand ()
{
TreeView tree = CreateTree (out Factory f, out Car c, out _);
// expose the car by expanding the factory
tree.Expand (f);
// car is not expanded
Assert.False (tree.IsExpanded (c));
//try to expand the car (should have no effect because cars have no children)
tree.Expand (c);
Assert.False (tree.IsExpanded (c));
// should also be ignored
tree.Collapse (c);
Assert.False (tree.IsExpanded (c));
}
/// Tests that and are consistent
[Fact]
public void IsExpanded_TrueAfterExpand ()
{
TreeView tree = CreateTree (out Factory f, out _, out _);
Assert.False (tree.IsExpanded (f));
tree.Expand (f);
Assert.True (tree.IsExpanded (f));
tree.Collapse (f);
Assert.False (tree.IsExpanded (f));
}
[Fact]
public void MultiSelect_GetAllSelectedObjects ()
{
var tree = new TreeView ();
TreeNode l1;
TreeNode l2;
TreeNode l3;
TreeNode l4;
var root = new TreeNode ("Root");
root.Children.Add (l1 = new TreeNode ("Leaf1"));
root.Children.Add (l2 = new TreeNode ("Leaf2"));
root.Children.Add (l3 = new TreeNode ("Leaf3"));
root.Children.Add (l4 = new TreeNode ("Leaf4"));
tree.AddObject (root);
tree.MultiSelect = true;
tree.Expand (root);
Assert.Empty (tree.GetAllSelectedObjects ());
tree.SelectedObject = root;
Assert.Single (tree.GetAllSelectedObjects (), root);
// move selection down 1
tree.AdjustSelection (1);
Assert.Single (tree.GetAllSelectedObjects (), l1);
// expand selection down 2 (e.g. shift down twice)
tree.AdjustSelection (1, true);
tree.AdjustSelection (1, true);
Assert.Equal (3, tree.GetAllSelectedObjects ().Count ());
Assert.Contains (l1, tree.GetAllSelectedObjects ());
Assert.Contains (l2, tree.GetAllSelectedObjects ());
Assert.Contains (l3, tree.GetAllSelectedObjects ());
tree.Collapse (root);
// No selected objects since the root was collapsed
Assert.Empty (tree.GetAllSelectedObjects ());
}
[Fact]
public void ObjectActivated_Called ()
{
TreeView tree = CreateTree (out Factory f, out Car car1, out _);
object activated = null;
var called = false;
// register for the event
tree.ObjectActivated += (s, e) =>
{
activated = e.ActivatedObject;
called = true;
};
Assert.False (called);
// no object is selected yet so no event should happen
tree.NewKeyDownEvent (Key.Enter);
Assert.Null (activated);
Assert.False (called);
// down to select factory
tree.NewKeyDownEvent (Key.CursorDown);
tree.NewKeyDownEvent (Key.Enter);
Assert.True (called);
Assert.Same (f, activated);
}
[Fact]
public void ObjectActivated_CustomKey ()
{
TreeView tree = CreateTree (out Factory f, out Car car1, out _);
tree.ObjectActivationKey = KeyCode.Delete;
object activated = null;
var called = false;
// register for the event
tree.ObjectActivated += (s, e) =>
{
activated = e.ActivatedObject;
called = true;
};
Assert.False (called);
// no object is selected yet so no event should happen
tree.NewKeyDownEvent (Key.Enter);
Assert.Null (activated);
Assert.False (called);
// down to select factory
tree.NewKeyDownEvent (Key.CursorDown);
tree.NewKeyDownEvent (Key.Enter);
// Enter is not the activation key in this unit test
Assert.Null (activated);
Assert.False (called);
// Delete is the activation key in this test so should result in activation occurring
tree.NewKeyDownEvent (Key.Delete);
Assert.True (called);
Assert.Same (f, activated);
}
[Fact]
public void ObjectActivationButton_DoubleClick ()
{
TreeView tree = CreateTree (out Factory f, out Car car1, out _);
object activated = null;
var called = false;
// register for the event
tree.ObjectActivated += (s, e) =>
{
activated = e.ActivatedObject;
called = true;
};
Assert.False (called);
// double click triggers activation
tree.NewMouseEvent (new MouseEvent { Flags = MouseFlags.Button1DoubleClicked });
Assert.True (called);
Assert.Same (f, activated);
Assert.Same (f, tree.SelectedObject);
}
[Fact]
public void ObjectActivationButton_RightClick ()
{
TreeView tree = CreateTree (out Factory f, out Car car1, out _);
tree.ObjectActivationButton = MouseFlags.Button2Clicked;
tree.ExpandAll ();
object activated = null;
var called = false;
// register for the event
tree.ObjectActivated += (s, e) =>
{
activated = e.ActivatedObject;
called = true;
};
Assert.False (called);
// double click does nothing because we changed button binding to right click
tree.NewMouseEvent (new MouseEvent { Position = new (0, 1), Flags = MouseFlags.Button1DoubleClicked });
Assert.Null (activated);
Assert.False (called);
tree.NewMouseEvent (new MouseEvent { Position = new (0, 1), Flags = MouseFlags.Button2Clicked });
Assert.True (called);
Assert.Same (car1, activated);
Assert.Same (car1, tree.SelectedObject);
}
[Fact]
public void ObjectActivationButton_SetToNull ()
{
TreeView tree = CreateTree (out Factory f, out Car car1, out _);
Assert.Null (tree.SelectedObject);
Assert.True (tree.SetFocus ());
tree.SelectedObject = null;
Assert.Null (tree.SelectedObject);
// disable activation
tree.ObjectActivationButton = null;
object activated = null;
var called = false;
// register for the event
tree.ObjectActivated += (s, e) =>
{
activated = e.ActivatedObject;
called = true;
};
Assert.False (called);
// double click does nothing because we changed button to null
tree.NewMouseEvent (new MouseEvent { Flags = MouseFlags.Button1DoubleClicked });
Assert.False (called);
Assert.Null (activated);
Assert.Null (tree.SelectedObject);
}
///
/// Same as but uses
/// instead of
///
[Fact]
public void RebuildTree_AfterChangingChildrenGetterDuringRuntime ()
{
TreeView tree = CreateTree (out Factory f, out Car c1, out Car c2);
var wheel = "Shiny Wheel";
// Expand the Factory
tree.Expand (f);
// c1 cannot have children
Assert.Equal (f, tree.GetParent (c1));
// expanding it does nothing
tree.Expand (c1);
Assert.False (tree.IsExpanded (c1));
// change the children getter so that now cars can have wheels
tree.TreeBuilder = new DelegateTreeBuilder (
o =>
// factories have cars
o is Factory
? new object [] { c1, c2 }
// cars have wheels
: new object [] { wheel }
);
// still cannot expand
tree.Expand (c1);
Assert.False (tree.IsExpanded (c1));
// Rebuild the tree
tree.RebuildTree ();
// Rebuild should not have collapsed any branches or done anything wierd
Assert.True (tree.IsExpanded (f));
tree.Expand (c1);
Assert.True (tree.IsExpanded (c1));
Assert.Equal (wheel, tree.GetChildren (c1).FirstOrDefault ());
}
///
/// Tests how the tree adapts to changes in the ChildrenGetter delegate during runtime when some branches are
/// expanded and the new delegate returns children for a node that previously didn't have any children
///
[Fact]
public void RefreshObject_AfterChangingChildrenGetterDuringRuntime ()
{
TreeView tree = CreateTree (out Factory f, out Car c1, out Car c2);
var wheel = "Shiny Wheel";
// Expand the Factory
tree.Expand (f);
// c1 cannot have children
Assert.Equal (f, tree.GetParent (c1));
// expanding it does nothing
tree.Expand (c1);
Assert.False (tree.IsExpanded (c1));
// change the children getter so that now cars can have wheels
tree.TreeBuilder = new DelegateTreeBuilder (
o =>
// factories have cars
o is Factory
? new object [] { c1, c2 }
// cars have wheels
: new object [] { wheel }
);
// still cannot expand
tree.Expand (c1);
Assert.False (tree.IsExpanded (c1));
tree.RefreshObject (c1);
tree.Expand (c1);
Assert.True (tree.IsExpanded (c1));
Assert.Equal (wheel, tree.GetChildren (c1).FirstOrDefault ());
}
///
/// Simulates behind the scenes changes to an object (which children it has) and how to sync that into the tree
/// using
///
[Fact]
public void RefreshObject_ChildRemoved ()
{
TreeView tree = CreateTree (out Factory f, out Car c1, out Car c2);
//reveal it by expanding the root object
tree.Expand (f);
Assert.Equal (0, tree.GetScrollOffsetOf (f));
Assert.Equal (1, tree.GetScrollOffsetOf (c1));
Assert.Equal (2, tree.GetScrollOffsetOf (c2));
// Factory now no longer makes Car c1 (only c2)
f.Cars = new [] { c2 };
// Tree does not know this yet
Assert.Equal (0, tree.GetScrollOffsetOf (f));
Assert.Equal (1, tree.GetScrollOffsetOf (c1));
Assert.Equal (2, tree.GetScrollOffsetOf (c2));
// If the user has selected the node c1
tree.SelectedObject = c1;
// When we refresh the tree
tree.RefreshObject (f);
// Now tree knows that factory has only one child node c2
Assert.Equal (0, tree.GetScrollOffsetOf (f));
Assert.Equal (-1, tree.GetScrollOffsetOf (c1));
Assert.Equal (1, tree.GetScrollOffsetOf (c2));
// The old selection was c1 which is now gone so selection should default to the parent of that branch (the factory)
Assert.Equal (f, tree.SelectedObject);
}
///
/// Simulates behind the scenes changes to an object (which children it has) and how to sync that into the tree
/// using
///
[Fact]
public void RefreshObject_EqualityTest ()
{
var obj1 = new EqualityTestObject { Name = "Bob", Age = 1 };
var obj2 = new EqualityTestObject { Name = "Bob", Age = 2 };
;
var root = "root";
TreeView tree = new ();
tree.TreeBuilder =
new DelegateTreeBuilder (s => ReferenceEquals (s, root) ? new object [] { obj1 } : null);
tree.AddObject (root);
// Tree is not expanded so the root has no children yet
Assert.Empty (tree.GetChildren (root));
tree.Expand (root);
// now that the tree is expanded we should get our child returned
Assert.Equal (1, tree.GetChildren (root).Count (child => ReferenceEquals (obj1, child)));
// change the getter to return an Equal object (but not the same reference - obj2)
tree.TreeBuilder =
new DelegateTreeBuilder (s => ReferenceEquals (s, root) ? new object [] { obj2 } : null);
// tree has cached the knowledge of what children the root has so won't know about the change (we still get obj1)
Assert.Equal (1, tree.GetChildren (root).Count (child => ReferenceEquals (obj1, child)));
// now that we refresh the root we should get the new child reference (obj2)
tree.RefreshObject (root);
Assert.Equal (1, tree.GetChildren (root).Count (child => ReferenceEquals (obj2, child)));
}
/// Tests illegal ranges for
[Fact]
public void ScrollOffset_CannotBeNegative ()
{
TreeView tree = CreateTree ();
Assert.Equal (0, tree.ScrollOffsetVertical);
tree.ScrollOffsetVertical = -100;
Assert.Equal (0, tree.ScrollOffsetVertical);
tree.ScrollOffsetVertical = 10;
Assert.Equal (10, tree.ScrollOffsetVertical);
}
[Fact]
[SetupFakeDriver]
public void TestBottomlessTreeView_MaxDepth_3 ()
{
TreeView tv = new () { Width = 20, Height = 10 };
tv.TreeBuilder = new DelegateTreeBuilder (
s => new [] { (int.Parse (s) + 1).ToString () }
);
tv.AddObject ("1");
tv.ColorScheme = new ColorScheme ();
tv.LayoutSubviews ();
tv.Draw ();
// Nothing expanded
TestHelpers.AssertDriverContentsAre (
@"└+1
",
_output
);
tv.MaxDepth = 3;
tv.ExpandAll ();
tv.Draw ();
// Normal drawing of the tree view
TestHelpers.AssertDriverContentsAre (
@"
└-1
└-2
└-3
└─4
",
_output
);
}
[Fact]
[SetupFakeDriver]
public void TestBottomlessTreeView_MaxDepth_5 ()
{
TreeView tv = new () { Width = 20, Height = 10 };
tv.TreeBuilder = new DelegateTreeBuilder (
s => new [] { (int.Parse (s) + 1).ToString () }
);
tv.AddObject ("1");
tv.ColorScheme = new ColorScheme ();
tv.LayoutSubviews ();
tv.Draw ();
// Nothing expanded
TestHelpers.AssertDriverContentsAre (
@"└+1
",
_output
);
tv.MaxDepth = 5;
tv.ExpandAll ();
tv.Draw ();
// Normal drawing of the tree view
TestHelpers.AssertDriverContentsAre (
@"
└-1
└-2
└-3
└-4
└-5
└─6
",
_output
);
Assert.False (tv.CanExpand ("6"));
Assert.False (tv.IsExpanded ("6"));
tv.Collapse ("6");
Assert.False (tv.CanExpand ("6"));
Assert.False (tv.IsExpanded ("6"));
tv.Collapse ("5");
Assert.True (tv.CanExpand ("5"));
Assert.False (tv.IsExpanded ("5"));
tv.Draw ();
// Normal drawing of the tree view
TestHelpers.AssertDriverContentsAre (
@"
└-1
└-2
└-3
└-4
└+5
",
_output
);
}
[Fact]
[SetupFakeDriver]
public void TestGetObjectOnRow ()
{
var tv = new TreeView { Width = 20, Height = 10 };
tv.BeginInit ();
tv.EndInit ();
var n1 = new TreeNode ("normal");
var n1_1 = new TreeNode ("pink");
var n1_2 = new TreeNode ("normal");
n1.Children.Add (n1_1);
n1.Children.Add (n1_2);
var n2 = new TreeNode ("pink");
tv.AddObject (n1);
tv.AddObject (n2);
tv.Expand (n1);
tv.ColorScheme = new ColorScheme ();
tv.LayoutSubviews ();
tv.Draw ();
TestHelpers.AssertDriverContentsAre (
@"├-normal
│ ├─pink
│ └─normal
└─pink
",
_output
);
Assert.Same (n1, tv.GetObjectOnRow (0));
Assert.Same (n1_1, tv.GetObjectOnRow (1));
Assert.Same (n1_2, tv.GetObjectOnRow (2));
Assert.Same (n2, tv.GetObjectOnRow (3));
Assert.Null (tv.GetObjectOnRow (4));
tv.Collapse (n1);
tv.Draw ();
TestHelpers.AssertDriverContentsAre (
@"├+normal
└─pink
",
_output
);
Assert.Same (n1, tv.GetObjectOnRow (0));
Assert.Same (n2, tv.GetObjectOnRow (1));
Assert.Null (tv.GetObjectOnRow (2));
Assert.Null (tv.GetObjectOnRow (3));
Assert.Null (tv.GetObjectOnRow (4));
}
[Fact]
[SetupFakeDriver]
public void TestGetObjectRow ()
{
var tv = new TreeView { Width = 20, Height = 10 };
var n1 = new TreeNode ("normal");
var n1_1 = new TreeNode ("pink");
var n1_2 = new TreeNode ("normal");
n1.Children.Add (n1_1);
n1.Children.Add (n1_2);
var n2 = new TreeNode ("pink");
tv.AddObject (n1);
tv.AddObject (n2);
tv.Expand (n1);
tv.ColorScheme = new ColorScheme ();
tv.LayoutSubviews ();
tv.Draw ();
TestHelpers.AssertDriverContentsAre (
@"├-normal
│ ├─pink
│ └─normal
└─pink
",
_output
);
Assert.Equal (0, tv.GetObjectRow (n1));
Assert.Equal (1, tv.GetObjectRow (n1_1));
Assert.Equal (2, tv.GetObjectRow (n1_2));
Assert.Equal (3, tv.GetObjectRow (n2));
tv.Collapse (n1);
tv.LayoutSubviews ();
tv.Draw ();
TestHelpers.AssertDriverContentsAre (
@"├+normal
└─pink
",
_output
);
Assert.Equal (0, tv.GetObjectRow (n1));
Assert.Null (tv.GetObjectRow (n1_1));
Assert.Null (tv.GetObjectRow (n1_2));
Assert.Equal (1, tv.GetObjectRow (n2));
// scroll down 1
tv.ScrollOffsetVertical = 1;
tv.LayoutSubviews ();
tv.Draw ();
TestHelpers.AssertDriverContentsAre (
@"└─pink
",
_output
);
Assert.Equal (-1, tv.GetObjectRow (n1));
Assert.Null (tv.GetObjectRow (n1_1));
Assert.Null (tv.GetObjectRow (n1_2));
Assert.Equal (0, tv.GetObjectRow (n2));
}
[Fact]
[SetupFakeDriver]
public void TestTreeView_DrawLineEvent ()
{
var tv = new TreeView { Width = 20, Height = 10 };
List> eventArgs = new ();
tv.DrawLine += (s, e) => { eventArgs.Add (e); };
var n1 = new TreeNode ("root one");
var n1_1 = new TreeNode ("leaf 1");
var n1_2 = new TreeNode ("leaf 2");
n1.Children.Add (n1_1);
n1.Children.Add (n1_2);
var n2 = new TreeNode ("root two");
tv.AddObject (n1);
tv.AddObject (n2);
tv.Expand (n1);
tv.ColorScheme = new ColorScheme ();
tv.LayoutSubviews ();
tv.Draw ();
// Normal drawing of the tree view
TestHelpers.AssertDriverContentsAre (
@"
├-root one
│ ├─leaf 1
│ └─leaf 2
└─root two
",
_output
);
Assert.Equal (4, eventArgs.Count ());
Assert.Equal (0, eventArgs [0].Y);
Assert.Equal (1, eventArgs [1].Y);
Assert.Equal (2, eventArgs [2].Y);
Assert.Equal (3, eventArgs [3].Y);
Assert.All (eventArgs, ea => Assert.Equal (ea.Tree, tv));
Assert.All (eventArgs, ea => Assert.False (ea.Handled));
Assert.Equal ("├-root one", eventArgs [0].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ());
Assert.Equal ("│ ├─leaf 1", eventArgs [1].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ());
Assert.Equal ("│ └─leaf 2", eventArgs [2].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ());
Assert.Equal ("└─root two", eventArgs [3].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ());
Assert.Equal (1, eventArgs [0].IndexOfExpandCollapseSymbol);
Assert.Equal (3, eventArgs [1].IndexOfExpandCollapseSymbol);
Assert.Equal (3, eventArgs [2].IndexOfExpandCollapseSymbol);
Assert.Equal (1, eventArgs [3].IndexOfExpandCollapseSymbol);
Assert.Equal (2, eventArgs [0].IndexOfModelText);
Assert.Equal (4, eventArgs [1].IndexOfModelText);
Assert.Equal (4, eventArgs [2].IndexOfModelText);
Assert.Equal (2, eventArgs [3].IndexOfModelText);
Assert.Equal ("root one", eventArgs [0].Model.Text);
Assert.Equal ("leaf 1", eventArgs [1].Model.Text);
Assert.Equal ("leaf 2", eventArgs [2].Model.Text);
Assert.Equal ("root two", eventArgs [3].Model.Text);
}
[Fact]
[SetupFakeDriver]
public void TestTreeView_DrawLineEvent_Handled ()
{
var tv = new TreeView { Width = 20, Height = 10 };
tv.DrawLine += (s, e) =>
{
if (e.Model.Text.Equals ("leaf 1"))
{
e.Handled = true;
for (var i = 0; i < 10; i++)
{
e.Tree.AddRune (i, e.Y, new Rune ('F'));
}
}
};
var n1 = new TreeNode ("root one");
var n1_1 = new TreeNode ("leaf 1");
var n1_2 = new TreeNode ("leaf 2");
n1.Children.Add (n1_1);
n1.Children.Add (n1_2);
var n2 = new TreeNode ("root two");
tv.AddObject (n1);
tv.AddObject (n2);
tv.Expand (n1);
tv.ColorScheme = new ColorScheme ();
tv.LayoutSubviews ();
tv.Draw ();
// Normal drawing of the tree view
TestHelpers.AssertDriverContentsAre (
@"
├-root one
FFFFFFFFFF
│ └─leaf 2
└─root two
",
_output
);
}
[Fact]
[SetupFakeDriver]
public void TestTreeView_DrawLineEvent_WithScrolling ()
{
var tv = new TreeView { Width = 20, Height = 10 };
List> eventArgs = new ();
tv.DrawLine += (s, e) => { eventArgs.Add (e); };
tv.ScrollOffsetHorizontal = 3;
tv.ScrollOffsetVertical = 1;
var n1 = new TreeNode ("root one");
var n1_1 = new TreeNode ("leaf 1");
var n1_2 = new TreeNode ("leaf 2");
n1.Children.Add (n1_1);
n1.Children.Add (n1_2);
var n2 = new TreeNode ("root two");
tv.AddObject (n1);
tv.AddObject (n2);
tv.Expand (n1);
tv.ColorScheme = new ColorScheme ();
tv.LayoutSubviews ();
tv.Draw ();
// Normal drawing of the tree view
TestHelpers.AssertDriverContentsAre (
@"
─leaf 1
─leaf 2
oot two
",
_output
);
Assert.Equal (3, eventArgs.Count ());
Assert.Equal (0, eventArgs [0].Y);
Assert.Equal (1, eventArgs [1].Y);
Assert.Equal (2, eventArgs [2].Y);
Assert.All (eventArgs, ea => Assert.Equal (ea.Tree, tv));
Assert.All (eventArgs, ea => Assert.False (ea.Handled));
Assert.Equal ("─leaf 1", eventArgs [0].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ());
Assert.Equal ("─leaf 2", eventArgs [1].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ());
Assert.Equal ("oot two", eventArgs [2].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ());
Assert.Equal (0, eventArgs [0].IndexOfExpandCollapseSymbol);
Assert.Equal (0, eventArgs [1].IndexOfExpandCollapseSymbol);
Assert.Null (eventArgs [2].IndexOfExpandCollapseSymbol);
Assert.Equal (1, eventArgs [0].IndexOfModelText);
Assert.Equal (1, eventArgs [1].IndexOfModelText);
Assert.Equal (-1, eventArgs [2].IndexOfModelText);
Assert.Equal ("leaf 1", eventArgs [0].Model.Text);
Assert.Equal ("leaf 2", eventArgs [1].Model.Text);
Assert.Equal ("root two", eventArgs [2].Model.Text);
}
[Fact]
[SetupFakeDriver]
public void TestTreeView_Filter ()
{
var tv = new TreeView { Width = 20, Height = 10 };
var n1 = new TreeNode ("root one");
var n1_1 = new TreeNode ("leaf 1");
var n1_2 = new TreeNode ("leaf 2");
n1.Children.Add (n1_1);
n1.Children.Add (n1_2);
var n2 = new TreeNode ("root two");
tv.AddObject (n1);
tv.AddObject (n2);
tv.Expand (n1);
tv.ColorScheme = new ColorScheme ();
tv.LayoutSubviews ();
tv.Draw ();
// Normal drawing of the tree view
TestHelpers.AssertDriverContentsAre (
@"
├-root one
│ ├─leaf 1
│ └─leaf 2
└─root two
",
_output
);
TreeViewTextFilter filter = new (tv);
tv.Filter = filter;
// matches nothing
filter.Text = "asdfjhasdf";
tv.Draw ();
// Normal drawing of the tree view
TestHelpers.AssertDriverContentsAre (
@"",
_output
);
// Matches everything
filter.Text = "root";
tv.Draw ();
TestHelpers.AssertDriverContentsAre (
@"
├-root one
│ ├─leaf 1
│ └─leaf 2
└─root two
",
_output
);
// Matches 2 leaf nodes
filter.Text = "leaf";
tv.Draw ();
TestHelpers.AssertDriverContentsAre (
@"
├-root one
│ ├─leaf 1
│ └─leaf 2
",
_output
);
// Matches 1 leaf nodes
filter.Text = "leaf 1";
tv.Draw ();
TestHelpers.AssertDriverContentsAre (
@"
├-root one
│ ├─leaf 1
",
_output
);
}
[Fact]
[SetupFakeDriver]
public void TestTreeViewColor ()
{
var tv = new TreeView { Width = 20, Height = 10 };
tv.BeginInit ();
tv.EndInit ();
var n1 = new TreeNode ("normal");
var n1_1 = new TreeNode ("pink");
var n1_2 = new TreeNode ("normal");
n1.Children.Add (n1_1);
n1.Children.Add (n1_2);
var n2 = new TreeNode ("pink");
tv.AddObject (n1);
tv.AddObject (n2);
tv.Expand (n1);
tv.ColorScheme = new ColorScheme ();
tv.LayoutSubviews ();
tv.Draw ();
// create a new color scheme
var pink = new Attribute (Color.Magenta, Color.Black);
var hotpink = new Attribute (Color.BrightMagenta, Color.Black);
// Normal drawing of the tree view
TestHelpers.AssertDriverContentsAre (
@"
├-normal
│ ├─pink
│ └─normal
└─pink
",
_output
);
// Should all be the same color
TestHelpers.AssertDriverAttributesAre (
@"
0000000000
0000000000
0000000000
0000000000
",
Application.Driver,
tv.ColorScheme.Normal,
pink
);
var pinkScheme = new ColorScheme { Normal = pink, Focus = hotpink };
// and a delegate that uses the pink color scheme
// for nodes "pink"
tv.ColorGetter = n => n.Text.Equals ("pink") ? pinkScheme : null;
// redraw now that the custom color
// delegate is registered
tv.SetNeedsDisplay ();
tv.Draw ();
// Same text
TestHelpers.AssertDriverContentsAre (
@"
├-normal
│ ├─pink
│ └─normal
└─pink
",
_output
);
// but now the item (only not lines) appear
// in pink when they are the word "pink"
TestHelpers.AssertDriverAttributesAre (
@"
00000000
00001111
0000000000
001111
",
Application.Driver,
tv.ColorScheme.Normal,
pink
);
}
[Fact]
public void TreeNode_WorksWithoutDelegate ()
{
var tree = new TreeView ();
var root = new TreeNode ("Root");
root.Children.Add (new TreeNode ("Leaf1"));
root.Children.Add (new TreeNode ("Leaf2"));
tree.AddObject (root);
tree.Expand (root);
Assert.Equal (2, tree.GetChildren (root).Count ());
}
/// Test object which considers for equality only
private class EqualityTestObject
{
public int Age { get; set; }
public string Name { get; set; }
public override bool Equals (object obj) { return obj is EqualityTestObject eto && Equals (Name, eto.Name); }
public override int GetHashCode () { return Name?.GetHashCode () ?? base.GetHashCode (); }
}
#region Test Setup Methods
private class Factory
{
public Car [] Cars { get; set; }
public override string ToString () { return "Factory"; }
}
private class Car
{
public string Name { get; set; }
public override string ToString () { return Name; }
}
private TreeView CreateTree () { return CreateTree (out _, out _, out _); }
private TreeView CreateTree (out Factory factory1, out Car car1, out Car car2)
{
car1 = new Car ();
car2 = new Car ();
factory1 = new Factory { Cars = new [] { car1, car2 } };
TreeView tree = new (new DelegateTreeBuilder (s => s is Factory f ? f.Cars : null));
tree.AddObject (factory1);
return tree;
}
#endregion
[Fact]
public void HotKey_Command_SetsFocus ()
{
var view = new TreeView ();
view.CanFocus = true;
Assert.False (view.HasFocus);
view.InvokeCommand (Command.HotKey);
Assert.True (view.HasFocus);
}
[Fact]
public void HotKey_Command_Does_Not_Accept ()
{
var treeView = new TreeView ();
var accepted = false;
treeView.Accepting += OnAccept;
treeView.InvokeCommand (Command.HotKey);
Assert.False (accepted);
return;
void OnAccept (object sender, CommandEventArgs e) { accepted = true; }
}
[Fact]
public void Accept_Command_Accepts_and_ActivatesObject ()
{
var treeView = CreateTree (out Factory f, out Car car1, out _);
Assert.NotNull (car1);
treeView.SelectedObject = car1;
var accepted = false;
var activated = false;
object selectedObject = null;
treeView.Accepting += Accept;
treeView.ObjectActivated += ObjectActivated;
treeView.InvokeCommand (Command.Accept);
Assert.True (accepted);
Assert.True (activated);
Assert.Equal (car1, selectedObject);
return;
void ObjectActivated (object sender, ObjectActivatedEventArgs e)
{
activated = true;
selectedObject = e.ActivatedObject;
}
void Accept (object sender, CommandEventArgs e) { accepted = true; }
}
[Fact]
public void Accept_Cancel_Event_Prevents_ObjectActivated ()
{
var treeView = CreateTree (out Factory f, out Car car1, out _);
treeView.SelectedObject = car1;
var accepted = false;
var activated = false;
object selectedObject = null;
treeView.Accepting += Accept;
treeView.ObjectActivated += ObjectActivated;
treeView.InvokeCommand (Command.Accept);
Assert.True (accepted);
Assert.False (activated);
Assert.Null (selectedObject);
return;
void ObjectActivated (object sender, ObjectActivatedEventArgs e)
{
activated = true;
selectedObject = e.ActivatedObject;
}
void Accept (object sender, CommandEventArgs e)
{
accepted = true;
e.Cancel = true;
}
}
}