mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-27 16:27:55 +01:00
* Removed resharper settings from editorconfig * Remove constructors with frame parameters from Button class. * Remove constructors with frame parameters from CheckBox class. * Cleanup code. * Remove constructors with frame parameters from ComboBox class. * @BDisp Remove constructors with frame parameters from FrameView class. * Remove constructors with frame parameters from Label class. * Remove constructors with frame parameters from ListView class. * Remove constructors with frame parameters from ScrollBarView class. * Remove constructors with frame parameters from ScrollView class. * Remove namespace braces. * Cleanup code. * Cleanup code. * Cleanup code. * Remove constructors with frame parameters from TextField class. * Remove constructors with frame parameters from TimeField class. * Fixes #3182. OnResizeNeeded returns int.MaxValue and int.MaxValue when Application.Top is null, should return Size.Empty. * Remove constructors with frame parameters from Toplevel class. * Remove constructors with frame parameters from Window class. * Fix merge errors. * Revert "Fixes #3182. OnResizeNeeded returns int.MaxValue and int.MaxValue when Application.Top is null, should return Size.Empty." This reverts commitcf9c24b846. * Revert unit test. * Fixes #2882. TabView: 'Frame.DrawFrame(Rect, bool)' is obsolete: 'This method is obsolete in v2. Use use LineCanvas or Frame (#2980) * Fixes #2882. TabView: 'Frame.DrawFrame(Rect, bool)' is obsolete: 'This method is obsolete in v2. Use use LineCanvas or Frame * Trying fix this unit test that sometimes fail. * Fixes #2983. View need a alternative DrawFrame for the v2. * Use new DrawFrame method. * Change _lines field to Lines property. * Add TabWindow unit test. * Add DrawIncompleteFrame method and unit tests. * Add more unit tests to LineCanvas. * Fix newline conflict errors. * Revert "Change _lines field to Lines property." This reverts commitab6c5f3094. * Add DrawIncompleteFrame method and unit tests. * Add more unit tests to LineCanvas. * Fix newline conflict errors. * Force render immediately instead of join. * I will never rely on zero-location-based unit test again. * Fix TestTreeViewColor unit test fail. * Using location of 3 to avoid be divisible by 2 and so avoiding bugs. * Revert "Using location of 3 to avoid be divisible by 2 and so avoiding bugs." This reverts commitdd3df135d8. * Revert "I will never rely on zero-location-based unit test again." This reverts commit62adf6f285. * Revert "Fix newline conflict errors." This reverts commit4acf72612d. * Revert "Add more unit tests to LineCanvas." This reverts commit66bc6f514e. * Revert "Add DrawIncompleteFrame method and unit tests." This reverts commit680ba264e1. * Resolving merge conflicts. * Revert "Use new DrawFrame method." This reverts commit69a7f17f19. * Revert "Fixes #2983. View need a alternative DrawFrame for the v2." This reverts commitdade9fd767. * Reverting this changes to start a new one. * Add horizontal and vertical support for combining glyphs. * Fix text and auto size behavior. * Add TabWidth property. * Add unit test for WordWrap. * Fixes #3017. View TextDirection returns incorrect size on a vertical direction instance with AutoSize as false. * Using Frame to force read from the get method. * Fix some issues with AutoSize and ForceValidatePosDim. * Fixing broken unit tests. * Restoring code I've broken. * Removing forgotten code. * Only LayoutStyle.Computed can change the Frame. * DateField and TimeField depends on LayoutStyle.Computed. * Fix unit tests related with LayoutStyle. * Implements tabs, left and right arrows as View. * Draws a minimum full border. * Adds missing XML parameter. * Adds assert tests for Frame. * Removes duplicates InlineData. * Adds more unit tests for minimum full border without Left and Right thickness. * Trying to fix the TestTreeViewColor unit test fail. * Prevents a user to set TextDirection to -1. * Prevents any invalid TextDirection value. * Removes (TextDirection)(-1). * Removes unnecessary TextDirection initialization. * Removes LayoutStyle. * Fixing unit tests with border. * Trying to fix TestTreeViewColor again. * Revert "Trying to fix TestTreeViewColor again." This reverts commitc2efa8e42e. * Trying to fix TestTreeViewColor again. * Fix merge errors. * Fix merge errors. * Restoring unit test. * Restores the right XML comment. * Fix Disposing unit tests that sometimes throws because some instances aren't cleared on others unit tests classes. * Fix Disposing unit tests that sometimes throws because some instances aren't cleared on others unit tests classes. * Only call OnResizeNeeded if it's LayoutStyle.Computed. * Fix merge errors. * Fix merge errors. * Fix unit tests fail. * Reformat. * Again. * Rename to OnDrawAdornments. * Fix failing unit tests. * Reduces indentation and cleanup code. * Cleanup code. * Fix bug done when cleanup. * Replace FrameHandledMouseEvent to AdornmentHandledMouseEvent. * Removes Tab constructor parameters. --------- Co-authored-by: Tig <tig@users.noreply.github.com> * Fix merge errors. * Remove constructors with parameters from Button. * Remove parenthesis on objects initializers from Button. * Remove constructors with parameters from CheckBox. * Remove parenthesis on objects initializers from CheckBox. * Remove constructors with parameters from ComboBox. * Remove constructors with parameters from FrameView. * Remove parenthesis on objects initializers from FrameView. * Initial commit * Renamed Direction enum for clarity in refactoring unit tests * Moved nav tests to NavigationTests * Moved view tests around * Cleaning up TextFormatter and View.AutoSize code * Fixed latent TextFormatter bug with \n * removed Application dependency on some autosize unit tests * Fixed Label tests to deal with auotsize overriding height/width * Fixed more label tests. WIP * Fixed all places where AutoSize = happend after setting Dims * Started adding new primitive View.Text tests * Code comments * WIP: Enforce that it makes no sense to set Width/Height if AutoSize = true. Update Unit tests to match. * WIP: Enforce that it makes no sense to set Width/Height if AutoSize = true. Update Unit tests to match. * Remove frame set from the View constructor and prevent SetRelativeLayout running if not yet initialized. * Changes needed for unit tests pass on remove parameters constructors from the Label class. * Remove constructors with parameters from Label. * Remove parenthesis on objects initializers from Label. * Prefix private fields with underscore. * Renamed to MaxLength. * Remove constructors with parameters from ListView. * MakeWrapper not needed anymore. * Remove parenthesis on objects initializers from ListView. * WIP: Enforce that it makes no sense to set Width/Height if AutoSize = true. Update Unit tests to match. * Massive code cleanup - use parameterless constructors and ensure AutoSize is set properly. Code reformat. * Massive code cleanup - use parameterless constructors and ensure AutoSize is set properly. Code reformat. * Fixed messagebox * Remove constructors with parameters from ScrollBarView and ScrollView. * Remove parenthesis on objects initializers from ScrollBarView and ScrollView. * Cleanup code. * Fix merge errors. * Add empty dotsettings for solution and projects. * Set ReSharper language analysis level for projects to C#12 * Make ReSharper consider itself the boss for style * Add rule to enforce property backing fields above the property * Disable auto-detection of naming rules so ReSharper doesn't change them by itself * Don't let someone's VS settings override the indent settings * Explicitly set tab width to 4 spaces and force spaces. * Rules to keep various multi-line constructs aligned within themselves * Curly brace rules (Using K&R style, per current project spec) * Blank line rules Mostly to add breaks in various situations, and also to enforce max of 1 blank line. * Increase auto-wrap to 160 from default of 120 * Line break at end of all files, to make Unixy systems happy * Keep attributes on their own lines except for methods and records that are themselves single-line * Increase attribute auto-wrap to 60 from default of 38 * Wrap/chop rules for long method signatures and record declarations Chop if either already multi-line or if over 8 parameters. Chop AFTER the left paren and BEFORE the first parameter. * Chop rules for generics For generics with multi-line type parameters or multiple type parameter constraints, chop in a way that has consistent alignment. * Always enforce enum members on their own lines * One-line functions completely on one line They're usually expression-bodied anyway, here, so this is almost irrelevant * Keep control flow statements on their own lines * Follow same chop rules for method invocation as method declaration * Chop long or multi-line method chains or patterns * Wrap rules for binary operators Operator at beginning of new lines Auto-chop if long or already multi-line * Spaces between keywords and their opening parentheses * Add file layout rules for organization of reorderable items like fields, properties, etc. Ugly XML, so load it up in the UI to look at it It's MOSTLY the ReSharper defaults, but with more aggressive sorting, generally by access modifier and name, within each grouping. * Deconstructors use per-member types * Use keywords for built-in types and increase severity of inspection for violations Also apply to IntPtr and similar, which should now be nint and similar * Apply syntax style on completion. * Use var when evident for built-in and simple types, but not elsewhere * Increase severity for some minor redundancy and clarity inspections * Enforce braces always required for blocks, and treat as error * Warn if a local function is not statement-bodied * Increase various inspection severities IF Condition => Severity: Attributes not wrapped property => Suggestion Constructors expression-bodied => Error default doesn't have type when it isn't clear => suggestion Namespaces not file-scoped => error Methods not statement-bodied => hint Null check pattern not the object property pattern => Error * Adjust preference order of null check patterns to make object pattern highest * XmlDoc rules to keep tags and contents aligned and wrapped * Add a few custom profiles for code cleanup and make the full profile default * Remove parts for languages not used in this solution * copied in v2_develop changes * Merged v2_develop * Added Begin/Init unit tests. Removed Application dependencey from AutoSizeFalse tests * TextFormatter.Lines -> GetLines () * Let ReSharper know we intend to localize things * TextFormatter code cleanup * copied in v2_develop changes * Merged v2_develop * Spaces aren't wanted here either. * Fix merge errors. * Fixes ContentBottomRightCorner related with https://github.com/gui-cs/Terminal.Gui/issues/3211#issue-2098878820 * Remove constructors with parameters from ContextMenu. * Remove commented code. * Remove constructors with parameters from OpenDialog. * Remove constructors with parameters from SaveDialog. * Remove constructors with parameters from TextField. * Remove constructors with parameters from TimeField. * Fix unit test. * Remove unnecessary SetInitialProperties method. * Remove unnecessary SetInitialProperties method. * Remove parenthesis on objects initializers from Toplevel and Window. * Remove constructors with parameters from RadioGroup. * Remove constructors with parameters from TextView. * Remove constructors with parameters from MenuBar. * TEMPORARY: Turn everything that was set to error down to warning or lower * Fixes #3219. MenuBar is opened by call OpenMenu even it's disabled. * Remove constructors with parameters from Menu. * Remove constructors with parameters from View. * Change constructor to internal because is mainly useful for testing. * ReSharper Cleanup Code. * Added format only r# config * Fixes #3224. TextFormatter.Lines should return a single string.Empty list even with Width or Height equal to zero. * Remove constructors with parameters from Dialog. * Remove constructors with parameters from TextValidateField. * Fixes https://github.com/gui-cs/Terminal.Gui/issues/3224#issuecomment-1924096038 * Fixes #3225. Press CursorDown on TabView doesn't move to the next view. * Fixes #3229. TextFormatter should have a FillRemaining property. * Testing formatting merge in bdisp * Testing formatting merge * Testing formatting merge bdisp * Testing formatting merge 2 * xmldoc format * R# Full Code Cleanup * R# Full Code Cleanup2 * R# Full Code Cleanup2 * Merged! But broke tests * Refixing... * Refixed DrawTests * Refixed ViewTests * Refixed Text Tests * Refixed more unit tests * Refixed scenarios * Refixed rest of scenarios * Refixed ViewsTests * Refixed rest of tests. All unit tests pass again! * Fixed warnings * Updated R# version. Added new code cleanup settings * Applied latest code cleanup to solution * Another code cleanup pass * Tweaked r# settings. .editorconfig now matches. * r# else on separate line * r# - update * r# - full solution * test commit * test commit * test commit * Removed extra profiles * Full cleanup following cleaning up profiles * Null checking pattern rules/inspections * Tabs and extra whitespace are evil * Attributes on their own lines * Code layout rules to put fields first and to put backing fields with their properties * Merged v2_develop * Full cleanup following dodexahedron's PR --------- Co-authored-by: BDisp <bd.bdisp@gmail.com> Co-authored-by: Brandon Thetford <github@snapsinazfs.com>
1351 lines
42 KiB
C#
1351 lines
42 KiB
C#
using System.Text;
|
|
using Xunit.Abstractions;
|
|
|
|
namespace Terminal.Gui.ViewsTests;
|
|
|
|
public class TreeViewTests
|
|
{
|
|
private readonly ITestOutputHelper _output;
|
|
public TreeViewTests (ITestOutputHelper output) { _output = output; }
|
|
|
|
/// <summary>Tests that <see cref="TreeView.Expand(object)"/> results in a correct content height</summary>
|
|
[Fact]
|
|
public void ContentHeight_BiggerAfterExpand ()
|
|
{
|
|
TreeView<object> 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<object> tree = CreateTree (out Factory f, out Car car1, out _);
|
|
tree.BeginInit ();
|
|
tree.EndInit ();
|
|
|
|
tree.Bounds = new Rect (0, 0, 10, 10);
|
|
|
|
InitFakeDriver ();
|
|
|
|
//-+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));
|
|
|
|
Application.Shutdown ();
|
|
}
|
|
|
|
[Fact]
|
|
public void ContentWidth_VisibleVsAll ()
|
|
{
|
|
TreeView<object> 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.Bounds = new Rect (0, 0, 20, 1);
|
|
|
|
InitFakeDriver ();
|
|
|
|
//-+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));
|
|
|
|
Application.Shutdown ();
|
|
}
|
|
|
|
[Fact]
|
|
[AutoInitShutdown]
|
|
public void DesiredCursorVisibility_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);
|
|
|
|
Application.Top.Add (tv);
|
|
Application.Begin (Application.Top);
|
|
|
|
Assert.True (tv.MultiSelect);
|
|
Assert.True (tv.HasFocus);
|
|
Assert.Equal (CursorVisibility.Invisible, tv.DesiredCursorVisibility);
|
|
|
|
tv.SelectAll ();
|
|
tv.DesiredCursorVisibility = CursorVisibility.Default;
|
|
Application.Refresh ();
|
|
Application.Driver.GetCursorVisibility (out CursorVisibility visibility);
|
|
Assert.Equal (CursorVisibility.Default, tv.DesiredCursorVisibility);
|
|
Assert.Equal (CursorVisibility.Default, visibility);
|
|
}
|
|
|
|
[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<string> emptyTree = new ();
|
|
Assert.Equal (0, emptyTree.ContentHeight);
|
|
Assert.Equal (0, emptyTree.GetContentWidth (true));
|
|
Assert.Equal (0, emptyTree.GetContentWidth (false));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests that <see cref="TreeView.GetChildren(object)"/> 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)
|
|
/// </summary>
|
|
[Fact]
|
|
public void GetChildren_ReturnsChildrenOnlyWhenExpanded ()
|
|
{
|
|
TreeView<object> 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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests that <see cref="TreeView.GetParent(object)"/> 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)
|
|
/// </summary>
|
|
[Fact]
|
|
public void GetParent_ReturnsParentOnlyWhenExpanded ()
|
|
{
|
|
TreeView<object> 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));
|
|
}
|
|
|
|
/// <summary>Tests <see cref="TreeView.GetScrollOffsetOf(object)"/> for objects that are as yet undiscovered by the tree</summary>
|
|
[Fact]
|
|
public void GetScrollOffsetOf_MinusOneForUnRevealed ()
|
|
{
|
|
TreeView<object> 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<object> 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.Bounds = new Rect (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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests that <see cref="TreeView.IsExpanded(object)"/> and <see cref="TreeView.Expand(object)"/> behaves
|
|
/// correctly when an object cannot be expanded (because it has no children)
|
|
/// </summary>
|
|
[Fact]
|
|
public void IsExpanded_FalseIfCannotExpand ()
|
|
{
|
|
TreeView<object> 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));
|
|
|
|
Application.Shutdown ();
|
|
}
|
|
|
|
/// <summary>Tests that <see cref="TreeView.Expand(object)"/> and <see cref="TreeView.IsExpanded(object)"/> are consistent</summary>
|
|
[Fact]
|
|
public void IsExpanded_TrueAfterExpand ()
|
|
{
|
|
TreeView<object> 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<object> tree = CreateTree (out Factory f, out Car car1, out _);
|
|
|
|
InitFakeDriver ();
|
|
|
|
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 (new Key (KeyCode.Enter));
|
|
|
|
Assert.Null (activated);
|
|
Assert.False (called);
|
|
|
|
// down to select factory
|
|
tree.NewKeyDownEvent (new Key (KeyCode.CursorDown));
|
|
|
|
tree.NewKeyDownEvent (new Key (KeyCode.Enter));
|
|
|
|
Assert.True (called);
|
|
Assert.Same (f, activated);
|
|
|
|
Application.Shutdown ();
|
|
}
|
|
|
|
[Fact]
|
|
public void ObjectActivated_CustomKey ()
|
|
{
|
|
TreeView<object> tree = CreateTree (out Factory f, out Car car1, out _);
|
|
|
|
InitFakeDriver ();
|
|
|
|
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 (new Key (KeyCode.Enter));
|
|
|
|
Assert.Null (activated);
|
|
Assert.False (called);
|
|
|
|
// down to select factory
|
|
tree.NewKeyDownEvent (new Key (KeyCode.CursorDown));
|
|
|
|
tree.NewKeyDownEvent (new Key (KeyCode.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 (new Key (KeyCode.Delete));
|
|
|
|
Assert.True (called);
|
|
Assert.Same (f, activated);
|
|
|
|
Application.Shutdown ();
|
|
}
|
|
|
|
[Fact]
|
|
public void ObjectActivationButton_DoubleClick ()
|
|
{
|
|
TreeView<object> tree = CreateTree (out Factory f, out Car car1, out _);
|
|
|
|
InitFakeDriver ();
|
|
|
|
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.MouseEvent (new MouseEvent { Y = 0, Flags = MouseFlags.Button1DoubleClicked });
|
|
|
|
Assert.True (called);
|
|
Assert.Same (f, activated);
|
|
Assert.Same (f, tree.SelectedObject);
|
|
|
|
Application.Shutdown ();
|
|
}
|
|
|
|
[Fact]
|
|
public void ObjectActivationButton_RightClick ()
|
|
{
|
|
TreeView<object> tree = CreateTree (out Factory f, out Car car1, out _);
|
|
|
|
InitFakeDriver ();
|
|
|
|
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.MouseEvent (new MouseEvent { Y = 1, Flags = MouseFlags.Button1DoubleClicked });
|
|
|
|
Assert.Null (activated);
|
|
Assert.False (called);
|
|
|
|
tree.MouseEvent (new MouseEvent { Y = 1, Flags = MouseFlags.Button2Clicked });
|
|
|
|
Assert.True (called);
|
|
Assert.Same (car1, activated);
|
|
Assert.Same (car1, tree.SelectedObject);
|
|
|
|
Application.Shutdown ();
|
|
}
|
|
|
|
[Fact]
|
|
public void ObjectActivationButton_SetToNull ()
|
|
{
|
|
TreeView<object> tree = CreateTree (out Factory f, out Car car1, out _);
|
|
|
|
InitFakeDriver ();
|
|
|
|
// 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.MouseEvent (new MouseEvent { Y = 0, Flags = MouseFlags.Button1DoubleClicked });
|
|
|
|
Assert.False (called);
|
|
Assert.Null (activated);
|
|
Assert.Null (tree.SelectedObject);
|
|
|
|
Application.Shutdown ();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Same as <see cref="RefreshObject_AfterChangingChildrenGetterDuringRuntime"/> but uses
|
|
/// <see cref="TreeView.RebuildTree()"/> instead of <see cref="TreeView.RefreshObject(object, bool)"/>
|
|
/// </summary>
|
|
[Fact]
|
|
public void RebuildTree_AfterChangingChildrenGetterDuringRuntime ()
|
|
{
|
|
TreeView<object> 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<object> (
|
|
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 ());
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
[Fact]
|
|
public void RefreshObject_AfterChangingChildrenGetterDuringRuntime ()
|
|
{
|
|
TreeView<object> 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<object> (
|
|
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 ());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Simulates behind the scenes changes to an object (which children it has) and how to sync that into the tree
|
|
/// using <see cref="TreeView.RefreshObject(object, bool)"/>
|
|
/// </summary>
|
|
[Fact]
|
|
public void RefreshObject_ChildRemoved ()
|
|
{
|
|
TreeView<object> 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Simulates behind the scenes changes to an object (which children it has) and how to sync that into the tree
|
|
/// using <see cref="TreeView.RefreshObject(object, bool)"/>
|
|
/// </summary>
|
|
[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<object> tree = new ();
|
|
|
|
tree.TreeBuilder =
|
|
new DelegateTreeBuilder<object> (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<object> (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)));
|
|
}
|
|
|
|
/// <summary>Tests illegal ranges for <see cref="TreeView.ScrollOffset"/></summary>
|
|
[Fact]
|
|
public void ScrollOffset_CannotBeNegative ()
|
|
{
|
|
TreeView<object> tree = CreateTree ();
|
|
|
|
Assert.Equal (0, tree.ScrollOffsetVertical);
|
|
|
|
tree.ScrollOffsetVertical = -100;
|
|
Assert.Equal (0, tree.ScrollOffsetVertical);
|
|
|
|
tree.ScrollOffsetVertical = 10;
|
|
Assert.Equal (10, tree.ScrollOffsetVertical);
|
|
}
|
|
|
|
[Fact]
|
|
[AutoInitShutdown]
|
|
public void TestBottomlessTreeView_MaxDepth_3 ()
|
|
{
|
|
TreeView<string> tv = new () { Width = 20, Height = 10 };
|
|
|
|
tv.TreeBuilder = new DelegateTreeBuilder<string> (
|
|
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]
|
|
[AutoInitShutdown]
|
|
public void TestBottomlessTreeView_MaxDepth_5 ()
|
|
{
|
|
TreeView<string> tv = new () { Width = 20, Height = 10 };
|
|
|
|
tv.TreeBuilder = new DelegateTreeBuilder<string> (
|
|
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]
|
|
[AutoInitShutdown]
|
|
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]
|
|
[AutoInitShutdown]
|
|
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]
|
|
[AutoInitShutdown]
|
|
public void TestTreeView_DrawLineEvent ()
|
|
{
|
|
var tv = new TreeView { Width = 20, Height = 10 };
|
|
|
|
List<DrawTreeViewLineEventArgs<ITreeNode>> 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].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ());
|
|
Assert.Equal ("│ ├─leaf 1", eventArgs [1].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ());
|
|
Assert.Equal ("│ └─leaf 2", eventArgs [2].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ());
|
|
Assert.Equal ("└─root two", eventArgs [3].RuneCells.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]
|
|
[AutoInitShutdown]
|
|
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]
|
|
[AutoInitShutdown]
|
|
public void TestTreeView_DrawLineEvent_WithScrolling ()
|
|
{
|
|
var tv = new TreeView { Width = 20, Height = 10 };
|
|
|
|
List<DrawTreeViewLineEventArgs<ITreeNode>> 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].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ());
|
|
Assert.Equal ("─leaf 2", eventArgs [1].RuneCells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ());
|
|
Assert.Equal ("oot two", eventArgs [2].RuneCells.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]
|
|
[AutoInitShutdown]
|
|
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<ITreeNode> 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]
|
|
[AutoInitShutdown]
|
|
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.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 ());
|
|
}
|
|
|
|
private void InitFakeDriver ()
|
|
{
|
|
var driver = new FakeDriver ();
|
|
Application.Init (driver);
|
|
driver.Init ();
|
|
}
|
|
|
|
/// <summary>Test object which considers for equality only <see cref="Name"/></summary>
|
|
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<object> CreateTree () { return CreateTree (out _, out _, out _); }
|
|
|
|
private TreeView<object> CreateTree (out Factory factory1, out Car car1, out Car car2)
|
|
{
|
|
car1 = new Car ();
|
|
car2 = new Car ();
|
|
|
|
factory1 = new Factory { Cars = new [] { car1, car2 } };
|
|
|
|
TreeView<object> tree = new (new DelegateTreeBuilder<object> (s => s is Factory f ? f.Cars : null));
|
|
tree.AddObject (factory1);
|
|
|
|
return tree;
|
|
}
|
|
|
|
#endregion
|
|
}
|