mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-27 08:17:53 +01:00
* Consider width2 chars that are not IsBmp
* Apply same fix in WindowsDriver
* Explicitly use type of local variable
* Revert changes to WindowsDriver
* Assume we are running in a terminal that supports true color by default unless user explicitly forces 16
* Switch to SetAttribute and WriteConsole instead of WriteConsoleOutput for 16 color mode
* Fix some cursor issues (WIP)
* Remove concept of 'dirty rows' from v2 as its never actually used
* Remove damageRegion as it does nothing
* Make string builder to console writing simpler
* Radically simplify Write method
* Simplify conditional logic
* Simplify restoring cursor position
* Reference local variable for console buffer
* Reduce calls to ConsoleWrite by accumulating till attribute changes
* When resizing v2 16 color mode on windows, recreate the back buffer to match its size
* Fixes for VTS enabled
* Fix _lastSize never being assigned
* Fixes VTS for Force16Colors
* Fixes force16Colors in VTS
* Fixes escape sequences always echoing in non-VTS
* Force Force16Colors in non-VTS. It have a bug in adding a newline in the last line
* WIP Add base class for NetOutput
* Abstract away how we change attribute
* WIP - Make WindowsOutput use base class
* WIP working to fix set cursor position
* Remove commented out code
* Fixes legacy output mode
* Fixes size with no alt buffer supported on VTS and size restore after maximized.
* Fix set cursor which also fixes the broken surrogate pairs
* Add force parameter
* Fixes an issue that only happens with Windows Terminal when paste surrogate pairs by press Ctrl+V
* In Windows escape sequences must be sent during the lifetime of the console which is created in input handle
* Ensure flush the input buffer before reset the console
* Flush input buffer before reset console in v2win
* Fixes issue in v2net not being refreshing the menu bar at start
* Only force layout and draw on size changed.
* Fix v2net issue not draw first line by forcing set cursor position
* Set _lastCursorPosition nullable and remove bool force from set cursor position
* Remove force parameter
* Add v2 version of fake driver attribute
* Make direct replacement and wire up window resizing events
* Update casts to use V2 fake driver instead
* Adjust interfaces to expose less internals
* Fix not raising iteration event in v2
* WIP investigate what it takes to do resize and redraw using TextAlignment_Centered as example
* Sketch adding component factory
* Create relevant fake component factories
* Add window size monitor into factory
* Fake size monitor injecting
* Add helper for faking console resize in AutoInitShutdown tests
* Fix size setting in FakeDriverV2
* Switch to new method
* Fix IsLegacy becoming false when using blank constructor
* Fix for Ready not being raised when showing same top twice also fixes garbage collection issue if running millions of top levels
* Fix tests
* Remove auto init
* Restore conditional compilation stuff
* Restore 'if running unit tests' logic
* Check only for the output being specific classes for the suppression
* Fix ShadowView blowing up with index out of bounds error
* Fix resize in fluent tests
* Fix for people using Iteration call directly
* Fix more calls to iteration to use
AutoInitShutdownAttribute.RunIteration ();
* Add comment
* Remove assumption that Run with prior view not disposed should throw
* Fix timings in Dialog_Opened_From_Another_Dialog
* Fix Zero_Buttons_Works
* Standardize and fix Button_IsDefault_True_Return_His_Index_On_Accepting
* Fix iteration counts on MessageBoxTests
* Fix WizartTests and DrawTests_Ruler
* Implement SendKeys into ConsoleDriverFacade
* Fix SendKeys in console driver facade such that FileDialogTests works
Fix when Clip is null in popover
* Add missing dispose call to test
* Fix support for Esc in facade SendKeys
* Fix AutocompleteTests
* Fix various tests
* Replace LayoutAndDraw with run iteration
* Fix draw issues
* fix draw order
* Fix run iteration calls
* Fix unit tests
* Fix SendKeys in facade.
* Manipulate upper and lower cases.
* Add IsValidInput method to the interface.
* Fix SendKeys scenario
* Fixes surrogate pairs in the label
* Make tests more sensible - they are testing draw functionality. Callbacks do not need to happen in Iteration method
* Fix tests and harden cleanup in AutoInitShutdownAttribute v2 lifecycle dispose
* Delete extra create input call
* Fix mocks and order of exceptions thrown in Run when things are not initialized
* Revert use of `MapConsoleKeyInfoToKeyCode`
* Ignore casing as it is not what test is really about
* Clear application top and top levels before each auto init shutdown test
* Fix for unstable tests
* Restore actually working SendKeys code
* option to pass logger in fluent ctor
* restore ToArray
* Fix SendKeys method and add extension to unit test
* Leverage the EscSeqUtils.MapConsoleKeyInfo method to avoid duplicate code
* Remove unnecessary hack
* Using only KeyCode for rKeys
* Recover modifier keys in surrogate pairs
* Reformat
* Remove iteration limit for benchmarking in v2
* remove iteration delay to identify bugs
* Remove nudge to unique key and make Then run on UI thread
* fix fluid assertions
* Ensure UI operations all happen on UI thread
* Add explicit error for WaitIteration during an invoke
* Remove timeout added for debug
* Catch failing asserts better
* Fix screenshot
* Fix null ref
* Fix race condition in processing input
* Test fixing
* Standardize asserts
* Remove calls to layout and draw, remove pointless lock and enable reading Cancelled from Dialog even if it is disposed
* fix bad merge
* Make logs access threadsafe
* add extra wait to remove race between iteration end and assert
* Code cleanup
* Remove test for crash on access Cancelled after dispose as this is no longer a restriction
* Change resize console to run on UI thread - fixing race condition with redrawing
* Restore original frame rate after test
* Restore nudge to unique key
* Code Cleanup
* Fix for cascading failures when an assert fails in a specific test
* fix for bad merge
* Address PR feedback
* Move classes to seperate files and add xmldoc
* xml doc warnings
* More xml comments docs
* Fix spelling
---------
Co-authored-by: BDisp <bd.bdisp@gmail.com>
1161 lines
34 KiB
C#
1161 lines
34 KiB
C#
using System.Collections;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using Moq;
|
|
using UnitTests;
|
|
using Xunit.Abstractions;
|
|
|
|
namespace Terminal.Gui.ViewsTests;
|
|
|
|
public class ListViewTests (ITestOutputHelper output)
|
|
{
|
|
[Fact]
|
|
public void Constructors_Defaults ()
|
|
{
|
|
var lv = new ListView ();
|
|
Assert.Null (lv.Source);
|
|
Assert.True (lv.CanFocus);
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
|
|
lv = new () { Source = new ListWrapper<string> (["One", "Two", "Three"]) };
|
|
Assert.NotNull (lv.Source);
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
|
|
lv = new () { Source = new NewListDataSource () };
|
|
Assert.NotNull (lv.Source);
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
|
|
lv = new ()
|
|
{
|
|
Y = 1, Width = 10, Height = 20, Source = new ListWrapper<string> (["One", "Two", "Three"])
|
|
};
|
|
Assert.NotNull (lv.Source);
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
Assert.Equal (new (0, 1, 10, 20), lv.Frame);
|
|
|
|
lv = new () { Y = 1, Width = 10, Height = 20, Source = new NewListDataSource () };
|
|
Assert.NotNull (lv.Source);
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
Assert.Equal (new (0, 1, 10, 20), lv.Frame);
|
|
}
|
|
|
|
[Fact]
|
|
[AutoInitShutdown]
|
|
public void Ensures_Visibility_SelectedItem_On_MoveDown_And_MoveUp ()
|
|
{
|
|
ObservableCollection<string> source = [];
|
|
|
|
for (var i = 0; i < 20; i++)
|
|
{
|
|
source.Add ($"Line{i}");
|
|
}
|
|
|
|
var lv = new ListView { Width = Dim.Fill (), Height = Dim.Fill (), Source = new ListWrapper<string> (source) };
|
|
var win = new Window ();
|
|
win.Add (lv);
|
|
var top = new Toplevel ();
|
|
top.Add (win);
|
|
RunState rs = Application.Begin (top);
|
|
AutoInitShutdownAttribute.FakeResize(new Size(12, 12));
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
┌──────────┐
|
|
│Line0 │
|
|
│Line1 │
|
|
│Line2 │
|
|
│Line3 │
|
|
│Line4 │
|
|
│Line5 │
|
|
│Line6 │
|
|
│Line7 │
|
|
│Line8 │
|
|
│Line9 │
|
|
└──────────┘",
|
|
output
|
|
);
|
|
|
|
Assert.True (lv.ScrollVertical (10));
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
┌──────────┐
|
|
│Line10 │
|
|
│Line11 │
|
|
│Line12 │
|
|
│Line13 │
|
|
│Line14 │
|
|
│Line15 │
|
|
│Line16 │
|
|
│Line17 │
|
|
│Line18 │
|
|
│Line19 │
|
|
└──────────┘",
|
|
output
|
|
);
|
|
|
|
Assert.True (lv.MoveDown ());
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
Assert.Equal (0, lv.SelectedItem);
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
┌──────────┐
|
|
│Line0 │
|
|
│Line1 │
|
|
│Line2 │
|
|
│Line3 │
|
|
│Line4 │
|
|
│Line5 │
|
|
│Line6 │
|
|
│Line7 │
|
|
│Line8 │
|
|
│Line9 │
|
|
└──────────┘",
|
|
output
|
|
);
|
|
|
|
Assert.True (lv.MoveEnd ());
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
Assert.Equal (19, lv.SelectedItem);
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
┌──────────┐
|
|
│Line10 │
|
|
│Line11 │
|
|
│Line12 │
|
|
│Line13 │
|
|
│Line14 │
|
|
│Line15 │
|
|
│Line16 │
|
|
│Line17 │
|
|
│Line18 │
|
|
│Line19 │
|
|
└──────────┘",
|
|
output
|
|
);
|
|
|
|
Assert.True (lv.ScrollVertical (-20));
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
Assert.Equal (19, lv.SelectedItem);
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
┌──────────┐
|
|
│Line0 │
|
|
│Line1 │
|
|
│Line2 │
|
|
│Line3 │
|
|
│Line4 │
|
|
│Line5 │
|
|
│Line6 │
|
|
│Line7 │
|
|
│Line8 │
|
|
│Line9 │
|
|
└──────────┘",
|
|
output
|
|
);
|
|
|
|
Assert.True (lv.MoveDown ());
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
Assert.Equal (19, lv.SelectedItem);
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
┌──────────┐
|
|
│Line10 │
|
|
│Line11 │
|
|
│Line12 │
|
|
│Line13 │
|
|
│Line14 │
|
|
│Line15 │
|
|
│Line16 │
|
|
│Line17 │
|
|
│Line18 │
|
|
│Line19 │
|
|
└──────────┘",
|
|
output
|
|
);
|
|
|
|
Assert.True (lv.ScrollVertical (-20));
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
Assert.Equal (19, lv.SelectedItem);
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
┌──────────┐
|
|
│Line0 │
|
|
│Line1 │
|
|
│Line2 │
|
|
│Line3 │
|
|
│Line4 │
|
|
│Line5 │
|
|
│Line6 │
|
|
│Line7 │
|
|
│Line8 │
|
|
│Line9 │
|
|
└──────────┘",
|
|
output
|
|
);
|
|
|
|
Assert.True (lv.MoveDown ());
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
Assert.Equal (19, lv.SelectedItem);
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
┌──────────┐
|
|
│Line10 │
|
|
│Line11 │
|
|
│Line12 │
|
|
│Line13 │
|
|
│Line14 │
|
|
│Line15 │
|
|
│Line16 │
|
|
│Line17 │
|
|
│Line18 │
|
|
│Line19 │
|
|
└──────────┘",
|
|
output
|
|
);
|
|
|
|
Assert.True (lv.MoveHome ());
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
Assert.Equal (0, lv.SelectedItem);
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
┌──────────┐
|
|
│Line0 │
|
|
│Line1 │
|
|
│Line2 │
|
|
│Line3 │
|
|
│Line4 │
|
|
│Line5 │
|
|
│Line6 │
|
|
│Line7 │
|
|
│Line8 │
|
|
│Line9 │
|
|
└──────────┘",
|
|
output
|
|
);
|
|
|
|
Assert.True (lv.ScrollVertical (20));
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
Assert.Equal (0, lv.SelectedItem);
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
┌──────────┐
|
|
│Line19 │
|
|
│ │
|
|
│ │
|
|
│ │
|
|
│ │
|
|
│ │
|
|
│ │
|
|
│ │
|
|
│ │
|
|
│ │
|
|
└──────────┘",
|
|
output
|
|
);
|
|
|
|
Assert.True (lv.MoveUp ());
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
Assert.Equal (0, lv.SelectedItem);
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
┌──────────┐
|
|
│Line0 │
|
|
│Line1 │
|
|
│Line2 │
|
|
│Line3 │
|
|
│Line4 │
|
|
│Line5 │
|
|
│Line6 │
|
|
│Line7 │
|
|
│Line8 │
|
|
│Line9 │
|
|
└──────────┘",
|
|
output
|
|
);
|
|
top.Dispose ();
|
|
}
|
|
|
|
[Fact]
|
|
[AutoInitShutdown]
|
|
public void EnsureSelectedItemVisible_SelectedItem ()
|
|
{
|
|
ObservableCollection<string> source = [];
|
|
|
|
for (var i = 0; i < 10; i++)
|
|
{
|
|
source.Add ($"Item {i}");
|
|
}
|
|
|
|
var lv = new ListView { Width = 10, Height = 5, Source = new ListWrapper<string> (source) };
|
|
var top = new Toplevel ();
|
|
top.Add (lv);
|
|
Application.Begin (top);
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
Item 0
|
|
Item 1
|
|
Item 2
|
|
Item 3
|
|
Item 4",
|
|
output
|
|
);
|
|
|
|
// EnsureSelectedItemVisible is auto enabled on the OnSelectedChanged
|
|
lv.SelectedItem = 6;
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
Item 2
|
|
Item 3
|
|
Item 4
|
|
Item 5
|
|
Item 6",
|
|
output
|
|
);
|
|
top.Dispose ();
|
|
}
|
|
|
|
[Fact]
|
|
[AutoInitShutdown]
|
|
public void EnsureSelectedItemVisible_Top ()
|
|
{
|
|
ObservableCollection<string> source = ["First", "Second"];
|
|
var lv = new ListView { Width = Dim.Fill (), Height = 1, Source = new ListWrapper<string> (source) };
|
|
lv.SelectedItem = 1;
|
|
var top = new Toplevel ();
|
|
top.Add (lv);
|
|
Application.Begin (top);
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
|
|
Assert.Equal ("Second ", GetContents (0));
|
|
Assert.Equal (new (' ', 7), GetContents (1));
|
|
|
|
lv.MoveUp ();
|
|
lv.Draw ();
|
|
|
|
Assert.Equal ("First ", GetContents (0));
|
|
Assert.Equal (new (' ', 7), GetContents (1));
|
|
|
|
string GetContents (int line)
|
|
{
|
|
var item = "";
|
|
|
|
for (var i = 0; i < 7; i++)
|
|
{
|
|
item += Application.Driver?.Contents [line, i].Rune;
|
|
}
|
|
|
|
return item;
|
|
}
|
|
top.Dispose ();
|
|
}
|
|
|
|
[Fact]
|
|
public void KeyBindings_Command ()
|
|
{
|
|
ObservableCollection<string> source = ["One", "Two", "Three"];
|
|
var lv = new ListView { Height = 2, AllowsMarking = true, Source = new ListWrapper<string> (source) };
|
|
lv.BeginInit ();
|
|
lv.EndInit ();
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
Assert.True (lv.NewKeyDownEvent (Key.CursorDown));
|
|
Assert.Equal (0, lv.SelectedItem);
|
|
Assert.True (lv.NewKeyDownEvent (Key.CursorUp));
|
|
Assert.Equal (0, lv.SelectedItem);
|
|
Assert.True (lv.NewKeyDownEvent (Key.PageDown));
|
|
Assert.Equal (2, lv.SelectedItem);
|
|
Assert.Equal (2, lv.TopItem);
|
|
Assert.True (lv.NewKeyDownEvent (Key.PageUp));
|
|
Assert.Equal (0, lv.SelectedItem);
|
|
Assert.Equal (0, lv.TopItem);
|
|
Assert.False (lv.Source.IsMarked (lv.SelectedItem));
|
|
Assert.True (lv.NewKeyDownEvent (Key.Space));
|
|
Assert.True (lv.Source.IsMarked (lv.SelectedItem));
|
|
var opened = false;
|
|
lv.OpenSelectedItem += (s, _) => opened = true;
|
|
Assert.True (lv.NewKeyDownEvent (Key.Enter));
|
|
Assert.True (opened);
|
|
Assert.True (lv.NewKeyDownEvent (Key.End));
|
|
Assert.Equal (2, lv.SelectedItem);
|
|
Assert.True (lv.NewKeyDownEvent (Key.Home));
|
|
Assert.Equal (0, lv.SelectedItem);
|
|
}
|
|
|
|
[Fact]
|
|
public void HotKey_Command_SetsFocus ()
|
|
{
|
|
var view = new ListView ();
|
|
|
|
view.CanFocus = true;
|
|
Assert.False (view.HasFocus);
|
|
view.InvokeCommand (Command.HotKey);
|
|
Assert.True (view.HasFocus);
|
|
}
|
|
|
|
[Fact]
|
|
public void HotKey_Command_Does_Not_Accept ()
|
|
{
|
|
var listView = new ListView ();
|
|
var accepted = false;
|
|
|
|
listView.Accepting += OnAccepted;
|
|
listView.InvokeCommand (Command.HotKey);
|
|
|
|
Assert.False (accepted);
|
|
|
|
return;
|
|
|
|
void OnAccepted (object sender, CommandEventArgs e) { accepted = true; }
|
|
}
|
|
|
|
[Fact]
|
|
public void Accept_Command_Accepts_and_Opens_Selected_Item ()
|
|
{
|
|
ObservableCollection<string> source = ["One", "Two", "Three"];
|
|
var listView = new ListView { Source = new ListWrapper<string> (source) };
|
|
listView.SelectedItem = 0;
|
|
|
|
var accepted = false;
|
|
var opened = false;
|
|
var selectedValue = string.Empty;
|
|
|
|
listView.Accepting += Accepted;
|
|
listView.OpenSelectedItem += OpenSelectedItem;
|
|
|
|
listView.InvokeCommand (Command.Accept);
|
|
|
|
Assert.True (accepted);
|
|
Assert.True (opened);
|
|
Assert.Equal (source [0], selectedValue);
|
|
|
|
return;
|
|
|
|
void OpenSelectedItem (object sender, ListViewItemEventArgs e)
|
|
{
|
|
opened = true;
|
|
selectedValue = e.Value.ToString ();
|
|
}
|
|
|
|
void Accepted (object sender, CommandEventArgs e) { accepted = true; }
|
|
}
|
|
|
|
[Fact]
|
|
public void Accept_Cancel_Event_Prevents_OpenSelectedItem ()
|
|
{
|
|
ObservableCollection<string> source = ["One", "Two", "Three"];
|
|
var listView = new ListView { Source = new ListWrapper<string> (source) };
|
|
listView.SelectedItem = 0;
|
|
|
|
var accepted = false;
|
|
var opened = false;
|
|
var selectedValue = string.Empty;
|
|
|
|
listView.Accepting += Accepted;
|
|
listView.OpenSelectedItem += OpenSelectedItem;
|
|
|
|
listView.InvokeCommand (Command.Accept);
|
|
|
|
Assert.True (accepted);
|
|
Assert.False (opened);
|
|
Assert.Equal (string.Empty, selectedValue);
|
|
|
|
return;
|
|
|
|
void OpenSelectedItem (object sender, ListViewItemEventArgs e)
|
|
{
|
|
opened = true;
|
|
selectedValue = e.Value.ToString ();
|
|
}
|
|
|
|
void Accepted (object sender, CommandEventArgs e)
|
|
{
|
|
accepted = true;
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests that when none of the Commands in a chained keybinding are possible the
|
|
/// <see cref="View.NewKeyDownEvent"/> returns the appropriate result
|
|
/// </summary>
|
|
[Fact]
|
|
public void ListViewProcessKeyReturnValue_WithMultipleCommands ()
|
|
{
|
|
var lv = new ListView { Source = new ListWrapper<string> (["One", "Two", "Three", "Four"]) };
|
|
|
|
Assert.NotNull (lv.Source);
|
|
|
|
// first item should be deselected by default
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
|
|
// bind shift down to move down twice in control
|
|
lv.KeyBindings.Add (Key.CursorDown.WithShift, Command.Down, Command.Down);
|
|
|
|
Key ev = Key.CursorDown.WithShift;
|
|
|
|
Assert.True (lv.NewKeyDownEvent (ev), "The first time we move down 2 it should be possible");
|
|
|
|
// After moving down twice from -1 we should be at 'Two'
|
|
Assert.Equal (1, lv.SelectedItem);
|
|
|
|
// clear the items
|
|
lv.SetSource<string> (null);
|
|
|
|
// Press key combo again - return should be false this time as none of the Commands are allowable
|
|
Assert.False (lv.NewKeyDownEvent (ev), "We cannot move down so will not respond to this");
|
|
}
|
|
|
|
[Fact]
|
|
public void AllowsMarking_True_SpaceWithShift_SelectsThenDown ()
|
|
{
|
|
var lv = new ListView { Source = new ListWrapper<string> (["One", "Two", "Three"]) };
|
|
lv.AllowsMarking = true;
|
|
|
|
Assert.NotNull (lv.Source);
|
|
|
|
// first item should be deselected by default
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
|
|
// nothing is ticked
|
|
Assert.False (lv.Source.IsMarked (0));
|
|
Assert.False (lv.Source.IsMarked (1));
|
|
Assert.False (lv.Source.IsMarked (2));
|
|
|
|
// view should indicate that it has accepted and consumed the event
|
|
Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
|
|
|
|
// first item should now be selected
|
|
Assert.Equal (0, lv.SelectedItem);
|
|
|
|
// none of the items should be ticked
|
|
Assert.False (lv.Source.IsMarked (0));
|
|
Assert.False (lv.Source.IsMarked (1));
|
|
Assert.False (lv.Source.IsMarked (2));
|
|
|
|
// Press key combo again
|
|
Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
|
|
|
|
// second item should now be selected
|
|
Assert.Equal (1, lv.SelectedItem);
|
|
|
|
// first item only should be ticked
|
|
Assert.True (lv.Source.IsMarked (0));
|
|
Assert.False (lv.Source.IsMarked (1));
|
|
Assert.False (lv.Source.IsMarked (2));
|
|
|
|
// Press key combo again
|
|
Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
|
|
Assert.Equal (2, lv.SelectedItem);
|
|
Assert.True (lv.Source.IsMarked (0));
|
|
Assert.True (lv.Source.IsMarked (1));
|
|
Assert.False (lv.Source.IsMarked (2));
|
|
|
|
// Press key combo again
|
|
Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
|
|
Assert.Equal (2, lv.SelectedItem); // cannot move down any further
|
|
Assert.True (lv.Source.IsMarked (0));
|
|
Assert.True (lv.Source.IsMarked (1));
|
|
Assert.True (lv.Source.IsMarked (2)); // but can toggle marked
|
|
|
|
// Press key combo again
|
|
Assert.True (lv.NewKeyDownEvent (Key.Space.WithShift));
|
|
Assert.Equal (2, lv.SelectedItem); // cannot move down any further
|
|
Assert.True (lv.Source.IsMarked (0));
|
|
Assert.True (lv.Source.IsMarked (1));
|
|
Assert.False (lv.Source.IsMarked (2)); // untoggle toggle marked
|
|
}
|
|
|
|
[Fact]
|
|
public void ListWrapper_StartsWith ()
|
|
{
|
|
var lw = new ListWrapper<string> (["One", "Two", "Three"]);
|
|
|
|
Assert.Equal (1, lw.StartsWith ("t"));
|
|
Assert.Equal (1, lw.StartsWith ("tw"));
|
|
Assert.Equal (2, lw.StartsWith ("th"));
|
|
Assert.Equal (1, lw.StartsWith ("T"));
|
|
Assert.Equal (1, lw.StartsWith ("TW"));
|
|
Assert.Equal (2, lw.StartsWith ("TH"));
|
|
|
|
lw = new (["One", "Two", "Three"]);
|
|
|
|
Assert.Equal (1, lw.StartsWith ("t"));
|
|
Assert.Equal (1, lw.StartsWith ("tw"));
|
|
Assert.Equal (2, lw.StartsWith ("th"));
|
|
Assert.Equal (1, lw.StartsWith ("T"));
|
|
Assert.Equal (1, lw.StartsWith ("TW"));
|
|
Assert.Equal (2, lw.StartsWith ("TH"));
|
|
}
|
|
|
|
[Fact]
|
|
public void OnEnter_Does_Not_Throw_Exception ()
|
|
{
|
|
var lv = new ListView ();
|
|
var top = new View ();
|
|
top.Add (lv);
|
|
Exception exception = Record.Exception (() => lv.SetFocus ());
|
|
Assert.Null (exception);
|
|
}
|
|
|
|
[Fact]
|
|
[AutoInitShutdown]
|
|
public void RowRender_Event ()
|
|
{
|
|
var rendered = false;
|
|
ObservableCollection<string> source = ["one", "two", "three"];
|
|
var lv = new ListView { Width = Dim.Fill (), Height = Dim.Fill () };
|
|
lv.RowRender += (s, _) => rendered = true;
|
|
var top = new Toplevel ();
|
|
top.Add (lv);
|
|
Application.Begin (top);
|
|
Assert.False (rendered);
|
|
|
|
lv.SetSource (source);
|
|
lv.Draw ();
|
|
Assert.True (rendered);
|
|
top.Dispose ();
|
|
}
|
|
|
|
[Fact]
|
|
public void SelectedItem_Get_Set ()
|
|
{
|
|
var lv = new ListView { Source = new ListWrapper<string> (["One", "Two", "Three"]) };
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
Assert.Throws<ArgumentException> (() => lv.SelectedItem = 3);
|
|
Exception exception = Record.Exception (() => lv.SelectedItem = -1);
|
|
Assert.Null (exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetSource_Preserves_ListWrapper_Instance_If_Not_Null ()
|
|
{
|
|
var lv = new ListView { Source = new ListWrapper<string> (["One", "Two"]) };
|
|
|
|
Assert.NotNull (lv.Source);
|
|
|
|
lv.SetSource<string> (null);
|
|
Assert.NotNull (lv.Source);
|
|
|
|
lv.Source = null;
|
|
Assert.Null (lv.Source);
|
|
|
|
lv = new () { Source = new ListWrapper<string> (["One", "Two"]) };
|
|
Assert.NotNull (lv.Source);
|
|
|
|
lv.SetSourceAsync<string> (null);
|
|
Assert.NotNull (lv.Source);
|
|
}
|
|
|
|
[Fact]
|
|
public void SettingEmptyKeybindingThrows ()
|
|
{
|
|
var lv = new ListView { Source = new ListWrapper<string> (["One", "Two", "Three"]) };
|
|
Assert.Throws<ArgumentException> (() => lv.KeyBindings.Add (Key.Space));
|
|
}
|
|
|
|
private class NewListDataSource : IListDataSource
|
|
{
|
|
#pragma warning disable CS0067
|
|
/// <inheritdoc />
|
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
|
#pragma warning restore CS0067
|
|
|
|
public int Count => 0;
|
|
public int Length => 0;
|
|
|
|
public bool SuspendCollectionChangedEvent { get => throw new NotImplementedException (); set => throw new NotImplementedException (); }
|
|
|
|
public bool IsMarked (int item) { throw new NotImplementedException (); }
|
|
|
|
public void Render (
|
|
ListView container,
|
|
bool selected,
|
|
int item,
|
|
int col,
|
|
int line,
|
|
int width,
|
|
int start = 0
|
|
)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
public void SetMark (int item, bool value) { throw new NotImplementedException (); }
|
|
public IList ToList () { return new List<string> { "One", "Two", "Three" }; }
|
|
|
|
public void Dispose ()
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
[AutoInitShutdown]
|
|
public void Clicking_On_Border_Is_Ignored ()
|
|
{
|
|
var selected = "";
|
|
|
|
var lv = new ListView
|
|
{
|
|
Height = 5,
|
|
Width = 7,
|
|
BorderStyle = LineStyle.Single
|
|
};
|
|
lv.SetSource (["One", "Two", "Three", "Four"]);
|
|
lv.SelectedItemChanged += (s, e) => selected = e.Value.ToString ();
|
|
var top = new Toplevel ();
|
|
top.Add (lv);
|
|
Application.Begin (top);
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
|
|
Assert.Equal (new (1), lv.Border.Thickness);
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
Assert.Equal ("", lv.Text);
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
┌─────┐
|
|
│One │
|
|
│Two │
|
|
│Three│
|
|
└─────┘",
|
|
output);
|
|
|
|
Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 0), Flags = MouseFlags.Button1Clicked });
|
|
Assert.Equal ("", selected);
|
|
Assert.Equal (-1, lv.SelectedItem);
|
|
|
|
Application.RaiseMouseEvent (
|
|
new ()
|
|
{
|
|
ScreenPosition = new (1, 1), Flags = MouseFlags.Button1Clicked
|
|
});
|
|
Assert.Equal ("One", selected);
|
|
Assert.Equal (0, lv.SelectedItem);
|
|
|
|
Application.RaiseMouseEvent (
|
|
new ()
|
|
{
|
|
ScreenPosition = new (1, 2), Flags = MouseFlags.Button1Clicked
|
|
});
|
|
Assert.Equal ("Two", selected);
|
|
Assert.Equal (1, lv.SelectedItem);
|
|
|
|
Application.RaiseMouseEvent (
|
|
new ()
|
|
{
|
|
ScreenPosition = new (1, 3), Flags = MouseFlags.Button1Clicked
|
|
});
|
|
Assert.Equal ("Three", selected);
|
|
Assert.Equal (2, lv.SelectedItem);
|
|
|
|
Application.RaiseMouseEvent (
|
|
new ()
|
|
{
|
|
ScreenPosition = new (1, 4), Flags = MouseFlags.Button1Clicked
|
|
});
|
|
Assert.Equal ("Three", selected);
|
|
Assert.Equal (2, lv.SelectedItem);
|
|
top.Dispose ();
|
|
}
|
|
|
|
[Fact]
|
|
[AutoInitShutdown]
|
|
public void LeftItem_TopItem_Tests ()
|
|
{
|
|
ObservableCollection<string> source = [];
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
source.Add ($"Item {i}");
|
|
}
|
|
|
|
var lv = new ListView
|
|
{
|
|
X = 1,
|
|
Source = new ListWrapper<string> (source)
|
|
};
|
|
lv.Height = lv.Source.Count;
|
|
lv.Width = lv.MaxLength;
|
|
var top = new Toplevel ();
|
|
top.Add (lv);
|
|
Application.Begin (top);
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
Item 0
|
|
Item 1
|
|
Item 2
|
|
Item 3
|
|
Item 4",
|
|
output);
|
|
|
|
lv.LeftItem = 1;
|
|
lv.TopItem = 1;
|
|
AutoInitShutdownAttribute.RunIteration ();
|
|
|
|
DriverAssert.AssertDriverContentsWithFrameAre (
|
|
@"
|
|
tem 1
|
|
tem 2
|
|
tem 3
|
|
tem 4",
|
|
output);
|
|
top.Dispose ();
|
|
}
|
|
|
|
[Fact]
|
|
public void CollectionChanged_Event ()
|
|
{
|
|
var added = 0;
|
|
var removed = 0;
|
|
ObservableCollection<string> source = [];
|
|
var lv = new ListView { Source = new ListWrapper<string> (source) };
|
|
|
|
lv.CollectionChanged += (sender, args) =>
|
|
{
|
|
if (args.Action == NotifyCollectionChangedAction.Add)
|
|
{
|
|
added++;
|
|
}
|
|
else if (args.Action == NotifyCollectionChangedAction.Remove)
|
|
{
|
|
removed++;
|
|
}
|
|
};
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
source.Add ($"Item{i}");
|
|
}
|
|
Assert.Equal (3, added);
|
|
Assert.Equal (0, removed);
|
|
|
|
added = 0;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
source.Remove (source [0]);
|
|
}
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (3, removed);
|
|
Assert.Empty (source);
|
|
}
|
|
|
|
[Fact]
|
|
public void CollectionChanged_Event_Is_Only_Subscribed_Once ()
|
|
{
|
|
var added = 0;
|
|
var removed = 0;
|
|
var otherActions = 0;
|
|
IList<string> source1 = [];
|
|
var lv = new ListView { Source = new ListWrapper<string> (new (source1)) };
|
|
|
|
lv.CollectionChanged += (sender, args) =>
|
|
{
|
|
if (args.Action == NotifyCollectionChangedAction.Add)
|
|
{
|
|
added++;
|
|
}
|
|
else if (args.Action == NotifyCollectionChangedAction.Remove)
|
|
{
|
|
removed++;
|
|
}
|
|
else
|
|
{
|
|
otherActions++;
|
|
}
|
|
};
|
|
|
|
ObservableCollection<string> source2 = [];
|
|
lv.Source = new ListWrapper<string> (source2);
|
|
ObservableCollection<string> source3 = [];
|
|
lv.Source = new ListWrapper<string> (source3);
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (0, removed);
|
|
Assert.Equal (0, otherActions);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
source1.Add ($"Item{i}");
|
|
source2.Add ($"Item{i}");
|
|
source3.Add ($"Item{i}");
|
|
}
|
|
Assert.Equal (3, added);
|
|
Assert.Equal (0, removed);
|
|
Assert.Equal (0, otherActions);
|
|
|
|
added = 0;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
source1.Remove (source1 [0]);
|
|
source2.Remove (source2 [0]);
|
|
source3.Remove (source3 [0]);
|
|
}
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (3, removed);
|
|
Assert.Equal (0, otherActions);
|
|
Assert.Empty (source1);
|
|
Assert.Empty (source2);
|
|
Assert.Empty (source3);
|
|
}
|
|
|
|
[Fact]
|
|
public void CollectionChanged_Event_UnSubscribe_Previous_If_New_Is_Null ()
|
|
{
|
|
var added = 0;
|
|
var removed = 0;
|
|
var otherActions = 0;
|
|
ObservableCollection<string> source1 = [];
|
|
var lv = new ListView { Source = new ListWrapper<string> (source1) };
|
|
|
|
lv.CollectionChanged += (sender, args) =>
|
|
{
|
|
if (args.Action == NotifyCollectionChangedAction.Add)
|
|
{
|
|
added++;
|
|
}
|
|
else if (args.Action == NotifyCollectionChangedAction.Remove)
|
|
{
|
|
removed++;
|
|
}
|
|
else
|
|
{
|
|
otherActions++;
|
|
}
|
|
};
|
|
|
|
lv.Source = new ListWrapper<string> (null);
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (0, removed);
|
|
Assert.Equal (0, otherActions);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
source1.Add ($"Item{i}");
|
|
}
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (0, removed);
|
|
Assert.Equal (0, otherActions);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
source1.Remove (source1 [0]);
|
|
}
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (0, removed);
|
|
Assert.Equal (0, otherActions);
|
|
Assert.Empty (source1);
|
|
}
|
|
|
|
[Fact]
|
|
public void ListWrapper_CollectionChanged_Event_Is_Only_Subscribed_Once ()
|
|
{
|
|
var added = 0;
|
|
var removed = 0;
|
|
var otherActions = 0;
|
|
ObservableCollection<string> source1 = [];
|
|
ListWrapper<string> lw = new (source1);
|
|
|
|
lw.CollectionChanged += (sender, args) =>
|
|
{
|
|
if (args.Action == NotifyCollectionChangedAction.Add)
|
|
{
|
|
added++;
|
|
}
|
|
else if (args.Action == NotifyCollectionChangedAction.Remove)
|
|
{
|
|
removed++;
|
|
}
|
|
else
|
|
{
|
|
otherActions++;
|
|
}
|
|
};
|
|
|
|
ObservableCollection<string> source2 = [];
|
|
lw = new (source2);
|
|
ObservableCollection<string> source3 = [];
|
|
lw = new (source3);
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (0, removed);
|
|
Assert.Equal (0, otherActions);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
source1.Add ($"Item{i}");
|
|
source2.Add ($"Item{i}");
|
|
source3.Add ($"Item{i}");
|
|
}
|
|
|
|
Assert.Equal (3, added);
|
|
Assert.Equal (0, removed);
|
|
Assert.Equal (0, otherActions);
|
|
|
|
added = 0;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
source1.Remove (source1 [0]);
|
|
source2.Remove (source2 [0]);
|
|
source3.Remove (source3 [0]);
|
|
}
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (3, removed);
|
|
Assert.Equal (0, otherActions);
|
|
Assert.Empty (source1);
|
|
Assert.Empty (source2);
|
|
Assert.Empty (source3);
|
|
}
|
|
|
|
[Fact]
|
|
public void ListWrapper_CollectionChanged_Event_UnSubscribe_Previous_Is_Disposed ()
|
|
{
|
|
var added = 0;
|
|
var removed = 0;
|
|
var otherActions = 0;
|
|
ObservableCollection<string> source1 = [];
|
|
ListWrapper<string> lw = new (source1);
|
|
|
|
lw.CollectionChanged += Lw_CollectionChanged;
|
|
|
|
lw.Dispose ();
|
|
lw = new (null);
|
|
Assert.Equal (0, lw.Count);
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (0, removed);
|
|
Assert.Equal (0, otherActions);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
source1.Add ($"Item{i}");
|
|
}
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (0, removed);
|
|
Assert.Equal (0, otherActions);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
source1.Remove (source1 [0]);
|
|
}
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (0, removed);
|
|
Assert.Equal (0, otherActions);
|
|
Assert.Empty (source1);
|
|
|
|
|
|
void Lw_CollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (e.Action == NotifyCollectionChangedAction.Add)
|
|
{
|
|
added++;
|
|
}
|
|
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
|
{
|
|
removed++;
|
|
}
|
|
else
|
|
{
|
|
otherActions++;
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ListWrapper_SuspendCollectionChangedEvent_ResumeSuspendCollectionChangedEvent_Tests ()
|
|
{
|
|
var added = 0;
|
|
ObservableCollection<string> source = [];
|
|
ListWrapper<string> lw = new (source);
|
|
|
|
lw.CollectionChanged += Lw_CollectionChanged;
|
|
|
|
lw.SuspendCollectionChangedEvent = true;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
source.Add ($"Item{i}");
|
|
}
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (3, lw.Count);
|
|
Assert.Equal (3, source.Count);
|
|
|
|
lw.SuspendCollectionChangedEvent = false;
|
|
|
|
for (int i = 3; i < 6; i++)
|
|
{
|
|
source.Add ($"Item{i}");
|
|
}
|
|
Assert.Equal (3, added);
|
|
Assert.Equal (6, lw.Count);
|
|
Assert.Equal (6, source.Count);
|
|
|
|
|
|
void Lw_CollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (e.Action == NotifyCollectionChangedAction.Add)
|
|
{
|
|
added++;
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ListView_SuspendCollectionChangedEvent_ResumeSuspendCollectionChangedEvent_Tests ()
|
|
{
|
|
var added = 0;
|
|
ObservableCollection<string> source = [];
|
|
ListView lv = new ListView { Source = new ListWrapper<string> (source) };
|
|
|
|
lv.CollectionChanged += Lw_CollectionChanged;
|
|
|
|
lv.SuspendCollectionChangedEvent ();
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
source.Add ($"Item{i}");
|
|
}
|
|
Assert.Equal (0, added);
|
|
Assert.Equal (3, lv.Source.Count);
|
|
Assert.Equal (3, source.Count);
|
|
|
|
lv.ResumeSuspendCollectionChangedEvent ();
|
|
|
|
for (int i = 3; i < 6; i++)
|
|
{
|
|
source.Add ($"Item{i}");
|
|
}
|
|
Assert.Equal (3, added);
|
|
Assert.Equal (6, lv.Source.Count);
|
|
Assert.Equal (6, source.Count);
|
|
|
|
|
|
void Lw_CollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (e.Action == NotifyCollectionChangedAction.Add)
|
|
{
|
|
added++;
|
|
}
|
|
}
|
|
}
|
|
} |