mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
* Tons of API doc updates * Removed stale test * Removed stale tests * Fixed Skipped Shadow test 1 * Fixed Skipped Shadow test 2 * Fixed Skipped Shadow test 3 * Removed stale test * Removed stale test2 * Explicit unregister of event handler on Application.Driver!.ClearedContents * Added Toplevels to dict * code cleanup * spelling error * Removed stale test3 * Removed stale test4 * Removed stale test5 * added script * tweaked script * tweaked script * Created StressTests project; moved some tests * Created IntegrationTests project; moved some tests * New yml * made old yml just unit tests * Tweaked Button_IsDefault_Raises_Accepted_Correctly * tweaked script * cleaned up ymls * tweakled up ymls * stress tests... * stress tests on ubuntu only * Fixed WindowsDriver in InvokeLeakTest * Fixed WindowsDriver in InvokeLeakTest2 * Added Directory.Packages.props. Added Directory.Build.props * Shortened StressTest time * Removed dupe file. * DemoFiles * Moved all tests to ./Tests dir. * Fixed release build issue * Fixed .sln file * Fixed .sl* files * Fixing ymls * Fixing interation tests * Create link to the file TestHelpers. * Created Tests/UnitTestsParallelizable. Moved all obviously parallelizable tests. Updated yml. * fixing logs * fixing logs2 * fixing logs3 * don't require stress to pass for PRs * Fix a failure? * tweaked script * Coudl this be it? * Moved tons of tests to parallelizable * Fixed some stuff * Script to find duplicate tests * Testing workflows * Updated to v4 * Fix RelativeBasePath issue * Replace powershell to pwsh * Add ignore projects. * Removed dupe unit tests * Code cleanup of tests * Cleaned up test warnings * yml tweak * Moved setter * tweak ymls * just randomly throwing spaghetti at a wall * Enable runing 5 test runners in par * Turned off DEBUG_DISPOSABLE for par tests * RunningUnitTests=true * code cleanup (forcing more Action runs) * DISABLE_DEBUG_IDISPOSABLE * Added View.DebugIDisposable. False by default. * Remobed bogus tareet * Remobed bogus tareet2 * fixed warning * added api doc * fixed warning * fixed warning * fixed warning2 * fixed warning3 * fixed warning4 --------- Co-authored-by: BDisp <bd.bdisp@gmail.com>
This commit is contained in:
68
Tests/UnitTests/View/Adornment/AdornmentSubViewTests.cs
Normal file
68
Tests/UnitTests/View/Adornment/AdornmentSubViewTests.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class AdornmentSubViewTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)] // Margin has no thickness, so false
|
||||
[InlineData (0, 1, false)] // Margin has no thickness, so false
|
||||
[InlineData (1, 0, true)]
|
||||
[InlineData (1, 1, true)]
|
||||
[InlineData (2, 1, true)]
|
||||
public void Adornment_WithSubView_GetViewsUnderMouse_Finds (int viewMargin, int subViewMargin, bool expectedFound)
|
||||
{
|
||||
Application.Top = new Toplevel()
|
||||
{
|
||||
Width = 10,
|
||||
Height = 10
|
||||
};
|
||||
Application.Top.Margin.Thickness = new Thickness (viewMargin);
|
||||
|
||||
var subView = new View ()
|
||||
{
|
||||
X = 0,
|
||||
Y = 0,
|
||||
Width = 5,
|
||||
Height = 5
|
||||
};
|
||||
subView.Margin.Thickness = new Thickness (subViewMargin);
|
||||
Application.Top.Margin.Add (subView);
|
||||
Application.Top.Layout ();
|
||||
|
||||
var foundView = View.GetViewsUnderMouse (new Point(0, 0)).LastOrDefault ();
|
||||
|
||||
bool found = foundView == subView || foundView == subView.Margin;
|
||||
Assert.Equal (expectedFound, found);
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (ignoreDisposed: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Adornment_WithNonVisibleSubView_GetViewsUnderMouse_Finds_Adornment ()
|
||||
{
|
||||
Application.Top = new Toplevel ()
|
||||
{
|
||||
Width = 10,
|
||||
Height = 10
|
||||
};
|
||||
Application.Top.Padding.Thickness = new Thickness (1);
|
||||
|
||||
var subView = new View ()
|
||||
{
|
||||
X = 0,
|
||||
Y = 0,
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
Visible = false
|
||||
};
|
||||
Application.Top.Padding.Add (subView);
|
||||
Application.Top.Layout ();
|
||||
|
||||
Assert.Equal (Application.Top.Padding, View.GetViewsUnderMouse (new Point(0, 0)).LastOrDefault ());
|
||||
Application.Top?.Dispose ();
|
||||
Application.ResetState (ignoreDisposed: true);
|
||||
}
|
||||
}
|
||||
73
Tests/UnitTests/View/Adornment/AdornmentTests.cs
Normal file
73
Tests/UnitTests/View/Adornment/AdornmentTests.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class AdornmentTests (ITestOutputHelper output)
|
||||
{
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Border_Is_Cleared_After_Margin_Thickness_Change ()
|
||||
{
|
||||
View view = new () { Text = "View", Width = 6, Height = 3, BorderStyle = LineStyle.Rounded };
|
||||
|
||||
// Remove border bottom thickness
|
||||
view.Border!.Thickness = new (1, 1, 1, 0);
|
||||
|
||||
// Add margin bottom thickness
|
||||
view.Margin!.Thickness = new (0, 0, 0, 1);
|
||||
|
||||
Assert.Equal (6, view.Width);
|
||||
Assert.Equal (3, view.Height);
|
||||
|
||||
view.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
╭────╮
|
||||
│View│
|
||||
",
|
||||
output
|
||||
);
|
||||
|
||||
// Add border bottom thickness
|
||||
view.Border!.Thickness = new (1, 1, 1, 1);
|
||||
|
||||
// Remove margin bottom thickness
|
||||
view.Margin!.Thickness = new (0, 0, 0, 0);
|
||||
|
||||
view.Draw ();
|
||||
|
||||
Assert.Equal (6, view.Width);
|
||||
Assert.Equal (3, view.Height);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
╭────╮
|
||||
│View│
|
||||
╰────╯
|
||||
",
|
||||
output
|
||||
);
|
||||
|
||||
// Remove border bottom thickness
|
||||
view.Border!.Thickness = new (1, 1, 1, 0);
|
||||
|
||||
// Add margin bottom thickness
|
||||
view.Margin!.Thickness = new (0, 0, 0, 1);
|
||||
|
||||
Assert.Equal (6, view.Width);
|
||||
Assert.Equal (3, view.Height);
|
||||
|
||||
View.SetClipToScreen ();
|
||||
view.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
╭────╮
|
||||
│View│
|
||||
",
|
||||
output
|
||||
);
|
||||
}
|
||||
}
|
||||
947
Tests/UnitTests/View/Adornment/BorderTests.cs
Normal file
947
Tests/UnitTests/View/Adornment/BorderTests.cs
Normal file
@@ -0,0 +1,947 @@
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class BorderTests (ITestOutputHelper output)
|
||||
{
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Border_Parent_HasFocus_Title_Uses_FocusAttribute ()
|
||||
{
|
||||
var superView = new View { Width = 10, Height = 10, CanFocus = true };
|
||||
var otherView = new View { Width = 0, Height = 0, CanFocus = true };
|
||||
superView.Add (otherView);
|
||||
|
||||
var view = new View { Title = "A", Height = 2, Width = 5 };
|
||||
superView.Add (view);
|
||||
|
||||
view.Border.Thickness = new (0, 1, 0, 0);
|
||||
view.Border.LineStyle = LineStyle.Single;
|
||||
|
||||
view.ColorScheme = new ()
|
||||
{
|
||||
Normal = new (Color.Red, Color.Green),
|
||||
Focus = new (Color.Green, Color.Red)
|
||||
};
|
||||
Assert.NotEqual (view.ColorScheme.Normal.Foreground, view.ColorScheme.Focus.Foreground);
|
||||
Assert.Equal (ColorName16.Red, view.Border.GetNormalColor ().Foreground.GetClosestNamedColor16 ());
|
||||
Assert.Equal (ColorName16.Green, view.Border.GetFocusColor ().Foreground.GetClosestNamedColor16 ());
|
||||
Assert.Equal (view.GetFocusColor (), view.Border.GetFocusColor ());
|
||||
|
||||
superView.BeginInit ();
|
||||
superView.EndInit ();
|
||||
superView.Draw ();
|
||||
|
||||
var expected = @"─┤A├─";
|
||||
DriverAssert.AssertDriverContentsAre (expected, output);
|
||||
DriverAssert.AssertDriverAttributesAre ("00000", output, null, view.ColorScheme.Normal);
|
||||
|
||||
view.CanFocus = true;
|
||||
view.SetFocus ();
|
||||
View.SetClipToScreen ();
|
||||
view.Draw ();
|
||||
Assert.Equal (view.GetFocusColor (), view.Border.GetFocusColor ());
|
||||
Assert.Equal (view.ColorScheme.Focus.Foreground, view.Border.GetFocusColor ().Foreground);
|
||||
Assert.Equal (view.ColorScheme.Normal.Foreground, view.Border.GetNormalColor ().Foreground);
|
||||
DriverAssert.AssertDriverAttributesAre ("00100", output, null, view.ColorScheme.Normal, view.GetFocusColor ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Border_Uses_Parent_ColorScheme ()
|
||||
{
|
||||
var view = new View { Title = "A", Height = 2, Width = 5 };
|
||||
view.Border.Thickness = new (0, 1, 0, 0);
|
||||
view.Border.LineStyle = LineStyle.Single;
|
||||
|
||||
view.ColorScheme = new ()
|
||||
{
|
||||
Normal = new (Color.Red, Color.Green), Focus = new (Color.Green, Color.Red)
|
||||
};
|
||||
Assert.Equal (ColorName16.Red, view.Border.GetNormalColor ().Foreground.GetClosestNamedColor16 ());
|
||||
Assert.Equal (ColorName16.Green, view.Border.GetFocusColor ().Foreground.GetClosestNamedColor16 ());
|
||||
Assert.Equal (view.GetNormalColor (), view.Border.GetNormalColor ());
|
||||
Assert.Equal (view.GetFocusColor (), view.Border.GetFocusColor ());
|
||||
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
view.Draw ();
|
||||
|
||||
var expected = @"─┤A├─";
|
||||
DriverAssert.AssertDriverContentsAre (expected, output);
|
||||
DriverAssert.AssertDriverAttributesAre ("00000", output, null, view.ColorScheme.Normal);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[AutoInitShutdown]
|
||||
[InlineData (0)]
|
||||
[InlineData (1)]
|
||||
[InlineData (2)]
|
||||
[InlineData (3)]
|
||||
[InlineData (4)]
|
||||
[InlineData (5)]
|
||||
[InlineData (6)]
|
||||
[InlineData (7)]
|
||||
[InlineData (8)]
|
||||
[InlineData (9)]
|
||||
[InlineData (10)]
|
||||
public void Border_With_Title_Border_Double_Thickness_Top_Four_Size_Width (int width)
|
||||
{
|
||||
var win = new Window
|
||||
{
|
||||
Title = "1234", Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Double
|
||||
};
|
||||
win.Border.Thickness = win.Border.Thickness with { Top = 4 };
|
||||
|
||||
RunState rs = Application.Begin (win);
|
||||
var firstIteration = false;
|
||||
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (width, 5);
|
||||
Application.RunIteration (ref rs, firstIteration);
|
||||
var expected = string.Empty;
|
||||
|
||||
switch (width)
|
||||
{
|
||||
case 1:
|
||||
Assert.Equal (new (0, 0, 1, 5), win.Frame);
|
||||
|
||||
expected = @"
|
||||
║
|
||||
║
|
||||
║";
|
||||
|
||||
break;
|
||||
case 2:
|
||||
Assert.Equal (new (0, 0, 2, 5), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╔╗
|
||||
║║
|
||||
╚╝";
|
||||
|
||||
break;
|
||||
case 3:
|
||||
Assert.Equal (new (0, 0, 3, 5), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╔═╗
|
||||
║ ║
|
||||
╚═╝";
|
||||
|
||||
break;
|
||||
case 4:
|
||||
Assert.Equal (new (0, 0, 4, 5), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒╕
|
||||
╔╡╞╗
|
||||
║╘╛║
|
||||
╚══╝";
|
||||
|
||||
break;
|
||||
case 5:
|
||||
Assert.Equal (new (0, 0, 5, 5), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒═╕
|
||||
╔╡1╞╗
|
||||
║╘═╛║
|
||||
╚═══╝";
|
||||
|
||||
break;
|
||||
case 6:
|
||||
Assert.Equal (new (0, 0, 6, 5), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒══╕
|
||||
╔╡12╞╗
|
||||
║╘══╛║
|
||||
╚════╝";
|
||||
|
||||
break;
|
||||
case 7:
|
||||
Assert.Equal (new (0, 0, 7, 5), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒═══╕
|
||||
╔╡123╞╗
|
||||
║╘═══╛║
|
||||
╚═════╝";
|
||||
|
||||
break;
|
||||
case 8:
|
||||
Assert.Equal (new (0, 0, 8, 5), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒════╕
|
||||
╔╡1234╞╗
|
||||
║╘════╛║
|
||||
╚══════╝";
|
||||
|
||||
break;
|
||||
case 9:
|
||||
Assert.Equal (new (0, 0, 9, 5), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒════╕
|
||||
╔╡1234╞═╗
|
||||
║╘════╛ ║
|
||||
╚═══════╝";
|
||||
|
||||
break;
|
||||
case 10:
|
||||
Assert.Equal (new (0, 0, 10, 5), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒════╕
|
||||
╔╡1234╞══╗
|
||||
║╘════╛ ║
|
||||
╚════════╝";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Application.End (rs);
|
||||
win.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[AutoInitShutdown]
|
||||
[InlineData (0)]
|
||||
[InlineData (1)]
|
||||
[InlineData (2)]
|
||||
[InlineData (3)]
|
||||
[InlineData (4)]
|
||||
[InlineData (5)]
|
||||
[InlineData (6)]
|
||||
[InlineData (7)]
|
||||
[InlineData (8)]
|
||||
[InlineData (9)]
|
||||
[InlineData (10)]
|
||||
public void Border_With_Title_Border_Double_Thickness_Top_Three_Size_Width (int width)
|
||||
{
|
||||
var win = new Window
|
||||
{
|
||||
Title = "1234", Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Double
|
||||
};
|
||||
win.Border.Thickness = win.Border.Thickness with { Top = 3 };
|
||||
|
||||
RunState rs = Application.Begin (win);
|
||||
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (width, 4);
|
||||
Application.RunIteration (ref rs, false);
|
||||
var expected = string.Empty;
|
||||
|
||||
switch (width)
|
||||
{
|
||||
case 1:
|
||||
Assert.Equal (new (0, 0, 1, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
║
|
||||
║
|
||||
║";
|
||||
|
||||
break;
|
||||
case 2:
|
||||
Assert.Equal (new (0, 0, 2, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╔╗
|
||||
║║
|
||||
╚╝";
|
||||
|
||||
break;
|
||||
case 3:
|
||||
Assert.Equal (new (0, 0, 3, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╔═╗
|
||||
║ ║
|
||||
╚═╝";
|
||||
|
||||
break;
|
||||
case 4:
|
||||
Assert.Equal (new (0, 0, 4, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒╕
|
||||
╔╡╞╗
|
||||
║╘╛║
|
||||
╚══╝";
|
||||
|
||||
break;
|
||||
case 5:
|
||||
Assert.Equal (new (0, 0, 5, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒═╕
|
||||
╔╡1╞╗
|
||||
║╘═╛║
|
||||
╚═══╝";
|
||||
|
||||
break;
|
||||
case 6:
|
||||
Assert.Equal (new (0, 0, 6, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒══╕
|
||||
╔╡12╞╗
|
||||
║╘══╛║
|
||||
╚════╝";
|
||||
|
||||
break;
|
||||
case 7:
|
||||
Assert.Equal (new (0, 0, 7, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒═══╕
|
||||
╔╡123╞╗
|
||||
║╘═══╛║
|
||||
╚═════╝";
|
||||
|
||||
break;
|
||||
case 8:
|
||||
Assert.Equal (new (0, 0, 8, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒════╕
|
||||
╔╡1234╞╗
|
||||
║╘════╛║
|
||||
╚══════╝";
|
||||
|
||||
break;
|
||||
case 9:
|
||||
Assert.Equal (new (0, 0, 9, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒════╕
|
||||
╔╡1234╞═╗
|
||||
║╘════╛ ║
|
||||
╚═══════╝";
|
||||
|
||||
break;
|
||||
case 10:
|
||||
Assert.Equal (new (0, 0, 10, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒════╕
|
||||
╔╡1234╞══╗
|
||||
║╘════╛ ║
|
||||
╚════════╝";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Application.End (rs);
|
||||
win.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[AutoInitShutdown]
|
||||
[InlineData (0)]
|
||||
[InlineData (1)]
|
||||
[InlineData (2)]
|
||||
[InlineData (3)]
|
||||
[InlineData (4)]
|
||||
[InlineData (5)]
|
||||
[InlineData (6)]
|
||||
[InlineData (7)]
|
||||
[InlineData (8)]
|
||||
[InlineData (9)]
|
||||
[InlineData (10)]
|
||||
public void Border_With_Title_Border_Double_Thickness_Top_Two_Size_Width (int width)
|
||||
{
|
||||
var win = new Window
|
||||
{
|
||||
Title = "1234", Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Double
|
||||
};
|
||||
win.Border.Thickness = win.Border.Thickness with { Top = 2 };
|
||||
|
||||
RunState rs = Application.Begin (win);
|
||||
var firstIteration = false;
|
||||
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (width, 4);
|
||||
Application.RunIteration (ref rs, firstIteration);
|
||||
var expected = string.Empty;
|
||||
|
||||
switch (width)
|
||||
{
|
||||
case 1:
|
||||
Assert.Equal (new (0, 0, 1, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
║
|
||||
║
|
||||
║";
|
||||
|
||||
break;
|
||||
case 2:
|
||||
Assert.Equal (new (0, 0, 2, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╔╗
|
||||
║║
|
||||
╚╝";
|
||||
|
||||
break;
|
||||
case 3:
|
||||
Assert.Equal (new (0, 0, 3, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╔═╗
|
||||
║ ║
|
||||
╚═╝";
|
||||
|
||||
break;
|
||||
case 4:
|
||||
Assert.Equal (new (0, 0, 4, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒╕
|
||||
╔╛╘╗
|
||||
║ ║
|
||||
╚══╝";
|
||||
|
||||
break;
|
||||
case 5:
|
||||
Assert.Equal (new (0, 0, 5, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒═╕
|
||||
╔╛1╘╗
|
||||
║ ║
|
||||
╚═══╝";
|
||||
|
||||
break;
|
||||
case 6:
|
||||
Assert.Equal (new (0, 0, 6, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒══╕
|
||||
╔╛12╘╗
|
||||
║ ║
|
||||
╚════╝";
|
||||
|
||||
break;
|
||||
case 7:
|
||||
Assert.Equal (new (0, 0, 7, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒═══╕
|
||||
╔╛123╘╗
|
||||
║ ║
|
||||
╚═════╝";
|
||||
|
||||
break;
|
||||
case 8:
|
||||
Assert.Equal (new (0, 0, 8, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒════╕
|
||||
╔╛1234╘╗
|
||||
║ ║
|
||||
╚══════╝";
|
||||
|
||||
break;
|
||||
case 9:
|
||||
Assert.Equal (new (0, 0, 9, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒════╕
|
||||
╔╛1234╘═╗
|
||||
║ ║
|
||||
╚═══════╝";
|
||||
|
||||
break;
|
||||
case 10:
|
||||
Assert.Equal (new (0, 0, 10, 4), win.Frame);
|
||||
|
||||
expected = @"
|
||||
╒════╕
|
||||
╔╛1234╘══╗
|
||||
║ ║
|
||||
╚════════╝";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Application.End (rs);
|
||||
win.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[AutoInitShutdown]
|
||||
[InlineData (0)]
|
||||
[InlineData (1)]
|
||||
[InlineData (2)]
|
||||
[InlineData (3)]
|
||||
public void Border_With_Title_Size_Height (int height)
|
||||
{
|
||||
var win = new Window { Title = "1234", Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
RunState rs = Application.Begin (win);
|
||||
var firstIteration = false;
|
||||
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (20, height);
|
||||
Application.RunIteration (ref rs, firstIteration);
|
||||
var expected = string.Empty;
|
||||
|
||||
switch (height)
|
||||
{
|
||||
case 0:
|
||||
//Assert.Equal (new (0, 0, 17, 0), subview.Frame);
|
||||
expected = @"
|
||||
";
|
||||
|
||||
break;
|
||||
case 1:
|
||||
//Assert.Equal (new (0, 0, 17, 0), subview.Frame);
|
||||
expected = @"
|
||||
─┤1234├─────────────";
|
||||
|
||||
break;
|
||||
case 2:
|
||||
//Assert.Equal (new (0, 0, 17, 1), subview.Frame);
|
||||
expected = @"
|
||||
┌┤1234├────────────┐
|
||||
└──────────────────┘
|
||||
";
|
||||
|
||||
break;
|
||||
case 3:
|
||||
//Assert.Equal (new (0, 0, 17, 2), subview.Frame);
|
||||
expected = @"
|
||||
┌┤1234├────────────┐
|
||||
│ │
|
||||
└──────────────────┘
|
||||
";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Application.End (rs);
|
||||
win.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[AutoInitShutdown]
|
||||
[InlineData (0)]
|
||||
[InlineData (1)]
|
||||
[InlineData (2)]
|
||||
[InlineData (3)]
|
||||
[InlineData (4)]
|
||||
[InlineData (5)]
|
||||
[InlineData (6)]
|
||||
[InlineData (7)]
|
||||
[InlineData (8)]
|
||||
[InlineData (9)]
|
||||
[InlineData (10)]
|
||||
public void Border_With_Title_Size_Width (int width)
|
||||
{
|
||||
var win = new Window { Title = "1234", Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
RunState rs = Application.Begin (win);
|
||||
var firstIteration = false;
|
||||
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (width, 3);
|
||||
Application.RunIteration (ref rs, firstIteration);
|
||||
var expected = string.Empty;
|
||||
|
||||
switch (width)
|
||||
{
|
||||
case 1:
|
||||
//Assert.Equal (new (0, 0, 17, 0), subview.Frame);
|
||||
expected = @"
|
||||
│
|
||||
│
|
||||
│";
|
||||
|
||||
break;
|
||||
case 2:
|
||||
//Assert.Equal (new (0, 0, 17, 1), subview.Frame);
|
||||
expected = @"
|
||||
┌┐
|
||||
││
|
||||
└┘";
|
||||
|
||||
break;
|
||||
case 3:
|
||||
//Assert.Equal (new (0, 0, 17, 2), subview.Frame);
|
||||
expected = @"
|
||||
┌─┐
|
||||
│ │
|
||||
└─┘
|
||||
";
|
||||
|
||||
break;
|
||||
case 4:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌┤├┐
|
||||
│ │
|
||||
└──┘";
|
||||
|
||||
break;
|
||||
case 5:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌┤1├┐
|
||||
│ │
|
||||
└───┘";
|
||||
|
||||
break;
|
||||
case 6:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌┤12├┐
|
||||
│ │
|
||||
└────┘";
|
||||
|
||||
break;
|
||||
case 7:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌┤123├┐
|
||||
│ │
|
||||
└─────┘";
|
||||
|
||||
break;
|
||||
case 8:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌┤1234├┐
|
||||
│ │
|
||||
└──────┘";
|
||||
|
||||
break;
|
||||
case 9:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌┤1234├─┐
|
||||
│ │
|
||||
└───────┘";
|
||||
|
||||
break;
|
||||
case 10:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌┤1234├──┐
|
||||
│ │
|
||||
└────────┘";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
|
||||
win.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0, 2, 2)]
|
||||
[InlineData (1, 0, 0, 4, 4)]
|
||||
[InlineData (2, 0, 0, 6, 6)]
|
||||
[InlineData (1, 1, 0, 5, 4)]
|
||||
[InlineData (1, 0, 1, 4, 5)]
|
||||
[InlineData (1, 1, 1, 5, 5)]
|
||||
[InlineData (1, 10, 10, 14, 14)]
|
||||
public void FrameToScreen_NestedSuperView_WithBorder (
|
||||
int superOffset,
|
||||
int frameX,
|
||||
int frameY,
|
||||
int expectedScreenX,
|
||||
int expectedScreenY
|
||||
)
|
||||
{
|
||||
var superSuper = new View
|
||||
{
|
||||
X = superOffset,
|
||||
Y = superOffset,
|
||||
Width = 30,
|
||||
Height = 30,
|
||||
BorderStyle = LineStyle.Single
|
||||
};
|
||||
|
||||
var super = new View
|
||||
{
|
||||
X = superOffset,
|
||||
Y = superOffset,
|
||||
Width = 20,
|
||||
Height = 20,
|
||||
BorderStyle = LineStyle.Single
|
||||
};
|
||||
superSuper.Add (super);
|
||||
|
||||
var view = new View { X = frameX, Y = frameY, Width = 10, Height = 10 };
|
||||
super.Add (view);
|
||||
superSuper.Layout ();
|
||||
|
||||
var expected = new Rectangle (expectedScreenX, expectedScreenY, 10, 10);
|
||||
Rectangle actual = view.FrameToScreen ();
|
||||
Assert.Equal (expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0, 1, 1)]
|
||||
[InlineData (1, 0, 0, 2, 2)]
|
||||
[InlineData (2, 0, 0, 3, 3)]
|
||||
[InlineData (1, 1, 0, 3, 2)]
|
||||
[InlineData (1, 0, 1, 2, 3)]
|
||||
[InlineData (1, 1, 1, 3, 3)]
|
||||
[InlineData (1, 10, 10, 12, 12)]
|
||||
public void FrameToScreen_SuperView_WithBorder (
|
||||
int superOffset,
|
||||
int frameX,
|
||||
int frameY,
|
||||
int expectedScreenX,
|
||||
int expectedScreenY
|
||||
)
|
||||
{
|
||||
var super = new View
|
||||
{
|
||||
X = superOffset,
|
||||
Y = superOffset,
|
||||
Width = 20,
|
||||
Height = 20,
|
||||
BorderStyle = LineStyle.Single
|
||||
};
|
||||
|
||||
var view = new View { X = frameX, Y = frameY, Width = 10, Height = 10 };
|
||||
super.Add (view);
|
||||
super.Layout ();
|
||||
|
||||
var expected = new Rectangle (expectedScreenX, expectedScreenY, 10, 10);
|
||||
Rectangle actual = view.FrameToScreen ();
|
||||
Assert.Equal (expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void HasSuperView ()
|
||||
{
|
||||
var top = new Toplevel ();
|
||||
top.BorderStyle = LineStyle.Double;
|
||||
|
||||
var frame = new FrameView { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
top.Add (frame);
|
||||
RunState rs = Application.Begin (top);
|
||||
var firstIteration = false;
|
||||
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (5, 5);
|
||||
Application.RunIteration (ref rs, firstIteration);
|
||||
|
||||
var expected = @"
|
||||
╔═══╗
|
||||
║┌─┐║
|
||||
║│ │║
|
||||
║└─┘║
|
||||
╚═══╝";
|
||||
|
||||
_ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Application.End (rs);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void HasSuperView_Title ()
|
||||
{
|
||||
var top = new Toplevel ();
|
||||
top.BorderStyle = LineStyle.Double;
|
||||
|
||||
var frame = new FrameView { Title = "1234", Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
top.Add (frame);
|
||||
RunState rs = Application.Begin (top);
|
||||
var firstIteration = false;
|
||||
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (10, 4);
|
||||
Application.RunIteration (ref rs, firstIteration);
|
||||
|
||||
var expected = @"
|
||||
╔════════╗
|
||||
║┌┤1234├┐║
|
||||
║└──────┘║
|
||||
╚════════╝";
|
||||
|
||||
_ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Application.End (rs);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void NoSuperView ()
|
||||
{
|
||||
var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
RunState rs = Application.Begin (win);
|
||||
var firstIteration = false;
|
||||
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (3, 3);
|
||||
Application.RunIteration (ref rs, firstIteration);
|
||||
|
||||
var expected = @"
|
||||
┌─┐
|
||||
│ │
|
||||
└─┘";
|
||||
|
||||
_ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
|
||||
win.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void View_BorderStyle_Defaults ()
|
||||
{
|
||||
var view = new View ();
|
||||
Assert.Equal (LineStyle.None, view.BorderStyle);
|
||||
Assert.Equal (Thickness.Empty, view.Border.Thickness);
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void View_SetBorderStyle ()
|
||||
{
|
||||
var view = new View ();
|
||||
view.BorderStyle = LineStyle.Single;
|
||||
Assert.Equal (LineStyle.Single, view.BorderStyle);
|
||||
Assert.Equal (new (1), view.Border.Thickness);
|
||||
|
||||
view.BorderStyle = LineStyle.Double;
|
||||
Assert.Equal (LineStyle.Double, view.BorderStyle);
|
||||
Assert.Equal (new (1), view.Border.Thickness);
|
||||
|
||||
view.BorderStyle = LineStyle.None;
|
||||
Assert.Equal (LineStyle.None, view.BorderStyle);
|
||||
Assert.Equal (Thickness.Empty, view.Border.Thickness);
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (false, @"
|
||||
┌───┐
|
||||
│ ║ │
|
||||
│═┌┄│
|
||||
│ ┊ │
|
||||
└───┘")]
|
||||
[InlineData (true, @"
|
||||
╔═╦─┐
|
||||
║ ║ │
|
||||
╠═╬┄┤
|
||||
│ ┊ ┊
|
||||
└─┴┄┘")]
|
||||
[SetupFakeDriver]
|
||||
public void SuperViewRendersLineCanvas_No_Subviews_AutoJoinsLines (bool superViewRendersLineCanvas, string expected)
|
||||
{
|
||||
View superView = new View ()
|
||||
{
|
||||
Id = "superView",
|
||||
Width = 5,
|
||||
Height = 5,
|
||||
BorderStyle = LineStyle.Single
|
||||
};
|
||||
|
||||
View view1 = new View ()
|
||||
{
|
||||
Id = "view1",
|
||||
Width = 3,
|
||||
Height = 3,
|
||||
X = -1,
|
||||
Y = -1,
|
||||
BorderStyle = LineStyle.Double,
|
||||
SuperViewRendersLineCanvas = superViewRendersLineCanvas
|
||||
};
|
||||
|
||||
View view2 = new View ()
|
||||
{
|
||||
Id = "view2",
|
||||
Width = 3,
|
||||
Height = 3,
|
||||
X = 1,
|
||||
Y = 1,
|
||||
BorderStyle = LineStyle.Dotted,
|
||||
SuperViewRendersLineCanvas = superViewRendersLineCanvas
|
||||
};
|
||||
|
||||
superView.Add (view1, view2);
|
||||
|
||||
superView.BeginInit ();
|
||||
superView.EndInit ();
|
||||
superView.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsAre (expected, output);
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData (false, @"
|
||||
┌┤A├──────┐
|
||||
│ ║ │
|
||||
│ ║ │
|
||||
│════┌┤C├┄│
|
||||
│ ┊ │
|
||||
│ ┊ │
|
||||
└─────────┘")]
|
||||
[InlineData (true, @"
|
||||
╔╡A╞═╦────┐
|
||||
║ ║ │
|
||||
║ ║ │
|
||||
╠════╬┤C├┄┤
|
||||
│ ┊ ┊
|
||||
│ ┊ ┊
|
||||
└────┴┄┄┄┄┘")]
|
||||
[SetupFakeDriver]
|
||||
public void SuperViewRendersLineCanvas_Title_AutoJoinsLines (bool superViewRendersLineCanvas, string expected)
|
||||
{
|
||||
View superView = new View ()
|
||||
{
|
||||
Id = "superView",
|
||||
Title = "A",
|
||||
Width = 11,
|
||||
Height = 7,
|
||||
CanFocus = true,
|
||||
BorderStyle = LineStyle.Single,
|
||||
};
|
||||
|
||||
View view1 = new View ()
|
||||
{
|
||||
Id = "view1",
|
||||
Title = "B",
|
||||
Width = 6,
|
||||
Height = 4,
|
||||
X = -1,
|
||||
Y = -1,
|
||||
CanFocus = true,
|
||||
BorderStyle = LineStyle.Double,
|
||||
SuperViewRendersLineCanvas = superViewRendersLineCanvas
|
||||
};
|
||||
|
||||
View view2 = new View ()
|
||||
{
|
||||
Id = "view2",
|
||||
Title = "C",
|
||||
Width = 6,
|
||||
Height = 4,
|
||||
X = 4,
|
||||
Y = 2,
|
||||
CanFocus = true,
|
||||
BorderStyle = LineStyle.Dotted,
|
||||
SuperViewRendersLineCanvas = superViewRendersLineCanvas
|
||||
};
|
||||
|
||||
superView.Add (view1, view2);
|
||||
|
||||
superView.BeginInit ();
|
||||
superView.EndInit ();
|
||||
superView.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsAre (expected, output);
|
||||
}
|
||||
}
|
||||
46
Tests/UnitTests/View/Adornment/MarginTests.cs
Normal file
46
Tests/UnitTests/View/Adornment/MarginTests.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class MarginTests (ITestOutputHelper output)
|
||||
{
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Margin_Is_Transparent ()
|
||||
{
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (5, 5);
|
||||
|
||||
var view = new View { Height = 3, Width = 3 };
|
||||
view.Margin.Diagnostics = ViewDiagnosticFlags.Thickness;
|
||||
view.Margin.Thickness = new (1);
|
||||
|
||||
Application.Top = new Toplevel ();
|
||||
Application.TopLevels.Push (Gui.Application.Top);
|
||||
|
||||
Application.Top.ColorScheme = new()
|
||||
{
|
||||
Normal = new (Color.Red, Color.Green), Focus = new (Color.Green, Color.Red)
|
||||
};
|
||||
|
||||
Application.Top.Add (view);
|
||||
Assert.Equal (ColorName16.Red, view.Margin.GetNormalColor ().Foreground.GetClosestNamedColor16 ());
|
||||
Assert.Equal (ColorName16.Red, Application.Top.GetNormalColor ().Foreground.GetClosestNamedColor16 ());
|
||||
|
||||
Application.Top.BeginInit ();
|
||||
Application.Top.EndInit ();
|
||||
Application.LayoutAndDraw();
|
||||
|
||||
DriverAssert.AssertDriverContentsAre (
|
||||
@"
|
||||
MMM
|
||||
M M
|
||||
MMM",
|
||||
output
|
||||
);
|
||||
DriverAssert.AssertDriverAttributesAre ("0", output, null, Application.Top.GetNormalColor ());
|
||||
|
||||
Application.ResetState (true);
|
||||
}
|
||||
}
|
||||
39
Tests/UnitTests/View/Adornment/PaddingTests.cs
Normal file
39
Tests/UnitTests/View/Adornment/PaddingTests.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class PaddingTests (ITestOutputHelper output)
|
||||
{
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Padding_Uses_Parent_ColorScheme ()
|
||||
{
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (5, 5);
|
||||
var view = new View { Height = 3, Width = 3 };
|
||||
view.Padding.Thickness = new (1);
|
||||
view.Padding.Diagnostics = ViewDiagnosticFlags.Thickness;
|
||||
|
||||
view.ColorScheme = new()
|
||||
{
|
||||
Normal = new (Color.Red, Color.Green), Focus = new (Color.Green, Color.Red)
|
||||
};
|
||||
|
||||
Assert.Equal (ColorName16.Red, view.Padding.GetNormalColor ().Foreground.GetClosestNamedColor16 ());
|
||||
Assert.Equal (view.GetNormalColor (), view.Padding.GetNormalColor ());
|
||||
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
view.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsAre (
|
||||
@"
|
||||
PPP
|
||||
P P
|
||||
PPP",
|
||||
output
|
||||
);
|
||||
DriverAssert.AssertDriverAttributesAre ("0", output, null, view.GetNormalColor ());
|
||||
}
|
||||
}
|
||||
127
Tests/UnitTests/View/Adornment/ShadowStyletests.cs
Normal file
127
Tests/UnitTests/View/Adornment/ShadowStyletests.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class ShadowStyleTests (ITestOutputHelper output)
|
||||
{
|
||||
[Theory]
|
||||
[InlineData (
|
||||
ShadowStyle.None,
|
||||
"""
|
||||
011
|
||||
111
|
||||
111
|
||||
""")]
|
||||
[InlineData (
|
||||
ShadowStyle.Transparent,
|
||||
"""
|
||||
031
|
||||
131
|
||||
111
|
||||
""")]
|
||||
[InlineData (
|
||||
ShadowStyle.Opaque,
|
||||
"""
|
||||
021
|
||||
221
|
||||
111
|
||||
""")]
|
||||
[SetupFakeDriver]
|
||||
public void ShadowView_Colors (ShadowStyle style, string expectedAttrs)
|
||||
{
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (5, 5);
|
||||
Color fg = Color.Red;
|
||||
Color bg = Color.Green;
|
||||
|
||||
// 0 - View
|
||||
// 1 - SuperView
|
||||
// 2 - Opaque - fg is Black, bg is SuperView.Bg
|
||||
// 3 - Transparent - fg is darker fg, bg is darker bg
|
||||
Attribute [] attributes =
|
||||
{
|
||||
Attribute.Default,
|
||||
new (fg, bg),
|
||||
new (Color.Black, bg),
|
||||
new (fg.GetDarkerColor (), bg.GetDarkerColor ())
|
||||
};
|
||||
|
||||
var superView = new Toplevel
|
||||
{
|
||||
Height = 3,
|
||||
Width = 3,
|
||||
Text = "012ABC!@#",
|
||||
ColorScheme = new (new Attribute (fg, bg))
|
||||
};
|
||||
superView.TextFormatter.WordWrap = true;
|
||||
|
||||
View view = new ()
|
||||
{
|
||||
Width = Dim.Auto (),
|
||||
Height = Dim.Auto (),
|
||||
Text = "*",
|
||||
ShadowStyle = style,
|
||||
ColorScheme = new (Attribute.Default)
|
||||
};
|
||||
superView.Add (view);
|
||||
Application.TopLevels.Push (superView);
|
||||
Application.LayoutAndDraw (true);
|
||||
DriverAssert.AssertDriverAttributesAre (expectedAttrs, output, Application.Driver, attributes);
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
// Visual tests
|
||||
[Theory]
|
||||
[InlineData (
|
||||
ShadowStyle.None,
|
||||
"""
|
||||
01#$
|
||||
AB#$
|
||||
!@#$
|
||||
!@#$
|
||||
""")]
|
||||
[InlineData (
|
||||
ShadowStyle.Opaque,
|
||||
"""
|
||||
01▖$
|
||||
AB▌$
|
||||
▝▀▘$
|
||||
!@#$
|
||||
""")]
|
||||
[InlineData (
|
||||
ShadowStyle.Transparent,
|
||||
"""
|
||||
01#$
|
||||
AB#$
|
||||
!@#$
|
||||
!@#$
|
||||
""")]
|
||||
[SetupFakeDriver]
|
||||
public void Visual_Test (ShadowStyle style, string expected)
|
||||
{
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (5, 5);
|
||||
|
||||
var superView = new Toplevel
|
||||
{
|
||||
Width = 4,
|
||||
Height = 4,
|
||||
Text = "!@#$".Repeat (4)!
|
||||
};
|
||||
superView.TextFormatter.WordWrap = true;
|
||||
|
||||
var view = new View
|
||||
{
|
||||
Text = "01\nAB",
|
||||
Width = Dim.Auto (),
|
||||
Height = Dim.Auto ()
|
||||
};
|
||||
view.ShadowStyle = style;
|
||||
superView.Add (view);
|
||||
Application.TopLevels.Push (superView);
|
||||
Application.LayoutAndDraw (true);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
|
||||
view.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
}
|
||||
17
Tests/UnitTests/View/ArrangementTests.cs
Normal file
17
Tests/UnitTests/View/ArrangementTests.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Text;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class ArrangementTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
// Test that TopResizable and Movable are mutually exclusive and Movable wins
|
||||
[Fact]
|
||||
public void TopResizableAndMovableMutuallyExclusive ()
|
||||
{
|
||||
// TODO: Write test.
|
||||
}
|
||||
|
||||
}
|
||||
36
Tests/UnitTests/View/ColorSchemeTests.cs
Normal file
36
Tests/UnitTests/View/ColorSchemeTests.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
#nullable enable
|
||||
using System.Text;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
[Trait ("Category", "Output")]
|
||||
public class ColorSchemeTests (ITestOutputHelper _output)
|
||||
{
|
||||
|
||||
|
||||
[Fact]
|
||||
public void GetHotNormalColor_ColorScheme ()
|
||||
{
|
||||
var view = new View { ColorScheme = Colors.ColorSchemes ["Base"] };
|
||||
|
||||
Assert.Equal (view.ColorScheme.HotNormal, view.GetHotNormalColor ());
|
||||
|
||||
view.Enabled = false;
|
||||
Assert.Equal (view.ColorScheme.Disabled, view.GetHotNormalColor ());
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetNormalColor_ColorScheme ()
|
||||
{
|
||||
var view = new View { ColorScheme = Colors.ColorSchemes ["Base"] };
|
||||
|
||||
Assert.Equal (view.ColorScheme.Normal, view.GetNormalColor ());
|
||||
|
||||
view.Enabled = false;
|
||||
Assert.Equal (view.ColorScheme.Disabled, view.GetNormalColor ());
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
}
|
||||
30
Tests/UnitTests/View/DiagnosticsTests.cs
Normal file
30
Tests/UnitTests/View/DiagnosticsTests.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
#nullable enable
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests <see cref="View.Diagnostics"/> static property and <see cref="ViewDiagnosticFlags"/> enum.
|
||||
/// </summary>
|
||||
/// <param name="output"></param>
|
||||
[Trait ("Category", "Output")]
|
||||
public class DiagnosticTests ()
|
||||
{
|
||||
/// <summary>
|
||||
/// /// Tests <see cref="View.Diagnostics"/> static property and <see cref="ViewDiagnosticFlags"/> enum.
|
||||
/// ///
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Diagnostics_Sets ()
|
||||
{
|
||||
// View.Diagnostics is a static property that returns the current diagnostic flags.
|
||||
Assert.Equal (ViewDiagnosticFlags.Off, View.Diagnostics);
|
||||
|
||||
// View.Diagnostics can be set to a new value.
|
||||
View.Diagnostics = ViewDiagnosticFlags.Thickness;
|
||||
Assert.Equal (ViewDiagnosticFlags.Thickness, View.Diagnostics);
|
||||
|
||||
// Ensure we turn off at the end of the test
|
||||
View.Diagnostics = ViewDiagnosticFlags.Off;
|
||||
}
|
||||
}
|
||||
60
Tests/UnitTests/View/Draw/AllViewsDrawTests.cs
Normal file
60
Tests/UnitTests/View/Draw/AllViewsDrawTests.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.LayoutTests;
|
||||
|
||||
public class AllViewsDrawTests (ITestOutputHelper _output) : TestsAllViews
|
||||
{
|
||||
[Theory]
|
||||
[SetupFakeDriver] // Required for spinner view that wants to register timeouts
|
||||
[MemberData (nameof (AllViewTypes))]
|
||||
public void AllViews_Draw_Does_Not_Layout (Type viewType)
|
||||
{
|
||||
Application.ResetState (true);
|
||||
// Required for spinner view that wants to register timeouts
|
||||
Application.MainLoop = new MainLoop (new FakeMainLoop (Application.Driver));
|
||||
|
||||
var view = (View)CreateInstanceIfNotGeneric (viewType);
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
_output.WriteLine ($"Ignoring {viewType} - It's a Generic");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_output.WriteLine ($"Testing {viewType}");
|
||||
|
||||
if (view is IDesignable designable)
|
||||
{
|
||||
designable.EnableForDesign ();
|
||||
}
|
||||
|
||||
var drawCompleteCount = 0;
|
||||
view.DrawComplete += (s, e) => drawCompleteCount++;
|
||||
|
||||
var layoutStartedCount = 0;
|
||||
view.SubviewLayout += (s, e) => layoutStartedCount++;
|
||||
|
||||
var layoutCompleteCount = 0;
|
||||
view.SubviewsLaidOut += (s, e) => layoutCompleteCount++;
|
||||
|
||||
view.SetNeedsLayout ();
|
||||
view.Layout ();
|
||||
|
||||
Assert.Equal (0, drawCompleteCount);
|
||||
Assert.Equal (1, layoutStartedCount);
|
||||
Assert.Equal (1, layoutCompleteCount);
|
||||
|
||||
if (view.Visible)
|
||||
{
|
||||
view.SetNeedsDraw ();
|
||||
view.Draw ();
|
||||
|
||||
Assert.Equal (1, drawCompleteCount);
|
||||
Assert.Equal (1, layoutStartedCount);
|
||||
Assert.Equal (1, layoutCompleteCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
413
Tests/UnitTests/View/Draw/ClearViewportTests.cs
Normal file
413
Tests/UnitTests/View/Draw/ClearViewportTests.cs
Normal file
@@ -0,0 +1,413 @@
|
||||
#nullable enable
|
||||
using Moq;
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
[Trait ("Category", "Output")]
|
||||
public class ClearViewportTests (ITestOutputHelper _output)
|
||||
{
|
||||
public class TestableView : View
|
||||
{
|
||||
public bool TestOnClearingViewport () { return OnClearingViewport (); }
|
||||
|
||||
public int OnClearingViewportCalled { get; set; } = 0;
|
||||
public bool CancelOnClearingViewport { get; set; }
|
||||
protected override bool OnClearingViewport ()
|
||||
{
|
||||
OnClearingViewportCalled++;
|
||||
return CancelOnClearingViewport;
|
||||
}
|
||||
|
||||
public int OnClearedViewportCalled { get; set; } = 0;
|
||||
protected override void OnClearedViewport ()
|
||||
{
|
||||
OnClearedViewportCalled++;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoClearViewport_ViewportIsTransparent_DoesNotClear ()
|
||||
{
|
||||
// Arrange
|
||||
Mock<TestableView> view = new Mock<TestableView> { CallBase = true };
|
||||
view.Object.ViewportSettings = ViewportSettings.Transparent;
|
||||
|
||||
// Act
|
||||
view.Object.DoClearViewport ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (0, view.Object.OnClearingViewportCalled);
|
||||
Assert.Equal (0, view.Object.OnClearedViewportCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoClearViewport_OnClearingViewportReturnsTrue_DoesNotClear ()
|
||||
{
|
||||
// Arrange
|
||||
Mock<TestableView> view = new Mock<TestableView> { CallBase = true };
|
||||
view.Object.CancelOnClearingViewport = true;
|
||||
|
||||
// Act
|
||||
view.Object.DoClearViewport ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (0, view.Object.OnClearedViewportCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoClearViewport_ClearingViewportEventCancelled_DoesNotClear ()
|
||||
{
|
||||
// Arrange
|
||||
Mock<TestableView> view = new Mock<TestableView> { CallBase = true };
|
||||
view.Object.ClearingViewport += (sender, e) => e.Cancel = true;
|
||||
|
||||
// Act
|
||||
view.Object.DoClearViewport ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (0, view.Object.OnClearedViewportCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoClearViewport_ClearsViewport ()
|
||||
{
|
||||
// Arrange
|
||||
Mock<TestableView> view = new Mock<TestableView> { CallBase = true };
|
||||
|
||||
// Act
|
||||
view.Object.DoClearViewport ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (1, view.Object.OnClearedViewportCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoClearViewport_RaisesClearingViewportEvent ()
|
||||
{
|
||||
// Arrange
|
||||
Mock<TestableView> view = new Mock<TestableView> { CallBase = true };
|
||||
var eventRaised = false;
|
||||
view.Object.ClearingViewport += (sender, e) => eventRaised = true;
|
||||
|
||||
// Act
|
||||
view.Object.DoClearViewport ();
|
||||
|
||||
// Assert
|
||||
Assert.True (eventRaised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Clear_ClearsEntireViewport ()
|
||||
{
|
||||
var superView = new View { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
var view = new View
|
||||
{
|
||||
Text = "X",
|
||||
X = 1, Y = 1,
|
||||
Width = 3, Height = 3,
|
||||
BorderStyle = LineStyle.Single
|
||||
};
|
||||
superView.Add (view);
|
||||
superView.BeginInit ();
|
||||
superView.EndInit ();
|
||||
superView.LayoutSubviews ();
|
||||
|
||||
superView.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│X│
|
||||
└─┘",
|
||||
_output);
|
||||
|
||||
// On Draw exit the view is excluded from the clip, so this will do nothing.
|
||||
view.ClearViewport ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│X│
|
||||
└─┘",
|
||||
_output);
|
||||
|
||||
View.SetClipToScreen ();
|
||||
|
||||
view.ClearViewport ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│ │
|
||||
└─┘",
|
||||
_output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Clear_WithClearVisibleContentOnly_ClearsVisibleContentOnly ()
|
||||
{
|
||||
var superView = new View { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
var view = new View
|
||||
{
|
||||
Text = "X",
|
||||
X = 1, Y = 1,
|
||||
Width = 3, Height = 3,
|
||||
BorderStyle = LineStyle.Single,
|
||||
ViewportSettings = ViewportSettings.ClearContentOnly
|
||||
};
|
||||
superView.Add (view);
|
||||
superView.BeginInit ();
|
||||
superView.EndInit ();
|
||||
superView.LayoutSubviews ();
|
||||
|
||||
superView.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│X│
|
||||
└─┘",
|
||||
_output);
|
||||
View.SetClipToScreen ();
|
||||
view.ClearViewport ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│ │
|
||||
└─┘",
|
||||
_output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Clear_Viewport_Can_Use_Driver_AddRune_Or_AddStr_Methods ()
|
||||
{
|
||||
var view = new FrameView { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
view.DrawingContent += (s, e) =>
|
||||
{
|
||||
Region savedClip = view.AddViewportToClip ();
|
||||
|
||||
for (var row = 0; row < view.Viewport.Height; row++)
|
||||
{
|
||||
Application.Driver?.Move (1, row + 1);
|
||||
|
||||
for (var col = 0; col < view.Viewport.Width; col++)
|
||||
{
|
||||
Application.Driver?.AddStr ($"{col}");
|
||||
}
|
||||
}
|
||||
|
||||
View.SetClip (savedClip);
|
||||
e.Cancel = true;
|
||||
};
|
||||
var top = new Toplevel ();
|
||||
top.Add (view);
|
||||
Application.Begin (top);
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (20, 10);
|
||||
|
||||
var expected = @"
|
||||
┌──────────────────┐
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
└──────────────────┘
|
||||
"
|
||||
;
|
||||
|
||||
Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, _output);
|
||||
Assert.Equal (new (0, 0, 20, 10), pos);
|
||||
|
||||
view.FillRect (view.Viewport);
|
||||
|
||||
expected = @"
|
||||
┌──────────────────┐
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└──────────────────┘
|
||||
"
|
||||
;
|
||||
|
||||
pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, _output);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Clear_Can_Use_Driver_AddRune_Or_AddStr_Methods ()
|
||||
{
|
||||
var view = new FrameView { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
view.DrawingContent += (s, e) =>
|
||||
{
|
||||
Region savedClip = view.AddViewportToClip ();
|
||||
|
||||
for (var row = 0; row < view.Viewport.Height; row++)
|
||||
{
|
||||
Application.Driver?.Move (1, row + 1);
|
||||
|
||||
for (var col = 0; col < view.Viewport.Width; col++)
|
||||
{
|
||||
Application.Driver?.AddStr ($"{col}");
|
||||
}
|
||||
}
|
||||
|
||||
View.SetClip (savedClip);
|
||||
e.Cancel = true;
|
||||
};
|
||||
var top = new Toplevel ();
|
||||
top.Add (view);
|
||||
Application.Begin (top);
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (20, 10);
|
||||
|
||||
var expected = @"
|
||||
┌──────────────────┐
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
│012345678910111213│
|
||||
└──────────────────┘
|
||||
"
|
||||
;
|
||||
|
||||
Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, _output);
|
||||
Assert.Equal (new (0, 0, 20, 10), pos);
|
||||
|
||||
view.FillRect (view.Viewport);
|
||||
|
||||
expected = @"
|
||||
┌──────────────────┐
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└──────────────────┘
|
||||
";
|
||||
|
||||
pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, _output);
|
||||
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[AutoInitShutdown (configLocation: ConfigLocations.Default)]
|
||||
[InlineData (true)]
|
||||
[InlineData (false)]
|
||||
public void Clear_Does_Not_Spillover_Its_Parent (bool label)
|
||||
{
|
||||
var root = new View { Width = 20, Height = 10, ColorScheme = Colors.ColorSchemes ["Base"] };
|
||||
|
||||
string text = new ('c', 100);
|
||||
|
||||
View v = label
|
||||
|
||||
// Label has Width/Height == AutoSize, so Frame.Size will be (100, 1)
|
||||
? new Label { Text = text }
|
||||
|
||||
// TextView has Width/Height == (Dim.Fill, 1), so Frame.Size will be 20 (width of root), 1
|
||||
: new TextView { Width = Dim.Fill (), Height = 1, Text = text };
|
||||
|
||||
root.Add (v);
|
||||
|
||||
var top = new Toplevel ();
|
||||
top.Add (root);
|
||||
RunState runState = Application.Begin (top);
|
||||
Application.RunIteration (ref runState);
|
||||
|
||||
if (label)
|
||||
{
|
||||
Assert.False (v.CanFocus);
|
||||
Assert.Equal (new (0, 0, text.Length, 1), v.Frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.True (v.CanFocus);
|
||||
Assert.Equal (new (0, 0, 20, 1), v.Frame);
|
||||
}
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
cccccccccccccccccccc",
|
||||
_output
|
||||
);
|
||||
|
||||
Attribute [] attributes =
|
||||
{
|
||||
Colors.ColorSchemes ["TopLevel"].Normal,
|
||||
Colors.ColorSchemes ["Base"].Normal,
|
||||
Colors.ColorSchemes ["Base"].Focus
|
||||
};
|
||||
|
||||
if (label)
|
||||
{
|
||||
DriverAssert.AssertDriverAttributesAre (
|
||||
@"
|
||||
111111111111111111110
|
||||
111111111111111111110",
|
||||
_output,
|
||||
Application.Driver,
|
||||
attributes
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
DriverAssert.AssertDriverAttributesAre (
|
||||
@"
|
||||
222222222222222222220
|
||||
111111111111111111110",
|
||||
_output,
|
||||
Application.Driver,
|
||||
attributes
|
||||
);
|
||||
}
|
||||
|
||||
if (label)
|
||||
{
|
||||
root.CanFocus = true;
|
||||
v.CanFocus = true;
|
||||
Assert.True (v.HasFocus);
|
||||
v.SetFocus ();
|
||||
Assert.True (v.HasFocus);
|
||||
Application.LayoutAndDraw ();
|
||||
|
||||
DriverAssert.AssertDriverAttributesAre (
|
||||
@"
|
||||
222222222222222222220
|
||||
111111111111111111110",
|
||||
_output,
|
||||
Application.Driver,
|
||||
attributes
|
||||
);
|
||||
}
|
||||
|
||||
Application.End (runState);
|
||||
top.Dispose ();
|
||||
}
|
||||
}
|
||||
304
Tests/UnitTests/View/Draw/ClipTests.cs
Normal file
304
Tests/UnitTests/View/Draw/ClipTests.cs
Normal file
@@ -0,0 +1,304 @@
|
||||
#nullable enable
|
||||
using System.Text;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
[Trait ("Category", "Output")]
|
||||
public class ClipTests (ITestOutputHelper _output)
|
||||
{
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Move_Is_Not_Constrained_To_Viewport ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = 3, Height = 3
|
||||
};
|
||||
view.Margin!.Thickness = new (1);
|
||||
|
||||
view.Move (0, 0);
|
||||
Assert.Equal (new (2, 2), new Point (Application.Driver!.Col, Application.Driver!.Row));
|
||||
|
||||
view.Move (-1, -1);
|
||||
Assert.Equal (new (1, 1), new Point (Application.Driver!.Col, Application.Driver!.Row));
|
||||
|
||||
view.Move (1, 1);
|
||||
Assert.Equal (new (3, 3), new Point (Application.Driver!.Col, Application.Driver!.Row));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void AddRune_Is_Constrained_To_Viewport ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = 3, Height = 3
|
||||
};
|
||||
view.Padding!.Thickness = new (1);
|
||||
view.Padding.Diagnostics = ViewDiagnosticFlags.Thickness;
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
view.Draw ();
|
||||
|
||||
// Only valid location w/in Viewport is 0, 0 (view) - 2, 2 (screen)
|
||||
Assert.Equal ((Rune)' ', Application.Driver?.Contents! [2, 2].Rune);
|
||||
|
||||
// When we exit Draw, the view is excluded from the clip. So drawing at 0,0, is not valid and is clipped.
|
||||
view.AddRune (0, 0, Rune.ReplacementChar);
|
||||
Assert.Equal ((Rune)' ', Application.Driver?.Contents! [2, 2].Rune);
|
||||
|
||||
view.AddRune (-1, -1, Rune.ReplacementChar);
|
||||
Assert.Equal ((Rune)'P', Application.Driver?.Contents! [1, 1].Rune);
|
||||
|
||||
view.AddRune (1, 1, Rune.ReplacementChar);
|
||||
Assert.Equal ((Rune)'P', Application.Driver?.Contents! [3, 3].Rune);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, 1, 1)]
|
||||
[InlineData (0, 0, 2, 2)]
|
||||
[InlineData (-1, -1, 2, 2)]
|
||||
[SetupFakeDriver]
|
||||
public void FillRect_Fills_HonorsClip (int x, int y, int width, int height)
|
||||
{
|
||||
var superView = new View { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
var view = new View
|
||||
{
|
||||
Text = "X",
|
||||
X = 1, Y = 1,
|
||||
Width = 3, Height = 3,
|
||||
BorderStyle = LineStyle.Single
|
||||
};
|
||||
superView.Add (view);
|
||||
superView.BeginInit ();
|
||||
superView.EndInit ();
|
||||
superView.LayoutSubviews ();
|
||||
|
||||
superView.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│X│
|
||||
└─┘",
|
||||
_output);
|
||||
|
||||
Rectangle toFill = new (x, y, width, height);
|
||||
View.SetClipToScreen ();
|
||||
view.FillRect (toFill);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│ │
|
||||
└─┘",
|
||||
_output);
|
||||
|
||||
// Now try to clear beyond Viewport (invalid; clipping should prevent)
|
||||
superView.SetNeedsDraw ();
|
||||
superView.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│X│
|
||||
└─┘",
|
||||
_output);
|
||||
toFill = new (-width, -height, width, height);
|
||||
view.FillRect (toFill);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│X│
|
||||
└─┘",
|
||||
_output);
|
||||
|
||||
// Now try to clear beyond Viewport (valid)
|
||||
superView.SetNeedsDraw ();
|
||||
superView.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│X│
|
||||
└─┘",
|
||||
_output);
|
||||
toFill = new (-1, -1, width + 1, height + 1);
|
||||
|
||||
View.SetClipToScreen ();
|
||||
view.FillRect (toFill);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│ │
|
||||
└─┘",
|
||||
_output);
|
||||
|
||||
// Now clear too much size
|
||||
superView.SetNeedsDraw ();
|
||||
superView.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│X│
|
||||
└─┘",
|
||||
_output);
|
||||
toFill = new (0, 0, width * 2, height * 2);
|
||||
View.SetClipToScreen ();
|
||||
view.FillRect (toFill);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌─┐
|
||||
│ │
|
||||
└─┘",
|
||||
_output);
|
||||
}
|
||||
|
||||
// TODO: Simplify this test to just use AddRune directly
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
[Trait ("Category", "Unicode")]
|
||||
public void Clipping_Wide_Runes ()
|
||||
{
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (30, 1);
|
||||
|
||||
var top = new View
|
||||
{
|
||||
Id = "top",
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill ()
|
||||
};
|
||||
|
||||
var frameView = new View
|
||||
{
|
||||
Id = "frameView",
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill (),
|
||||
Text = """
|
||||
これは広いルーンラインです。
|
||||
"""
|
||||
};
|
||||
frameView.Border!.LineStyle = LineStyle.Single;
|
||||
frameView.Border.Thickness = new (1, 0, 0, 0);
|
||||
|
||||
top.Add (frameView);
|
||||
View.SetClipToScreen ();
|
||||
top.Layout ();
|
||||
top.Draw ();
|
||||
|
||||
var expectedOutput = """
|
||||
│これは広いルーンラインです。
|
||||
""";
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (expectedOutput, _output);
|
||||
|
||||
var view = new View
|
||||
{
|
||||
Text = "0123456789",
|
||||
|
||||
//Text = "ワイドルー。",
|
||||
X = 2,
|
||||
Height = Dim.Auto (),
|
||||
Width = Dim.Auto (),
|
||||
BorderStyle = LineStyle.Single
|
||||
};
|
||||
view.Border!.Thickness = new (1, 0, 1, 0);
|
||||
|
||||
top.Add (view);
|
||||
top.Layout ();
|
||||
View.SetClipToScreen ();
|
||||
top.Draw ();
|
||||
|
||||
// 012345678901234567890123456789012345678
|
||||
// 012 34 56 78 90 12 34 56 78 90 12 34 56 78
|
||||
// │こ れ は 広 い ル ー ン ラ イ ン で す 。
|
||||
// 01 2345678901234 56 78 90 12 34 56
|
||||
// │<> |0123456989│<39> ン ラ イ ン で す 。
|
||||
expectedOutput = """
|
||||
│<EFBFBD>│0123456789│<EFBFBD>ンラインです。
|
||||
""";
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (expectedOutput, _output);
|
||||
}
|
||||
|
||||
// TODO: Add more AddRune tests to cover all the cases where wide runes are clipped
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void SetClip_ClipVisibleContentOnly_VisibleContentIsClipped ()
|
||||
{
|
||||
// Screen is 25x25
|
||||
// View is 25x25
|
||||
// Viewport is (0, 0, 23, 23)
|
||||
// ContentSize is (10, 10)
|
||||
// ViewportToScreen is (1, 1, 23, 23)
|
||||
// Visible content is (1, 1, 10, 10)
|
||||
// Expected clip is (1, 1, 10, 10) - same as visible content
|
||||
Rectangle expectedClip = new (1, 1, 10, 10);
|
||||
|
||||
// Arrange
|
||||
var view = new View
|
||||
{
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill (),
|
||||
ViewportSettings = ViewportSettings.ClipContentOnly
|
||||
};
|
||||
view.SetContentSize (new Size (10, 10));
|
||||
view.Border!.Thickness = new (1);
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
Assert.Equal (view.Frame, View.GetClip ()!.GetBounds ());
|
||||
|
||||
// Act
|
||||
view.AddViewportToClip ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (expectedClip, View.GetClip ()!.GetBounds ());
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void SetClip_Default_ClipsToViewport ()
|
||||
{
|
||||
// Screen is 25x25
|
||||
// View is 25x25
|
||||
// Viewport is (0, 0, 23, 23)
|
||||
// ContentSize is (10, 10)
|
||||
// ViewportToScreen is (1, 1, 23, 23)
|
||||
// Visible content is (1, 1, 10, 10)
|
||||
// Expected clip is (1, 1, 23, 23) - same as Viewport
|
||||
Rectangle expectedClip = new (1, 1, 23, 23);
|
||||
|
||||
// Arrange
|
||||
var view = new View
|
||||
{
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill ()
|
||||
};
|
||||
view.SetContentSize (new Size (10, 10));
|
||||
view.Border!.Thickness = new (1);
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
Assert.Equal (view.Frame, View.GetClip ()!.GetBounds ());
|
||||
view.Viewport = view.Viewport with { X = 1, Y = 1 };
|
||||
|
||||
// Act
|
||||
view.AddViewportToClip ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (expectedClip, View.GetClip ()!.GetBounds ());
|
||||
view.Dispose ();
|
||||
}
|
||||
}
|
||||
30
Tests/UnitTests/View/Draw/DrawEventTests.cs
Normal file
30
Tests/UnitTests/View/Draw/DrawEventTests.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
#nullable enable
|
||||
using UnitTests;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
[Trait ("Category", "Output")]
|
||||
public class DrawEventTests
|
||||
{
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void DrawContentComplete_Event_Is_Always_Called ()
|
||||
{
|
||||
var viewCalled = false;
|
||||
var tvCalled = false;
|
||||
|
||||
var view = new View { Width = 10, Height = 10, Text = "View" };
|
||||
view.DrawComplete += (s, e) => viewCalled = true;
|
||||
var tv = new TextView { Y = 11, Width = 10, Height = 10 };
|
||||
tv.DrawComplete += (s, e) => tvCalled = true;
|
||||
|
||||
var top = new Toplevel ();
|
||||
top.Add (view, tv);
|
||||
RunState runState = Application.Begin (top);
|
||||
Application.RunIteration (ref runState);
|
||||
|
||||
Assert.True (viewCalled);
|
||||
Assert.True (tvCalled);
|
||||
top.Dispose ();
|
||||
}
|
||||
}
|
||||
932
Tests/UnitTests/View/Draw/DrawTests.cs
Normal file
932
Tests/UnitTests/View/Draw/DrawTests.cs
Normal file
@@ -0,0 +1,932 @@
|
||||
#nullable enable
|
||||
using System.Text;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
[Trait ("Category", "Output")]
|
||||
public class DrawTests (ITestOutputHelper _output)
|
||||
{
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
[Trait ("Category", "Unicode")]
|
||||
public void CJK_Compatibility_Ideographs_ConsoleWidth_ColumnWidth_Equal_Two ()
|
||||
{
|
||||
const string us = "\U0000f900";
|
||||
var r = (Rune)0xf900;
|
||||
|
||||
Assert.Equal ("豈", us);
|
||||
Assert.Equal ("豈", r.ToString ());
|
||||
Assert.Equal (us, r.ToString ());
|
||||
|
||||
Assert.Equal (2, us.GetColumns ());
|
||||
Assert.Equal (2, r.GetColumns ());
|
||||
|
||||
var win = new Window { Title = us };
|
||||
var view = new View { Text = r.ToString (), Height = Dim.Fill (), Width = Dim.Fill () };
|
||||
var tf = new TextField { Text = us, Y = 1, Width = 3 };
|
||||
win.Add (view, tf);
|
||||
Toplevel top = new ();
|
||||
top.Add (win);
|
||||
|
||||
Application.Begin (top);
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (10, 4);
|
||||
|
||||
const string expectedOutput = """
|
||||
|
||||
┌┤豈├────┐
|
||||
│豈 │
|
||||
│豈 │
|
||||
└────────┘
|
||||
""";
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (expectedOutput, _output);
|
||||
|
||||
DriverAssert.AssertDriverContentsAre (expectedOutput, _output);
|
||||
|
||||
// This test has nothing to do with color - removing as it is not relevant and fragile
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
[Trait ("Category", "Output")]
|
||||
public void Colors_On_TextAlignment_Right_And_Bottom ()
|
||||
{
|
||||
var viewRight = new View
|
||||
{
|
||||
Text = "Test",
|
||||
Width = 6,
|
||||
Height = 1,
|
||||
TextAlignment = Alignment.End,
|
||||
ColorScheme = Colors.ColorSchemes ["Base"]
|
||||
};
|
||||
|
||||
var viewBottom = new View
|
||||
{
|
||||
Text = "Test",
|
||||
TextDirection = TextDirection.TopBottom_LeftRight,
|
||||
Y = 1,
|
||||
Width = 1,
|
||||
Height = 6,
|
||||
VerticalTextAlignment = Alignment.End,
|
||||
ColorScheme = Colors.ColorSchemes ["Base"]
|
||||
};
|
||||
Toplevel top = new ();
|
||||
top.Add (viewRight, viewBottom);
|
||||
|
||||
var rs = Application.Begin (top);
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (7, 7);
|
||||
Application.RunIteration (ref rs);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
Test
|
||||
|
||||
|
||||
T
|
||||
e
|
||||
s
|
||||
t
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
DriverAssert.AssertDriverAttributesAre (
|
||||
"""
|
||||
|
||||
000000
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
""",
|
||||
_output,
|
||||
Application.Driver,
|
||||
Colors.ColorSchemes ["Base"]!.Normal
|
||||
);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Draw_Minimum_Full_Border_With_Empty_Viewport ()
|
||||
{
|
||||
var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
|
||||
Assert.True (view.NeedsLayout);
|
||||
Assert.True (view.NeedsDraw);
|
||||
view.Layout ();
|
||||
|
||||
Assert.Equal (new (0, 0, 2, 2), view.Frame);
|
||||
Assert.Equal (Rectangle.Empty, view.Viewport);
|
||||
|
||||
Assert.True (view.NeedsDraw);
|
||||
view.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
┌┐
|
||||
└┘
|
||||
""",
|
||||
_output
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Bottom ()
|
||||
{
|
||||
var view = new View { Width = 2, Height = 1, BorderStyle = LineStyle.Single };
|
||||
view.Border!.Thickness = new (1, 1, 1, 0);
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
view.SetRelativeLayout (Application.Screen.Size);
|
||||
|
||||
Assert.Equal (new (0, 0, 2, 1), view.Frame);
|
||||
Assert.Equal (Rectangle.Empty, view.Viewport);
|
||||
|
||||
view.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre ("──", _output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Left ()
|
||||
{
|
||||
var view = new View { Width = 1, Height = 2, BorderStyle = LineStyle.Single };
|
||||
view.Border!.Thickness = new (0, 1, 1, 1);
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
view.SetRelativeLayout (Application.Screen.Size);
|
||||
|
||||
Assert.Equal (new (0, 0, 1, 2), view.Frame);
|
||||
Assert.Equal (Rectangle.Empty, view.Viewport);
|
||||
|
||||
view.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
│
|
||||
│
|
||||
""",
|
||||
_output
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Right ()
|
||||
{
|
||||
var view = new View { Width = 1, Height = 2, BorderStyle = LineStyle.Single };
|
||||
view.Border!.Thickness = new (1, 1, 0, 1);
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
view.SetRelativeLayout (Application.Screen.Size);
|
||||
|
||||
Assert.Equal (new (0, 0, 1, 2), view.Frame);
|
||||
Assert.Equal (Rectangle.Empty, view.Viewport);
|
||||
|
||||
view.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
│
|
||||
│
|
||||
""",
|
||||
_output
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Draw_Minimum_Full_Border_With_Empty_Viewport_Without_Top ()
|
||||
{
|
||||
var view = new View { Width = 2, Height = 1, BorderStyle = LineStyle.Single };
|
||||
view.Border!.Thickness = new (1, 0, 1, 1);
|
||||
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
view.SetRelativeLayout (Application.Screen.Size);
|
||||
|
||||
Assert.Equal (new (0, 0, 2, 1), view.Frame);
|
||||
Assert.Equal (Rectangle.Empty, view.Viewport);
|
||||
|
||||
view.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"││",
|
||||
_output
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Draw_Negative_Viewport_Horizontal_With_New_Lines ()
|
||||
{
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
X = 1,
|
||||
Width = 1,
|
||||
Height = 7,
|
||||
Text = """
|
||||
s
|
||||
u
|
||||
b
|
||||
V
|
||||
i
|
||||
e
|
||||
w
|
||||
"""
|
||||
};
|
||||
|
||||
var view = new View
|
||||
{
|
||||
Id = "view", Width = 2, Height = 20, Text = """
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
"""
|
||||
};
|
||||
view.Add (subView);
|
||||
var content = new View { Id = "content", Width = 20, Height = 20 };
|
||||
content.Add (view);
|
||||
|
||||
var container = new View
|
||||
{
|
||||
Id = "container",
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = 5,
|
||||
Height = 5
|
||||
};
|
||||
container.Add (content);
|
||||
Toplevel top = new ();
|
||||
top.Add (container);
|
||||
var rs = Application.Begin (top);
|
||||
|
||||
top.Draw ();
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
0s
|
||||
1u
|
||||
2b
|
||||
3V
|
||||
4i
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.X = -1;
|
||||
Application.LayoutAndDraw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
s
|
||||
u
|
||||
b
|
||||
V
|
||||
i
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.X = -2;
|
||||
Application.LayoutAndDraw ();
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (@"", _output);
|
||||
|
||||
content.X = 0;
|
||||
content.Y = -1;
|
||||
Application.LayoutAndDraw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
1u
|
||||
2b
|
||||
3V
|
||||
4i
|
||||
5e
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.Y = -6;
|
||||
Application.LayoutAndDraw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
6w
|
||||
7
|
||||
8
|
||||
9
|
||||
0
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.Y = -19;
|
||||
Application.LayoutAndDraw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
9
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.Y = -20;
|
||||
Application.LayoutAndDraw ();
|
||||
DriverAssert.AssertDriverContentsWithFrameAre ("", _output);
|
||||
|
||||
content.X = -2;
|
||||
content.Y = 0;
|
||||
Application.LayoutAndDraw ();
|
||||
DriverAssert.AssertDriverContentsWithFrameAre ("", _output);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Draw_Negative_Viewport_Horizontal_Without_New_Lines ()
|
||||
{
|
||||
// BUGBUG: This previously assumed the default height of a View was 1.
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
Y = 1,
|
||||
Width = 7,
|
||||
Height = 1,
|
||||
Text = "subView"
|
||||
};
|
||||
var view = new View { Id = "view", Width = 20, Height = 2, Text = "01234567890123456789" };
|
||||
view.Add (subView);
|
||||
var content = new View { Id = "content", Width = 20, Height = 20 };
|
||||
content.Add (view);
|
||||
|
||||
var container = new View
|
||||
{
|
||||
Id = "container",
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = 5,
|
||||
Height = 5
|
||||
};
|
||||
container.Add (content);
|
||||
Toplevel top = new ();
|
||||
top.Add (container);
|
||||
|
||||
// BUGBUG: v2 - it's bogus to reference .Frame before BeginInit. And why is the clip being set anyway???
|
||||
|
||||
top.SubviewsLaidOut += Top_LayoutComplete;
|
||||
Application.Begin (top);
|
||||
|
||||
Application.LayoutAndDraw ();
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
01234
|
||||
subVi
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.X = -1;
|
||||
Application.LayoutAndDraw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
12345
|
||||
ubVie
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.Y = -1;
|
||||
Application.LayoutAndDraw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
ubVie
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.Y = -2;
|
||||
Application.LayoutAndDraw ();
|
||||
DriverAssert.AssertDriverContentsWithFrameAre ("", _output);
|
||||
|
||||
content.X = -20;
|
||||
content.Y = 0;
|
||||
Application.LayoutAndDraw ();
|
||||
DriverAssert.AssertDriverContentsWithFrameAre ("", _output);
|
||||
top.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void Top_LayoutComplete (object? sender, LayoutEventArgs e) { Application.Driver!.Clip = new (container.Frame); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Draw_Negative_Viewport_Vertical ()
|
||||
{
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
X = 1,
|
||||
Width = 1,
|
||||
Height = 7,
|
||||
Text = "subView",
|
||||
TextDirection = TextDirection.TopBottom_LeftRight
|
||||
};
|
||||
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
Width = 2,
|
||||
Height = 20,
|
||||
Text = "01234567890123456789",
|
||||
TextDirection = TextDirection.TopBottom_LeftRight
|
||||
};
|
||||
view.Add (subView);
|
||||
var content = new View { Id = "content", Width = 20, Height = 20 };
|
||||
content.Add (view);
|
||||
|
||||
var container = new View
|
||||
{
|
||||
Id = "container",
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = 5,
|
||||
Height = 5
|
||||
};
|
||||
container.Add (content);
|
||||
Toplevel top = new ();
|
||||
top.Add (container);
|
||||
Application.Begin (top);
|
||||
|
||||
Application.LayoutAndDraw ();
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
0s
|
||||
1u
|
||||
2b
|
||||
3V
|
||||
4i
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.X = -1;
|
||||
Application.LayoutAndDraw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
s
|
||||
u
|
||||
b
|
||||
V
|
||||
i
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.X = -2;
|
||||
Application.LayoutAndDraw ();
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (@"", _output);
|
||||
|
||||
content.X = 0;
|
||||
content.Y = -1;
|
||||
Application.LayoutAndDraw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
1u
|
||||
2b
|
||||
3V
|
||||
4i
|
||||
5e
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.Y = -6;
|
||||
Application.LayoutAndDraw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
6w
|
||||
7
|
||||
8
|
||||
9
|
||||
0
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.Y = -19;
|
||||
Application.LayoutAndDraw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
"""
|
||||
|
||||
9
|
||||
""",
|
||||
_output
|
||||
);
|
||||
|
||||
content.Y = -20;
|
||||
Application.LayoutAndDraw ();
|
||||
DriverAssert.AssertDriverContentsWithFrameAre ("", _output);
|
||||
|
||||
content.X = -2;
|
||||
content.Y = 0;
|
||||
Application.LayoutAndDraw ();
|
||||
DriverAssert.AssertDriverContentsWithFrameAre ("", _output);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[SetupFakeDriver]
|
||||
[InlineData ("𝔽𝕆𝕆𝔹𝔸R")]
|
||||
[InlineData ("a𐐀b")]
|
||||
public void DrawHotString_NonBmp (string expected)
|
||||
{
|
||||
var view = new View { Width = 10, Height = 1 };
|
||||
view.DrawHotString (expected, Attribute.Default, Attribute.Default);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (expected, _output);
|
||||
}
|
||||
|
||||
// TODO: The tests below that use Label should use View instead.
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Non_Bmp_ConsoleWidth_ColumnWidth_Equal_Two ()
|
||||
{
|
||||
var us = "\U0001d539";
|
||||
var r = (Rune)0x1d539;
|
||||
|
||||
Assert.Equal ("𝔹", us);
|
||||
Assert.Equal ("𝔹", r.ToString ());
|
||||
Assert.Equal (us, r.ToString ());
|
||||
|
||||
Assert.Equal (1, us.GetColumns ());
|
||||
Assert.Equal (1, r.GetColumns ());
|
||||
|
||||
var win = new Window { Title = us };
|
||||
var view = new Label { Text = r.ToString () };
|
||||
var tf = new TextField { Text = us, Y = 1, Width = 3 };
|
||||
win.Add (view, tf);
|
||||
Toplevel top = new ();
|
||||
top.Add (win);
|
||||
|
||||
Application.Begin (top);
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (10, 4);
|
||||
|
||||
var expected = """
|
||||
|
||||
┌┤𝔹├─────┐
|
||||
│𝔹 │
|
||||
│𝔹 │
|
||||
└────────┘
|
||||
""";
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (expected, _output);
|
||||
|
||||
DriverAssert.AssertDriverContentsAre (expected, _output);
|
||||
top.Dispose ();
|
||||
|
||||
// This test has nothing to do with color - removing as it is not relevant and fragile
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void Draw_Throws_IndexOutOfRangeException_With_Negative_Bounds ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
Toplevel top = new ();
|
||||
|
||||
var view = new View { X = -2, Text = "view" };
|
||||
top.Add (view);
|
||||
|
||||
Application.Iteration += (s, a) =>
|
||||
{
|
||||
Assert.Equal (-2, view.X);
|
||||
|
||||
Application.RequestStop ();
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Application.Run (top);
|
||||
}
|
||||
catch (IndexOutOfRangeException ex)
|
||||
{
|
||||
// After the fix this exception will not be caught.
|
||||
Assert.IsType<IndexOutOfRangeException> (ex);
|
||||
}
|
||||
|
||||
top.Dispose ();
|
||||
|
||||
// Shutdown must be called to safely clean up Application if Init has been called
|
||||
Application.Shutdown ();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Correct_Redraw_Viewport_NeedDisplay_On_Shrink_And_Move_Down_Right_Using_Frame ()
|
||||
{
|
||||
var label = new Label { Text = "At 0,0" };
|
||||
|
||||
var view = new DerivedView
|
||||
{
|
||||
X = 2,
|
||||
Y = 2,
|
||||
Width = 30,
|
||||
Height = 2,
|
||||
Text = "A text with some long width\n and also with two lines."
|
||||
};
|
||||
Toplevel top = new ();
|
||||
top.Add (label, view);
|
||||
RunState runState = Application.Begin (top);
|
||||
Application.RunIteration (ref runState);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
At 0,0
|
||||
|
||||
A text with some long width
|
||||
and also with two lines. "
|
||||
,
|
||||
_output
|
||||
);
|
||||
|
||||
view.Frame = new (3, 3, 10, 1);
|
||||
Assert.Equal (new (3, 3, 10, 1), view.Frame);
|
||||
Assert.Equal (new (0, 0, 10, 1), view.Viewport);
|
||||
Assert.Equal (new (0, 0, 10, 1), view._needsDrawRect);
|
||||
//Application.Refresh();
|
||||
top.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
At 0,0
|
||||
|
||||
|
||||
A text wit",
|
||||
_output
|
||||
);
|
||||
Application.End (runState);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Correct_Redraw_Viewport_NeedDisplay_On_Shrink_And_Move_Down_Right_Using_Pos_Dim ()
|
||||
{
|
||||
var label = new Label { Text = "At 0,0" };
|
||||
|
||||
var view = new DerivedView
|
||||
{
|
||||
X = 2,
|
||||
Y = 2,
|
||||
Width = 30,
|
||||
Height = 2,
|
||||
Text = "A text with some long width\n and also with two lines."
|
||||
};
|
||||
Toplevel top = new ();
|
||||
top.Add (label, view);
|
||||
RunState runState = Application.Begin (top);
|
||||
|
||||
top.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
At 0,0
|
||||
|
||||
A text with some long width
|
||||
and also with two lines. "
|
||||
,
|
||||
_output
|
||||
);
|
||||
|
||||
view.X = 3;
|
||||
view.Y = 3;
|
||||
view.Width = 10;
|
||||
view.Height = 1;
|
||||
Assert.Equal (new (3, 3, 10, 1), view.Frame);
|
||||
Assert.Equal (new (0, 0, 10, 1), view.Viewport);
|
||||
Assert.Equal (new (0, 0, 10, 1), view._needsDrawRect);
|
||||
View.SetClipToScreen ();
|
||||
top.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
At 0,0
|
||||
|
||||
|
||||
A text wit"
|
||||
,
|
||||
_output
|
||||
);
|
||||
Application.End (runState);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Correct_Redraw_Viewport_NeedDisplay_On_Shrink_And_Move_Up_Left_Using_Frame ()
|
||||
{
|
||||
var label = new Label { Text = "At 0,0" };
|
||||
|
||||
var view = new DerivedView
|
||||
{
|
||||
X = 2,
|
||||
Y = 2,
|
||||
Width = 30,
|
||||
Height = 2,
|
||||
Text = "A text with some long width\n and also with two lines."
|
||||
};
|
||||
Toplevel top = new ();
|
||||
top.Add (label, view);
|
||||
RunState runState = Application.Begin (top);
|
||||
Application.RunIteration (ref runState);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
At 0,0
|
||||
|
||||
A text with some long width
|
||||
and also with two lines. "
|
||||
,
|
||||
_output
|
||||
);
|
||||
|
||||
view.Frame = new (1, 1, 10, 1);
|
||||
Assert.Equal (new (1, 1, 10, 1), view.Frame);
|
||||
Assert.Equal (new (0, 0, 10, 1), view.Viewport);
|
||||
Assert.Equal (new (0, 0, 10, 1), view._needsDrawRect);
|
||||
top.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
At 0,0
|
||||
A text wit"
|
||||
,
|
||||
_output
|
||||
);
|
||||
Application.End (runState);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Correct_Redraw_Viewport_NeedDisplay_On_Shrink_And_Move_Up_Left_Using_Pos_Dim ()
|
||||
{
|
||||
var label = new Label { Text = "At 0,0" };
|
||||
|
||||
var view = new DerivedView
|
||||
{
|
||||
X = 2,
|
||||
Y = 2,
|
||||
Width = 30,
|
||||
Height = 2,
|
||||
Text = "A text with some long width\n and also with two lines."
|
||||
};
|
||||
Toplevel top = new ();
|
||||
top.Add (label, view);
|
||||
RunState runState = Application.Begin (top);
|
||||
|
||||
top.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
At 0,0
|
||||
|
||||
A text with some long width
|
||||
and also with two lines. "
|
||||
,
|
||||
_output
|
||||
);
|
||||
|
||||
view.X = 1;
|
||||
view.Y = 1;
|
||||
view.Width = 10;
|
||||
view.Height = 1;
|
||||
Assert.Equal (new (1, 1, 10, 1), view.Frame);
|
||||
Assert.Equal (new (0, 0, 10, 1), view.Viewport);
|
||||
Assert.Equal (new (0, 0, 10, 1), view._needsDrawRect);
|
||||
View.SetClipToScreen ();
|
||||
|
||||
top.Draw ();
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
At 0,0
|
||||
A text wit"
|
||||
,
|
||||
_output
|
||||
);
|
||||
Application.End (runState);
|
||||
top.Dispose ();
|
||||
}
|
||||
public class DerivedView : View
|
||||
{
|
||||
public DerivedView () { CanFocus = true; }
|
||||
public bool IsKeyDown { get; set; }
|
||||
public bool IsKeyPress { get; set; }
|
||||
public bool IsKeyUp { get; set; }
|
||||
public override string Text { get; set; }
|
||||
|
||||
protected override bool OnDrawingContent ()
|
||||
{
|
||||
var idx = 0;
|
||||
|
||||
// BUGBUG: v2 - this should use Viewport, not Frame
|
||||
for (var r = 0; r < Frame.Height; r++)
|
||||
{
|
||||
for (var c = 0; c < Frame.Width; c++)
|
||||
{
|
||||
if (idx < Text.Length)
|
||||
{
|
||||
char rune = Text [idx];
|
||||
|
||||
if (rune != '\n')
|
||||
{
|
||||
AddRune (c, r, (Rune)Text [idx]);
|
||||
}
|
||||
|
||||
idx++;
|
||||
|
||||
if (rune == '\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearNeedsDraw ();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown (Key keyEvent)
|
||||
{
|
||||
IsKeyDown = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool OnKeyUp (Key keyEvent)
|
||||
{
|
||||
IsKeyUp = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnKeyDownNotHandled (Key keyEvent)
|
||||
{
|
||||
IsKeyPress = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
67
Tests/UnitTests/View/Draw/NeedsDrawTests.cs
Normal file
67
Tests/UnitTests/View/Draw/NeedsDrawTests.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
#nullable enable
|
||||
using UnitTests;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
[Trait ("Category", "Output")]
|
||||
public class NeedsDrawTests ()
|
||||
{
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Frame_Set_After_Initialize_Update_NeededDisplay ()
|
||||
{
|
||||
var frame = new FrameView ();
|
||||
|
||||
var label = new Label
|
||||
{
|
||||
ColorScheme = Colors.ColorSchemes ["Menu"], X = 0, Y = 0, Text = "This should be the first line."
|
||||
};
|
||||
|
||||
var view = new View
|
||||
{
|
||||
X = 0, // don't overcomplicate unit tests
|
||||
Y = 1,
|
||||
Height = Dim.Auto (DimAutoStyle.Text),
|
||||
Width = Dim.Auto (DimAutoStyle.Text),
|
||||
Text = "Press me!"
|
||||
};
|
||||
|
||||
frame.Add (label, view);
|
||||
|
||||
frame.X = Pos.Center ();
|
||||
frame.Y = Pos.Center ();
|
||||
frame.Width = 40;
|
||||
frame.Height = 8;
|
||||
|
||||
Toplevel top = new ();
|
||||
|
||||
top.Add (frame);
|
||||
|
||||
RunState runState = Application.Begin (top);
|
||||
|
||||
top.SubviewsLaidOut += (s, e) => { Assert.Equal (new (0, 0, 80, 25), top._needsDrawRect); };
|
||||
|
||||
frame.SubviewsLaidOut += (s, e) => { Assert.Equal (new (0, 0, 40, 8), frame._needsDrawRect); };
|
||||
|
||||
label.SubviewsLaidOut += (s, e) => { Assert.Equal (new (0, 0, 38, 1), label._needsDrawRect); };
|
||||
|
||||
view.SubviewsLaidOut += (s, e) => { Assert.Equal (new (0, 0, 13, 1), view._needsDrawRect); };
|
||||
|
||||
Assert.Equal (new (0, 0, 80, 25), top.Frame);
|
||||
Assert.Equal (new (20, 8, 40, 8), frame.Frame);
|
||||
|
||||
Assert.Equal (
|
||||
new (20, 8, 60, 16),
|
||||
new Rectangle (
|
||||
frame.Frame.Left,
|
||||
frame.Frame.Top,
|
||||
frame.Frame.Right,
|
||||
frame.Frame.Bottom
|
||||
)
|
||||
);
|
||||
Assert.Equal (new (0, 0, 30, 1), label.Frame);
|
||||
Assert.Equal (new (0, 1, 9, 1), view.Frame); // this proves frame was set
|
||||
Application.End (runState);
|
||||
top.Dispose ();
|
||||
}
|
||||
}
|
||||
109
Tests/UnitTests/View/Draw/TransparentTests.cs
Normal file
109
Tests/UnitTests/View/Draw/TransparentTests.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
#nullable enable
|
||||
using System.Text;
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
[Trait ("Category", "Output")]
|
||||
public class TransparentTests (ITestOutputHelper _output)
|
||||
{
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
|
||||
public void Transparent_Text_Occludes ()
|
||||
{
|
||||
var super = new View
|
||||
{
|
||||
Id = "super",
|
||||
Width = 20,
|
||||
Height = 5,
|
||||
};
|
||||
super.DrawingContent += (sender, args) =>
|
||||
{
|
||||
var s = sender as View;
|
||||
s!.FillRect(s!.Viewport, Glyphs.Stipple);
|
||||
args.Cancel = true;
|
||||
};
|
||||
|
||||
var sub = new View
|
||||
{
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = 15,
|
||||
Height = 3,
|
||||
Id = "sub",
|
||||
Text = "Sub",
|
||||
ViewportSettings = ViewportSettings.Transparent,
|
||||
BorderStyle = LineStyle.Single
|
||||
};
|
||||
|
||||
super.Add (sub);
|
||||
|
||||
super.Layout ();
|
||||
super.Draw ();
|
||||
|
||||
_ = DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
░░░░░░░░░░░░░░░░░░░░
|
||||
░┌─────────────┐░░░░
|
||||
░│Sub░░░░░░░░░░│░░░░
|
||||
░└─────────────┘░░░░
|
||||
░░░░░░░░░░░░░░░░░░░░", _output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
|
||||
public void Transparent_Subview_Occludes ()
|
||||
{
|
||||
var super = new View
|
||||
{
|
||||
Id = "super",
|
||||
Width = 20,
|
||||
Height = 5,
|
||||
};
|
||||
super.DrawingContent += (sender, args) =>
|
||||
{
|
||||
var s = sender as View;
|
||||
s!.FillRect (s!.Viewport, Glyphs.Stipple);
|
||||
args.Cancel = true;
|
||||
};
|
||||
|
||||
var sub = new View
|
||||
{
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = 15,
|
||||
Height = 3,
|
||||
Id = "sub",
|
||||
ViewportSettings = ViewportSettings.Transparent,
|
||||
BorderStyle = LineStyle.Single
|
||||
};
|
||||
|
||||
var subSub = new View
|
||||
{
|
||||
X = Pos.Center(),
|
||||
Y = Pos.Center(),
|
||||
Width = Dim.Auto(),
|
||||
Height = Dim.Auto(),
|
||||
Id = "subSub",
|
||||
Text = "subSub",
|
||||
};
|
||||
sub.Add (subSub);
|
||||
|
||||
super.Add (sub);
|
||||
|
||||
super.Layout ();
|
||||
super.Draw ();
|
||||
|
||||
_ = DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
░░░░░░░░░░░░░░░░░░░░
|
||||
░┌─────────────┐░░░░
|
||||
░│░░░subSub░░░░│░░░░
|
||||
░└─────────────┘░░░░
|
||||
░░░░░░░░░░░░░░░░░░░░", _output);
|
||||
}
|
||||
}
|
||||
165
Tests/UnitTests/View/InitTests.cs
Normal file
165
Tests/UnitTests/View/InitTests.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
/// <summary>Tests View BeginInit/EndInit/Initialized functionality.</summary>
|
||||
public class InitTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
public InitTests (ITestOutputHelper output) { _output = output; }
|
||||
|
||||
// Test behavior of calling BeginInit multiple times
|
||||
[Fact]
|
||||
public void BeginInit_Called_Multiple_Times_Throws ()
|
||||
{
|
||||
var view = new View ();
|
||||
var superView = new View ();
|
||||
superView.Add (view);
|
||||
|
||||
Assert.False (view.IsInitialized, "View should not be initialized");
|
||||
Assert.False (superView.IsInitialized, "SuperView should not be initialized");
|
||||
|
||||
superView.BeginInit ();
|
||||
superView.EndInit ();
|
||||
Assert.True (view.IsInitialized, "View should be initialized");
|
||||
Assert.True (superView.IsInitialized, "SuperView should be initialized");
|
||||
|
||||
Assert.Throws<InvalidOperationException> (() => superView.BeginInit ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BeginInit_EndInit_Initialized ()
|
||||
{
|
||||
var view = new View ();
|
||||
Assert.False (view.IsInitialized, "View should not be initialized");
|
||||
view.BeginInit ();
|
||||
Assert.False (view.IsInitialized, "View should not be initialized");
|
||||
view.EndInit ();
|
||||
Assert.True (view.IsInitialized, "View should be initialized");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BeginInit_EndInit_Initialized_WithSuperView ()
|
||||
{
|
||||
var view = new View ();
|
||||
var superView = new View ();
|
||||
superView.Add (view);
|
||||
Assert.False (view.IsInitialized, "View should not be initialized");
|
||||
view.BeginInit ();
|
||||
Assert.False (view.IsInitialized, "View should not be initialized");
|
||||
view.EndInit ();
|
||||
Assert.True (view.IsInitialized, "View should be initialized");
|
||||
|
||||
Assert.False (superView.IsInitialized, "SuperView should not be initialized");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BeginInit_EndInit_SuperView_Initialized ()
|
||||
{
|
||||
var view = new View ();
|
||||
var superView = new View ();
|
||||
superView.Add (view);
|
||||
Assert.False (view.IsInitialized, "View should not be initialized");
|
||||
Assert.False (superView.IsInitialized, "SuperView should not be initialized");
|
||||
superView.BeginInit ();
|
||||
Assert.False (view.IsInitialized, "View should not be initialized");
|
||||
Assert.False (superView.IsInitialized, "SuperView should not be initialized");
|
||||
superView.EndInit ();
|
||||
Assert.True (view.IsInitialized, "View should be initialized");
|
||||
Assert.True (superView.IsInitialized, "SuperView should be initialized");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BeginInit_EndInit_SuperView_Initialized_WithSuperSuperView ()
|
||||
{
|
||||
var view = new View ();
|
||||
var superView = new View ();
|
||||
var superSuperView = new View ();
|
||||
superSuperView.Add (superView);
|
||||
superView.Add (view);
|
||||
Assert.False (view.IsInitialized, "View should not be initialized");
|
||||
Assert.False (superView.IsInitialized, "SuperView should not be initialized");
|
||||
Assert.False (superSuperView.IsInitialized, "SuperSuperView should not be initialized");
|
||||
superSuperView.BeginInit ();
|
||||
Assert.False (view.IsInitialized, "View should not be initialized");
|
||||
Assert.False (superView.IsInitialized, "SuperView should not be initialized");
|
||||
Assert.False (superSuperView.IsInitialized, "SuperSuperView should not be initialized");
|
||||
superSuperView.EndInit ();
|
||||
Assert.True (view.IsInitialized, "View should be initialized");
|
||||
Assert.True (superView.IsInitialized, "SuperView should be initialized");
|
||||
Assert.True (superSuperView.IsInitialized, "SuperSuperView should be initialized");
|
||||
}
|
||||
|
||||
// Test behavior of calling EndInit multiple times
|
||||
[Fact]
|
||||
public void EndInit_Called_Multiple_Times_Throws ()
|
||||
{
|
||||
var view = new View ();
|
||||
var superView = new View ();
|
||||
superView.Add (view);
|
||||
|
||||
var initialized = false;
|
||||
view.Initialized += (s, e) => initialized = true;
|
||||
|
||||
var superViewInitialized = false;
|
||||
superView.Initialized += (s, e) => superViewInitialized = true;
|
||||
|
||||
Assert.False (view.IsInitialized, "View should not be initialized");
|
||||
Assert.False (superView.IsInitialized, "SuperView should not be initialized");
|
||||
|
||||
superView.BeginInit ();
|
||||
superView.EndInit ();
|
||||
Assert.True (view.IsInitialized, "View should be initialized");
|
||||
Assert.True (superView.IsInitialized, "SuperView should be initialized");
|
||||
Assert.True (initialized, "View: Initialized event should have been raised");
|
||||
Assert.True (superViewInitialized, "SuperView: Initialized event should have been raised");
|
||||
|
||||
Assert.Throws<InvalidOperationException> (() => superView.EndInit ());
|
||||
}
|
||||
|
||||
// Test calling EndInit without first calling BeginInit
|
||||
[Fact]
|
||||
public void EndInit_Called_Without_BeginInit_Throws ()
|
||||
{
|
||||
var view = new View ();
|
||||
var superView = new View ();
|
||||
superView.Add (view);
|
||||
|
||||
//var initialized = false;
|
||||
//view.Initialized += (s, e) => initialized = true;
|
||||
|
||||
//var superViewInitialized = false;
|
||||
//superView.Initialized += (s, e) => superViewInitialized = true;
|
||||
|
||||
Assert.False (view.IsInitialized, "View should not be initialized");
|
||||
Assert.False (superView.IsInitialized, "SuperView should not be initialized");
|
||||
|
||||
// TODO: Implement logic that does this in Begin/EndInit
|
||||
//Assert.Throws<InvalidOperationException> (() => superView.EndInit ());
|
||||
}
|
||||
|
||||
// Initialized event
|
||||
[Fact]
|
||||
public void InitializedEvent_Fires_On_EndInit ()
|
||||
{
|
||||
var view = new View ();
|
||||
var superView = new View ();
|
||||
superView.Add (view);
|
||||
var initialized = false;
|
||||
view.Initialized += (s, e) => initialized = true;
|
||||
|
||||
var superViewInitialized = false;
|
||||
superView.Initialized += (s, e) => superViewInitialized = true;
|
||||
|
||||
Assert.False (initialized, "View: Initialized event should not have been raised");
|
||||
Assert.False (superViewInitialized, "SuperView: Initialized event should not have been raised");
|
||||
superView.BeginInit ();
|
||||
Assert.False (initialized, "View: Initialized event should not have been raised");
|
||||
Assert.False (superViewInitialized, "SuperView: Initialized event should not have been raised");
|
||||
superView.EndInit ();
|
||||
Assert.True (initialized, "View: Initialized event should have been raised");
|
||||
Assert.True (superViewInitialized, "SuperView: Initialized event should have been raised");
|
||||
}
|
||||
|
||||
// TODO: Create tests that prove ISupportInitialize and ISupportInitializeNotifications work properly
|
||||
}
|
||||
421
Tests/UnitTests/View/Keyboard/HotKeyTests.cs
Normal file
421
Tests/UnitTests/View/Keyboard/HotKeyTests.cs
Normal file
@@ -0,0 +1,421 @@
|
||||
using System.Text;
|
||||
using UICatalog.Scenarios;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class HotKeyTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
public HotKeyTests (ITestOutputHelper output) { _output = output; }
|
||||
|
||||
[Theory]
|
||||
[InlineData (KeyCode.A)]
|
||||
[InlineData (KeyCode.A | KeyCode.ShiftMask)]
|
||||
[InlineData (KeyCode.D1)]
|
||||
[InlineData (KeyCode.D1 | KeyCode.ShiftMask)] // '!'
|
||||
[InlineData ((KeyCode)'х')] // Cyrillic x
|
||||
[InlineData ((KeyCode)'你')] // Chinese ni
|
||||
public void AddKeyBindingsForHotKey_Sets (KeyCode key)
|
||||
{
|
||||
var view = new View ();
|
||||
view.HotKey = KeyCode.Z;
|
||||
Assert.Equal (string.Empty, view.Title);
|
||||
Assert.Equal (KeyCode.Z, view.HotKey);
|
||||
|
||||
view.AddKeyBindingsForHotKey (KeyCode.Null, key);
|
||||
|
||||
// Verify key bindings were set
|
||||
|
||||
// As passed
|
||||
Command [] commands = view.HotKeyBindings.GetCommands (key);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
commands = view.HotKeyBindings.GetCommands (key | KeyCode.AltMask);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
|
||||
KeyCode baseKey = key & ~KeyCode.ShiftMask;
|
||||
|
||||
// If A...Z, with and without shift
|
||||
if (baseKey is >= KeyCode.A and <= KeyCode.Z)
|
||||
{
|
||||
commands = view.HotKeyBindings.GetCommands (key | KeyCode.ShiftMask);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
commands = view.HotKeyBindings.GetCommands (key & ~KeyCode.ShiftMask);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
commands = view.HotKeyBindings.GetCommands (key | KeyCode.AltMask);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
commands = view.HotKeyBindings.GetCommands ((key & ~KeyCode.ShiftMask) | KeyCode.AltMask);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non A..Z keys should not have shift bindings
|
||||
if (key.HasFlag (KeyCode.ShiftMask))
|
||||
{
|
||||
commands = view.HotKeyBindings.GetCommands (key & ~KeyCode.ShiftMask);
|
||||
Assert.Empty (commands);
|
||||
}
|
||||
else
|
||||
{
|
||||
commands = view.HotKeyBindings.GetCommands (key | KeyCode.ShiftMask);
|
||||
Assert.Empty (commands);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddKeyBindingsForHotKey_SetsBinding_Key ()
|
||||
{
|
||||
var view = new View ();
|
||||
view.HotKey = KeyCode.Z;
|
||||
Assert.Equal (string.Empty, view.Title);
|
||||
Assert.Equal (KeyCode.Z, view.HotKey);
|
||||
|
||||
view.AddKeyBindingsForHotKey (view.HotKey, Key.A);
|
||||
view.HotKeyBindings.TryGet (Key.A, out var binding);
|
||||
Assert.Equal (Key.A, binding.Key);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddKeyBindingsForHotKey_SetsBinding_Data ()
|
||||
{
|
||||
var view = new View ();
|
||||
view.HotKey = KeyCode.Z;
|
||||
Assert.Equal (KeyCode.Z, view.HotKey);
|
||||
|
||||
view.AddKeyBindingsForHotKey (view.HotKey, Key.A, "data");
|
||||
view.HotKeyBindings.TryGet (Key.A, out var binding);
|
||||
Assert.Equal ("data", binding.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Defaults ()
|
||||
{
|
||||
var view = new View ();
|
||||
Assert.Equal (string.Empty, view.Title);
|
||||
Assert.Equal (KeyCode.Null, view.HotKey);
|
||||
|
||||
// Verify key bindings were set
|
||||
Command [] commands = view.KeyBindings.GetCommands (KeyCode.Null);
|
||||
Assert.Empty (commands);
|
||||
|
||||
commands = view.HotKeyBindings.GetCommands (KeyCode.Null);
|
||||
Assert.Empty (commands);
|
||||
|
||||
Assert.Empty (view.HotKeyBindings.GetBindings ());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (KeyCode.Null, true)] // non-shift
|
||||
[InlineData (KeyCode.ShiftMask, true)]
|
||||
[InlineData (KeyCode.AltMask, true)]
|
||||
[InlineData (KeyCode.ShiftMask | KeyCode.AltMask, true)]
|
||||
[InlineData (KeyCode.CtrlMask, false)]
|
||||
[InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask, false)]
|
||||
public void NewKeyDownEvent_Runs_Default_HotKey_Command (KeyCode mask, bool expected)
|
||||
{
|
||||
var view = new View { HotKeySpecifier = (Rune)'^', Title = "^Test" };
|
||||
view.CanFocus = true;
|
||||
Assert.False (view.HasFocus);
|
||||
view.NewKeyDownEvent (KeyCode.T | mask);
|
||||
Assert.Equal (expected, view.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewKeyDownEvent_Ignores_Focus_KeyBindings_SuperView ()
|
||||
{
|
||||
var view = new View ();
|
||||
view.HotKeyBindings.Add (Key.A, Command.HotKey);
|
||||
view.KeyDownNotHandled += (s, e) => { Assert.Fail (); };
|
||||
|
||||
var superView = new View ();
|
||||
superView.Add (view);
|
||||
|
||||
var ke = Key.A;
|
||||
superView.NewKeyDownEvent (ke);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewKeyDownEvent_Honors_HotKey_KeyBindings_SuperView ()
|
||||
{
|
||||
var view = new View ();
|
||||
view.HotKeyBindings.Add (Key.A, Command.HotKey);
|
||||
bool hotKeyInvoked = false;
|
||||
view.HandlingHotKey += (s, e) => { hotKeyInvoked = true; };
|
||||
|
||||
bool notHandled = false;
|
||||
view.KeyDownNotHandled += (s, e) => { notHandled = true; };
|
||||
|
||||
var superView = new View ();
|
||||
superView.Add (view);
|
||||
|
||||
var ke = Key.A;
|
||||
superView.NewKeyDownEvent (ke);
|
||||
|
||||
Assert.False (notHandled);
|
||||
Assert.True (hotKeyInvoked);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void NewKeyDownEvent_InNewKeyDownEvent_Invokes_HotKey_Command_With_SuperView ()
|
||||
{
|
||||
var superView = new View ()
|
||||
{
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var view1 = new View
|
||||
{
|
||||
HotKeySpecifier = (Rune)'^',
|
||||
Title = "view^1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var view2 = new View
|
||||
{
|
||||
HotKeySpecifier = (Rune)'^',
|
||||
Title = "view^2",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
superView.Add (view1, view2);
|
||||
|
||||
superView.SetFocus ();
|
||||
Assert.True (view1.HasFocus);
|
||||
|
||||
var ke = Key.D2;
|
||||
superView.NewKeyDownEvent (ke);
|
||||
Assert.True (view2.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Set_RemovesOldKeyBindings ()
|
||||
{
|
||||
var view = new View ();
|
||||
view.HotKey = KeyCode.A;
|
||||
Assert.Equal (string.Empty, view.Title);
|
||||
Assert.Equal (KeyCode.A, view.HotKey);
|
||||
|
||||
// Verify key bindings were set
|
||||
Command [] commands = view.HotKeyBindings.GetCommands (KeyCode.A);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
|
||||
commands = view.HotKeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
|
||||
commands = view.HotKeyBindings.GetCommands (KeyCode.A | KeyCode.AltMask);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
|
||||
commands = view.HotKeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
|
||||
// Now set again
|
||||
view.HotKey = KeyCode.B;
|
||||
Assert.Equal (string.Empty, view.Title);
|
||||
Assert.Equal (KeyCode.B, view.HotKey);
|
||||
|
||||
commands = view.HotKeyBindings.GetCommands (KeyCode.A);
|
||||
Assert.DoesNotContain (Command.HotKey, commands);
|
||||
|
||||
commands = view.HotKeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask);
|
||||
Assert.DoesNotContain (Command.HotKey, commands);
|
||||
|
||||
commands = view.HotKeyBindings.GetCommands (KeyCode.A | KeyCode.AltMask);
|
||||
Assert.DoesNotContain (Command.HotKey, commands);
|
||||
|
||||
commands = view.HotKeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask);
|
||||
Assert.DoesNotContain (Command.HotKey, commands);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (KeyCode.A)]
|
||||
[InlineData (KeyCode.A | KeyCode.ShiftMask)]
|
||||
[InlineData (KeyCode.D1)]
|
||||
[InlineData (KeyCode.D1 | KeyCode.ShiftMask)]
|
||||
[InlineData ((KeyCode)'!')]
|
||||
[InlineData ((KeyCode)'х')] // Cyrillic x
|
||||
[InlineData ((KeyCode)'你')] // Chinese ni
|
||||
[InlineData ((KeyCode)'ö')] // German o umlaut
|
||||
[InlineData (KeyCode.Null)]
|
||||
public void Set_Sets_WithValidKey (KeyCode key)
|
||||
{
|
||||
var view = new View ();
|
||||
view.HotKey = key;
|
||||
Assert.Equal (key, view.HotKey);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (KeyCode.A)]
|
||||
[InlineData (KeyCode.A | KeyCode.ShiftMask)]
|
||||
[InlineData (KeyCode.D1)]
|
||||
[InlineData (KeyCode.D1 | KeyCode.ShiftMask)] // '!'
|
||||
[InlineData ((KeyCode)'х')] // Cyrillic x
|
||||
[InlineData ((KeyCode)'你')] // Chinese ni
|
||||
[InlineData ((KeyCode)'ö')] // German o umlaut
|
||||
public void Set_SetsKeyBindings (KeyCode key)
|
||||
{
|
||||
var view = new View ();
|
||||
view.HotKey = key;
|
||||
Assert.Equal (string.Empty, view.Title);
|
||||
Assert.Equal (key, view.HotKey);
|
||||
|
||||
// Verify key bindings were set
|
||||
|
||||
// As passed
|
||||
Command [] commands = view.HotKeyBindings.GetCommands (view.HotKey);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
|
||||
Key baseKey = view.HotKey.NoShift;
|
||||
|
||||
// If A...Z, with and without shift
|
||||
if (baseKey.IsKeyCodeAtoZ)
|
||||
{
|
||||
commands = view.HotKeyBindings.GetCommands (view.HotKey.WithShift);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
commands = view.HotKeyBindings.GetCommands (view.HotKey.NoShift);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
commands = view.HotKeyBindings.GetCommands (view.HotKey.WithAlt);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
commands = view.HotKeyBindings.GetCommands (view.HotKey.NoShift.WithAlt);
|
||||
Assert.Contains (Command.HotKey, commands);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non A..Z keys should not have shift bindings
|
||||
if (view.HotKey.IsShift)
|
||||
{
|
||||
commands = view.HotKeyBindings.GetCommands (view.HotKey.NoShift);
|
||||
Assert.Empty (commands);
|
||||
}
|
||||
else
|
||||
{
|
||||
commands = view.HotKeyBindings.GetCommands (view.HotKey.WithShift);
|
||||
Assert.Empty (commands);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Set_Throws_If_Modifiers_Are_Included ()
|
||||
{
|
||||
var view = new View ();
|
||||
|
||||
// A..Z must be naked (Alt is assumed)
|
||||
view.HotKey = Key.A.WithAlt;
|
||||
Assert.Throws<ArgumentException> (() => view.HotKey = Key.A.WithCtrl);
|
||||
|
||||
Assert.Throws<ArgumentException> (
|
||||
() =>
|
||||
view.HotKey =
|
||||
KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask
|
||||
);
|
||||
|
||||
// All others must not have Ctrl (Alt is assumed)
|
||||
view.HotKey = Key.D1.WithAlt;
|
||||
Assert.Throws<ArgumentException> (() => view.HotKey = Key.D1.WithCtrl);
|
||||
|
||||
Assert.Throws<ArgumentException> (
|
||||
() =>
|
||||
view.HotKey =
|
||||
KeyCode.D1 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask
|
||||
);
|
||||
|
||||
// Shift is ok (e.g. this is '!')
|
||||
view.HotKey = Key.D1.WithShift;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (KeyCode.Delete)]
|
||||
[InlineData (KeyCode.Backspace)]
|
||||
[InlineData (KeyCode.Tab)]
|
||||
[InlineData (KeyCode.Enter)]
|
||||
[InlineData (KeyCode.Esc)]
|
||||
[InlineData (KeyCode.Space)]
|
||||
[InlineData (KeyCode.CursorLeft)]
|
||||
[InlineData (KeyCode.F1)]
|
||||
[InlineData (KeyCode.Null | KeyCode.ShiftMask)]
|
||||
public void Set_Throws_With_Invalid_Key (KeyCode key)
|
||||
{
|
||||
var view = new View ();
|
||||
Assert.Throws<ArgumentException> (() => view.HotKey = key);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData ("Test", KeyCode.Null)]
|
||||
[InlineData ("^Test", KeyCode.T)]
|
||||
[InlineData ("T^est", KeyCode.E)]
|
||||
[InlineData ("Te^st", KeyCode.S)]
|
||||
[InlineData ("Tes^t", KeyCode.T)]
|
||||
[InlineData ("other", KeyCode.Null)]
|
||||
[InlineData ("oTher", KeyCode.Null)]
|
||||
[InlineData ("^Öther", (KeyCode)'Ö')]
|
||||
[InlineData ("^öther", (KeyCode)'ö')]
|
||||
|
||||
// BUGBUG: '!' should be supported. Line 968 of TextFormatter filters on char.IsLetterOrDigit
|
||||
//[InlineData ("Test^!", (Key)'!')]
|
||||
public void Title_Change_Sets_HotKey (string title, KeyCode expectedHotKey)
|
||||
{
|
||||
var view = new View { HotKeySpecifier = new Rune ('^'), Title = "^Hello" };
|
||||
Assert.Equal (KeyCode.H, view.HotKey);
|
||||
|
||||
view.Title = title;
|
||||
Assert.Equal (expectedHotKey, view.HotKey);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData ("^Test")]
|
||||
public void Title_Empty_Sets_HotKey_To_Null (string title)
|
||||
{
|
||||
var view = new View { HotKeySpecifier = (Rune)'^', Title = title };
|
||||
|
||||
Assert.Equal (title, view.Title);
|
||||
Assert.Equal (KeyCode.T, view.HotKey);
|
||||
|
||||
view.Title = string.Empty;
|
||||
Assert.Equal ("", view.Title);
|
||||
Assert.Equal (KeyCode.Null, view.HotKey);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void HotKey_Raises_HotKeyCommand ()
|
||||
{
|
||||
var hotKeyRaised = false;
|
||||
var acceptRaised = false;
|
||||
var selectRaised = false;
|
||||
Application.Top = new Toplevel ();
|
||||
var view = new View
|
||||
{
|
||||
CanFocus = true,
|
||||
HotKeySpecifier = new Rune ('_'),
|
||||
Title = "_Test"
|
||||
};
|
||||
Application.Top.Add (view);
|
||||
view.HandlingHotKey += (s, e) => hotKeyRaised = true;
|
||||
view.Accepting += (s, e) => acceptRaised = true;
|
||||
view.Selecting += (s, e) => selectRaised = true;
|
||||
|
||||
Assert.Equal (KeyCode.T, view.HotKey);
|
||||
Assert.True (Application.RaiseKeyDownEvent (Key.T));
|
||||
Assert.True (hotKeyRaised);
|
||||
Assert.False (acceptRaised);
|
||||
Assert.False (selectRaised);
|
||||
|
||||
hotKeyRaised = false;
|
||||
Assert.True (Application.RaiseKeyDownEvent (Key.T.WithAlt));
|
||||
Assert.True (hotKeyRaised);
|
||||
Assert.False (acceptRaised);
|
||||
Assert.False (selectRaised);
|
||||
|
||||
hotKeyRaised = false;
|
||||
view.HotKey = KeyCode.E;
|
||||
Assert.True (Application.RaiseKeyDownEvent (Key.E.WithAlt));
|
||||
Assert.True (hotKeyRaised);
|
||||
Assert.False (acceptRaised);
|
||||
Assert.False (selectRaised);
|
||||
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
}
|
||||
151
Tests/UnitTests/View/Keyboard/KeyBindingsTests.cs
Normal file
151
Tests/UnitTests/View/Keyboard/KeyBindingsTests.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for View.KeyBindings
|
||||
/// </summary>
|
||||
public class KeyBindingsTests ()
|
||||
{
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Focused_HotKey_Application_All_Work ()
|
||||
{
|
||||
var view = new ScopedKeyBindingView ();
|
||||
var keyWasHandled = false;
|
||||
view.KeyDownNotHandled += (s, e) => keyWasHandled = true;
|
||||
|
||||
var top = new Toplevel ();
|
||||
top.Add (view);
|
||||
Application.Begin (top);
|
||||
|
||||
Application.RaiseKeyDownEvent (Key.A);
|
||||
Assert.False (keyWasHandled);
|
||||
Assert.True (view.ApplicationCommand);
|
||||
|
||||
keyWasHandled = false;
|
||||
Application.RaiseKeyDownEvent (Key.H);
|
||||
Assert.True (view.HotKeyCommand);
|
||||
Assert.False (keyWasHandled);
|
||||
|
||||
keyWasHandled = false;
|
||||
Assert.False (view.HasFocus);
|
||||
Application.RaiseKeyDownEvent (Key.F);
|
||||
Assert.False (keyWasHandled);
|
||||
Assert.False (view.FocusedCommand);
|
||||
|
||||
keyWasHandled = false;
|
||||
view.CanFocus = true;
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Application.RaiseKeyDownEvent (Key.F);
|
||||
Assert.True (view.FocusedCommand);
|
||||
Assert.False (keyWasHandled); // Command was invoked, but wasn't handled
|
||||
|
||||
Assert.True (view.ApplicationCommand);
|
||||
Assert.True (view.HotKeyCommand);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void KeyBinding_Negative ()
|
||||
{
|
||||
var view = new ScopedKeyBindingView ();
|
||||
var keyWasHandled = false;
|
||||
view.KeyDownNotHandled += (s, e) => keyWasHandled = true;
|
||||
|
||||
var top = new Toplevel ();
|
||||
top.Add (view);
|
||||
Application.Begin (top);
|
||||
|
||||
Application.RaiseKeyDownEvent (Key.Z);
|
||||
Assert.False (keyWasHandled);
|
||||
Assert.False (view.ApplicationCommand);
|
||||
Assert.False (view.HotKeyCommand);
|
||||
Assert.False (view.FocusedCommand);
|
||||
|
||||
keyWasHandled = false;
|
||||
Assert.False (view.HasFocus);
|
||||
Application.RaiseKeyDownEvent (Key.F);
|
||||
Assert.False (keyWasHandled);
|
||||
Assert.False (view.ApplicationCommand);
|
||||
Assert.False (view.HotKeyCommand);
|
||||
Assert.False (view.FocusedCommand);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void HotKey_KeyBinding ()
|
||||
{
|
||||
var view = new ScopedKeyBindingView ();
|
||||
var keyWasHandled = false;
|
||||
view.KeyDownNotHandled += (s, e) => keyWasHandled = true;
|
||||
|
||||
var top = new Toplevel ();
|
||||
top.Add (view);
|
||||
Application.Begin (top);
|
||||
|
||||
keyWasHandled = false;
|
||||
Application.RaiseKeyDownEvent (Key.H);
|
||||
Assert.True (view.HotKeyCommand);
|
||||
Assert.False (keyWasHandled);
|
||||
|
||||
view.HotKey = KeyCode.Z;
|
||||
keyWasHandled = false;
|
||||
view.HotKeyCommand = false;
|
||||
Application.RaiseKeyDownEvent (Key.H); // old hot key
|
||||
Assert.False (keyWasHandled);
|
||||
Assert.False (view.HotKeyCommand);
|
||||
|
||||
Application.RaiseKeyDownEvent (Key.Z); // new hot key
|
||||
Assert.True (view.HotKeyCommand);
|
||||
Assert.False (keyWasHandled);
|
||||
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void HotKey_KeyBinding_Negative ()
|
||||
{
|
||||
var view = new ScopedKeyBindingView ();
|
||||
var keyWasHandled = false;
|
||||
view.KeyDownNotHandled += (s, e) => keyWasHandled = true;
|
||||
|
||||
var top = new Toplevel ();
|
||||
top.Add (view);
|
||||
Application.Begin (top);
|
||||
|
||||
Application.RaiseKeyDownEvent (Key.Z);
|
||||
Assert.False (keyWasHandled);
|
||||
Assert.False (view.HotKeyCommand);
|
||||
|
||||
keyWasHandled = false;
|
||||
Application.RaiseKeyDownEvent (Key.F);
|
||||
Assert.False (view.HotKeyCommand);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
// tests that test KeyBindingScope.Focus and KeyBindingScope.HotKey (tests for KeyBindingScope.Application are in Application/KeyboardTests.cs)
|
||||
|
||||
public class ScopedKeyBindingView : View
|
||||
{
|
||||
public ScopedKeyBindingView ()
|
||||
{
|
||||
AddCommand (Command.Save, () => ApplicationCommand = true);
|
||||
AddCommand (Command.HotKey, () => HotKeyCommand = true);
|
||||
AddCommand (Command.Left, () => FocusedCommand = true);
|
||||
|
||||
Application.KeyBindings.Add (Key.A, this, Command.Save);
|
||||
HotKey = KeyCode.H;
|
||||
KeyBindings.Add (Key.F, Command.Left);
|
||||
}
|
||||
|
||||
public bool ApplicationCommand { get; set; }
|
||||
public bool FocusedCommand { get; set; }
|
||||
public bool HotKeyCommand { get; set; }
|
||||
}
|
||||
}
|
||||
333
Tests/UnitTests/View/Keyboard/KeyboardEventTests.cs
Normal file
333
Tests/UnitTests/View/Keyboard/KeyboardEventTests.cs
Normal file
@@ -0,0 +1,333 @@
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
// Alias Console to MockConsole so we don't accidentally use Console
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
|
||||
{
|
||||
/// <summary>
|
||||
/// This tests that when a new key down event is sent to the view will fire the key-down related
|
||||
/// events: KeyDown and KeyDownNotHandled. Note that KeyUp is independent.
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[SetupFakeDriver] // Required for spinner view that wants to register timeouts
|
||||
[MemberData (nameof (AllViewTypes))]
|
||||
public void AllViews_NewKeyDownEvent_All_EventsFire (Type viewType)
|
||||
{
|
||||
var view = CreateInstanceIfNotGeneric (viewType);
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
output.WriteLine ($"ERROR: Skipping generic view: {viewType}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
output.WriteLine ($"Testing {viewType}");
|
||||
|
||||
var keyDown = false;
|
||||
|
||||
view.KeyDown += (s, a) =>
|
||||
{
|
||||
a.Handled = false; // don't handle it so the other events are called
|
||||
keyDown = true;
|
||||
};
|
||||
|
||||
var keyDownNotHandled = false;
|
||||
|
||||
view.KeyDownNotHandled += (s, a) =>
|
||||
{
|
||||
a.Handled = true;
|
||||
keyDownNotHandled = true;
|
||||
};
|
||||
|
||||
// Key.Empty is invalid, but it's used here to test that the event is fired
|
||||
Assert.True (view.NewKeyDownEvent (Key.Empty)); // this will be true because the ProcessKeyDown event handled it
|
||||
Assert.True (keyDown);
|
||||
Assert.True (keyDownNotHandled);
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This tests that when a new key up event is sent to the view the view will fire the 1 key-up related event:
|
||||
/// KeyUp
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[SetupFakeDriver] // Required for spinner view that wants to register timeouts
|
||||
[MemberData (nameof (AllViewTypes))]
|
||||
public void AllViews_NewKeyUpEvent_All_EventsFire (Type viewType)
|
||||
{
|
||||
var view = CreateInstanceIfNotGeneric (viewType);
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
output.WriteLine ($"ERROR: Generic view {viewType}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
output.WriteLine ($"Testing {view.GetType ().Name}");
|
||||
|
||||
var keyUp = false;
|
||||
|
||||
view.KeyUp += (s, a) =>
|
||||
{
|
||||
a.Handled = true;
|
||||
keyUp = true;
|
||||
};
|
||||
|
||||
Assert.True (view.NewKeyUpEvent (Key.A)); // this will be true because the KeyUp event handled it
|
||||
Assert.True (keyUp);
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (true, false, false)]
|
||||
[InlineData (true, true, false)]
|
||||
[InlineData (true, true, true)]
|
||||
public void NewKeyDownUpEvents_Events_Are_Raised_With_Only_Key_Modifiers (bool shift, bool alt, bool control)
|
||||
{
|
||||
var keyDown = false;
|
||||
var keyDownNotHandled = false;
|
||||
var keyUp = false;
|
||||
|
||||
var view = new OnNewKeyTestView ();
|
||||
view.CancelVirtualMethods = false;
|
||||
|
||||
view.KeyDown += (s, e) =>
|
||||
{
|
||||
Assert.Equal (KeyCode.Null, e.KeyCode & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask);
|
||||
Assert.Equal (shift, e.IsShift);
|
||||
Assert.Equal (alt, e.IsAlt);
|
||||
Assert.Equal (control, e.IsCtrl);
|
||||
Assert.False (keyDown);
|
||||
Assert.True (view.OnKeyDownCalled);
|
||||
keyDown = true;
|
||||
};
|
||||
view.KeyDownNotHandled += (s, e) => { keyDownNotHandled = true; };
|
||||
|
||||
view.KeyUp += (s, e) =>
|
||||
{
|
||||
Assert.Equal (KeyCode.Null, e.KeyCode & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask);
|
||||
Assert.Equal (shift, e.IsShift);
|
||||
Assert.Equal (alt, e.IsAlt);
|
||||
Assert.Equal (control, e.IsCtrl);
|
||||
Assert.False (keyUp);
|
||||
Assert.True (view.OnKeyUpCalled);
|
||||
keyUp = true;
|
||||
};
|
||||
|
||||
view.NewKeyDownEvent (
|
||||
new (
|
||||
KeyCode.Null
|
||||
| (shift ? KeyCode.ShiftMask : 0)
|
||||
| (alt ? KeyCode.AltMask : 0)
|
||||
| (control ? KeyCode.CtrlMask : 0)
|
||||
)
|
||||
);
|
||||
Assert.True (keyDownNotHandled);
|
||||
Assert.True (view.OnKeyDownCalled);
|
||||
Assert.True (view.OnProcessKeyDownCalled);
|
||||
|
||||
view.NewKeyUpEvent (
|
||||
new (
|
||||
KeyCode.Null
|
||||
| (shift ? KeyCode.ShiftMask : 0)
|
||||
| (alt ? KeyCode.AltMask : 0)
|
||||
| (control ? KeyCode.CtrlMask : 0)
|
||||
)
|
||||
);
|
||||
Assert.True (keyUp);
|
||||
Assert.True (view.OnKeyUpCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewKeyDownEvent_Handled_True_Stops_Processing ()
|
||||
{
|
||||
var keyDown = false;
|
||||
var keyDownNotHandled = false;
|
||||
|
||||
var view = new OnNewKeyTestView ();
|
||||
Assert.True (view.CanFocus);
|
||||
view.CancelVirtualMethods = false;
|
||||
|
||||
view.KeyDown += (s, e) =>
|
||||
{
|
||||
Assert.Equal (KeyCode.A, e.KeyCode);
|
||||
Assert.False (keyDown);
|
||||
Assert.True (view.OnKeyDownCalled);
|
||||
e.Handled = true;
|
||||
keyDown = true;
|
||||
};
|
||||
|
||||
|
||||
view.KeyDownNotHandled += (s, e) =>
|
||||
{
|
||||
Assert.Equal (KeyCode.A, e.KeyCode);
|
||||
Assert.False (keyDownNotHandled);
|
||||
Assert.False (view.OnProcessKeyDownCalled);
|
||||
e.Handled = true;
|
||||
keyDownNotHandled = true;
|
||||
};
|
||||
|
||||
view.NewKeyDownEvent (Key.A);
|
||||
Assert.True (keyDown);
|
||||
Assert.False (keyDownNotHandled);
|
||||
|
||||
Assert.True (view.OnKeyDownCalled);
|
||||
Assert.False (view.OnProcessKeyDownCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewKeyDownEvent_KeyDown_Handled_Stops_Processing ()
|
||||
{
|
||||
var view = new View ();
|
||||
var keyDownNotHandled = false;
|
||||
var setHandledTo = false;
|
||||
|
||||
view.KeyDown += (s, e) =>
|
||||
{
|
||||
e.Handled = setHandledTo;
|
||||
Assert.Equal (setHandledTo, e.Handled);
|
||||
Assert.Equal (KeyCode.N, e.KeyCode);
|
||||
};
|
||||
|
||||
view.KeyDownNotHandled += (s, e) =>
|
||||
{
|
||||
keyDownNotHandled = true;
|
||||
Assert.False (e.Handled);
|
||||
Assert.Equal (KeyCode.N, e.KeyCode);
|
||||
};
|
||||
|
||||
view.NewKeyDownEvent (Key.N);
|
||||
Assert.True (keyDownNotHandled);
|
||||
|
||||
keyDownNotHandled = false;
|
||||
setHandledTo = true;
|
||||
view.NewKeyDownEvent (Key.N);
|
||||
Assert.False (keyDownNotHandled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewKeyDownEvent_ProcessKeyDown_Handled_Stops_Processing ()
|
||||
{
|
||||
var keyDown = false;
|
||||
var keyDownNotHandled = false;
|
||||
|
||||
var view = new OnNewKeyTestView ();
|
||||
Assert.True (view.CanFocus);
|
||||
view.CancelVirtualMethods = false;
|
||||
|
||||
view.KeyDown += (s, e) =>
|
||||
{
|
||||
Assert.Equal (KeyCode.A, e.KeyCode);
|
||||
Assert.False (keyDown);
|
||||
Assert.True (view.OnKeyDownCalled);
|
||||
e.Handled = false;
|
||||
keyDown = true;
|
||||
};
|
||||
|
||||
view.KeyDownNotHandled += (s, e) =>
|
||||
{
|
||||
Assert.Equal (KeyCode.A, e.KeyCode);
|
||||
Assert.False (keyDownNotHandled);
|
||||
Assert.True (view.OnProcessKeyDownCalled);
|
||||
e.Handled = true;
|
||||
keyDownNotHandled = true;
|
||||
};
|
||||
|
||||
view.NewKeyDownEvent (Key.A);
|
||||
Assert.True (keyDown);
|
||||
Assert.True (keyDownNotHandled);
|
||||
|
||||
Assert.True (view.OnKeyDownCalled);
|
||||
Assert.True (view.OnProcessKeyDownCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewKeyUpEvent_KeyUp_Handled_True_Stops_Processing ()
|
||||
{
|
||||
var keyUp = false;
|
||||
|
||||
var view = new OnNewKeyTestView ();
|
||||
Assert.True (view.CanFocus);
|
||||
view.CancelVirtualMethods = false;
|
||||
|
||||
view.KeyUp += (s, e) =>
|
||||
{
|
||||
Assert.Equal (KeyCode.A, e.KeyCode);
|
||||
Assert.False (keyUp);
|
||||
Assert.False (view.OnProcessKeyDownCalled);
|
||||
e.Handled = true;
|
||||
keyUp = true;
|
||||
};
|
||||
|
||||
view.NewKeyUpEvent (Key.A);
|
||||
Assert.True (keyUp);
|
||||
|
||||
Assert.True (view.OnKeyUpCalled);
|
||||
Assert.False (view.OnKeyDownCalled);
|
||||
Assert.False (view.OnProcessKeyDownCalled);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (null, null)]
|
||||
[InlineData (true, true)]
|
||||
[InlineData (false, false)]
|
||||
public void InvokeCommands_Returns_Nullable_Properly (bool? toReturn, bool? expected)
|
||||
{
|
||||
var view = new KeyBindingsTestView ();
|
||||
view.CommandReturns = toReturn;
|
||||
|
||||
bool? result = view.InvokeCommands (Key.A);
|
||||
Assert.Equal (expected, result);
|
||||
}
|
||||
|
||||
/// <summary>A view that overrides the OnKey* methods so we can test that they are called.</summary>
|
||||
public class KeyBindingsTestView : View
|
||||
{
|
||||
public KeyBindingsTestView ()
|
||||
{
|
||||
CanFocus = true;
|
||||
AddCommand (Command.HotKey, () => CommandReturns);
|
||||
KeyBindings.Add (Key.A, Command.HotKey);
|
||||
}
|
||||
|
||||
public bool? CommandReturns { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>A view that overrides the OnKey* methods so we can test that they are called.</summary>
|
||||
public class OnNewKeyTestView : View
|
||||
{
|
||||
public OnNewKeyTestView () { CanFocus = true; }
|
||||
public bool CancelVirtualMethods { set; private get; }
|
||||
public bool OnKeyDownCalled { get; set; }
|
||||
public bool OnProcessKeyDownCalled { get; set; }
|
||||
public bool OnKeyUpCalled { get; set; }
|
||||
public override string Text { get; set; }
|
||||
|
||||
protected override bool OnKeyDown (Key keyEvent)
|
||||
{
|
||||
OnKeyDownCalled = true;
|
||||
|
||||
return CancelVirtualMethods;
|
||||
}
|
||||
|
||||
public override bool OnKeyUp (Key keyEvent)
|
||||
{
|
||||
OnKeyUpCalled = true;
|
||||
|
||||
return CancelVirtualMethods;
|
||||
}
|
||||
|
||||
protected override bool OnKeyDownNotHandled (Key keyEvent)
|
||||
{
|
||||
OnProcessKeyDownCalled = true;
|
||||
|
||||
return CancelVirtualMethods;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Tests/UnitTests/View/Layout/Dim.FillTests.cs
Normal file
28
Tests/UnitTests/View/Layout/Dim.FillTests.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.LayoutTests;
|
||||
|
||||
public class DimFillTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void DimFill_SizedCorrectly ()
|
||||
{
|
||||
var view = new View { Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Single };
|
||||
var top = new Toplevel ();
|
||||
top.Add (view);
|
||||
RunState rs = Application.Begin (top);
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (32, 5);
|
||||
|
||||
top.Layout ();
|
||||
|
||||
//view.SetRelativeLayout (new (0, 0, 32, 5));
|
||||
Assert.Equal (32, view.Frame.Width);
|
||||
Assert.Equal (5, view.Frame.Height);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
}
|
||||
256
Tests/UnitTests/View/Layout/Dim.Tests.cs
Normal file
256
Tests/UnitTests/View/Layout/Dim.Tests.cs
Normal file
@@ -0,0 +1,256 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
using static Terminal.Gui.Dim;
|
||||
|
||||
namespace Terminal.Gui.LayoutTests;
|
||||
|
||||
public class DimTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public DimTests (ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
Console.OutputEncoding = Encoding.Default;
|
||||
|
||||
// Change current culture
|
||||
var culture = CultureInfo.CreateSpecificCulture ("en-US");
|
||||
Thread.CurrentThread.CurrentCulture = culture;
|
||||
Thread.CurrentThread.CurrentUICulture = culture;
|
||||
}
|
||||
|
||||
|
||||
// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
|
||||
// TODO: A new test that calls SetRelativeLayout directly is needed.
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Only_DimAbsolute_And_DimFactor_As_A_Different_Procedure_For_Assigning_Value_To_Width_Or_Height ()
|
||||
{
|
||||
// Override CM
|
||||
Button.DefaultShadow = ShadowStyle.None;
|
||||
|
||||
// Testing with the Button because it properly handles the Dim class.
|
||||
Toplevel t = new ();
|
||||
|
||||
var w = new Window { Width = 100, Height = 100 };
|
||||
|
||||
var f1 = new FrameView
|
||||
{
|
||||
X = 0,
|
||||
Y = 0,
|
||||
Width = Percent (50),
|
||||
Height = 5,
|
||||
Title = "f1"
|
||||
};
|
||||
|
||||
var f2 = new FrameView
|
||||
{
|
||||
X = Pos.Right (f1),
|
||||
Y = 0,
|
||||
Width = Fill (),
|
||||
Height = 5,
|
||||
Title = "f2"
|
||||
};
|
||||
|
||||
var v1 = new Button
|
||||
{
|
||||
X = Pos.X (f1) + 2,
|
||||
Y = Pos.Bottom (f1) + 2,
|
||||
Width = Width (f1) - 2,
|
||||
Height = Fill () - 2,
|
||||
ValidatePosDim = true,
|
||||
Text = "v1"
|
||||
};
|
||||
|
||||
var v2 = new Button
|
||||
{
|
||||
X = Pos.X (f2) + 2,
|
||||
Y = Pos.Bottom (f2) + 2,
|
||||
Width = Width (f2) - 2,
|
||||
Height = Fill () - 2,
|
||||
ValidatePosDim = true,
|
||||
Text = "v2"
|
||||
};
|
||||
|
||||
var v3 = new Button
|
||||
{
|
||||
Width = Percent (10),
|
||||
Height = Percent (10),
|
||||
ValidatePosDim = true,
|
||||
Text = "v3"
|
||||
};
|
||||
|
||||
var v4 = new Button
|
||||
{
|
||||
Width = Absolute (50),
|
||||
Height = Absolute (50),
|
||||
ValidatePosDim = true,
|
||||
Text = "v4"
|
||||
};
|
||||
|
||||
var v5 = new Button
|
||||
{
|
||||
Width = Width (v1) - Width (v3),
|
||||
Height = Height (v1) - Height (v3),
|
||||
ValidatePosDim = true,
|
||||
Text = "v5"
|
||||
};
|
||||
|
||||
var v6 = new Button
|
||||
{
|
||||
X = Pos.X (f2),
|
||||
Y = Pos.Bottom (f2) + 2,
|
||||
Width = Percent (20, DimPercentMode.Position),
|
||||
Height = Percent (20, DimPercentMode.Position),
|
||||
ValidatePosDim = true,
|
||||
Text = "v6"
|
||||
};
|
||||
|
||||
w.Add (f1, f2, v1, v2, v3, v4, v5, v6);
|
||||
t.Add (w);
|
||||
|
||||
t.Ready += (s, e) =>
|
||||
{
|
||||
Assert.Equal ("Absolute(100)", w.Width.ToString ());
|
||||
Assert.Equal ("Absolute(100)", w.Height.ToString ());
|
||||
Assert.Equal (100, w.Frame.Width);
|
||||
Assert.Equal (100, w.Frame.Height);
|
||||
|
||||
Assert.Equal ("Absolute(5)", f1.Height.ToString ());
|
||||
Assert.Equal (49, f1.Frame.Width); // 50-1=49
|
||||
Assert.Equal (5, f1.Frame.Height);
|
||||
|
||||
Assert.Equal ("Fill(Absolute(0))", f2.Width.ToString ());
|
||||
Assert.Equal ("Absolute(5)", f2.Height.ToString ());
|
||||
Assert.Equal (49, f2.Frame.Width); // 50-1=49
|
||||
Assert.Equal (5, f2.Frame.Height);
|
||||
|
||||
#if DEBUG
|
||||
Assert.Equal ($"Combine(View(Width,FrameView(f1){f1.Border.Frame})-Absolute(2))", v1.Width.ToString ());
|
||||
#else
|
||||
Assert.Equal ($"Combine(View(Width,FrameView(){f1.Border.Frame})-Absolute(2))", v1.Width.ToString ());
|
||||
#endif
|
||||
Assert.Equal ("Combine(Fill(Absolute(0))-Absolute(2))", v1.Height.ToString ());
|
||||
Assert.Equal (47, v1.Frame.Width); // 49-2=47
|
||||
Assert.Equal (89, v1.Frame.Height); // 98-5-2-2=89
|
||||
|
||||
#if DEBUG
|
||||
Assert.Equal (
|
||||
$"Combine(View(Width,FrameView(f2){f2.Frame})-Absolute(2))",
|
||||
v2.Width.ToString ()
|
||||
#else
|
||||
Assert.Equal (
|
||||
$"Combine(View(Width,FrameView(){f2.Frame})-Absolute(2))",
|
||||
v2.Width.ToString ()
|
||||
#endif
|
||||
);
|
||||
#if DEBUG
|
||||
Assert.Equal ("Combine(Fill(Absolute(0))-Absolute(2))", v2.Height.ToString ());
|
||||
#else
|
||||
Assert.Equal ("Combine(Fill(Absolute(0))-Absolute(2))", v2.Height.ToString ());
|
||||
#endif
|
||||
Assert.Equal (47, v2.Frame.Width); // 49-2=47
|
||||
Assert.Equal (89, v2.Frame.Height); // 98-5-2-2=89
|
||||
|
||||
Assert.Equal (9, v3.Frame.Width); // 98*10%=9
|
||||
Assert.Equal (9, v3.Frame.Height); // 98*10%=9
|
||||
|
||||
Assert.Equal ("Absolute(50)", v4.Width.ToString ());
|
||||
Assert.Equal ("Absolute(50)", v4.Height.ToString ());
|
||||
Assert.Equal (50, v4.Frame.Width);
|
||||
Assert.Equal (50, v4.Frame.Height);
|
||||
#if DEBUG
|
||||
Assert.Equal ($"Combine(View(Width,Button(v1){v1.Frame})-View(Width,Button(v3){v3.Viewport}))", v5.Width.ToString ());
|
||||
#else
|
||||
Assert.Equal ($"Combine(View(Height,Button(){v1.Frame})-View(Height,Button(){v3.Viewport}))", v5.Height.ToString ( ));
|
||||
#endif
|
||||
Assert.Equal (38, v5.Frame.Width); // 47-9=38
|
||||
Assert.Equal (80, v5.Frame.Height); // 89-9=80
|
||||
|
||||
Assert.Equal (9, v6.Frame.Width); // 47*20%=9
|
||||
Assert.Equal (18, v6.Frame.Height); // 89*20%=18
|
||||
|
||||
w.Width = 200;
|
||||
Assert.True (t.NeedsLayout);
|
||||
w.Height = 200;
|
||||
t.LayoutSubviews ();
|
||||
|
||||
Assert.Equal ("Absolute(200)", w.Width.ToString ());
|
||||
Assert.Equal ("Absolute(200)", w.Height.ToString ());
|
||||
Assert.Equal (200, w.Frame.Width);
|
||||
Assert.Equal (200, w.Frame.Height);
|
||||
|
||||
f1.Text = "Frame1";
|
||||
Assert.Equal (99, f1.Frame.Width); // 100-1=99
|
||||
Assert.Equal (5, f1.Frame.Height);
|
||||
|
||||
f2.Text = "Frame2";
|
||||
Assert.Equal ("Fill(Absolute(0))", f2.Width.ToString ());
|
||||
Assert.Equal ("Absolute(5)", f2.Height.ToString ());
|
||||
Assert.Equal (99, f2.Frame.Width); // 100-1=99
|
||||
Assert.Equal (5, f2.Frame.Height);
|
||||
|
||||
v1.Text = "Button1";
|
||||
#if DEBUG
|
||||
Assert.Equal ($"Combine(View(Width,FrameView(f1){f1.Frame})-Absolute(2))", v1.Width.ToString ());
|
||||
#else
|
||||
Assert.Equal ($"Combine(View(Width,FrameView(){f1.Frame})-Absolute(2))", v1.Width.ToString ());
|
||||
#endif
|
||||
Assert.Equal ("Combine(Fill(Absolute(0))-Absolute(2))", v1.Height.ToString ());
|
||||
Assert.Equal (97, v1.Frame.Width); // 99-2=97
|
||||
Assert.Equal (189, v1.Frame.Height); // 198-2-7=189
|
||||
|
||||
v2.Text = "Button2";
|
||||
|
||||
#if DEBUG
|
||||
Assert.Equal ($"Combine(View(Width,FrameView(f2){f2.Frame})-Absolute(2))", v2.Width.ToString ());
|
||||
#else
|
||||
Assert.Equal ($"Combine(View(Width,FrameView(){f2.Frame})-Absolute(2))", v2.Width.ToString ());
|
||||
#endif
|
||||
Assert.Equal ("Combine(Fill(Absolute(0))-Absolute(2))", v2.Height.ToString ());
|
||||
Assert.Equal (97, v2.Frame.Width); // 99-2=97
|
||||
Assert.Equal (189, v2.Frame.Height); // 198-2-7=189
|
||||
|
||||
v3.Text = "Button3";
|
||||
|
||||
// 198*10%=19 * Percent is related to the super-view if it isn't null otherwise the view width
|
||||
Assert.Equal (19, v3.Frame.Width);
|
||||
|
||||
// 199*10%=19
|
||||
Assert.Equal (19, v3.Frame.Height);
|
||||
|
||||
v4.Text = "Button4";
|
||||
v4.Width = Auto (DimAutoStyle.Text);
|
||||
v4.Height = Auto (DimAutoStyle.Text);
|
||||
v4.Layout ();
|
||||
Assert.Equal (Auto (DimAutoStyle.Text), v4.Width);
|
||||
Assert.Equal (Auto (DimAutoStyle.Text), v4.Height);
|
||||
Assert.Equal (11, v4.Frame.Width); // 11 is the text length and because is DimAbsolute
|
||||
Assert.Equal (1, v4.Frame.Height); // 1 because is DimAbsolute
|
||||
|
||||
v5.Text = "Button5";
|
||||
|
||||
#if DEBUG
|
||||
Assert.Equal ($"Combine(View(Width,Button(v1){v1.Frame})-View(Width,Button(v3){v3.Frame}))", v5.Width.ToString ());
|
||||
Assert.Equal ($"Combine(View(Height,Button(v1){v1.Frame})-View(Height,Button(v3){v3.Frame}))", v5.Height.ToString ());
|
||||
#else
|
||||
Assert.Equal ($"Combine(View(Width,Button(){v1.Frame})-View(Width,Button(){v3.Frame}))", v5.Width.ToString ());
|
||||
Assert.Equal ($"Combine(View(Height,Button(){v1.Frame})-View(Height,Button(){v3.Frame}))", v5.Height.ToString ());
|
||||
#endif
|
||||
|
||||
Assert.Equal (78, v5.Frame.Width); // 97-9=78
|
||||
Assert.Equal (170, v5.Frame.Height); // 189-19=170
|
||||
|
||||
v6.Text = "Button6";
|
||||
Assert.Equal (19, v6.Frame.Width); // 99*20%=19
|
||||
Assert.Equal (38, v6.Frame.Height); // 198-7*20=18
|
||||
};
|
||||
|
||||
Application.Iteration += (s, a) => Application.RequestStop ();
|
||||
|
||||
Application.Run (t);
|
||||
t.Dispose ();
|
||||
}
|
||||
}
|
||||
49
Tests/UnitTests/View/Layout/LayoutTests.cs
Normal file
49
Tests/UnitTests/View/Layout/LayoutTests.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.LayoutTests;
|
||||
|
||||
public class LayoutTests (ITestOutputHelper _output) : TestsAllViews
|
||||
{
|
||||
[Theory]
|
||||
[SetupFakeDriver] // Required for spinner view that wants to register timeouts
|
||||
[MemberData (nameof (AllViewTypes))]
|
||||
public void AllViews_Layout_Does_Not_Draw (Type viewType)
|
||||
{
|
||||
|
||||
// Required for spinner view that wants to register timeouts
|
||||
Application.MainLoop = new MainLoop (new FakeMainLoop (Application.Driver));
|
||||
|
||||
var view = (View)CreateInstanceIfNotGeneric (viewType);
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
_output.WriteLine ($"Ignoring {viewType} - It's a Generic");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (view is IDesignable designable)
|
||||
{
|
||||
designable.EnableForDesign ();
|
||||
}
|
||||
|
||||
var drawContentCount = 0;
|
||||
view.DrawingContent += (s, e) => drawContentCount++;
|
||||
|
||||
var layoutStartedCount = 0;
|
||||
view.SubviewLayout += (s, e) => layoutStartedCount++;
|
||||
|
||||
var layoutCompleteCount = 0;
|
||||
view.SubviewsLaidOut += (s, e) => layoutCompleteCount++;
|
||||
|
||||
view.SetNeedsLayout ();
|
||||
view.SetNeedsDraw();
|
||||
view.Layout ();
|
||||
|
||||
Assert.Equal (0, drawContentCount);
|
||||
Assert.Equal (1, layoutStartedCount);
|
||||
Assert.Equal (1, layoutCompleteCount);
|
||||
}
|
||||
}
|
||||
76
Tests/UnitTests/View/Layout/Pos.AnchorEndTests.cs
Normal file
76
Tests/UnitTests/View/Layout/Pos.AnchorEndTests.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
using static Terminal.Gui.Dim;
|
||||
using static Terminal.Gui.Pos;
|
||||
|
||||
namespace Terminal.Gui.LayoutTests;
|
||||
|
||||
public class PosAnchorEndTests (ITestOutputHelper output)
|
||||
{
|
||||
|
||||
|
||||
// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
|
||||
// TODO: A new test that calls SetRelativeLayout directly is needed.
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void PosAnchorEnd_Equal_Inside_Window ()
|
||||
{
|
||||
var viewWidth = 10;
|
||||
var viewHeight = 1;
|
||||
|
||||
var tv = new TextView
|
||||
{
|
||||
X = Pos.AnchorEnd (viewWidth), Y = Pos.AnchorEnd (viewHeight), Width = viewWidth, Height = viewHeight
|
||||
};
|
||||
|
||||
var win = new Window ();
|
||||
|
||||
win.Add (tv);
|
||||
|
||||
Toplevel top = new ();
|
||||
top.Add (win);
|
||||
RunState rs = Application.Begin (top);
|
||||
|
||||
Assert.Equal (new (0, 0, 80, 25), top.Frame);
|
||||
Assert.Equal (new (0, 0, 80, 25), win.Frame);
|
||||
Assert.Equal (new (68, 22, 10, 1), tv.Frame);
|
||||
Application.End (rs);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
//// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
|
||||
//// TODO: A new test that calls SetRelativeLayout directly is needed.
|
||||
//[Fact]
|
||||
//[AutoInitShutdown]
|
||||
//public void PosAnchorEnd_Equal_Inside_Window_With_MenuBar_And_StatusBar_On_Toplevel ()
|
||||
//{
|
||||
// var viewWidth = 10;
|
||||
// var viewHeight = 1;
|
||||
|
||||
// var tv = new TextView
|
||||
// {
|
||||
// X = Pos.AnchorEnd (viewWidth), Y = Pos.AnchorEnd (viewHeight), Width = viewWidth, Height = viewHeight
|
||||
// };
|
||||
|
||||
// var win = new Window ();
|
||||
|
||||
// win.Add (tv);
|
||||
|
||||
// var menu = new MenuBar ();
|
||||
// var status = new StatusBar ();
|
||||
// Toplevel top = new ();
|
||||
// top.Add (win, menu, status);
|
||||
// RunState rs = Application.Begin (top);
|
||||
|
||||
// Assert.Equal (new (0, 0, 80, 25), top.Frame);
|
||||
// Assert.Equal (new (0, 0, 80, 1), menu.Frame);
|
||||
// Assert.Equal (new (0, 24, 80, 1), status.Frame);
|
||||
// Assert.Equal (new (0, 1, 80, 23), win.Frame);
|
||||
// Assert.Equal (new (68, 20, 10, 1), tv.Frame);
|
||||
|
||||
// Application.End (rs);
|
||||
// top.Dispose ();
|
||||
//}
|
||||
|
||||
}
|
||||
329
Tests/UnitTests/View/Layout/Pos.CenterTests.cs
Normal file
329
Tests/UnitTests/View/Layout/Pos.CenterTests.cs
Normal file
@@ -0,0 +1,329 @@
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
using static Terminal.Gui.Dim;
|
||||
using static Terminal.Gui.Pos;
|
||||
|
||||
namespace Terminal.Gui.LayoutTests;
|
||||
|
||||
public class PosCenterTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
[Theory]
|
||||
[AutoInitShutdown]
|
||||
[InlineData (1)]
|
||||
[InlineData (2)]
|
||||
[InlineData (3)]
|
||||
[InlineData (4)]
|
||||
[InlineData (5)]
|
||||
[InlineData (6)]
|
||||
[InlineData (7)]
|
||||
[InlineData (8)]
|
||||
[InlineData (9)]
|
||||
[InlineData (10)]
|
||||
public void PosCenter_SubView_85_Percent_Height (int height)
|
||||
{
|
||||
var win = new Window { Width = Fill (), Height = Fill () };
|
||||
|
||||
var subview = new Window
|
||||
{
|
||||
X = Center (), Y = Center (), Width = Dim.Percent (85), Height = Dim.Percent (85)
|
||||
};
|
||||
|
||||
win.Add (subview);
|
||||
|
||||
RunState rs = Application.Begin (win);
|
||||
var firstIteration = false;
|
||||
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (20, height);
|
||||
Application.RunIteration (ref rs, firstIteration);
|
||||
var expected = string.Empty;
|
||||
|
||||
switch (height)
|
||||
{
|
||||
case 1:
|
||||
//Assert.Equal (new (0, 0, 17, 0), subview.Frame);
|
||||
expected = @"
|
||||
────────────────────";
|
||||
|
||||
break;
|
||||
case 2:
|
||||
//Assert.Equal (new (0, 0, 17, 1), subview.Frame);
|
||||
expected = @"
|
||||
┌──────────────────┐
|
||||
└──────────────────┘
|
||||
";
|
||||
|
||||
break;
|
||||
case 3:
|
||||
//Assert.Equal (new (0, 0, 17, 2), subview.Frame);
|
||||
expected = @"
|
||||
┌──────────────────┐
|
||||
│ │
|
||||
└──────────────────┘
|
||||
";
|
||||
|
||||
break;
|
||||
case 4:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌──────────────────┐
|
||||
│ ─────────────── │
|
||||
│ │
|
||||
└──────────────────┘";
|
||||
|
||||
break;
|
||||
case 5:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌──────────────────┐
|
||||
│ ┌─────────────┐ │
|
||||
│ └─────────────┘ │
|
||||
│ │
|
||||
└──────────────────┘";
|
||||
|
||||
break;
|
||||
case 6:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌──────────────────┐
|
||||
│ ┌─────────────┐ │
|
||||
│ │ │ │
|
||||
│ └─────────────┘ │
|
||||
│ │
|
||||
└──────────────────┘";
|
||||
|
||||
break;
|
||||
case 7:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌──────────────────┐
|
||||
│ ┌─────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ └─────────────┘ │
|
||||
│ │
|
||||
└──────────────────┘";
|
||||
|
||||
break;
|
||||
case 8:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌──────────────────┐
|
||||
│ ┌─────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ └─────────────┘ │
|
||||
│ │
|
||||
└──────────────────┘";
|
||||
|
||||
break;
|
||||
case 9:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌──────────────────┐
|
||||
│ │
|
||||
│ ┌─────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ └─────────────┘ │
|
||||
│ │
|
||||
└──────────────────┘";
|
||||
|
||||
break;
|
||||
case 10:
|
||||
//Assert.Equal (new (0, 0, 17, 3), subview.Frame);
|
||||
expected = @"
|
||||
┌──────────────────┐
|
||||
│ │
|
||||
│ ┌─────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ └─────────────┘ │
|
||||
│ │
|
||||
└──────────────────┘"
|
||||
;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_ = DriverAssert.AssertDriverContentsWithFrameAre (expected, _output);
|
||||
Application.End (rs);
|
||||
win.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[AutoInitShutdown]
|
||||
[InlineData (1)]
|
||||
[InlineData (2)]
|
||||
[InlineData (3)]
|
||||
[InlineData (4)]
|
||||
[InlineData (5)]
|
||||
[InlineData (6)]
|
||||
[InlineData (7)]
|
||||
[InlineData (8)]
|
||||
[InlineData (9)]
|
||||
[InlineData (10)]
|
||||
public void PosCenter_SubView_85_Percent_Width (int width)
|
||||
{
|
||||
var win = new Window { Width = Fill (), Height = Fill () };
|
||||
|
||||
var subview = new Window
|
||||
{
|
||||
X = Center (), Y = Center (), Width = Dim.Percent (85), Height = Dim.Percent (85)
|
||||
};
|
||||
|
||||
win.Add (subview);
|
||||
|
||||
RunState rs = Application.Begin (win);
|
||||
var firstIteration = false;
|
||||
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (width, 7);
|
||||
Application.RunIteration (ref rs, firstIteration);
|
||||
var expected = string.Empty;
|
||||
|
||||
switch (width)
|
||||
{
|
||||
case 1:
|
||||
Assert.Equal (new (0, 0, 0, 4), subview.Frame);
|
||||
|
||||
expected = @"
|
||||
│
|
||||
│
|
||||
│
|
||||
│
|
||||
│
|
||||
│
|
||||
│";
|
||||
|
||||
break;
|
||||
case 2:
|
||||
Assert.Equal (new (0, 0, 0, 4), subview.Frame);
|
||||
|
||||
expected = @"
|
||||
┌┐
|
||||
││
|
||||
││
|
||||
││
|
||||
││
|
||||
││
|
||||
└┘";
|
||||
|
||||
break;
|
||||
case 3:
|
||||
Assert.Equal (new (0, 0, 0, 4), subview.Frame);
|
||||
|
||||
expected = @"
|
||||
┌─┐
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─┘";
|
||||
|
||||
break;
|
||||
case 4:
|
||||
Assert.Equal (new (0, 0, 1, 4), subview.Frame);
|
||||
|
||||
expected = @"
|
||||
┌──┐
|
||||
││ │
|
||||
││ │
|
||||
││ │
|
||||
││ │
|
||||
│ │
|
||||
└──┘";
|
||||
|
||||
break;
|
||||
case 5:
|
||||
Assert.Equal (new (0, 0, 2, 4), subview.Frame);
|
||||
|
||||
expected = @"
|
||||
┌───┐
|
||||
│┌┐ │
|
||||
│││ │
|
||||
│││ │
|
||||
│└┘ │
|
||||
│ │
|
||||
└───┘";
|
||||
|
||||
break;
|
||||
case 6:
|
||||
Assert.Equal (new (0, 0, 3, 4), subview.Frame);
|
||||
|
||||
expected = @"
|
||||
┌────┐
|
||||
│┌─┐ │
|
||||
││ │ │
|
||||
││ │ │
|
||||
│└─┘ │
|
||||
│ │
|
||||
└────┘";
|
||||
|
||||
break;
|
||||
case 7:
|
||||
Assert.Equal (new (0, 0, 4, 4), subview.Frame);
|
||||
|
||||
expected = @"
|
||||
┌─────┐
|
||||
│┌──┐ │
|
||||
││ │ │
|
||||
││ │ │
|
||||
│└──┘ │
|
||||
│ │
|
||||
└─────┘";
|
||||
|
||||
break;
|
||||
case 8:
|
||||
Assert.Equal (new (0, 0, 5, 4), subview.Frame);
|
||||
|
||||
expected = @"
|
||||
┌──────┐
|
||||
│┌───┐ │
|
||||
││ │ │
|
||||
││ │ │
|
||||
│└───┘ │
|
||||
│ │
|
||||
└──────┘";
|
||||
|
||||
break;
|
||||
case 9:
|
||||
Assert.Equal (new (1, 0, 5, 4), subview.Frame);
|
||||
|
||||
expected = @"
|
||||
┌───────┐
|
||||
│ ┌───┐ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ └───┘ │
|
||||
│ │
|
||||
└───────┘";
|
||||
|
||||
break;
|
||||
case 10:
|
||||
Assert.Equal (new (1, 0, 6, 4), subview.Frame);
|
||||
|
||||
expected = @"
|
||||
┌────────┐
|
||||
│ ┌────┐ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ └────┘ │
|
||||
│ │
|
||||
└────────┘"
|
||||
;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_ = DriverAssert.AssertDriverContentsWithFrameAre (expected, _output);
|
||||
Application.End (rs);
|
||||
win.Dispose ();
|
||||
}
|
||||
}
|
||||
122
Tests/UnitTests/View/Layout/Pos.CombineTests.cs
Normal file
122
Tests/UnitTests/View/Layout/Pos.CombineTests.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using Microsoft.VisualStudio.TestPlatform.Utilities;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
using static Terminal.Gui.Dim;
|
||||
using static Terminal.Gui.Pos;
|
||||
|
||||
namespace Terminal.Gui.LayoutTests;
|
||||
|
||||
public class PosCombineTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
|
||||
// TODO: A new test that calls SetRelativeLayout directly is needed.
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void PosCombine_Will_Throws ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
Toplevel t = new ();
|
||||
|
||||
var w = new Window { X = Pos.Left (t) + 2, Y = Pos.Top (t) + 2 };
|
||||
var f = new FrameView ();
|
||||
var v1 = new View { X = Pos.Left (w) + 2, Y = Pos.Top (w) + 2 };
|
||||
var v2 = new View { X = Pos.Left (v1) + 2, Y = Pos.Top (v1) + 2 };
|
||||
|
||||
f.Add (v1); // v2 not added
|
||||
w.Add (f);
|
||||
t.Add (w);
|
||||
|
||||
f.X = Pos.X (v2) - Pos.X (v1);
|
||||
f.Y = Pos.Y (v2) - Pos.Y (v1);
|
||||
|
||||
Assert.Throws<LayoutException> (() => Application.Run (t));
|
||||
t.Dispose ();
|
||||
Application.Shutdown ();
|
||||
|
||||
v2.Dispose ();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void PosCombine_DimCombine_View_With_SubViews ()
|
||||
{
|
||||
Application.Top = new Toplevel () { Width = 80, Height = 25 };
|
||||
var win1 = new Window { Id = "win1", Width = 20, Height = 10 };
|
||||
var view1 = new View
|
||||
{
|
||||
Text = "view1",
|
||||
Width = Auto (DimAutoStyle.Text),
|
||||
Height = Auto (DimAutoStyle.Text)
|
||||
|
||||
};
|
||||
var win2 = new Window { Id = "win2", Y = Pos.Bottom (view1) + 1, Width = 10, Height = 3 };
|
||||
var view2 = new View { Id = "view2", Width = Dim.Fill (), Height = 1, CanFocus = true };
|
||||
|
||||
//var clicked = false;
|
||||
//view2.MouseClick += (sender, e) => clicked = true;
|
||||
var view3 = new View { Id = "view3", Width = Dim.Fill (1), Height = 1, CanFocus = true };
|
||||
|
||||
view2.Add (view3);
|
||||
win2.Add (view2);
|
||||
win1.Add (view1, win2);
|
||||
Application.Top.Add (win1);
|
||||
Application.Top.Layout ();
|
||||
|
||||
Assert.Equal (new Rectangle (0, 0, 80, 25), Application.Top.Frame);
|
||||
Assert.Equal (new Rectangle (0, 0, 5, 1), view1.Frame);
|
||||
Assert.Equal (new Rectangle (0, 0, 20, 10), win1.Frame);
|
||||
Assert.Equal (new Rectangle (0, 2, 10, 3), win2.Frame);
|
||||
Assert.Equal (new Rectangle (0, 0, 8, 1), view2.Frame);
|
||||
Assert.Equal (new Rectangle (0, 0, 7, 1), view3.Frame);
|
||||
var foundView = View.GetViewsUnderMouse (new Point(9, 4)).LastOrDefault ();
|
||||
Assert.Equal (foundView, view2);
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (ignoreDisposed: true);
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PosCombine_Refs_SuperView_Throws ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
var top = new Toplevel ();
|
||||
var w = new Window { X = Pos.Left (top) + 2, Y = Pos.Top (top) + 2 };
|
||||
var f = new FrameView ();
|
||||
var v1 = new View { X = Pos.Left (w) + 2, Y = Pos.Top (w) + 2 };
|
||||
var v2 = new View { X = Pos.Left (v1) + 2, Y = Pos.Top (v1) + 2 };
|
||||
|
||||
f.Add (v1, v2);
|
||||
w.Add (f);
|
||||
top.Add (w);
|
||||
Application.Begin (top);
|
||||
|
||||
f.X = Pos.X (Application.Top) + Pos.X (v2) - Pos.X (v1);
|
||||
f.Y = Pos.Y (Application.Top) + Pos.Y (v2) - Pos.Y (v1);
|
||||
|
||||
Application.Top.SubviewsLaidOut += (s, e) =>
|
||||
{
|
||||
Assert.Equal (0, Application.Top.Frame.X);
|
||||
Assert.Equal (0, Application.Top.Frame.Y);
|
||||
Assert.Equal (2, w.Frame.X);
|
||||
Assert.Equal (2, w.Frame.Y);
|
||||
Assert.Equal (2, f.Frame.X);
|
||||
Assert.Equal (2, f.Frame.Y);
|
||||
Assert.Equal (4, v1.Frame.X);
|
||||
Assert.Equal (4, v1.Frame.Y);
|
||||
Assert.Equal (6, v2.Frame.X);
|
||||
Assert.Equal (6, v2.Frame.Y);
|
||||
};
|
||||
|
||||
Application.Iteration += (s, a) => Application.RequestStop ();
|
||||
|
||||
Assert.Throws<LayoutException> (() => Application.Run ());
|
||||
top.Dispose ();
|
||||
Application.ResetState (ignoreDisposed: true);
|
||||
}
|
||||
|
||||
}
|
||||
332
Tests/UnitTests/View/Layout/Pos.Tests.cs
Normal file
332
Tests/UnitTests/View/Layout/Pos.Tests.cs
Normal file
@@ -0,0 +1,332 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
using static Terminal.Gui.Dim;
|
||||
using static Terminal.Gui.Pos;
|
||||
|
||||
namespace Terminal.Gui.LayoutTests;
|
||||
|
||||
public class PosTests ()
|
||||
{
|
||||
[Fact]
|
||||
public void
|
||||
Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
Toplevel t = new ();
|
||||
|
||||
var w = new Window { X = Pos.Left (t) + 2, Y = Pos.Absolute (2) };
|
||||
|
||||
var v = new View { X = Pos.Center (), Y = Pos.Percent (10) };
|
||||
|
||||
w.Add (v);
|
||||
t.Add (w);
|
||||
|
||||
t.Ready += (s, e) =>
|
||||
{
|
||||
v.Frame = new Rectangle (2, 2, 10, 10);
|
||||
Assert.Equal (2, v.X = 2);
|
||||
Assert.Equal (2, v.Y = 2);
|
||||
};
|
||||
|
||||
Application.Iteration += (s, a) => Application.RequestStop ();
|
||||
|
||||
Application.Run (t);
|
||||
t.Dispose ();
|
||||
Application.Shutdown ();
|
||||
}
|
||||
|
||||
|
||||
// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
|
||||
// TODO: A new test that calls SetRelativeLayout directly is needed.
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void PosCombine_WHY_Throws ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
Toplevel t = new Toplevel ();
|
||||
|
||||
var w = new Window { X = Pos.Left (t) + 2, Y = Pos.Top (t) + 2 };
|
||||
var f = new FrameView ();
|
||||
var v1 = new View { X = Pos.Left (w) + 2, Y = Pos.Top (w) + 2 };
|
||||
var v2 = new View { X = Pos.Left (v1) + 2, Y = Pos.Top (v1) + 2 };
|
||||
|
||||
f.Add (v1); // v2 not added
|
||||
w.Add (f);
|
||||
t.Add (w);
|
||||
|
||||
f.X = Pos.X (v2) - Pos.X (v1);
|
||||
f.Y = Pos.Y (v2) - Pos.Y (v1);
|
||||
|
||||
Assert.Throws<LayoutException> (() => Application.Run (t));
|
||||
t.Dispose ();
|
||||
Application.Shutdown ();
|
||||
|
||||
v2.Dispose ();
|
||||
}
|
||||
|
||||
// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
|
||||
// TODO: A new test that calls SetRelativeLayout directly is needed.
|
||||
// See: https://github.com/gui-cs/Terminal.Gui/issues/504
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void LeftTopBottomRight_Win_ShouldNotThrow ()
|
||||
{
|
||||
// Test cases:
|
||||
(Toplevel top, Window win, Button button) app = Setup ();
|
||||
app.button.Y = Pos.Left (app.win);
|
||||
RunState rs = Application.Begin (app.top);
|
||||
|
||||
// If Application.RunState is used then we must use Application.RunLoop with the rs parameter
|
||||
Application.RunLoop (rs);
|
||||
Cleanup (rs);
|
||||
|
||||
app = Setup ();
|
||||
app.button.Y = Pos.X (app.win);
|
||||
rs = Application.Begin (app.top);
|
||||
|
||||
// If Application.RunState is used then we must use Application.RunLoop with the rs parameter
|
||||
Application.RunLoop (rs);
|
||||
Cleanup (rs);
|
||||
|
||||
app = Setup ();
|
||||
app.button.Y = Pos.Top (app.win);
|
||||
rs = Application.Begin (app.top);
|
||||
|
||||
// If Application.RunState is used then we must use Application.RunLoop with the rs parameter
|
||||
Application.RunLoop (rs);
|
||||
Cleanup (rs);
|
||||
|
||||
app = Setup ();
|
||||
app.button.Y = Pos.Y (app.win);
|
||||
rs = Application.Begin (app.top);
|
||||
|
||||
// If Application.RunState is used then we must use Application.RunLoop with the rs parameter
|
||||
Application.RunLoop (rs);
|
||||
Cleanup (rs);
|
||||
|
||||
app = Setup ();
|
||||
app.button.Y = Pos.Bottom (app.win);
|
||||
rs = Application.Begin (app.top);
|
||||
|
||||
// If Application.RunState is used then we must use Application.RunLoop with the rs parameter
|
||||
Application.RunLoop (rs);
|
||||
Cleanup (rs);
|
||||
|
||||
app = Setup ();
|
||||
app.button.Y = Pos.Right (app.win);
|
||||
rs = Application.Begin (app.top);
|
||||
|
||||
// If Application.RunState is used then we must use Application.RunLoop with the rs parameter
|
||||
Application.RunLoop (rs);
|
||||
Cleanup (rs);
|
||||
|
||||
return;
|
||||
|
||||
void Cleanup (RunState rs)
|
||||
{
|
||||
// Cleanup
|
||||
Application.End (rs);
|
||||
|
||||
Application.Top.Dispose ();
|
||||
|
||||
// Shutdown must be called to safely clean up Application if Init has been called
|
||||
Application.Shutdown ();
|
||||
}
|
||||
|
||||
// Setup Fake driver
|
||||
(Toplevel top, Window win, Button button) Setup ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
Application.Iteration += (s, a) => { Application.RequestStop (); };
|
||||
var win = new Window { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
var top = new Toplevel ();
|
||||
top.Add (win);
|
||||
|
||||
var button = new Button { X = Pos.Center (), Text = "button" };
|
||||
win.Add (button);
|
||||
|
||||
return (top, win, button);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
|
||||
// TODO: A new test that calls SetRelativeLayout directly is needed.
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void Pos_Add_Operator ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
Toplevel top = new ();
|
||||
|
||||
var view = new View { X = 0, Y = 0, Width = 20, Height = 20 };
|
||||
var field = new TextField { X = 0, Y = 0, Width = 20 };
|
||||
var count = 0;
|
||||
|
||||
field.KeyDown += (s, k) =>
|
||||
{
|
||||
if (k.KeyCode == KeyCode.Enter)
|
||||
{
|
||||
field.Text = $"View {count}";
|
||||
var view2 = new View { X = 0, Y = field.Y, Width = 20, Text = field.Text };
|
||||
view.Add (view2);
|
||||
Assert.Equal ($"View {count}", view2.Text);
|
||||
Assert.Equal ($"Absolute({count})", view2.Y.ToString ());
|
||||
|
||||
Assert.Equal ($"Absolute({count})", field.Y.ToString ());
|
||||
field.Y += 1;
|
||||
count++;
|
||||
Assert.Equal ($"Absolute({count})", field.Y.ToString ());
|
||||
}
|
||||
};
|
||||
|
||||
Application.Iteration += (s, a) =>
|
||||
{
|
||||
while (count < 20)
|
||||
{
|
||||
field.NewKeyDownEvent (Key.Enter);
|
||||
}
|
||||
|
||||
Application.RequestStop ();
|
||||
};
|
||||
|
||||
var win = new Window ();
|
||||
win.Add (view);
|
||||
win.Add (field);
|
||||
|
||||
top.Add (win);
|
||||
|
||||
Application.Run (top);
|
||||
|
||||
Assert.Equal (20, count);
|
||||
|
||||
top.Dispose ();
|
||||
|
||||
// Shutdown must be called to safely clean up Application if Init has been called
|
||||
Application.Shutdown ();
|
||||
}
|
||||
|
||||
// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
|
||||
// TODO: A new test that calls SetRelativeLayout directly is needed.
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void Pos_Subtract_Operator ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
Toplevel top = new ();
|
||||
|
||||
var view = new View { X = 0, Y = 0, Width = 20, Height = 20 };
|
||||
var field = new TextField { X = 0, Y = 0, Width = 20 };
|
||||
var count = 20;
|
||||
List<View> listViews = new ();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
field.Text = $"View {i}";
|
||||
var view2 = new View { X = 0, Y = field.Y, Width = 20, Text = field.Text };
|
||||
view.Add (view2);
|
||||
Assert.Equal ($"View {i}", view2.Text);
|
||||
Assert.Equal ($"Absolute({i})", field.Y.ToString ());
|
||||
listViews.Add (view2);
|
||||
|
||||
Assert.Equal ($"Absolute({i})", field.Y.ToString ());
|
||||
field.Y += 1;
|
||||
Assert.Equal ($"Absolute({i + 1})", field.Y.ToString ());
|
||||
}
|
||||
|
||||
field.KeyDown += (s, k) =>
|
||||
{
|
||||
if (k.KeyCode == KeyCode.Enter)
|
||||
{
|
||||
Assert.Equal ($"View {count - 1}", listViews [count - 1].Text);
|
||||
view.Remove (listViews [count - 1]);
|
||||
listViews [count - 1].Dispose ();
|
||||
|
||||
Assert.Equal ($"Absolute({count})", field.Y.ToString ());
|
||||
field.Y -= 1;
|
||||
count--;
|
||||
Assert.Equal ($"Absolute({count})", field.Y.ToString ());
|
||||
}
|
||||
};
|
||||
|
||||
Application.Iteration += (s, a) =>
|
||||
{
|
||||
while (count > 0)
|
||||
{
|
||||
field.NewKeyDownEvent (Key.Enter);
|
||||
}
|
||||
|
||||
Application.RequestStop ();
|
||||
};
|
||||
|
||||
var win = new Window ();
|
||||
win.Add (view);
|
||||
win.Add (field);
|
||||
|
||||
top.Add (win);
|
||||
|
||||
Application.Run (top);
|
||||
|
||||
Assert.Equal (0, count);
|
||||
|
||||
top.Dispose ();
|
||||
|
||||
// Shutdown must be called to safely clean up Application if Init has been called
|
||||
Application.Shutdown ();
|
||||
}
|
||||
|
||||
// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
|
||||
// TODO: A new test that calls SetRelativeLayout directly is needed.
|
||||
[Fact]
|
||||
public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Null ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
Toplevel t = new ();
|
||||
|
||||
var w = new Window { X = 1, Y = 2, Width = 3, Height = 5 };
|
||||
t.Add (w);
|
||||
|
||||
t.Ready += (s, e) =>
|
||||
{
|
||||
Assert.Equal (2, w.X = 2);
|
||||
Assert.Equal (2, w.Y = 2);
|
||||
};
|
||||
|
||||
Application.Iteration += (s, a) => Application.RequestStop ();
|
||||
|
||||
Application.Run (t);
|
||||
t.Dispose ();
|
||||
Application.Shutdown ();
|
||||
}
|
||||
|
||||
|
||||
// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
|
||||
// TODO: A new test that calls SetRelativeLayout directly is needed.
|
||||
[Fact]
|
||||
public void Validation_Does_Not_Throw_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Null ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
Toplevel t = new Toplevel ();
|
||||
|
||||
var w = new Window { X = 1, Y = 2, Width = 3, Height = 5 };
|
||||
t.Add (w);
|
||||
|
||||
t.Ready += (s, e) =>
|
||||
{
|
||||
Assert.Equal (2, w.X = 2);
|
||||
Assert.Equal (2, w.Y = 2);
|
||||
};
|
||||
|
||||
Application.Iteration += (s, a) => Application.RequestStop ();
|
||||
|
||||
Application.Run (t);
|
||||
t.Dispose ();
|
||||
Application.Shutdown ();
|
||||
}
|
||||
}
|
||||
78
Tests/UnitTests/View/Layout/Pos.ViewTests.cs
Normal file
78
Tests/UnitTests/View/Layout/Pos.ViewTests.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
using static Terminal.Gui.Pos;
|
||||
|
||||
namespace Terminal.Gui.LayoutTests;
|
||||
|
||||
public class PosViewTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
// TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
|
||||
// TODO: A new test that calls SetRelativeLayout directly is needed.
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void Subtract_Operator ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
var top = new Toplevel ();
|
||||
|
||||
var view = new View { X = 0, Y = 0, Width = 20, Height = 20 };
|
||||
var field = new TextField { X = 0, Y = 0, Width = 20 };
|
||||
var count = 20;
|
||||
List<View> listViews = new ();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
field.Text = $"View {i}";
|
||||
var view2 = new View { X = 0, Y = field.Y, Width = 20, Text = field.Text };
|
||||
view.Add (view2);
|
||||
Assert.Equal ($"View {i}", view2.Text);
|
||||
Assert.Equal ($"Absolute({i})", field.Y.ToString ());
|
||||
listViews.Add (view2);
|
||||
|
||||
Assert.Equal ($"Absolute({i})", field.Y.ToString ());
|
||||
field.Y += 1;
|
||||
Assert.Equal ($"Absolute({i + 1})", field.Y.ToString ());
|
||||
}
|
||||
|
||||
field.KeyDown += (s, k) =>
|
||||
{
|
||||
if (k.KeyCode == KeyCode.Enter)
|
||||
{
|
||||
Assert.Equal ($"View {count - 1}", listViews [count - 1].Text);
|
||||
view.Remove (listViews [count - 1]);
|
||||
listViews [count - 1].Dispose ();
|
||||
|
||||
Assert.Equal ($"Absolute({count})", field.Y.ToString ());
|
||||
field.Y -= 1;
|
||||
count--;
|
||||
Assert.Equal ($"Absolute({count})", field.Y.ToString ());
|
||||
}
|
||||
};
|
||||
|
||||
Application.Iteration += (s, a) =>
|
||||
{
|
||||
while (count > 0)
|
||||
{
|
||||
field.NewKeyDownEvent (new (KeyCode.Enter));
|
||||
}
|
||||
|
||||
Application.RequestStop ();
|
||||
};
|
||||
|
||||
var win = new Window ();
|
||||
win.Add (view);
|
||||
win.Add (field);
|
||||
|
||||
top.Add (win);
|
||||
|
||||
Application.Run (top);
|
||||
top.Dispose ();
|
||||
Assert.Equal (0, count);
|
||||
|
||||
// Shutdown must be called to safely clean up Application if Init has been called
|
||||
Application.Shutdown ();
|
||||
}
|
||||
}
|
||||
41
Tests/UnitTests/View/Layout/SetLayoutTests.cs
Normal file
41
Tests/UnitTests/View/Layout/SetLayoutTests.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.LayoutTests;
|
||||
|
||||
public class SetLayoutTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Screen_Size_Change_Causes_Layout ()
|
||||
{
|
||||
Application.Top = new ();
|
||||
|
||||
var view = new View
|
||||
{
|
||||
X = 3,
|
||||
Y = 2,
|
||||
Width = 10,
|
||||
Height = 1,
|
||||
Text = "0123456789"
|
||||
};
|
||||
Application.Top.Add (view);
|
||||
|
||||
var rs = Application.Begin (Application.Top);
|
||||
|
||||
Assert.Equal (new (0, 0, 80, 25), new Rectangle (0, 0, View.Driver.Cols, View.Driver.Rows));
|
||||
Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), Application.Top.Frame);
|
||||
Assert.Equal (new (0, 0, 80, 25), Application.Top.Frame);
|
||||
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (20, 10);
|
||||
Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), Application.Top.Frame);
|
||||
|
||||
Assert.Equal (new (0, 0, 20, 10), Application.Top.Frame);
|
||||
|
||||
Application.End (rs);
|
||||
|
||||
}
|
||||
}
|
||||
805
Tests/UnitTests/View/Mouse/GetViewsUnderMouseTests.cs
Normal file
805
Tests/UnitTests/View/Mouse/GetViewsUnderMouseTests.cs
Normal file
@@ -0,0 +1,805 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Terminal.Gui.ViewMouseTests;
|
||||
|
||||
[Trait ("Category", "Input")]
|
||||
public class GetViewsUnderMouseTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0, 0, 0, -1, -1, null)]
|
||||
[InlineData (0, 0, 0, 0, 0, 0, 0, typeof (Toplevel))]
|
||||
[InlineData (0, 0, 0, 0, 0, 1, 1, typeof (Toplevel))]
|
||||
[InlineData (0, 0, 0, 0, 0, 4, 4, typeof (Toplevel))]
|
||||
[InlineData (0, 0, 0, 0, 0, 9, 9, typeof (Toplevel))]
|
||||
[InlineData (0, 0, 0, 0, 0, 10, 10, null)]
|
||||
[InlineData (1, 1, 0, 0, 0, -1, -1, null)]
|
||||
[InlineData (1, 1, 0, 0, 0, 0, 0, null)]
|
||||
[InlineData (1, 1, 0, 0, 0, 1, 1, typeof (Toplevel))]
|
||||
[InlineData (1, 1, 0, 0, 0, 4, 4, typeof (Toplevel))]
|
||||
[InlineData (1, 1, 0, 0, 0, 9, 9, typeof (Toplevel))]
|
||||
[InlineData (1, 1, 0, 0, 0, 10, 10, typeof (Toplevel))]
|
||||
[InlineData (0, 0, 1, 0, 0, -1, -1, null)]
|
||||
[InlineData (0, 0, 1, 0, 0, 0, 0, typeof (Margin))]
|
||||
[InlineData (0, 0, 1, 0, 0, 1, 1, typeof (Toplevel))]
|
||||
[InlineData (0, 0, 1, 0, 0, 4, 4, typeof (Toplevel))]
|
||||
[InlineData (0, 0, 1, 0, 0, 9, 9, typeof (Margin))]
|
||||
[InlineData (0, 0, 1, 0, 0, 10, 10, null)]
|
||||
[InlineData (0, 0, 1, 1, 0, -1, -1, null)]
|
||||
[InlineData (0, 0, 1, 1, 0, 0, 0, typeof (Margin))]
|
||||
[InlineData (0, 0, 1, 1, 0, 1, 1, typeof (Border))]
|
||||
[InlineData (0, 0, 1, 1, 0, 4, 4, typeof (Toplevel))]
|
||||
[InlineData (0, 0, 1, 1, 0, 9, 9, typeof (Margin))]
|
||||
[InlineData (0, 0, 1, 1, 0, 10, 10, null)]
|
||||
[InlineData (0, 0, 1, 1, 1, -1, -1, null)]
|
||||
[InlineData (0, 0, 1, 1, 1, 0, 0, typeof (Margin))]
|
||||
[InlineData (0, 0, 1, 1, 1, 1, 1, typeof (Border))]
|
||||
[InlineData (0, 0, 1, 1, 1, 2, 2, typeof (Padding))]
|
||||
[InlineData (0, 0, 1, 1, 1, 4, 4, typeof (Toplevel))]
|
||||
[InlineData (0, 0, 1, 1, 1, 9, 9, typeof (Margin))]
|
||||
[InlineData (0, 0, 1, 1, 1, 10, 10, null)]
|
||||
[InlineData (1, 1, 1, 0, 0, -1, -1, null)]
|
||||
[InlineData (1, 1, 1, 0, 0, 0, 0, null)]
|
||||
[InlineData (1, 1, 1, 0, 0, 1, 1, typeof (Margin))]
|
||||
[InlineData (1, 1, 1, 0, 0, 4, 4, typeof (Toplevel))]
|
||||
[InlineData (1, 1, 1, 0, 0, 9, 9, typeof (Toplevel))]
|
||||
[InlineData (1, 1, 1, 0, 0, 10, 10, typeof (Margin))]
|
||||
[InlineData (1, 1, 1, 1, 0, -1, -1, null)]
|
||||
[InlineData (1, 1, 1, 1, 0, 0, 0, null)]
|
||||
[InlineData (1, 1, 1, 1, 0, 1, 1, typeof (Margin))]
|
||||
[InlineData (1, 1, 1, 1, 0, 4, 4, typeof (Toplevel))]
|
||||
[InlineData (1, 1, 1, 1, 0, 9, 9, typeof (Border))]
|
||||
[InlineData (1, 1, 1, 1, 0, 10, 10, typeof (Margin))]
|
||||
[InlineData (1, 1, 1, 1, 1, -1, -1, null)]
|
||||
[InlineData (1, 1, 1, 1, 1, 0, 0, null)]
|
||||
[InlineData (1, 1, 1, 1, 1, 1, 1, typeof (Margin))]
|
||||
[InlineData (1, 1, 1, 1, 1, 2, 2, typeof (Border))]
|
||||
[InlineData (1, 1, 1, 1, 1, 3, 3, typeof (Padding))]
|
||||
[InlineData (1, 1, 1, 1, 1, 4, 4, typeof (Toplevel))]
|
||||
[InlineData (1, 1, 1, 1, 1, 8, 8, typeof (Padding))]
|
||||
[InlineData (1, 1, 1, 1, 1, 9, 9, typeof (Border))]
|
||||
[InlineData (1, 1, 1, 1, 1, 10, 10, typeof (Margin))]
|
||||
public void GetViewsUnderMouse_Top_Adornments_Returns_Correct_View (
|
||||
int frameX,
|
||||
int frameY,
|
||||
int marginThickness,
|
||||
int borderThickness,
|
||||
int paddingThickness,
|
||||
int testX,
|
||||
int testY,
|
||||
Type? expectedViewType
|
||||
)
|
||||
{
|
||||
// Arrange
|
||||
Application.Top = new ()
|
||||
{
|
||||
Frame = new (frameX, frameY, 10, 10)
|
||||
};
|
||||
Application.Top.Margin.Thickness = new (marginThickness);
|
||||
Application.Top.Border.Thickness = new (borderThickness);
|
||||
Application.Top.Padding.Thickness = new (paddingThickness);
|
||||
|
||||
var location = new Point (testX, testY);
|
||||
|
||||
// Act
|
||||
List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
|
||||
|
||||
// Assert
|
||||
if (expectedViewType == null)
|
||||
{
|
||||
Assert.Empty (viewsUnderMouse);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Contains (viewsUnderMouse, v => v?.GetType () == expectedViewType);
|
||||
}
|
||||
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0)]
|
||||
[InlineData (1, 1)]
|
||||
[InlineData (2, 2)]
|
||||
public void GetViewsUnderMouse_Returns_Top_If_No_SubViews (int testX, int testY)
|
||||
{
|
||||
// Arrange
|
||||
Application.Top = new ()
|
||||
{
|
||||
Frame = new (0, 0, 10, 10)
|
||||
};
|
||||
|
||||
var location = new Point (testX, testY);
|
||||
|
||||
// Act
|
||||
List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
|
||||
|
||||
// Assert
|
||||
Assert.Contains (viewsUnderMouse, v => v == Application.Top);
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0)]
|
||||
[InlineData (2, 1)]
|
||||
[InlineData (20, 20)]
|
||||
public void GetViewsUnderMouse_Returns_Null_If_No_SubViews_Coords_Outside (int testX, int testY)
|
||||
{
|
||||
// Arrange
|
||||
var view = new View
|
||||
{
|
||||
Frame = new (0, 0, 10, 10)
|
||||
};
|
||||
|
||||
var location = new Point (testX, testY);
|
||||
|
||||
// Act
|
||||
List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
|
||||
|
||||
// Assert
|
||||
Assert.Empty (viewsUnderMouse);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0)]
|
||||
[InlineData (2, 1)]
|
||||
[InlineData (20, 20)]
|
||||
public void GetViewsUnderMouse_Returns_Null_If_Start_Not_Visible (int testX, int testY)
|
||||
{
|
||||
// Arrange
|
||||
var view = new View
|
||||
{
|
||||
Frame = new (0, 0, 10, 10),
|
||||
Visible = false
|
||||
};
|
||||
|
||||
var location = new Point (testX, testY);
|
||||
|
||||
// Act
|
||||
List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
|
||||
|
||||
// Assert
|
||||
Assert.Empty (viewsUnderMouse);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, true)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (6, 7, true)]
|
||||
[InlineData (1, 2, true)]
|
||||
[InlineData (5, 6, true)]
|
||||
public void GetViewsUnderMouse_Returns_Correct_If_SubViews (int testX, int testY, bool expected)
|
||||
{
|
||||
// Arrange
|
||||
Application.Top = new ()
|
||||
{
|
||||
Frame = new (0, 0, 10, 10)
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Frame = new (1, 1, 8, 8)
|
||||
};
|
||||
|
||||
Application.Top.Add (subView);
|
||||
|
||||
var location = new Point (testX, testY);
|
||||
|
||||
// Act
|
||||
List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
|
||||
|
||||
// Assert
|
||||
if (expected)
|
||||
{
|
||||
Assert.Contains (viewsUnderMouse, v => v == subView);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.DoesNotContain (viewsUnderMouse, v => v == subView);
|
||||
}
|
||||
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0, 0, 0, -1, -1, null)]
|
||||
[InlineData (0, 0, 0, 0, 0, 0, 0, typeof (View))]
|
||||
[InlineData (0, 0, 0, 0, 0, 1, 1, typeof (View))]
|
||||
[InlineData (0, 0, 0, 0, 0, 4, 4, typeof (View))]
|
||||
[InlineData (0, 0, 0, 0, 0, 9, 9, typeof (View))]
|
||||
[InlineData (0, 0, 0, 0, 0, 10, 10, null)]
|
||||
[InlineData (1, 1, 0, 0, 0, -1, -1, null)]
|
||||
[InlineData (1, 1, 0, 0, 0, 0, 0, null)]
|
||||
[InlineData (1, 1, 0, 0, 0, 1, 1, typeof (View))]
|
||||
[InlineData (1, 1, 0, 0, 0, 4, 4, typeof (View))]
|
||||
[InlineData (1, 1, 0, 0, 0, 9, 9, typeof (View))]
|
||||
[InlineData (1, 1, 0, 0, 0, 10, 10, typeof (View))]
|
||||
[InlineData (0, 0, 1, 0, 0, -1, -1, null)]
|
||||
[InlineData (0, 0, 1, 0, 0, 0, 0, typeof (Margin))]
|
||||
[InlineData (0, 0, 1, 0, 0, 1, 1, typeof (View))]
|
||||
[InlineData (0, 0, 1, 0, 0, 4, 4, typeof (View))]
|
||||
[InlineData (0, 0, 1, 0, 0, 9, 9, typeof (Margin))]
|
||||
[InlineData (0, 0, 1, 0, 0, 10, 10, null)]
|
||||
[InlineData (0, 0, 1, 1, 0, -1, -1, null)]
|
||||
[InlineData (0, 0, 1, 1, 0, 0, 0, typeof (Margin))]
|
||||
[InlineData (0, 0, 1, 1, 0, 1, 1, typeof (Border))]
|
||||
[InlineData (0, 0, 1, 1, 0, 4, 4, typeof (View))]
|
||||
[InlineData (0, 0, 1, 1, 0, 9, 9, typeof (Margin))]
|
||||
[InlineData (0, 0, 1, 1, 0, 10, 10, null)]
|
||||
[InlineData (0, 0, 1, 1, 1, -1, -1, null)]
|
||||
[InlineData (0, 0, 1, 1, 1, 0, 0, typeof (Margin))]
|
||||
[InlineData (0, 0, 1, 1, 1, 1, 1, typeof (Border))]
|
||||
[InlineData (0, 0, 1, 1, 1, 2, 2, typeof (Padding))]
|
||||
[InlineData (0, 0, 1, 1, 1, 4, 4, typeof (View))]
|
||||
[InlineData (0, 0, 1, 1, 1, 9, 9, typeof (Margin))]
|
||||
[InlineData (0, 0, 1, 1, 1, 10, 10, null)]
|
||||
[InlineData (1, 1, 1, 0, 0, -1, -1, null)]
|
||||
[InlineData (1, 1, 1, 0, 0, 0, 0, null)]
|
||||
[InlineData (1, 1, 1, 0, 0, 1, 1, typeof (Margin))]
|
||||
[InlineData (1, 1, 1, 0, 0, 4, 4, typeof (View))]
|
||||
[InlineData (1, 1, 1, 0, 0, 9, 9, typeof (View))]
|
||||
[InlineData (1, 1, 1, 0, 0, 10, 10, typeof (Margin))]
|
||||
[InlineData (1, 1, 1, 1, 0, -1, -1, null)]
|
||||
[InlineData (1, 1, 1, 1, 0, 0, 0, null)]
|
||||
[InlineData (1, 1, 1, 1, 0, 1, 1, typeof (Margin))]
|
||||
[InlineData (1, 1, 1, 1, 0, 4, 4, typeof (View))]
|
||||
[InlineData (1, 1, 1, 1, 0, 9, 9, typeof (Border))]
|
||||
[InlineData (1, 1, 1, 1, 0, 10, 10, typeof (Margin))]
|
||||
[InlineData (1, 1, 1, 1, 1, -1, -1, null)]
|
||||
[InlineData (1, 1, 1, 1, 1, 0, 0, null)]
|
||||
[InlineData (1, 1, 1, 1, 1, 1, 1, typeof (Margin))]
|
||||
[InlineData (1, 1, 1, 1, 1, 2, 2, typeof (Border))]
|
||||
[InlineData (1, 1, 1, 1, 1, 3, 3, typeof (Padding))]
|
||||
[InlineData (1, 1, 1, 1, 1, 4, 4, typeof (View))]
|
||||
[InlineData (1, 1, 1, 1, 1, 8, 8, typeof (Padding))]
|
||||
[InlineData (1, 1, 1, 1, 1, 9, 9, typeof (Border))]
|
||||
[InlineData (1, 1, 1, 1, 1, 10, 10, typeof (Margin))]
|
||||
public void Contains (
|
||||
int frameX,
|
||||
int frameY,
|
||||
int marginThickness,
|
||||
int borderThickness,
|
||||
int paddingThickness,
|
||||
int testX,
|
||||
int testY,
|
||||
Type? expectedAdornmentType
|
||||
)
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
X = frameX, Y = frameY,
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
view.Margin.Thickness = new (marginThickness);
|
||||
view.Border.Thickness = new (borderThickness);
|
||||
view.Padding.Thickness = new (paddingThickness);
|
||||
|
||||
Type? containedType = null;
|
||||
|
||||
if (view.Contains (new (testX, testY)))
|
||||
{
|
||||
containedType = view.GetType ();
|
||||
}
|
||||
|
||||
if (view.Margin.Contains (new (testX, testY)))
|
||||
{
|
||||
containedType = view.Margin.GetType ();
|
||||
}
|
||||
|
||||
if (view.Border.Contains (new (testX, testY)))
|
||||
{
|
||||
containedType = view.Border.GetType ();
|
||||
}
|
||||
|
||||
if (view.Padding.Contains (new (testX, testY)))
|
||||
{
|
||||
containedType = view.Padding.GetType ();
|
||||
}
|
||||
|
||||
Assert.Equal (expectedAdornmentType, containedType);
|
||||
}
|
||||
|
||||
// Test that GetViewsUnderMouse returns the correct view if the start view has no subviews
|
||||
[Theory]
|
||||
[InlineData (0, 0)]
|
||||
[InlineData (1, 1)]
|
||||
[InlineData (2, 2)]
|
||||
public void Returns_Start_If_No_SubViews (int testX, int testY)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
|
||||
Assert.Same (Application.Top, View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ());
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
// Test that GetViewsUnderMouse returns null if the start view has no subviews and coords are outside the view
|
||||
[Theory]
|
||||
[InlineData (0, 0)]
|
||||
[InlineData (2, 1)]
|
||||
[InlineData (20, 20)]
|
||||
public void Returns_Null_If_No_SubViews_Coords_Outside (int testX, int testY)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
|
||||
Assert.Null (View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ());
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0)]
|
||||
[InlineData (2, 1)]
|
||||
[InlineData (20, 20)]
|
||||
public void Returns_Null_If_Start_Not_Visible (int testX, int testY)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 10, Height = 10,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
Assert.Null (View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ());
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
// Test that GetViewsUnderMouse returns the correct view if the start view has subviews
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (6, 7, false)]
|
||||
[InlineData (1, 2, true)]
|
||||
[InlineData (5, 6, true)]
|
||||
public void Returns_Correct_If_SubViews (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
|
||||
var subview = new View
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 5, Height = 5
|
||||
};
|
||||
Application.Top.Add (subview);
|
||||
|
||||
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == subview);
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (6, 7, false)]
|
||||
[InlineData (1, 2, false)]
|
||||
[InlineData (5, 6, false)]
|
||||
public void Returns_Null_If_SubView_NotVisible (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
|
||||
var subview = new View
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 5, Height = 5,
|
||||
Visible = false
|
||||
};
|
||||
Application.Top.Add (subview);
|
||||
|
||||
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == subview);
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (6, 7, false)]
|
||||
[InlineData (1, 2, false)]
|
||||
[InlineData (5, 6, false)]
|
||||
public void Returns_Null_If_Not_Visible_And_SubView_Visible (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
Width = 10, Height = 10,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
var subview = new View
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 5, Height = 5
|
||||
};
|
||||
Application.Top.Add (subview);
|
||||
subview.Visible = true;
|
||||
Assert.True (subview.Visible);
|
||||
Assert.False (Application.Top.Visible);
|
||||
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == subview);
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
// Test that GetViewsUnderMouse works if the start view has positive Adornments
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (7, 8, false)]
|
||||
[InlineData (1, 2, false)]
|
||||
[InlineData (2, 3, true)]
|
||||
[InlineData (5, 6, true)]
|
||||
[InlineData (6, 7, true)]
|
||||
public void Returns_Correct_If_Start_Has_Adornments (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
Application.Top.Margin.Thickness = new (1);
|
||||
|
||||
var subview = new View
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 5, Height = 5
|
||||
};
|
||||
Application.Top.Add (subview);
|
||||
|
||||
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == subview);
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
// Test that GetViewsUnderMouse works if the start view has offset Viewport location
|
||||
[Theory]
|
||||
[InlineData (1, 0, 0, true)]
|
||||
[InlineData (1, 1, 1, true)]
|
||||
[InlineData (1, 2, 2, false)]
|
||||
[InlineData (-1, 3, 3, true)]
|
||||
[InlineData (-1, 2, 2, true)]
|
||||
[InlineData (-1, 1, 1, false)]
|
||||
[InlineData (-1, 0, 0, false)]
|
||||
public void Returns_Correct_If_Start_Has_Offset_Viewport (int offset, int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
Width = 10, Height = 10,
|
||||
ViewportSettings = ViewportSettings.AllowNegativeLocation
|
||||
};
|
||||
Application.Top.Viewport = new (offset, offset, 10, 10);
|
||||
|
||||
var subview = new View
|
||||
{
|
||||
X = 1, Y = 1,
|
||||
Width = 2, Height = 2
|
||||
};
|
||||
Application.Top.Add (subview);
|
||||
|
||||
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == subview);
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (9, 9, true)]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (7, 8, false)]
|
||||
[InlineData (1, 2, false)]
|
||||
[InlineData (2, 3, false)]
|
||||
[InlineData (5, 6, false)]
|
||||
[InlineData (6, 7, false)]
|
||||
public void Returns_Correct_If_Start_Has_Adornment_WithSubview (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
Application.Top.Padding.Thickness = new (1);
|
||||
|
||||
var subview = new View
|
||||
{
|
||||
X = Pos.AnchorEnd (1), Y = Pos.AnchorEnd (1),
|
||||
Width = 1, Height = 1
|
||||
};
|
||||
Application.Top.Padding.Add (subview);
|
||||
Application.Top.BeginInit ();
|
||||
Application.Top.EndInit ();
|
||||
|
||||
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == subview);
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, typeof (Margin))]
|
||||
[InlineData (9, 9, typeof (Margin))]
|
||||
[InlineData (1, 1, typeof (Border))]
|
||||
[InlineData (8, 8, typeof (Border))]
|
||||
[InlineData (2, 2, typeof (Padding))]
|
||||
[InlineData (7, 7, typeof (Padding))]
|
||||
[InlineData (5, 5, typeof (Toplevel))]
|
||||
public void Returns_Adornment_If_Start_Has_Adornments (int testX, int testY, Type expectedAdornmentType)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
Application.Top.Margin.Thickness = new (1);
|
||||
Application.Top.Border.Thickness = new (1);
|
||||
Application.Top.Padding.Thickness = new (1);
|
||||
|
||||
var subview = new View
|
||||
{
|
||||
X = 1, Y = 1,
|
||||
Width = 1, Height = 1
|
||||
};
|
||||
Application.Top.Add (subview);
|
||||
|
||||
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
|
||||
Assert.Equal (expectedAdornmentType, found!.GetType ());
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
// Test that GetViewsUnderMouse works if the subview has positive Adornments
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (7, 8, false)]
|
||||
[InlineData (6, 7, false)]
|
||||
[InlineData (1, 2, false)]
|
||||
[InlineData (5, 6, false)]
|
||||
[InlineData (2, 3, true)]
|
||||
public void Returns_Correct_If_SubView_Has_Adornments (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
|
||||
var subview = new View
|
||||
{
|
||||
X = 1, Y = 2,
|
||||
Width = 5, Height = 5
|
||||
};
|
||||
subview.Margin.Thickness = new (1);
|
||||
Application.Top.Add (subview);
|
||||
|
||||
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == subview);
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (7, 8, false)]
|
||||
[InlineData (6, 7, false)]
|
||||
[InlineData (1, 2, false)]
|
||||
[InlineData (5, 6, false)]
|
||||
[InlineData (6, 5, false)]
|
||||
[InlineData (5, 5, true)]
|
||||
public void Returns_Correct_If_SubView_Has_Adornment_WithSubview (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
|
||||
// A subview with + Padding
|
||||
var subview = new View
|
||||
{
|
||||
X = 1, Y = 1,
|
||||
Width = 5, Height = 5
|
||||
};
|
||||
subview.Padding.Thickness = new (1);
|
||||
|
||||
// This subview will be at the bottom-right-corner of subview
|
||||
// So screen-relative location will be X + Width - 1 = 5
|
||||
var paddingSubview = new View
|
||||
{
|
||||
X = Pos.AnchorEnd (1),
|
||||
Y = Pos.AnchorEnd (1),
|
||||
Width = 1,
|
||||
Height = 1
|
||||
};
|
||||
subview.Padding.Add (paddingSubview);
|
||||
Application.Top.Add (subview);
|
||||
Application.Top.BeginInit ();
|
||||
Application.Top.EndInit ();
|
||||
|
||||
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == paddingSubview);
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, false)]
|
||||
[InlineData (1, 1, false)]
|
||||
[InlineData (9, 9, false)]
|
||||
[InlineData (10, 10, false)]
|
||||
[InlineData (7, 8, false)]
|
||||
[InlineData (6, 7, false)]
|
||||
[InlineData (1, 2, false)]
|
||||
[InlineData (5, 6, false)]
|
||||
[InlineData (6, 5, false)]
|
||||
[InlineData (5, 5, true)]
|
||||
public void Returns_Correct_If_SubView_Is_Scrolled_And_Has_Adornment_WithSubview (int testX, int testY, bool expectedSubViewFound)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
|
||||
// A subview with + Padding
|
||||
var subview = new View
|
||||
{
|
||||
X = 1, Y = 1,
|
||||
Width = 5, Height = 5
|
||||
};
|
||||
subview.Padding.Thickness = new (1);
|
||||
|
||||
// Scroll the subview
|
||||
subview.SetContentSize (new (10, 10));
|
||||
subview.Viewport = subview.Viewport with { Location = new (1, 1) };
|
||||
|
||||
// This subview will be at the bottom-right-corner of subview
|
||||
// So screen-relative location will be X + Width - 1 = 5
|
||||
var paddingSubview = new View
|
||||
{
|
||||
X = Pos.AnchorEnd (1),
|
||||
Y = Pos.AnchorEnd (1),
|
||||
Width = 1,
|
||||
Height = 1
|
||||
};
|
||||
subview.Padding.Add (paddingSubview);
|
||||
Application.Top.Add (subview);
|
||||
Application.Top.BeginInit ();
|
||||
Application.Top.EndInit ();
|
||||
|
||||
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
|
||||
|
||||
Assert.Equal (expectedSubViewFound, found == paddingSubview);
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
// Test that GetViewsUnderMouse works with nested subviews
|
||||
[Theory]
|
||||
[InlineData (0, 0, -1)]
|
||||
[InlineData (9, 9, -1)]
|
||||
[InlineData (10, 10, -1)]
|
||||
[InlineData (1, 1, 0)]
|
||||
[InlineData (1, 2, 0)]
|
||||
[InlineData (2, 2, 1)]
|
||||
[InlineData (3, 3, 2)]
|
||||
[InlineData (5, 5, 2)]
|
||||
public void Returns_Correct_With_NestedSubViews (int testX, int testY, int expectedSubViewFound)
|
||||
{
|
||||
Application.Top = new ()
|
||||
{
|
||||
Width = 10, Height = 10
|
||||
};
|
||||
|
||||
var numSubViews = 3;
|
||||
List<View> subviews = new ();
|
||||
|
||||
for (var i = 0; i < numSubViews; i++)
|
||||
{
|
||||
var subview = new View
|
||||
{
|
||||
X = 1, Y = 1,
|
||||
Width = 5, Height = 5
|
||||
};
|
||||
subviews.Add (subview);
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
subviews [i - 1].Add (subview);
|
||||
}
|
||||
}
|
||||
|
||||
Application.Top.Add (subviews [0]);
|
||||
|
||||
View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
|
||||
Assert.Equal (expectedSubViewFound, subviews.IndexOf (found!));
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, new [] { "top" })]
|
||||
[InlineData (9, 9, new [] { "top" })]
|
||||
[InlineData (10, 10, new string [] { })]
|
||||
[InlineData (1, 1, new [] { "top", "view" })]
|
||||
[InlineData (1, 2, new [] { "top", "view" })]
|
||||
[InlineData (2, 1, new [] { "top", "view" })]
|
||||
[InlineData (2, 2, new [] { "top", "view", "subView" })]
|
||||
[InlineData (3, 3, new [] { "top" })] // clipped
|
||||
[InlineData (2, 3, new [] { "top" })] // clipped
|
||||
public void GetViewsUnderMouse_Tiled_Subviews (int mouseX, int mouseY, string [] viewIdStrings)
|
||||
{
|
||||
// Arrange
|
||||
Application.Top = new ()
|
||||
{
|
||||
Frame = new (0, 0, 10, 10),
|
||||
Id = "top"
|
||||
};
|
||||
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = 2,
|
||||
Height = 2,
|
||||
Arrangement = ViewArrangement.Overlapped
|
||||
}; // at 1,1 to 3,2 (screen)
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = 2,
|
||||
Height = 2,
|
||||
Arrangement = ViewArrangement.Overlapped
|
||||
}; // at 2,2 to 4,3 (screen)
|
||||
view.Add (subView);
|
||||
Application.Top.Add (view);
|
||||
|
||||
List<View?> found = View.GetViewsUnderMouse (new (mouseX, mouseY));
|
||||
|
||||
string [] foundIds = found.Select (v => v!.Id).ToArray ();
|
||||
|
||||
Assert.Equal (viewIdStrings, foundIds);
|
||||
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState (true);
|
||||
}
|
||||
}
|
||||
349
Tests/UnitTests/View/Mouse/MouseEnterLeaveTests.cs
Normal file
349
Tests/UnitTests/View/Mouse/MouseEnterLeaveTests.cs
Normal file
@@ -0,0 +1,349 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Terminal.Gui.ViewMouseTests;
|
||||
|
||||
[Trait ("Category", "Input")]
|
||||
public class MouseEnterLeaveTests
|
||||
{
|
||||
private class TestView : View
|
||||
{
|
||||
public TestView ()
|
||||
{
|
||||
MouseEnter += OnMouseEnterHandler;
|
||||
MouseLeave += OnMouseLeaveHandler;
|
||||
}
|
||||
|
||||
public bool CancelOnEnter { get; init; }
|
||||
|
||||
public bool CancelEnterEvent { get; init; }
|
||||
|
||||
public bool OnMouseEnterCalled { get; private set; }
|
||||
public bool OnMouseLeaveCalled { get; private set; }
|
||||
|
||||
protected override bool OnMouseEnter (CancelEventArgs eventArgs)
|
||||
{
|
||||
OnMouseEnterCalled = true;
|
||||
eventArgs.Cancel = CancelOnEnter;
|
||||
|
||||
base.OnMouseEnter (eventArgs);
|
||||
|
||||
return eventArgs.Cancel;
|
||||
}
|
||||
|
||||
protected override void OnMouseLeave ()
|
||||
{
|
||||
OnMouseLeaveCalled = true;
|
||||
|
||||
base.OnMouseLeave ();
|
||||
}
|
||||
|
||||
public bool MouseEnterRaised { get; private set; }
|
||||
public bool MouseLeaveRaised { get; private set; }
|
||||
|
||||
private void OnMouseEnterHandler (object s, CancelEventArgs e)
|
||||
{
|
||||
MouseEnterRaised = true;
|
||||
|
||||
if (CancelEnterEvent)
|
||||
{
|
||||
e.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseLeaveHandler (object s, EventArgs e) { MouseLeaveRaised = true; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewMouseEnterEvent_ViewIsEnabledAndVisible_CallsOnMouseEnter ()
|
||||
{
|
||||
// Arrange
|
||||
var view = new TestView
|
||||
{
|
||||
Enabled = true,
|
||||
Visible = true
|
||||
};
|
||||
|
||||
var mouseEvent = new MouseEventArgs ();
|
||||
|
||||
var eventArgs = new CancelEventArgs ();
|
||||
|
||||
// Act
|
||||
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
|
||||
|
||||
// Assert
|
||||
Assert.True (view.OnMouseEnterCalled);
|
||||
Assert.False (cancelled);
|
||||
Assert.False (eventArgs.Cancel);
|
||||
|
||||
// Cleanup
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewMouseEnterEvent_ViewIsDisabled_CallsOnMouseEnter ()
|
||||
{
|
||||
// Arrange
|
||||
var view = new TestView
|
||||
{
|
||||
Enabled = false,
|
||||
Visible = true
|
||||
};
|
||||
|
||||
var eventArgs = new CancelEventArgs ();
|
||||
|
||||
// Act
|
||||
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
|
||||
|
||||
// Assert
|
||||
Assert.True (view.OnMouseEnterCalled);
|
||||
Assert.False (cancelled);
|
||||
Assert.False (eventArgs.Cancel);
|
||||
|
||||
// Cleanup
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewMouseEnterEvent_ViewIsNotVisible_DoesNotCallOnMouseEnter ()
|
||||
{
|
||||
// Arrange
|
||||
var view = new TestView
|
||||
{
|
||||
Enabled = true,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
var eventArgs = new CancelEventArgs ();
|
||||
|
||||
// Act
|
||||
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
|
||||
|
||||
// Assert
|
||||
Assert.False (view.OnMouseEnterCalled);
|
||||
Assert.Null (cancelled);
|
||||
Assert.False (eventArgs.Cancel);
|
||||
|
||||
// Cleanup
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewMouseLeaveEvent_ViewIsVisible_CallsOnMouseLeave ()
|
||||
{
|
||||
// Arrange
|
||||
var view = new TestView
|
||||
{
|
||||
Enabled = true, Visible = true
|
||||
};
|
||||
|
||||
var mouseEvent = new MouseEventArgs ();
|
||||
|
||||
// Act
|
||||
view.NewMouseLeaveEvent ();
|
||||
|
||||
// Assert
|
||||
Assert.True (view.OnMouseLeaveCalled);
|
||||
Assert.False (mouseEvent.Handled);
|
||||
|
||||
// Cleanup
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewMouseLeaveEvent_ViewIsNotVisible_CallsOnMouseLeave ()
|
||||
{
|
||||
// Arrange
|
||||
var view = new TestView
|
||||
{
|
||||
Enabled = true,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
var mouseEvent = new MouseEventArgs ();
|
||||
|
||||
// Act
|
||||
view.NewMouseLeaveEvent ();
|
||||
|
||||
// Assert
|
||||
Assert.True (view.OnMouseLeaveCalled);
|
||||
Assert.False (mouseEvent.Handled);
|
||||
|
||||
// Cleanup
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
[Fact]
|
||||
public void NewMouseEnterEvent_ViewIsEnabledAndVisible_RaisesMouseEnter ()
|
||||
{
|
||||
// Arrange
|
||||
var view = new TestView
|
||||
{
|
||||
Enabled = true,
|
||||
Visible = true
|
||||
};
|
||||
|
||||
var eventArgs = new CancelEventArgs ();
|
||||
|
||||
// Act
|
||||
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
|
||||
|
||||
// Assert
|
||||
Assert.True (view.MouseEnterRaised);
|
||||
Assert.False (cancelled);
|
||||
Assert.False (eventArgs.Cancel);
|
||||
|
||||
// Cleanup
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewMouseEnterEvent_ViewIsDisabled_RaisesMouseEnter ()
|
||||
{
|
||||
// Arrange
|
||||
var view = new TestView
|
||||
{
|
||||
Enabled = false,
|
||||
Visible = true
|
||||
};
|
||||
|
||||
var eventArgs = new CancelEventArgs ();
|
||||
|
||||
// Act
|
||||
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
|
||||
|
||||
// Assert
|
||||
Assert.True (view.MouseEnterRaised);
|
||||
Assert.False (cancelled);
|
||||
Assert.False (eventArgs.Cancel);
|
||||
|
||||
// Cleanup
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewMouseEnterEvent_ViewIsNotVisible_DoesNotRaiseMouseEnter ()
|
||||
{
|
||||
// Arrange
|
||||
var view = new TestView
|
||||
{
|
||||
Enabled = true,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
var eventArgs = new CancelEventArgs ();
|
||||
|
||||
// Act
|
||||
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
|
||||
|
||||
// Assert
|
||||
Assert.False (view.MouseEnterRaised);
|
||||
Assert.Null (cancelled);
|
||||
Assert.False (eventArgs.Cancel);
|
||||
|
||||
// Cleanup
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewMouseLeaveEvent_ViewIsVisible_RaisesMouseLeave ()
|
||||
{
|
||||
// Arrange
|
||||
var view = new TestView
|
||||
{
|
||||
Enabled = true,
|
||||
Visible = true
|
||||
};
|
||||
|
||||
var mouseEvent = new MouseEventArgs ();
|
||||
|
||||
// Act
|
||||
view.NewMouseLeaveEvent ();
|
||||
|
||||
// Assert
|
||||
Assert.True (view.MouseLeaveRaised);
|
||||
Assert.False (mouseEvent.Handled);
|
||||
|
||||
// Cleanup
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewMouseLeaveEvent_ViewIsNotVisible_RaisesMouseLeave ()
|
||||
{
|
||||
// Arrange
|
||||
var view = new TestView
|
||||
{
|
||||
Enabled = true,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
var mouseEvent = new MouseEventArgs ();
|
||||
|
||||
// Act
|
||||
view.NewMouseLeaveEvent ();
|
||||
|
||||
// Assert
|
||||
Assert.True (view.MouseLeaveRaised);
|
||||
Assert.False (mouseEvent.Handled);
|
||||
|
||||
// Cleanup
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
// Handled tests
|
||||
[Fact]
|
||||
public void NewMouseEnterEvent_HandleOnMouseEnter_Event_Not_Raised ()
|
||||
{
|
||||
// Arrange
|
||||
var view = new TestView
|
||||
{
|
||||
Enabled = true,
|
||||
Visible = true,
|
||||
CancelOnEnter = true
|
||||
};
|
||||
|
||||
var eventArgs = new CancelEventArgs ();
|
||||
|
||||
// Act
|
||||
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
|
||||
|
||||
// Assert
|
||||
Assert.True (view.OnMouseEnterCalled);
|
||||
Assert.True (cancelled);
|
||||
Assert.True (eventArgs.Cancel);
|
||||
|
||||
Assert.False (view.MouseEnterRaised);
|
||||
|
||||
// Cleanup
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewMouseEnterEvent_HandleMouseEnterEvent ()
|
||||
{
|
||||
// Arrange
|
||||
var view = new TestView
|
||||
{
|
||||
Enabled = true,
|
||||
Visible = true,
|
||||
CancelEnterEvent = true
|
||||
};
|
||||
|
||||
var eventArgs = new CancelEventArgs ();
|
||||
|
||||
// Act
|
||||
bool? cancelled = view.NewMouseEnterEvent (eventArgs);
|
||||
|
||||
// Assert
|
||||
Assert.True (view.OnMouseEnterCalled);
|
||||
Assert.True (cancelled);
|
||||
Assert.True (eventArgs.Cancel);
|
||||
|
||||
Assert.True (view.MouseEnterRaised);
|
||||
|
||||
// Cleanup
|
||||
view.Dispose ();
|
||||
}
|
||||
}
|
||||
545
Tests/UnitTests/View/Mouse/MouseTests.cs
Normal file
545
Tests/UnitTests/View/Mouse/MouseTests.cs
Normal file
@@ -0,0 +1,545 @@
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewMouseTests;
|
||||
|
||||
[Trait ("Category", "Input")]
|
||||
|
||||
public class MouseTests (ITestOutputHelper output) : TestsAllViews
|
||||
{
|
||||
[Theory]
|
||||
[InlineData (false, false, false)]
|
||||
[InlineData (true, false, true)]
|
||||
[InlineData (true, true, true)]
|
||||
public void MouseClick_SetsFocus_If_CanFocus (bool canFocus, bool setFocus, bool expectedHasFocus)
|
||||
{
|
||||
var superView = new View { CanFocus = true, Height = 1, Width = 15 };
|
||||
var focusedView = new View { CanFocus = true, Width = 1, Height = 1 };
|
||||
var testView = new View { CanFocus = canFocus, X = 4, Width = 4, Height = 1 };
|
||||
superView.Add (focusedView, testView);
|
||||
|
||||
focusedView.SetFocus ();
|
||||
|
||||
Assert.True (superView.HasFocus);
|
||||
Assert.True (focusedView.HasFocus);
|
||||
Assert.False (testView.HasFocus);
|
||||
|
||||
if (setFocus)
|
||||
{
|
||||
testView.SetFocus ();
|
||||
}
|
||||
|
||||
testView.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked });
|
||||
Assert.True (superView.HasFocus);
|
||||
Assert.Equal (expectedHasFocus, testView.HasFocus);
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData (false, false, 1)]
|
||||
[InlineData (true, false, 1)]
|
||||
[InlineData (true, true, 1)]
|
||||
public void MouseClick_Raises_Selecting (bool canFocus, bool setFocus, int expectedSelectingCount)
|
||||
{
|
||||
var superView = new View { CanFocus = true, Height = 1, Width = 15 };
|
||||
var focusedView = new View { CanFocus = true, Width = 1, Height = 1 };
|
||||
var testView = new View { CanFocus = canFocus, X = 4, Width = 4, Height = 1 };
|
||||
superView.Add (focusedView, testView);
|
||||
|
||||
focusedView.SetFocus ();
|
||||
|
||||
Assert.True (superView.HasFocus);
|
||||
Assert.True (focusedView.HasFocus);
|
||||
Assert.False (testView.HasFocus);
|
||||
|
||||
if (setFocus)
|
||||
{
|
||||
testView.SetFocus ();
|
||||
}
|
||||
|
||||
int selectingCount = 0;
|
||||
testView.Selecting += (sender, args) => selectingCount++;
|
||||
|
||||
testView.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked });
|
||||
Assert.True (superView.HasFocus);
|
||||
Assert.Equal (expectedSelectingCount, selectingCount);
|
||||
}
|
||||
|
||||
|
||||
// TODO: Add more tests that ensure the above test works with positive adornments
|
||||
|
||||
// Test drag to move
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0, 0, false)]
|
||||
[InlineData (0, 0, 0, 4, false)]
|
||||
[InlineData (1, 0, 0, 4, false)]
|
||||
[InlineData (0, 1, 0, 4, true)]
|
||||
[InlineData (0, 0, 1, 4, false)]
|
||||
|
||||
[InlineData (1, 1, 0, 3, false)]
|
||||
[InlineData (1, 1, 0, 4, false)]
|
||||
[InlineData (1, 1, 0, 5, true)]
|
||||
[InlineData (1, 1, 0, 6, false)]
|
||||
|
||||
|
||||
[InlineData (1, 1, 0, 11, false)]
|
||||
[InlineData (1, 1, 0, 12, true)]
|
||||
[InlineData (1, 1, 0, 13, false)]
|
||||
[InlineData (1, 1, 0, 14, false)]
|
||||
[AutoInitShutdown]
|
||||
public void ButtonPressed_In_Border_Starts_Drag (int marginThickness, int borderThickness, int paddingThickness, int xy, bool expectedMoved)
|
||||
{
|
||||
var testView = new View
|
||||
{
|
||||
CanFocus = true,
|
||||
X = 4,
|
||||
Y = 4,
|
||||
Width = 10,
|
||||
Height = 10,
|
||||
Arrangement = ViewArrangement.Movable
|
||||
};
|
||||
testView.Margin.Thickness = new (marginThickness);
|
||||
testView.Border.Thickness = new (borderThickness);
|
||||
testView.Padding.Thickness = new (paddingThickness);
|
||||
|
||||
var top = new Toplevel ();
|
||||
top.Add (testView);
|
||||
|
||||
var rs = Application.Begin (top);
|
||||
Assert.Equal (4, testView.Frame.X);
|
||||
|
||||
Assert.Equal (new Point (4, 4), testView.Frame.Location);
|
||||
Application.RaiseMouseEvent (new () { ScreenPosition = new (xy, xy), Flags = MouseFlags.Button1Pressed });
|
||||
|
||||
Application.RaiseMouseEvent (new () { ScreenPosition = new (xy + 1, xy + 1), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition });
|
||||
Application.RunIteration(ref rs, false);
|
||||
|
||||
Assert.Equal (expectedMoved, new Point (5, 5) == testView.Frame.Location);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (MouseFlags.WheeledUp | MouseFlags.ButtonCtrl, MouseFlags.WheeledLeft)]
|
||||
[InlineData (MouseFlags.WheeledDown | MouseFlags.ButtonCtrl, MouseFlags.WheeledRight)]
|
||||
public void WheeledLeft_WheeledRight (MouseFlags mouseFlags, MouseFlags expectedMouseFlagsFromEvent)
|
||||
{
|
||||
MouseFlags mouseFlagsFromEvent = MouseFlags.None;
|
||||
var view = new View ();
|
||||
view.MouseEvent += (s, e) => mouseFlagsFromEvent = e.Flags;
|
||||
|
||||
view.NewMouseEvent (new MouseEventArgs () { Flags = mouseFlags });
|
||||
Assert.Equal (mouseFlagsFromEvent, expectedMouseFlagsFromEvent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NewMouseEvent_Invokes_MouseEvent_Properly ()
|
||||
{
|
||||
View view = new ()
|
||||
{
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
};
|
||||
bool mouseEventInvoked = false;
|
||||
view.MouseEvent += (s, e) =>
|
||||
{
|
||||
mouseEventInvoked = true;
|
||||
e.Handled = true;
|
||||
};
|
||||
|
||||
MouseEventArgs me = new ();
|
||||
view.NewMouseEvent (me);
|
||||
Assert.True (mouseEventInvoked);
|
||||
Assert.True (me.Handled);
|
||||
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[SetupFakeDriver] // Required for spinner view that wants to register timeouts
|
||||
[MemberData (nameof (AllViewTypes))]
|
||||
public void AllViews_NewMouseEvent_Enabled_False_Does_Not_Set_Handled (Type viewType)
|
||||
{
|
||||
var view = CreateInstanceIfNotGeneric (viewType);
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
output.WriteLine ($"Ignoring {viewType} - It's a Generic");
|
||||
return;
|
||||
}
|
||||
|
||||
view.Enabled = false;
|
||||
var me = new MouseEventArgs ();
|
||||
view.NewMouseEvent (me);
|
||||
Assert.False (me.Handled);
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[SetupFakeDriver] // Required for spinner view that wants to register timeouts
|
||||
[MemberData (nameof (AllViewTypes))]
|
||||
public void AllViews_NewMouseEvent_Clicked_Enabled_False_Does_Not_Set_Handled (Type viewType)
|
||||
{
|
||||
var view = CreateInstanceIfNotGeneric (viewType);
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
output.WriteLine ($"Ignoring {viewType} - It's a Generic");
|
||||
return;
|
||||
}
|
||||
|
||||
view.Enabled = false;
|
||||
var me = new MouseEventArgs ()
|
||||
{
|
||||
Flags = MouseFlags.Button1Clicked
|
||||
};
|
||||
view.NewMouseEvent (me);
|
||||
Assert.False (me.Handled);
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (MouseFlags.Button1Pressed, MouseFlags.Button1Released, MouseFlags.Button1Clicked)]
|
||||
[InlineData (MouseFlags.Button2Pressed, MouseFlags.Button2Released, MouseFlags.Button2Clicked)]
|
||||
[InlineData (MouseFlags.Button3Pressed, MouseFlags.Button3Released, MouseFlags.Button3Clicked)]
|
||||
[InlineData (MouseFlags.Button4Pressed, MouseFlags.Button4Released, MouseFlags.Button4Clicked)]
|
||||
public void WantContinuousButtonPressed_False_Button_Press_Release_DoesNotClick (MouseFlags pressed, MouseFlags released, MouseFlags clicked)
|
||||
{
|
||||
var me = new MouseEventArgs ();
|
||||
|
||||
var view = new View ()
|
||||
{
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
WantContinuousButtonPressed = false
|
||||
};
|
||||
|
||||
var clickedCount = 0;
|
||||
|
||||
view.MouseClick += (s, e) => clickedCount++;
|
||||
|
||||
me.Flags = pressed;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (0, clickedCount);
|
||||
me.Handled = false;
|
||||
|
||||
me.Flags = pressed;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (0, clickedCount);
|
||||
me.Handled = false;
|
||||
|
||||
me.Flags = released;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (0, clickedCount);
|
||||
me.Handled = false;
|
||||
|
||||
me.Flags = clicked;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (1, clickedCount);
|
||||
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData (MouseFlags.Button1Clicked)]
|
||||
[InlineData (MouseFlags.Button2Clicked)]
|
||||
[InlineData (MouseFlags.Button3Clicked)]
|
||||
[InlineData (MouseFlags.Button4Clicked)]
|
||||
public void WantContinuousButtonPressed_True_Button_Clicked_Raises_MouseClick (MouseFlags clicked)
|
||||
{
|
||||
var me = new MouseEventArgs ();
|
||||
|
||||
var view = new View ()
|
||||
{
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
WantContinuousButtonPressed = true
|
||||
};
|
||||
|
||||
var clickedCount = 0;
|
||||
|
||||
view.MouseClick += (s, e) => clickedCount++;
|
||||
|
||||
me.Flags = clicked;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (1, clickedCount);
|
||||
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData (MouseFlags.Button1Clicked)]
|
||||
[InlineData (MouseFlags.Button2Clicked)]
|
||||
[InlineData (MouseFlags.Button3Clicked)]
|
||||
[InlineData (MouseFlags.Button4Clicked)]
|
||||
public void WantContinuousButtonPressed_True_Button_Clicked_Raises_Selecting (MouseFlags clicked)
|
||||
{
|
||||
var me = new MouseEventArgs ();
|
||||
|
||||
var view = new View ()
|
||||
{
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
WantContinuousButtonPressed = true
|
||||
};
|
||||
|
||||
var selectingCount = 0;
|
||||
|
||||
view.Selecting += (s, e) => selectingCount++;
|
||||
|
||||
me.Flags = clicked;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (1, selectingCount);
|
||||
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (MouseFlags.Button1Pressed, MouseFlags.Button1Released)]
|
||||
[InlineData (MouseFlags.Button2Pressed, MouseFlags.Button2Released)]
|
||||
[InlineData (MouseFlags.Button3Pressed, MouseFlags.Button3Released)]
|
||||
[InlineData (MouseFlags.Button4Pressed, MouseFlags.Button4Released)]
|
||||
public void WantContinuousButtonPressed_True_And_WantMousePositionReports_True_Button_Press_Release_Clicks (MouseFlags pressed, MouseFlags released)
|
||||
{
|
||||
var me = new MouseEventArgs ();
|
||||
|
||||
var view = new View ()
|
||||
{
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
WantContinuousButtonPressed = true,
|
||||
WantMousePositionReports = true
|
||||
};
|
||||
|
||||
var clickedCount = 0;
|
||||
|
||||
view.MouseClick += (s, e) => clickedCount++;
|
||||
|
||||
me.Flags = pressed;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (0, clickedCount);
|
||||
me.Handled = false;
|
||||
|
||||
me.Flags = pressed;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (1, clickedCount);
|
||||
me.Handled = false;
|
||||
|
||||
me.Flags = released;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (1, clickedCount);
|
||||
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (MouseFlags.Button1Pressed, MouseFlags.Button1Released, MouseFlags.Button1Clicked)]
|
||||
[InlineData (MouseFlags.Button2Pressed, MouseFlags.Button2Released, MouseFlags.Button2Clicked)]
|
||||
[InlineData (MouseFlags.Button3Pressed, MouseFlags.Button3Released, MouseFlags.Button3Clicked)]
|
||||
[InlineData (MouseFlags.Button4Pressed, MouseFlags.Button4Released, MouseFlags.Button4Clicked)]
|
||||
public void WantContinuousButtonPressed_True_And_WantMousePositionReports_True_Button_Press_Release_Clicks_Repeatedly (MouseFlags pressed, MouseFlags released, MouseFlags clicked)
|
||||
{
|
||||
var me = new MouseEventArgs ();
|
||||
|
||||
var view = new View ()
|
||||
{
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
WantContinuousButtonPressed = true,
|
||||
WantMousePositionReports = true
|
||||
};
|
||||
|
||||
var clickedCount = 0;
|
||||
|
||||
view.MouseClick += (s, e) => clickedCount++;
|
||||
|
||||
me.Flags = pressed;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (0, clickedCount);
|
||||
me.Handled = false;
|
||||
|
||||
me.Flags = pressed;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (1, clickedCount);
|
||||
me.Handled = false;
|
||||
|
||||
me.Flags = released;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (1, clickedCount);
|
||||
me.Handled = false;
|
||||
|
||||
me.Flags = clicked;
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (1, clickedCount);
|
||||
|
||||
view.Dispose ();
|
||||
Application.ResetState (ignoreDisposed: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WantContinuousButtonPressed_True_And_WantMousePositionReports_True_Move_InViewport_OutOfViewport_Keeps_Counting ()
|
||||
{
|
||||
var me = new MouseEventArgs ();
|
||||
|
||||
var view = new View ()
|
||||
{
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
WantContinuousButtonPressed = true,
|
||||
WantMousePositionReports = true
|
||||
};
|
||||
|
||||
var clickedCount = 0;
|
||||
|
||||
view.MouseClick += (s, e) => clickedCount++;
|
||||
|
||||
// Start in Viewport
|
||||
me.Flags = MouseFlags.Button1Pressed;
|
||||
me.Position = me.Position with { X = 0 };
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (0, clickedCount);
|
||||
me.Handled = false;
|
||||
|
||||
// Move out of Viewport
|
||||
me.Flags = MouseFlags.Button1Pressed;
|
||||
me.Position = me.Position with { X = 1 };
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (1, clickedCount);
|
||||
me.Handled = false;
|
||||
|
||||
// Move into Viewport
|
||||
me.Flags = MouseFlags.Button1Pressed;
|
||||
me.Position = me.Position with { X = 0 };
|
||||
view.NewMouseEvent (me);
|
||||
Assert.Equal (2, clickedCount);
|
||||
me.Handled = false;
|
||||
|
||||
view.Dispose ();
|
||||
Application.ResetState (ignoreDisposed: true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (HighlightStyle.None, 0, 0)]
|
||||
[InlineData (HighlightStyle.Pressed | HighlightStyle.PressedOutside, 1, 1)]
|
||||
public void HighlightOnPress_Fires_Events_And_Highlights (HighlightStyle highlightOnPress, int expectedEnabling, int expectedDisabling)
|
||||
{
|
||||
var view = new View ()
|
||||
{
|
||||
CanFocus = true,
|
||||
HighlightStyle = highlightOnPress,
|
||||
Height = 1,
|
||||
Width = 1
|
||||
};
|
||||
|
||||
int enablingHighlight = 0;
|
||||
int disablingHighlight = 0;
|
||||
view.Highlight += View_Highlight;
|
||||
view.ColorScheme = new ColorScheme (new Attribute (ColorName16.Red, ColorName16.Blue));
|
||||
ColorScheme originalColorScheme = view.ColorScheme;
|
||||
|
||||
view.NewMouseEvent (new () { Flags = MouseFlags.Button1Pressed, });
|
||||
|
||||
if (highlightOnPress != HighlightStyle.None)
|
||||
{
|
||||
Assert.NotEqual (originalColorScheme, view.ColorScheme);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal (originalColorScheme, view.ColorScheme);
|
||||
}
|
||||
|
||||
view.NewMouseEvent (new () { Flags = MouseFlags.Button1Released, });
|
||||
Assert.Equal (originalColorScheme, view.ColorScheme);
|
||||
Assert.Equal (expectedEnabling, enablingHighlight);
|
||||
Assert.Equal (expectedDisabling, disablingHighlight);
|
||||
|
||||
view.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void View_Highlight (object sender, CancelEventArgs<HighlightStyle> e)
|
||||
{
|
||||
if (e.NewValue == HighlightStyle.None)
|
||||
{
|
||||
disablingHighlight++;
|
||||
}
|
||||
else
|
||||
{
|
||||
enablingHighlight++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add tests for each combination of HighlightFlags
|
||||
|
||||
[Theory]
|
||||
[InlineData (0)]
|
||||
[InlineData (1)]
|
||||
[InlineData (10)]
|
||||
public void HighlightOnPress_Move_Keeps_Highlight (int x)
|
||||
{
|
||||
var view = new View ()
|
||||
{
|
||||
CanFocus = true,
|
||||
HighlightStyle = HighlightStyle.Pressed | HighlightStyle.PressedOutside,
|
||||
Height = 1,
|
||||
Width = 1
|
||||
};
|
||||
int enablingHighlight = 0;
|
||||
int disablingHighlight = 0;
|
||||
view.Highlight += View_Highlight;
|
||||
bool inViewport = view.Viewport.Contains (x, 0);
|
||||
|
||||
// Start at 0,0 ; in viewport
|
||||
view.NewMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
|
||||
Assert.Equal (1, enablingHighlight);
|
||||
Assert.Equal (0, disablingHighlight);
|
||||
|
||||
// Move to x,0
|
||||
view.NewMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
|
||||
|
||||
if (inViewport)
|
||||
{
|
||||
Assert.Equal (2, enablingHighlight);
|
||||
Assert.Equal (0, disablingHighlight);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal (2, enablingHighlight);
|
||||
Assert.Equal (0, disablingHighlight);
|
||||
}
|
||||
|
||||
// Move backto 0,0 ; in viewport
|
||||
view.NewMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
|
||||
if (inViewport)
|
||||
{
|
||||
Assert.Equal (3, enablingHighlight);
|
||||
Assert.Equal (0, disablingHighlight);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal (3, enablingHighlight);
|
||||
Assert.Equal (0, disablingHighlight);
|
||||
}
|
||||
|
||||
view.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void View_Highlight (object sender, CancelEventArgs<HighlightStyle> e)
|
||||
{
|
||||
if (e.NewValue == HighlightStyle.None)
|
||||
{
|
||||
disablingHighlight++;
|
||||
}
|
||||
else
|
||||
{
|
||||
enablingHighlight++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
242
Tests/UnitTests/View/Navigation/AddRemoveTests.cs
Normal file
242
Tests/UnitTests/View/Navigation/AddRemoveTests.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class AddRemoveNavigationTests () : TestsAllViews
|
||||
{
|
||||
[Fact]
|
||||
public void Add_First_Subview_Gets_Focus ()
|
||||
{
|
||||
View top = new View ()
|
||||
{
|
||||
Id = "top",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
top.SetFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
|
||||
View subView = new View ()
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
top.Add (subView);
|
||||
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.Equal (subView, top.Focused);
|
||||
Assert.True (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_Subsequent_Subview_Gets_Focus ()
|
||||
{
|
||||
View top = new View ()
|
||||
{
|
||||
Id = "top",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
top.SetFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
|
||||
View subView = new View ()
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
top.Add (subView);
|
||||
|
||||
Assert.True (subView.HasFocus);
|
||||
|
||||
View subView2 = new View ()
|
||||
{
|
||||
Id = "subView2",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
top.Add (subView2);
|
||||
|
||||
Assert.True (subView2.HasFocus);
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_Nested_Subviews_Deepest_Gets_Focus ()
|
||||
{
|
||||
View top = new View ()
|
||||
{
|
||||
Id = "top",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
top.SetFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
|
||||
View subView = new View ()
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
View subSubView = new View ()
|
||||
{
|
||||
Id = "subSubView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
subView.Add (subSubView);
|
||||
|
||||
top.Add (subView);
|
||||
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.Equal (subView, top.Focused);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.True (subSubView.HasFocus);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Remove_Subview_Raises_HasFocusChanged ()
|
||||
{
|
||||
var top = new View
|
||||
{
|
||||
Id = "top",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView1 = new View
|
||||
{
|
||||
Id = "subView1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView2 = new View
|
||||
{
|
||||
Id = "subView2",
|
||||
CanFocus = true
|
||||
};
|
||||
top.Add (subView1, subView2);
|
||||
|
||||
var subView1HasFocusChangedTrueCount = 0;
|
||||
var subView1HasFocusChangedFalseCount = 0;
|
||||
|
||||
subView1.HasFocusChanged += (s, e) =>
|
||||
{
|
||||
if (e.NewValue)
|
||||
{
|
||||
subView1HasFocusChangedTrueCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
subView1HasFocusChangedFalseCount++;
|
||||
}
|
||||
};
|
||||
|
||||
var subView2HasFocusChangedTrueCount = 0;
|
||||
var subView2HasFocusChangedFalseCount = 0;
|
||||
|
||||
subView2.HasFocusChanged += (s, e) =>
|
||||
{
|
||||
if (e.NewValue)
|
||||
{
|
||||
subView2HasFocusChangedTrueCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
subView2HasFocusChangedFalseCount++;
|
||||
}
|
||||
};
|
||||
|
||||
top.SetFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.True (subView1.HasFocus);
|
||||
Assert.False (subView2.HasFocus);
|
||||
|
||||
Assert.Equal (1, subView1HasFocusChangedTrueCount);
|
||||
Assert.Equal (0, subView1HasFocusChangedFalseCount);
|
||||
|
||||
Assert.Equal (0, subView2HasFocusChangedTrueCount);
|
||||
Assert.Equal (0, subView2HasFocusChangedFalseCount);
|
||||
|
||||
top.Remove (subView1); // this should have the same resuilt as top.AdvanceFocus (NavigationDirection.Forward, null);
|
||||
|
||||
Assert.False (subView1.HasFocus);
|
||||
Assert.True (subView2.HasFocus);
|
||||
|
||||
Assert.Equal (1, subView1HasFocusChangedTrueCount);
|
||||
Assert.Equal (1, subView1HasFocusChangedFalseCount);
|
||||
|
||||
Assert.Equal (1, subView2HasFocusChangedTrueCount);
|
||||
Assert.Equal (0, subView2HasFocusChangedFalseCount);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Remove_Focused_Subview_Keeps_Focus_And_SubView_Looses_Focus ()
|
||||
{
|
||||
View top = new View ()
|
||||
{
|
||||
Id = "top",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
View subView = new View ()
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
top.Add (subView);
|
||||
|
||||
top.SetFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.Equal (subView, top.Focused);
|
||||
Assert.True (subView.HasFocus);
|
||||
|
||||
top.Remove (subView);
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.Null (top.Focused);
|
||||
Assert.False (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Remove_Focused_Subview_Keeps_Focus_And_SubView_Looses_Focus_And_Next_Gets_Focus ()
|
||||
{
|
||||
View top = new View ()
|
||||
{
|
||||
Id = "top",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
View subView1 = new View ()
|
||||
{
|
||||
Id = "subView1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
View subView2 = new View ()
|
||||
{
|
||||
Id = "subView2",
|
||||
CanFocus = true
|
||||
};
|
||||
top.Add (subView1, subView2);
|
||||
|
||||
top.SetFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.Equal (subView1, top.Focused);
|
||||
Assert.True (subView1.HasFocus);
|
||||
Assert.False (subView2.HasFocus);
|
||||
|
||||
top.Remove (subView1);
|
||||
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.True (subView2.HasFocus);
|
||||
Assert.Equal (subView2, top.Focused);
|
||||
Assert.False (subView1.HasFocus);
|
||||
}
|
||||
}
|
||||
647
Tests/UnitTests/View/Navigation/AdvanceFocusTests.cs
Normal file
647
Tests/UnitTests/View/Navigation/AdvanceFocusTests.cs
Normal file
@@ -0,0 +1,647 @@
|
||||
using System.Runtime.Intrinsics;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class AdvanceFocusTests ()
|
||||
{
|
||||
[Fact]
|
||||
public void AdvanceFocus_CanFocus_Mixed ()
|
||||
{
|
||||
var r = new View ();
|
||||
var v1 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
|
||||
var v2 = new View { CanFocus = false, TabStop = TabBehavior.TabStop };
|
||||
var v3 = new View { CanFocus = false, TabStop = TabBehavior.NoStop };
|
||||
|
||||
r.Add (v1, v2, v3);
|
||||
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
r.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[CombinatorialData]
|
||||
public void AdvanceFocus_Change_CanFocus_Works ([CombinatorialValues (TabBehavior.NoStop, TabBehavior.TabStop, TabBehavior.TabGroup)] TabBehavior behavior)
|
||||
{
|
||||
var r = new View { CanFocus = true };
|
||||
var v1 = new View ();
|
||||
var v2 = new View ();
|
||||
var v3 = new View ();
|
||||
Assert.True (r.CanFocus);
|
||||
Assert.False (v1.CanFocus);
|
||||
Assert.False (v2.CanFocus);
|
||||
Assert.False (v3.CanFocus);
|
||||
|
||||
r.Add (v1, v2, v3);
|
||||
|
||||
r.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
|
||||
v1.CanFocus = true;
|
||||
v1.TabStop = behavior;
|
||||
r.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
|
||||
v2.CanFocus = true;
|
||||
v2.TabStop = behavior;
|
||||
r.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.True (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
|
||||
v3.CanFocus = true;
|
||||
v3.TabStop = behavior;
|
||||
r.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.True (v3.HasFocus);
|
||||
r.Dispose ();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void AdvanceFocus_Subviews_TabStop ()
|
||||
{
|
||||
TabBehavior behavior = TabBehavior.TabStop;
|
||||
var top = new View { Id = "top", CanFocus = true };
|
||||
|
||||
var v1 = new View { Id = "v1", CanFocus = true, TabStop = behavior };
|
||||
var v2 = new View { Id = "v2", CanFocus = true, TabStop = behavior };
|
||||
var v3 = new View { Id = "v3", CanFocus = false, TabStop = behavior };
|
||||
|
||||
top.Add (v1, v2, v3);
|
||||
|
||||
// Cycle through v1 & v2
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (v1.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (v2.HasFocus);
|
||||
|
||||
// Should cycle back to v1
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (v1.HasFocus);
|
||||
|
||||
// Go backwards
|
||||
top.AdvanceFocus (NavigationDirection.Backward, behavior);
|
||||
Assert.True (v2.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Backward, behavior);
|
||||
Assert.True (v1.HasFocus);
|
||||
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdvanceFocus_Compound_Subview_TabStop ()
|
||||
{
|
||||
TabBehavior behavior = TabBehavior.TabStop;
|
||||
var top = new View { Id = "top", CanFocus = true };
|
||||
|
||||
var compoundSubview = new View
|
||||
{
|
||||
CanFocus = true,
|
||||
Id = "compoundSubview",
|
||||
TabStop = behavior
|
||||
};
|
||||
var v1 = new View { Id = "v1", CanFocus = true, TabStop = behavior };
|
||||
var v2 = new View { Id = "v2", CanFocus = true, TabStop = behavior };
|
||||
var v3 = new View { Id = "v3", CanFocus = false, TabStop = behavior };
|
||||
|
||||
compoundSubview.Add (v1, v2, v3);
|
||||
|
||||
top.Add (compoundSubview);
|
||||
|
||||
// Cycle through v1 & v2
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.True (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
|
||||
// Add another subview
|
||||
View otherSubview = new ()
|
||||
{
|
||||
CanFocus = true,
|
||||
TabStop = behavior,
|
||||
Id = "otherSubview"
|
||||
};
|
||||
|
||||
top.Add (otherSubview);
|
||||
|
||||
// Adding a focusable subview causes advancefocus
|
||||
Assert.True (otherSubview.HasFocus);
|
||||
Assert.False (v1.HasFocus);
|
||||
|
||||
// Cycle through v1 & v2
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.True (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
|
||||
Assert.True (otherSubview.HasFocus);
|
||||
|
||||
// v2 was previously focused down the compoundSubView focus chain
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.True (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void AdvanceFocus_CompoundCompound_Subview_TabStop ()
|
||||
{
|
||||
TabBehavior behavior = TabBehavior.TabStop;
|
||||
var top = new View { Id = "top", CanFocus = true };
|
||||
var topv1 = new View { Id = "topv1", CanFocus = true, TabStop = behavior };
|
||||
var topv2 = new View { Id = "topv2", CanFocus = true, TabStop = behavior };
|
||||
var topv3 = new View { Id = "topv3", CanFocus = false, TabStop = behavior };
|
||||
top.Add (topv1, topv2, topv3);
|
||||
|
||||
var compoundSubview = new View
|
||||
{
|
||||
CanFocus = true,
|
||||
Id = "compoundSubview",
|
||||
TabStop = behavior
|
||||
};
|
||||
var v1 = new View { Id = "v1", CanFocus = true, TabStop = behavior };
|
||||
var v2 = new View { Id = "v2", CanFocus = true, TabStop = behavior };
|
||||
var v3 = new View { Id = "v3", CanFocus = false, TabStop = behavior };
|
||||
|
||||
compoundSubview.Add (v1, v2, v3);
|
||||
|
||||
|
||||
var compoundCompoundSubview = new View
|
||||
{
|
||||
CanFocus = true,
|
||||
Id = "compoundCompoundSubview",
|
||||
TabStop = behavior
|
||||
};
|
||||
var v4 = new View { Id = "v4", CanFocus = true, TabStop = behavior };
|
||||
var v5 = new View { Id = "v5", CanFocus = true, TabStop = behavior };
|
||||
var v6 = new View { Id = "v6", CanFocus = false, TabStop = behavior };
|
||||
|
||||
compoundCompoundSubview.Add (v4, v5, v6);
|
||||
|
||||
compoundSubview.Add (compoundCompoundSubview);
|
||||
|
||||
top.Add (compoundSubview);
|
||||
|
||||
top.SetFocus ();
|
||||
Assert.True (topv1.HasFocus);
|
||||
|
||||
// Cycle through topv2
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (topv2.HasFocus);
|
||||
|
||||
// Cycle v1, v2, v4, v5
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (v1.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (v2.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (v4.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (v5.HasFocus);
|
||||
|
||||
// Should cycle back to topv1
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (topv1.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (topv2.HasFocus);
|
||||
|
||||
// Add another top subview. Should cycle to it after v5
|
||||
View otherSubview = new ()
|
||||
{
|
||||
CanFocus = true,
|
||||
TabStop = behavior,
|
||||
Id = "otherSubview"
|
||||
};
|
||||
|
||||
top.Add (otherSubview);
|
||||
|
||||
// Adding a focusable subview causes advancefocus
|
||||
Assert.True (otherSubview.HasFocus);
|
||||
|
||||
// Cycle through topv1, topv2, v1, v2, v4, v5
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (topv1.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (topv2.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
|
||||
// the above should have cycled to v5 since it was the previously most focused subview of compoundSubView
|
||||
Assert.True (v5.HasFocus);
|
||||
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (otherSubview.HasFocus);
|
||||
|
||||
// Should cycle back to topv1
|
||||
top.AdvanceFocus (NavigationDirection.Forward, behavior);
|
||||
Assert.True (topv1.HasFocus);
|
||||
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdvanceFocus_Compound_Subview_TabGroup ()
|
||||
{
|
||||
var top = new View { Id = "top", CanFocus = true, TabStop = TabBehavior.TabGroup };
|
||||
|
||||
var compoundSubview = new View
|
||||
{
|
||||
CanFocus = true,
|
||||
Id = "compoundSubview",
|
||||
TabStop = TabBehavior.TabGroup
|
||||
};
|
||||
var tabStopView = new View { Id = "tabStop", CanFocus = true, TabStop = TabBehavior.TabStop };
|
||||
var tabGroupView1 = new View { Id = "tabGroup1", CanFocus = true, TabStop = TabBehavior.TabGroup };
|
||||
var tabGroupView2 = new View { Id = "tabGroup2", CanFocus = true, TabStop = TabBehavior.TabGroup };
|
||||
|
||||
compoundSubview.Add (tabStopView, tabGroupView1, tabGroupView2);
|
||||
|
||||
top.Add (compoundSubview);
|
||||
top.SetFocus ();
|
||||
Assert.True (tabStopView.HasFocus);
|
||||
|
||||
// TabGroup should cycle to tabGroup1 then tabGroup2
|
||||
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
|
||||
Assert.False (tabStopView.HasFocus);
|
||||
Assert.True (tabGroupView1.HasFocus);
|
||||
Assert.False (tabGroupView2.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
|
||||
Assert.False (tabStopView.HasFocus);
|
||||
Assert.False (tabGroupView1.HasFocus);
|
||||
Assert.True (tabGroupView2.HasFocus);
|
||||
|
||||
// Add another TabGroup subview
|
||||
View otherTabGroupSubview = new ()
|
||||
{
|
||||
CanFocus = true,
|
||||
TabStop = TabBehavior.TabGroup,
|
||||
Id = "otherTabGroupSubview"
|
||||
};
|
||||
|
||||
top.Add (otherTabGroupSubview);
|
||||
|
||||
// Adding a focusable subview causes advancefocus
|
||||
Assert.True (otherTabGroupSubview.HasFocus);
|
||||
Assert.False (tabStopView.HasFocus);
|
||||
|
||||
// TagBroup navs to the other subview
|
||||
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
|
||||
Assert.Equal (compoundSubview, top.Focused);
|
||||
Assert.True (tabStopView.HasFocus);
|
||||
|
||||
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
|
||||
Assert.Equal (compoundSubview, top.Focused);
|
||||
Assert.True (tabGroupView1.HasFocus);
|
||||
|
||||
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
|
||||
Assert.Equal (compoundSubview, top.Focused);
|
||||
Assert.True (tabGroupView2.HasFocus);
|
||||
|
||||
// Now go backwards
|
||||
top.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
|
||||
Assert.Equal (compoundSubview, top.Focused);
|
||||
Assert.True (tabGroupView1.HasFocus);
|
||||
|
||||
top.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
|
||||
Assert.Equal (otherTabGroupSubview, top.Focused);
|
||||
Assert.True (otherTabGroupSubview.HasFocus);
|
||||
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdvanceFocus_NoStop_And_CanFocus_True_No_Focus ()
|
||||
{
|
||||
var r = new View ();
|
||||
var v1 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
|
||||
var v2 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
|
||||
var v3 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
|
||||
|
||||
r.Add (v1, v2, v3);
|
||||
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
r.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdvanceFocus_NoStop_Change_Enables_Stop ()
|
||||
{
|
||||
var r = new View { CanFocus = true };
|
||||
var v1 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
|
||||
var v2 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
|
||||
var v3 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
|
||||
|
||||
r.Add (v1, v2, v3);
|
||||
|
||||
v1.TabStop = TabBehavior.TabStop;
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.True (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
|
||||
v2.TabStop = TabBehavior.TabStop;
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.True (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
|
||||
v3.TabStop = TabBehavior.TabStop;
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.True (v3.HasFocus);
|
||||
r.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdvanceFocus_NoStop_Prevents_Stop ()
|
||||
{
|
||||
var r = new View ();
|
||||
var v1 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
|
||||
var v2 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
|
||||
var v3 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
|
||||
|
||||
r.Add (v1, v2, v3);
|
||||
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdvanceFocus_Null_And_CanFocus_False_No_Advance ()
|
||||
{
|
||||
var r = new View ();
|
||||
var v1 = new View ();
|
||||
var v2 = new View ();
|
||||
var v3 = new View ();
|
||||
Assert.False (v1.CanFocus);
|
||||
Assert.Null (v1.TabStop);
|
||||
|
||||
r.Add (v1, v2, v3);
|
||||
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
r.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdvanceFocus_Subviews_Raises_HasFocusChanged ()
|
||||
{
|
||||
var top = new View
|
||||
{
|
||||
Id = "top",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView1 = new View
|
||||
{
|
||||
Id = "subView1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView2 = new View
|
||||
{
|
||||
Id = "subView2",
|
||||
CanFocus = true
|
||||
};
|
||||
top.Add (subView1, subView2);
|
||||
|
||||
var subView1HasFocusChangedTrueCount = 0;
|
||||
var subView1HasFocusChangedFalseCount = 0;
|
||||
|
||||
subView1.HasFocusChanged += (s, e) =>
|
||||
{
|
||||
if (e.NewValue)
|
||||
{
|
||||
subView1HasFocusChangedTrueCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
subView1HasFocusChangedFalseCount++;
|
||||
}
|
||||
};
|
||||
|
||||
var subView2HasFocusChangedTrueCount = 0;
|
||||
var subView2HasFocusChangedFalseCount = 0;
|
||||
|
||||
subView2.HasFocusChanged += (s, e) =>
|
||||
{
|
||||
if (e.NewValue)
|
||||
{
|
||||
subView2HasFocusChangedTrueCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
subView2HasFocusChangedFalseCount++;
|
||||
}
|
||||
};
|
||||
|
||||
top.SetFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.True (subView1.HasFocus);
|
||||
Assert.False (subView2.HasFocus);
|
||||
|
||||
Assert.Equal (1, subView1HasFocusChangedTrueCount);
|
||||
Assert.Equal (0, subView1HasFocusChangedFalseCount);
|
||||
|
||||
Assert.Equal (0, subView2HasFocusChangedTrueCount);
|
||||
Assert.Equal (0, subView2HasFocusChangedFalseCount);
|
||||
|
||||
top.AdvanceFocus (NavigationDirection.Forward, null);
|
||||
Assert.False (subView1.HasFocus);
|
||||
Assert.True (subView2.HasFocus);
|
||||
|
||||
Assert.Equal (1, subView1HasFocusChangedTrueCount);
|
||||
Assert.Equal (1, subView1HasFocusChangedFalseCount);
|
||||
|
||||
Assert.Equal (1, subView2HasFocusChangedTrueCount);
|
||||
Assert.Equal (0, subView2HasFocusChangedFalseCount);
|
||||
|
||||
top.AdvanceFocus (NavigationDirection.Forward, null);
|
||||
Assert.True (subView1.HasFocus);
|
||||
Assert.False (subView2.HasFocus);
|
||||
|
||||
Assert.Equal (2, subView1HasFocusChangedTrueCount);
|
||||
Assert.Equal (1, subView1HasFocusChangedFalseCount);
|
||||
|
||||
Assert.Equal (1, subView2HasFocusChangedTrueCount);
|
||||
Assert.Equal (1, subView2HasFocusChangedFalseCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdvanceFocus_Subviews_Raises_HasFocusChanging ()
|
||||
{
|
||||
var top = new View
|
||||
{
|
||||
Id = "top",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView1 = new View
|
||||
{
|
||||
Id = "subView1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView2 = new View
|
||||
{
|
||||
Id = "subView2",
|
||||
CanFocus = true
|
||||
};
|
||||
top.Add (subView1, subView2);
|
||||
|
||||
var subView1HasFocusChangingTrueCount = 0;
|
||||
var subView1HasFocusChangingFalseCount = 0;
|
||||
|
||||
subView1.HasFocusChanging += (s, e) =>
|
||||
{
|
||||
if (e.NewValue)
|
||||
{
|
||||
subView1HasFocusChangingTrueCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
subView1HasFocusChangingFalseCount++;
|
||||
}
|
||||
};
|
||||
|
||||
var subView2HasFocusChangingTrueCount = 0;
|
||||
var subView2HasFocusChangingFalseCount = 0;
|
||||
|
||||
subView2.HasFocusChanging += (s, e) =>
|
||||
{
|
||||
if (e.NewValue)
|
||||
{
|
||||
subView2HasFocusChangingTrueCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
subView2HasFocusChangingFalseCount++;
|
||||
}
|
||||
};
|
||||
|
||||
top.SetFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.True (subView1.HasFocus);
|
||||
Assert.False (subView2.HasFocus);
|
||||
|
||||
Assert.Equal (1, subView1HasFocusChangingTrueCount);
|
||||
Assert.Equal (0, subView1HasFocusChangingFalseCount);
|
||||
|
||||
Assert.Equal (0, subView2HasFocusChangingTrueCount);
|
||||
Assert.Equal (0, subView2HasFocusChangingFalseCount);
|
||||
|
||||
top.AdvanceFocus (NavigationDirection.Forward, null);
|
||||
Assert.False (subView1.HasFocus);
|
||||
Assert.True (subView2.HasFocus);
|
||||
|
||||
Assert.Equal (1, subView1HasFocusChangingTrueCount);
|
||||
Assert.Equal (1, subView1HasFocusChangingFalseCount);
|
||||
|
||||
Assert.Equal (1, subView2HasFocusChangingTrueCount);
|
||||
Assert.Equal (0, subView2HasFocusChangingFalseCount);
|
||||
|
||||
top.AdvanceFocus (NavigationDirection.Forward, null);
|
||||
Assert.True (subView1.HasFocus);
|
||||
Assert.False (subView2.HasFocus);
|
||||
|
||||
Assert.Equal (2, subView1HasFocusChangingTrueCount);
|
||||
Assert.Equal (1, subView1HasFocusChangingFalseCount);
|
||||
|
||||
Assert.Equal (1, subView2HasFocusChangingTrueCount);
|
||||
Assert.Equal (1, subView2HasFocusChangingFalseCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdvanceFocus_With_CanFocus_Are_All_True ()
|
||||
{
|
||||
var top = new View { Id = "top", CanFocus = true };
|
||||
var v1 = new View { Id = "v1", CanFocus = true };
|
||||
var v2 = new View { Id = "v2", CanFocus = true };
|
||||
var v3 = new View { Id = "v3", CanFocus = true };
|
||||
|
||||
top.Add (v1, v2, v3);
|
||||
|
||||
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.True (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.True (v2.HasFocus);
|
||||
Assert.False (v3.HasFocus);
|
||||
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
|
||||
Assert.False (v1.HasFocus);
|
||||
Assert.False (v2.HasFocus);
|
||||
Assert.True (v3.HasFocus);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[CombinatorialData]
|
||||
public void TabStop_And_CanFocus_Are_Decoupled (bool canFocus, TabBehavior tabStop)
|
||||
{
|
||||
var view = new View { CanFocus = canFocus, TabStop = tabStop };
|
||||
|
||||
Assert.Equal (canFocus, view.CanFocus);
|
||||
Assert.Equal (tabStop, view.TabStop);
|
||||
}
|
||||
}
|
||||
365
Tests/UnitTests/View/Navigation/CanFocusTests.cs
Normal file
365
Tests/UnitTests/View/Navigation/CanFocusTests.cs
Normal file
@@ -0,0 +1,365 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class CanFocusTests () : TestsAllViews
|
||||
{
|
||||
[Fact]
|
||||
public void CanFocus_False_Prevents_SubSubView_HasFocus ()
|
||||
{
|
||||
var view = new View { };
|
||||
var subView = new View { };
|
||||
var subSubView = new View { CanFocus = true };
|
||||
|
||||
subView.Add (subSubView);
|
||||
view.Add (subView);
|
||||
|
||||
Assert.False (view.CanFocus);
|
||||
Assert.False (subView.CanFocus);
|
||||
Assert.True (subSubView.CanFocus);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.False (view.HasFocus);
|
||||
|
||||
subView.SetFocus ();
|
||||
Assert.False (subView.HasFocus);
|
||||
|
||||
subSubView.SetFocus ();
|
||||
Assert.False (subSubView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanFocus_False_Prevents_SubView_HasFocus ()
|
||||
{
|
||||
var view = new View { };
|
||||
var subView = new View { CanFocus = true };
|
||||
var subSubView = new View { };
|
||||
|
||||
subView.Add (subSubView);
|
||||
view.Add (subView);
|
||||
|
||||
Assert.False (view.CanFocus);
|
||||
Assert.True (subView.CanFocus);
|
||||
Assert.False (subSubView.CanFocus);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.False (view.HasFocus);
|
||||
|
||||
subView.SetFocus ();
|
||||
Assert.False (subView.HasFocus);
|
||||
|
||||
subSubView.SetFocus ();
|
||||
Assert.False (subSubView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanFocus_Set_True_No_SuperView_Doesnt_Set_HasFocus ()
|
||||
{
|
||||
var view = new View { };
|
||||
|
||||
// Act
|
||||
view.CanFocus = true;
|
||||
Assert.False (view.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanFocus_Set_True_Sets_HasFocus_To_True ()
|
||||
{
|
||||
var view = new View { };
|
||||
var subView = new View { };
|
||||
view.Add (subView);
|
||||
|
||||
Assert.False (view.CanFocus);
|
||||
Assert.False (subView.CanFocus);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
|
||||
view.CanFocus = true;
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
|
||||
// Act
|
||||
subView.CanFocus = true;
|
||||
Assert.True (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanFocus_Set_SubView_True_Sets_HasFocus_To_True ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
CanFocus = true
|
||||
};
|
||||
var subView = new View
|
||||
{
|
||||
CanFocus = false
|
||||
};
|
||||
var subSubView = new View
|
||||
{
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
subView.Add (subSubView);
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
Assert.False (subSubView.HasFocus);
|
||||
|
||||
// Act
|
||||
subView.CanFocus = true;
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.True (subSubView.HasFocus);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void CanFocus_Set_SubView_True_Does_Not_Change_Focus_If_SuperView_Focused_Is_True ()
|
||||
{
|
||||
var top = new View
|
||||
{
|
||||
Id = "top",
|
||||
CanFocus = true
|
||||
};
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
var subSubView = new View
|
||||
{
|
||||
Id = "subSubView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
subView.Add (subSubView);
|
||||
|
||||
var subView2 = new View
|
||||
{
|
||||
Id = "subView2",
|
||||
CanFocus = false
|
||||
};
|
||||
|
||||
top.Add (subView, subView2);
|
||||
|
||||
top.SetFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.Equal (subView, top.Focused);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.True (subSubView.HasFocus);
|
||||
|
||||
// Act
|
||||
subView2.CanFocus = true;
|
||||
Assert.False (subView2.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.True (subSubView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanFocus_Set_False_Sets_HasFocus_To_False ()
|
||||
{
|
||||
var view = new View { CanFocus = true };
|
||||
var view2 = new View { CanFocus = true };
|
||||
view2.Add (view);
|
||||
|
||||
Assert.True (view.CanFocus);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
|
||||
view.CanFocus = false;
|
||||
Assert.False (view.CanFocus);
|
||||
Assert.False (view.HasFocus);
|
||||
}
|
||||
|
||||
// TODO: Figure out what this test is supposed to be testing
|
||||
[Fact]
|
||||
public void CanFocus_Faced_With_Container ()
|
||||
{
|
||||
var t = new Toplevel ();
|
||||
var w = new Window ();
|
||||
var f = new FrameView ();
|
||||
var v = new View { CanFocus = true };
|
||||
f.Add (v);
|
||||
w.Add (f);
|
||||
t.Add (w);
|
||||
|
||||
Assert.True (t.CanFocus);
|
||||
Assert.True (w.CanFocus);
|
||||
Assert.True (f.CanFocus);
|
||||
Assert.True (v.CanFocus);
|
||||
|
||||
f.CanFocus = false;
|
||||
Assert.False (f.CanFocus);
|
||||
Assert.True (v.CanFocus);
|
||||
|
||||
v.CanFocus = false;
|
||||
Assert.False (f.CanFocus);
|
||||
Assert.False (v.CanFocus);
|
||||
|
||||
v.CanFocus = true;
|
||||
Assert.False (f.CanFocus);
|
||||
Assert.True (v.CanFocus);
|
||||
}
|
||||
|
||||
// TODO: Figure out what this test is supposed to be testing
|
||||
[Fact]
|
||||
public void CanFocus_Faced_With_Container_Before_Run ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
Toplevel t = new ();
|
||||
|
||||
var w = new Window ();
|
||||
var f = new FrameView ();
|
||||
var v = new View { CanFocus = true };
|
||||
f.Add (v);
|
||||
w.Add (f);
|
||||
t.Add (w);
|
||||
|
||||
Assert.True (t.CanFocus);
|
||||
Assert.True (w.CanFocus);
|
||||
Assert.True (f.CanFocus);
|
||||
Assert.True (v.CanFocus);
|
||||
|
||||
f.CanFocus = false;
|
||||
Assert.False (f.CanFocus);
|
||||
Assert.True (v.CanFocus);
|
||||
|
||||
v.CanFocus = false;
|
||||
Assert.False (f.CanFocus);
|
||||
Assert.False (v.CanFocus);
|
||||
|
||||
v.CanFocus = true;
|
||||
Assert.False (f.CanFocus);
|
||||
Assert.True (v.CanFocus);
|
||||
|
||||
Application.Iteration += (s, a) => Application.RequestStop ();
|
||||
|
||||
Application.Run (t);
|
||||
t.Dispose ();
|
||||
Application.Shutdown ();
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public void CanFocus_Set_Changes_TabIndex_And_TabStop ()
|
||||
//{
|
||||
// var r = new View ();
|
||||
// var v1 = new View { Text = "1" };
|
||||
// var v2 = new View { Text = "2" };
|
||||
// var v3 = new View { Text = "3" };
|
||||
|
||||
// r.Add (v1, v2, v3);
|
||||
|
||||
// v2.CanFocus = true;
|
||||
// Assert.Equal (r.TabIndexes.IndexOf (v2), v2.TabIndex);
|
||||
// Assert.Equal (0, v2.TabIndex);
|
||||
// Assert.Equal (TabBehavior.TabStop, v2.TabStop);
|
||||
|
||||
// v1.CanFocus = true;
|
||||
// Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex);
|
||||
// Assert.Equal (1, v1.TabIndex);
|
||||
// Assert.Equal (TabBehavior.TabStop, v1.TabStop);
|
||||
|
||||
// v1.TabIndex = 2;
|
||||
// Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex);
|
||||
// Assert.Equal (1, v1.TabIndex);
|
||||
// v3.CanFocus = true;
|
||||
// Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex);
|
||||
// Assert.Equal (1, v1.TabIndex);
|
||||
// Assert.Equal (r.TabIndexes.IndexOf (v3), v3.TabIndex);
|
||||
// Assert.Equal (2, v3.TabIndex);
|
||||
// Assert.Equal (TabBehavior.TabStop, v3.TabStop);
|
||||
|
||||
// v2.CanFocus = false;
|
||||
// Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex);
|
||||
// Assert.Equal (1, v1.TabIndex);
|
||||
// Assert.Equal (TabBehavior.TabStop, v1.TabStop);
|
||||
// Assert.Equal (r.TabIndexes.IndexOf (v2), v2.TabIndex); // TabIndex is not changed
|
||||
// Assert.NotEqual (-1, v2.TabIndex);
|
||||
// Assert.Equal (TabBehavior.TabStop, v2.TabStop); // TabStop is not changed
|
||||
// Assert.Equal (r.TabIndexes.IndexOf (v3), v3.TabIndex);
|
||||
// Assert.Equal (2, v3.TabIndex);
|
||||
// Assert.Equal (TabBehavior.TabStop, v3.TabStop);
|
||||
// r.Dispose ();
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public void CanFocus_True_Focuses ()
|
||||
{
|
||||
View view = new ()
|
||||
{
|
||||
Id = "view"
|
||||
};
|
||||
|
||||
View superView = new ()
|
||||
{
|
||||
Id = "superView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
superView.Add (view);
|
||||
|
||||
superView.SetFocus ();
|
||||
Assert.True (superView.HasFocus);
|
||||
Assert.NotEqual (view, superView.Focused);
|
||||
|
||||
view.CanFocus = true;
|
||||
Assert.True (superView.HasFocus);
|
||||
Assert.Equal (view, superView.Focused);
|
||||
Assert.True (view.HasFocus);
|
||||
|
||||
view.CanFocus = false;
|
||||
Assert.True (superView.HasFocus);
|
||||
Assert.NotEqual (view, superView.Focused);
|
||||
Assert.False (view.HasFocus);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void CanFocus_Set_True_Get_AdvanceFocus_Works ()
|
||||
{
|
||||
Label label = new () { Text = "label" };
|
||||
View view = new () { Text = "view", CanFocus = true };
|
||||
Application.Navigation = new ();
|
||||
Application.Top = new ();
|
||||
Application.Top.Add (label, view);
|
||||
|
||||
Application.Top.SetFocus ();
|
||||
Assert.Equal (view, Application.Navigation.GetFocused ());
|
||||
Assert.False (label.CanFocus);
|
||||
Assert.False (label.HasFocus);
|
||||
Assert.True (view.CanFocus);
|
||||
Assert.True (view.HasFocus);
|
||||
|
||||
Assert.False (Application.Navigation.AdvanceFocus (NavigationDirection.Forward, null));
|
||||
Assert.False (label.HasFocus);
|
||||
Assert.True (view.HasFocus);
|
||||
|
||||
// Set label CanFocus to true
|
||||
label.CanFocus = true;
|
||||
Assert.False (label.HasFocus);
|
||||
Assert.True (view.HasFocus);
|
||||
|
||||
// label can now be focused, so AdvanceFocus should move to it.
|
||||
Assert.True (Application.Navigation.AdvanceFocus (NavigationDirection.Forward, null));
|
||||
Assert.True (label.HasFocus);
|
||||
Assert.False (view.HasFocus);
|
||||
|
||||
// Move back to view
|
||||
view.SetFocus ();
|
||||
Assert.False (label.HasFocus);
|
||||
Assert.True (view.HasFocus);
|
||||
|
||||
Assert.True (Application.RaiseKeyDownEvent (Key.Tab));
|
||||
Assert.True (label.HasFocus);
|
||||
Assert.False (view.HasFocus);
|
||||
|
||||
Application.Top.Dispose ();
|
||||
Application.ResetState ();
|
||||
}
|
||||
}
|
||||
319
Tests/UnitTests/View/Navigation/EnabledTests.cs
Normal file
319
Tests/UnitTests/View/Navigation/EnabledTests.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class EnabledTests () : TestsAllViews
|
||||
{
|
||||
[Fact]
|
||||
public void Enabled_False_Leaves ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
|
||||
view.Enabled = false;
|
||||
Assert.False (view.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enabled_False_Leaves_Subview ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
|
||||
view.Enabled = false;
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enabled_False_Leaves_Subview2 ()
|
||||
{
|
||||
var view = new Window
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
|
||||
view.Enabled = false;
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enabled_False_On_Subview_Leaves_Just_Subview ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
|
||||
subView.Enabled = false;
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enabled_False_Focuses_Deepest_Focusable_Subview ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView1 = new View
|
||||
{
|
||||
Id = "subViewSubView1",
|
||||
CanFocus = false
|
||||
};
|
||||
|
||||
var subViewSubView2 = new View
|
||||
{
|
||||
Id = "subViewSubView2",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView3 = new View
|
||||
{
|
||||
Id = "subViewSubView3",
|
||||
CanFocus = true // This is the one that will be focused
|
||||
};
|
||||
subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
Assert.Equal (subViewSubView2, subView.Focused);
|
||||
|
||||
subViewSubView2.Enabled = false;
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
Assert.Equal (subViewSubView3, subView.Focused);
|
||||
Assert.True (subViewSubView3.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enabled_True_Subview_Focuses_SubView ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true,
|
||||
Enabled = false
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
|
||||
view.Enabled = true;
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enabled_True_On_Subview_Focuses ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true,
|
||||
Enabled = false
|
||||
};
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
|
||||
subView.Enabled = true;
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enabled_True_Focuses_Deepest_Focusable_Subview ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true,
|
||||
Enabled = false
|
||||
};
|
||||
|
||||
var subViewSubView1 = new View
|
||||
{
|
||||
Id = "subViewSubView1",
|
||||
CanFocus = false
|
||||
};
|
||||
|
||||
var subViewSubView2 = new View
|
||||
{
|
||||
Id = "subViewSubView2",
|
||||
CanFocus = true // This is the one that will be focused
|
||||
};
|
||||
|
||||
var subViewSubView3 = new View
|
||||
{
|
||||
Id = "subViewSubView3",
|
||||
CanFocus = true
|
||||
};
|
||||
subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.False (subView.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
|
||||
subView.Enabled = true;
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
Assert.Equal (subViewSubView2, subView.Focused);
|
||||
Assert.True (subViewSubView2.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void _Enabled_Sets_Also_Sets_Subviews ()
|
||||
{
|
||||
var wasClicked = false;
|
||||
var button = new Button { Text = "Click Me" };
|
||||
button.IsDefault = true;
|
||||
button.Accepting += (s, e) => wasClicked = !wasClicked;
|
||||
var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
win.Add (button);
|
||||
var top = new Toplevel ();
|
||||
top.Add (win);
|
||||
|
||||
var iterations = 0;
|
||||
|
||||
Application.Iteration += (s, a) =>
|
||||
{
|
||||
iterations++;
|
||||
|
||||
win.NewKeyDownEvent (Key.Enter);
|
||||
Assert.True (wasClicked);
|
||||
button.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked });
|
||||
Assert.False (wasClicked);
|
||||
Assert.True (button.Enabled);
|
||||
Assert.True (button.CanFocus);
|
||||
Assert.True (button.HasFocus);
|
||||
Assert.True (win.Enabled);
|
||||
Assert.True (win.CanFocus);
|
||||
Assert.True (win.HasFocus);
|
||||
|
||||
Assert.True (button.HasFocus);
|
||||
win.Enabled = false;
|
||||
Assert.False (button.HasFocus);
|
||||
button.NewKeyDownEvent (Key.Enter);
|
||||
Assert.False (wasClicked);
|
||||
button.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked });
|
||||
Assert.False (wasClicked);
|
||||
Assert.False (button.Enabled);
|
||||
Assert.True (button.CanFocus);
|
||||
Assert.False (button.HasFocus);
|
||||
Assert.False (win.Enabled);
|
||||
Assert.True (win.CanFocus);
|
||||
Assert.False (win.HasFocus);
|
||||
button.SetFocus ();
|
||||
Assert.False (button.HasFocus);
|
||||
Assert.False (win.HasFocus);
|
||||
win.SetFocus ();
|
||||
Assert.False (button.HasFocus);
|
||||
Assert.False (win.HasFocus);
|
||||
|
||||
win.Enabled = true;
|
||||
win.FocusDeepest (NavigationDirection.Forward, null);
|
||||
Assert.True (button.HasFocus);
|
||||
Assert.True (win.HasFocus);
|
||||
|
||||
Application.RequestStop ();
|
||||
};
|
||||
|
||||
Application.Run (top);
|
||||
|
||||
Assert.Equal (1, iterations);
|
||||
top.Dispose ();
|
||||
}
|
||||
}
|
||||
1046
Tests/UnitTests/View/Navigation/HasFocusChangeEventTests.cs
Normal file
1046
Tests/UnitTests/View/Navigation/HasFocusChangeEventTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
234
Tests/UnitTests/View/Navigation/HasFocusTests.cs
Normal file
234
Tests/UnitTests/View/Navigation/HasFocusTests.cs
Normal file
@@ -0,0 +1,234 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class HasFocusTests () : TestsAllViews
|
||||
{
|
||||
|
||||
[Fact]
|
||||
public void HasFocus_False ()
|
||||
{
|
||||
var view = new View ()
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
|
||||
view.HasFocus = false;
|
||||
Assert.False (view.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasFocus_False_WithSuperView_Does_Not_Change_SuperView ()
|
||||
{
|
||||
var view = new View ()
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subview = new View ()
|
||||
{
|
||||
Id = "subview",
|
||||
CanFocus = true
|
||||
};
|
||||
view.Add (subview);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subview.HasFocus);
|
||||
|
||||
subview.HasFocus = false;
|
||||
Assert.False (subview.HasFocus);
|
||||
Assert.True (view.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasFocus_False_WithSubview_Leaves_All ()
|
||||
{
|
||||
var view = new View ()
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subview = new View ()
|
||||
{
|
||||
Id = "subview",
|
||||
CanFocus = true
|
||||
};
|
||||
view.Add (subview);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subview.HasFocus);
|
||||
Assert.Equal (subview, view.Focused);
|
||||
|
||||
view.HasFocus = false;
|
||||
Assert.Null (view.Focused);
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.False (subview.HasFocus);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Enabled_False_Sets_HasFocus_To_False ()
|
||||
{
|
||||
var wasClicked = false;
|
||||
var view = new Button { Text = "Click Me" };
|
||||
view.Accepting += (s, e) => wasClicked = !wasClicked;
|
||||
|
||||
view.NewKeyDownEvent (Key.Space);
|
||||
Assert.True (wasClicked);
|
||||
view.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked });
|
||||
Assert.False (wasClicked);
|
||||
Assert.True (view.Enabled);
|
||||
Assert.True (view.CanFocus);
|
||||
Assert.True (view.HasFocus);
|
||||
|
||||
view.Enabled = false;
|
||||
view.NewKeyDownEvent (Key.Space);
|
||||
Assert.False (wasClicked);
|
||||
view.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked });
|
||||
Assert.False (wasClicked);
|
||||
Assert.False (view.Enabled);
|
||||
Assert.True (view.CanFocus);
|
||||
Assert.False (view.HasFocus);
|
||||
view.SetFocus ();
|
||||
Assert.False (view.HasFocus);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public void HasFocus_False_CompoundSubView_Leaves_All ()
|
||||
{
|
||||
var view = new View ()
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View ()
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView1 = new View ()
|
||||
{
|
||||
Id = "subViewSubView1",
|
||||
CanFocus = false
|
||||
};
|
||||
|
||||
var subViewSubView2 = new View ()
|
||||
{
|
||||
Id = "subViewSubView2",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView3 = new View ()
|
||||
{
|
||||
Id = "subViewSubView3",
|
||||
CanFocus = false
|
||||
};
|
||||
|
||||
subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.True (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.HasFocus = false;
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasFocus_False_Subview_Raises_HasFocusChanged ()
|
||||
{
|
||||
var top = new View
|
||||
{
|
||||
Id = "top",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView1 = new View
|
||||
{
|
||||
Id = "subView1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView2 = new View
|
||||
{
|
||||
Id = "subView2",
|
||||
CanFocus = true
|
||||
};
|
||||
top.Add (subView1, subView2);
|
||||
|
||||
var subView1HasFocusChangedTrueCount = 0;
|
||||
var subView1HasFocusChangedFalseCount = 0;
|
||||
|
||||
subView1.HasFocusChanged += (s, e) =>
|
||||
{
|
||||
if (e.NewValue)
|
||||
{
|
||||
subView1HasFocusChangedTrueCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
subView1HasFocusChangedFalseCount++;
|
||||
}
|
||||
};
|
||||
|
||||
var subView2HasFocusChangedTrueCount = 0;
|
||||
var subView2HasFocusChangedFalseCount = 0;
|
||||
|
||||
subView2.HasFocusChanged += (s, e) =>
|
||||
{
|
||||
if (e.NewValue)
|
||||
{
|
||||
subView2HasFocusChangedTrueCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
subView2HasFocusChangedFalseCount++;
|
||||
}
|
||||
};
|
||||
|
||||
top.SetFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.True (subView1.HasFocus);
|
||||
Assert.False (subView2.HasFocus);
|
||||
|
||||
Assert.Equal (1, subView1HasFocusChangedTrueCount);
|
||||
Assert.Equal (0, subView1HasFocusChangedFalseCount);
|
||||
|
||||
Assert.Equal (0, subView2HasFocusChangedTrueCount);
|
||||
Assert.Equal (0, subView2HasFocusChangedFalseCount);
|
||||
|
||||
subView1.HasFocus = false; // this should have the same resuilt as top.AdvanceFocus (NavigationDirection.Forward, null);
|
||||
|
||||
Assert.False (subView1.HasFocus);
|
||||
Assert.True (subView2.HasFocus);
|
||||
|
||||
Assert.Equal (1, subView1HasFocusChangedTrueCount);
|
||||
Assert.Equal (1, subView1HasFocusChangedFalseCount);
|
||||
|
||||
Assert.Equal (1, subView2HasFocusChangedTrueCount);
|
||||
Assert.Equal (0, subView2HasFocusChangedFalseCount);
|
||||
}
|
||||
}
|
||||
426
Tests/UnitTests/View/Navigation/NavigationTests.cs
Normal file
426
Tests/UnitTests/View/Navigation/NavigationTests.cs
Normal file
@@ -0,0 +1,426 @@
|
||||
using JetBrains.Annotations;
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
|
||||
{
|
||||
[Theory]
|
||||
[MemberData (nameof (AllViewTypes))]
|
||||
[SetupFakeDriver] // SetupFakeDriver resets app state; helps to avoid test pollution
|
||||
public void AllViews_AtLeastOneNavKey_Advances (Type viewType)
|
||||
{
|
||||
View view = CreateInstanceIfNotGeneric (viewType);
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
_output.WriteLine ($"Ignoring {viewType} - It's a Generic");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!view.CanFocus)
|
||||
{
|
||||
_output.WriteLine ($"Ignoring {viewType} - It can't focus.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Toplevel top = new ();
|
||||
Application.Top = top;
|
||||
Application.Navigation = new ApplicationNavigation ();
|
||||
|
||||
View otherView = new ()
|
||||
{
|
||||
Id = "otherView",
|
||||
CanFocus = true,
|
||||
TabStop = view.TabStop == TabBehavior.NoStop ? TabBehavior.TabStop : view.TabStop
|
||||
};
|
||||
|
||||
top.Add (view, otherView);
|
||||
|
||||
// Start with the focus on our test view
|
||||
view.SetFocus ();
|
||||
|
||||
Key [] navKeys = [Key.Tab, Key.Tab.WithShift, Key.CursorUp, Key.CursorDown, Key.CursorLeft, Key.CursorRight];
|
||||
|
||||
if (view.TabStop == TabBehavior.TabGroup)
|
||||
{
|
||||
navKeys = new [] { Key.F6, Key.F6.WithShift };
|
||||
}
|
||||
|
||||
var left = false;
|
||||
|
||||
foreach (Key key in navKeys)
|
||||
{
|
||||
switch (view.TabStop)
|
||||
{
|
||||
case TabBehavior.TabStop:
|
||||
case TabBehavior.NoStop:
|
||||
case TabBehavior.TabGroup:
|
||||
Application.RaiseKeyDownEvent (key);
|
||||
|
||||
if (view.HasFocus)
|
||||
{
|
||||
// Try once more (HexView)
|
||||
Application.RaiseKeyDownEvent (key);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Application.RaiseKeyDownEvent (Key.Tab);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!view.HasFocus)
|
||||
{
|
||||
left = true;
|
||||
_output.WriteLine ($"{view.GetType ().Name} - {key} Left.");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_output.WriteLine ($"{view.GetType ().Name} - {key} did not Leave.");
|
||||
}
|
||||
|
||||
top.Dispose ();
|
||||
Application.ResetState ();
|
||||
|
||||
Assert.True (left);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData (nameof (AllViewTypes))]
|
||||
[SetupFakeDriver] // SetupFakeDriver resets app state; helps to avoid test pollution
|
||||
public void AllViews_HasFocus_Changed_Event (Type viewType)
|
||||
{
|
||||
View view = CreateInstanceIfNotGeneric (viewType);
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
_output.WriteLine ($"Ignoring {viewType} - It's a Generic");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!view.CanFocus)
|
||||
{
|
||||
_output.WriteLine ($"Ignoring {viewType} - It can't focus.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (view is Toplevel && ((Toplevel)view).Modal)
|
||||
{
|
||||
_output.WriteLine ($"Ignoring {viewType} - It's a Modal Toplevel");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Toplevel top = new ();
|
||||
Application.Top = top;
|
||||
Application.Navigation = new ApplicationNavigation ();
|
||||
|
||||
View otherView = new ()
|
||||
{
|
||||
Id = "otherView",
|
||||
CanFocus = true,
|
||||
TabStop = view.TabStop == TabBehavior.NoStop ? TabBehavior.TabStop : view.TabStop
|
||||
};
|
||||
|
||||
var hasFocusTrue = 0;
|
||||
var hasFocusFalse = 0;
|
||||
|
||||
view.HasFocusChanged += (s, e) =>
|
||||
{
|
||||
if (e.NewValue)
|
||||
{
|
||||
hasFocusTrue++;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasFocusFalse++;
|
||||
}
|
||||
};
|
||||
|
||||
top.Add (view, otherView);
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.False (otherView.HasFocus);
|
||||
|
||||
// Ensure the view is Visible
|
||||
view.Visible = true;
|
||||
|
||||
Application.Top.SetFocus ();
|
||||
Assert.True (Application.Top!.HasFocus);
|
||||
Assert.True (top.HasFocus);
|
||||
|
||||
// Start with the focus on our test view
|
||||
Assert.True (view.HasFocus);
|
||||
|
||||
Assert.Equal (1, hasFocusTrue);
|
||||
Assert.Equal (0, hasFocusFalse);
|
||||
|
||||
// Use keyboard to navigate to next view (otherView).
|
||||
var tries = 0;
|
||||
|
||||
while (view.HasFocus)
|
||||
{
|
||||
if (++tries > 10)
|
||||
{
|
||||
Assert.Fail ($"{view} is not leaving.");
|
||||
}
|
||||
|
||||
switch (view.TabStop)
|
||||
{
|
||||
case null:
|
||||
case TabBehavior.NoStop:
|
||||
case TabBehavior.TabStop:
|
||||
if (Application.RaiseKeyDownEvent (Key.Tab))
|
||||
{
|
||||
if (view.HasFocus)
|
||||
{
|
||||
// Try another nav key (e.g. for TextView that eats Tab)
|
||||
Application.RaiseKeyDownEvent (Key.CursorDown);
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
case TabBehavior.TabGroup:
|
||||
Application.RaiseKeyDownEvent (Key.F6);
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException ();
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Equal (1, hasFocusTrue);
|
||||
Assert.Equal (1, hasFocusFalse);
|
||||
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.True (otherView.HasFocus);
|
||||
|
||||
// Now navigate back to our test view
|
||||
switch (view.TabStop)
|
||||
{
|
||||
case TabBehavior.NoStop:
|
||||
view.SetFocus ();
|
||||
|
||||
break;
|
||||
case TabBehavior.TabStop:
|
||||
Application.RaiseKeyDownEvent (Key.Tab);
|
||||
|
||||
break;
|
||||
case TabBehavior.TabGroup:
|
||||
if (!Application.RaiseKeyDownEvent (Key.F6))
|
||||
{
|
||||
view.SetFocus ();
|
||||
}
|
||||
|
||||
break;
|
||||
case null:
|
||||
Application.RaiseKeyDownEvent (Key.Tab);
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException ();
|
||||
}
|
||||
|
||||
Assert.Equal (2, hasFocusTrue);
|
||||
Assert.Equal (1, hasFocusFalse);
|
||||
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.False (otherView.HasFocus);
|
||||
|
||||
// Cache state because Shutdown has side effects.
|
||||
// Also ensures other tests can continue running if there's a fail
|
||||
bool otherViewHasFocus = otherView.HasFocus;
|
||||
bool viewHasFocus = view.HasFocus;
|
||||
|
||||
int enterCount = hasFocusTrue;
|
||||
int leaveCount = hasFocusFalse;
|
||||
|
||||
top.Dispose ();
|
||||
|
||||
Assert.False (otherViewHasFocus);
|
||||
Assert.True (viewHasFocus);
|
||||
|
||||
Assert.Equal (2, enterCount);
|
||||
Assert.Equal (1, leaveCount);
|
||||
|
||||
Application.ResetState ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData (nameof (AllViewTypes))]
|
||||
[SetupFakeDriver] // SetupFakeDriver resets app state; helps to avoid test pollution
|
||||
public void AllViews_Visible_False_No_HasFocus_Events (Type viewType)
|
||||
{
|
||||
View view = CreateInstanceIfNotGeneric (viewType);
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
_output.WriteLine ($"Ignoring {viewType} - It's a Generic");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!view.CanFocus)
|
||||
{
|
||||
_output.WriteLine ($"Ignoring {viewType} - It can't focus.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (view is Toplevel && ((Toplevel)view).Modal)
|
||||
{
|
||||
_output.WriteLine ($"Ignoring {viewType} - It's a Modal Toplevel");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Toplevel top = new ();
|
||||
|
||||
Application.Top = top;
|
||||
Application.Navigation = new ApplicationNavigation ();
|
||||
|
||||
View otherView = new ()
|
||||
{
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Visible = false;
|
||||
|
||||
var hasFocusChangingCount = 0;
|
||||
var hasFocusChangedCount = 0;
|
||||
|
||||
view.HasFocusChanging += (s, e) => hasFocusChangingCount++;
|
||||
view.HasFocusChanged += (s, e) => hasFocusChangedCount++;
|
||||
|
||||
top.Add (view, otherView);
|
||||
|
||||
// Start with the focus on our test view
|
||||
view.SetFocus ();
|
||||
|
||||
Assert.Equal (0, hasFocusChangingCount);
|
||||
Assert.Equal (0, hasFocusChangedCount);
|
||||
|
||||
Application.RaiseKeyDownEvent (Key.Tab);
|
||||
|
||||
Assert.Equal (0, hasFocusChangingCount);
|
||||
Assert.Equal (0, hasFocusChangedCount);
|
||||
|
||||
Application.RaiseKeyDownEvent (Key.F6);
|
||||
|
||||
Assert.Equal (0, hasFocusChangingCount);
|
||||
Assert.Equal (0, hasFocusChangedCount);
|
||||
|
||||
top.Dispose ();
|
||||
|
||||
Application.ResetState ();
|
||||
|
||||
}
|
||||
|
||||
// View.Focused & View.MostFocused tests
|
||||
|
||||
// View.Focused - No subviews
|
||||
[Fact]
|
||||
public void Focused_NoSubviews ()
|
||||
{
|
||||
var view = new View ();
|
||||
Assert.Null (view.Focused);
|
||||
|
||||
view.CanFocus = true;
|
||||
view.SetFocus ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMostFocused_NoSubviews_Returns_Null ()
|
||||
{
|
||||
var view = new View ();
|
||||
Assert.Null (view.Focused);
|
||||
|
||||
view.CanFocus = true;
|
||||
Assert.False (view.HasFocus);
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.Null (view.MostFocused);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMostFocused_Returns_Most ()
|
||||
{
|
||||
var view = new View ()
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subview = new View ()
|
||||
{
|
||||
Id = "subview",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Add (subview);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subview.HasFocus);
|
||||
Assert.Equal (subview, view.MostFocused);
|
||||
|
||||
var subview2 = new View ()
|
||||
{
|
||||
Id = "subview2",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Add (subview2);
|
||||
Assert.Equal (subview2, view.MostFocused);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[SetupFakeDriver]
|
||||
public void Navigation_With_Null_Focused_View ()
|
||||
{
|
||||
// Non-regression test for #882 (NullReferenceException during keyboard navigation when Focused is null)
|
||||
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
var top = new Toplevel ();
|
||||
top.Ready += (s, e) => { Assert.Null (top.Focused); };
|
||||
|
||||
// Keyboard navigation with tab
|
||||
FakeConsole.MockKeyPresses.Push (new ('\t', ConsoleKey.Tab, false, false, false));
|
||||
|
||||
Application.Iteration += (s, a) => Application.RequestStop ();
|
||||
|
||||
Application.Run (top);
|
||||
top.Dispose ();
|
||||
Application.Shutdown ();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Application_Begin_FocusesDeepest ()
|
||||
{
|
||||
var win1 = new Window { Id = "win1", Width = 10, Height = 1 };
|
||||
var view1 = new View { Id = "view1", Width = Dim.Fill (), Height = Dim.Fill (), CanFocus = true };
|
||||
var win2 = new Window { Id = "win2", Y = 6, Width = 10, Height = 1 };
|
||||
var view2 = new View { Id = "view2", Width = Dim.Fill (), Height = Dim.Fill (), CanFocus = true };
|
||||
win2.Add (view2);
|
||||
win1.Add (view1, win2);
|
||||
|
||||
Application.Begin (win1);
|
||||
|
||||
Assert.True (win1.HasFocus);
|
||||
Assert.True (view1.HasFocus);
|
||||
Assert.False (win2.HasFocus);
|
||||
Assert.False (view2.HasFocus);
|
||||
win1.Dispose ();
|
||||
}
|
||||
}
|
||||
174
Tests/UnitTests/View/Navigation/RestoreFocusTests.cs
Normal file
174
Tests/UnitTests/View/Navigation/RestoreFocusTests.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class RestoreFocusTests () : TestsAllViews
|
||||
{
|
||||
[Fact]
|
||||
public void RestoreFocus_Restores ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView1 = new View
|
||||
{
|
||||
Id = "subViewSubView1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView2 = new View
|
||||
{
|
||||
Id = "subViewSubView2",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView3 = new View
|
||||
{
|
||||
Id = "subViewSubView3",
|
||||
CanFocus = true
|
||||
};
|
||||
subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
Assert.True (subViewSubView1.HasFocus);
|
||||
Assert.Equal (subViewSubView1, subView.Focused);
|
||||
|
||||
view.HasFocus = false;
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.RestoreFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
Assert.Equal (subViewSubView1, subView.Focused);
|
||||
Assert.True (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
subViewSubView2.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.True (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.HasFocus = false;
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.RestoreFocus ();
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
Assert.True (subViewSubView2.HasFocus);
|
||||
Assert.Equal (subViewSubView2, subView.Focused);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RestoreFocus_Across_TabGroup ()
|
||||
{
|
||||
var top = new View
|
||||
{
|
||||
Id = "top",
|
||||
CanFocus = true,
|
||||
TabStop = TabBehavior.TabGroup
|
||||
};
|
||||
|
||||
var tabGroup1 = new View
|
||||
{
|
||||
Id = "tabGroup1",
|
||||
CanFocus = true,
|
||||
TabStop = TabBehavior.TabGroup
|
||||
};
|
||||
|
||||
var tabGroup1SubView1 = new View
|
||||
{
|
||||
Id = "tabGroup1SubView1",
|
||||
CanFocus = true,
|
||||
TabStop = TabBehavior.TabStop
|
||||
};
|
||||
|
||||
var tabGroup1SubView2 = new View
|
||||
{
|
||||
Id = "tabGroup1SubView2",
|
||||
CanFocus = true,
|
||||
TabStop = TabBehavior.TabStop
|
||||
};
|
||||
tabGroup1.Add (tabGroup1SubView1, tabGroup1SubView2);
|
||||
|
||||
var tabGroup2 = new View
|
||||
{
|
||||
Id = "tabGroup2",
|
||||
CanFocus = true,
|
||||
TabStop = TabBehavior.TabGroup
|
||||
};
|
||||
|
||||
var tabGroup2SubView1 = new View
|
||||
{
|
||||
Id = "tabGroup2SubView1",
|
||||
CanFocus = true,
|
||||
TabStop = TabBehavior.TabStop
|
||||
};
|
||||
|
||||
var tabGroup2SubView2 = new View
|
||||
{
|
||||
Id = "tabGroup2SubView2",
|
||||
CanFocus = true,
|
||||
TabStop = TabBehavior.TabStop
|
||||
};
|
||||
tabGroup2.Add (tabGroup2SubView1, tabGroup2SubView2);
|
||||
|
||||
top.Add (tabGroup1, tabGroup2);
|
||||
|
||||
top.SetFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.Equal (tabGroup1, top.Focused);
|
||||
Assert.Equal (tabGroup1SubView1, tabGroup1.Focused);
|
||||
|
||||
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.Equal (tabGroup2, top.Focused);
|
||||
Assert.Equal (tabGroup2SubView1, tabGroup2.Focused);
|
||||
|
||||
top.HasFocus = false;
|
||||
Assert.False (top.HasFocus);
|
||||
|
||||
top.RestoreFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.Equal (tabGroup2, top.Focused);
|
||||
Assert.Equal (tabGroup2SubView1, tabGroup2.Focused);
|
||||
|
||||
top.HasFocus = false;
|
||||
Assert.False (top.HasFocus);
|
||||
|
||||
top.RestoreFocus ();
|
||||
Assert.True (top.HasFocus);
|
||||
Assert.Equal (tabGroup2, top.Focused);
|
||||
Assert.Equal (tabGroup2SubView1, tabGroup2.Focused);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
396
Tests/UnitTests/View/Navigation/SetFocusTests.cs
Normal file
396
Tests/UnitTests/View/Navigation/SetFocusTests.cs
Normal file
@@ -0,0 +1,396 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class SetFocusTests () : TestsAllViews
|
||||
{
|
||||
[Fact]
|
||||
public void SetFocus_With_Null_Superview_Does_Not_Throw_Exception ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
Assert.True (view.CanFocus);
|
||||
Assert.False (view.HasFocus);
|
||||
|
||||
Exception exception = Record.Exception (() => view.SetFocus ());
|
||||
Assert.Null (exception);
|
||||
|
||||
Assert.True (view.CanFocus);
|
||||
Assert.True (view.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetFocus_SetsFocus ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
Assert.True (view.CanFocus);
|
||||
Assert.False (view.HasFocus);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetFocus_NoSubView_Focused_Is_Null ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
Assert.True (view.CanFocus);
|
||||
Assert.False (view.HasFocus);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.Null (view.Focused);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetFocus_SubView_Focused_Is_Set ()
|
||||
{
|
||||
var view = new Window
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subview = new View
|
||||
{
|
||||
Id = "subview",
|
||||
CanFocus = true
|
||||
};
|
||||
view.Add (subview);
|
||||
Assert.True (view.CanFocus);
|
||||
Assert.False (view.HasFocus);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.Equal (subview, view.Focused);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetFocus_SetsFocus_DeepestSubView ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subview = new View
|
||||
{
|
||||
Id = "subview",
|
||||
CanFocus = true
|
||||
};
|
||||
view.Add (subview);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (subview.HasFocus);
|
||||
Assert.Equal (subview, view.Focused);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetFocus_SetsFocus_DeepestSubView_CompoundSubView ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView1 = new View
|
||||
{
|
||||
Id = "subViewSubView1",
|
||||
CanFocus = false
|
||||
};
|
||||
|
||||
var subViewSubView2 = new View
|
||||
{
|
||||
Id = "subViewSubView2",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView3 = new View
|
||||
{
|
||||
Id = "subViewSubView3",
|
||||
CanFocus = false
|
||||
};
|
||||
subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
Assert.Equal (subViewSubView2, subView.Focused);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetFocus_CompoundSubView_SetFocus_Sets ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView1 = new View
|
||||
{
|
||||
Id = "subViewSubView1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView2 = new View
|
||||
{
|
||||
Id = "subViewSubView2",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView3 = new View
|
||||
{
|
||||
Id = "subViewSubView3",
|
||||
CanFocus = true
|
||||
};
|
||||
subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
Assert.True (subViewSubView1.HasFocus);
|
||||
Assert.Equal (subViewSubView1, subView.Focused);
|
||||
|
||||
subViewSubView2.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.True (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void SetFocus_AdornmentSubView_SetFocus_Sets ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
var borderSubView = new View
|
||||
{
|
||||
Id = "borderSubView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
|
||||
var subViewSubView1 = new View
|
||||
{
|
||||
Id = "subViewSubView1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView2 = new View
|
||||
{
|
||||
Id = "subViewSubView2",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView3 = new View
|
||||
{
|
||||
Id = "subViewSubView3",
|
||||
CanFocus = true
|
||||
};
|
||||
borderSubView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
|
||||
|
||||
view.Border.Add (borderSubView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.False (borderSubView.HasFocus);
|
||||
|
||||
view.Border.CanFocus = true;
|
||||
subViewSubView1.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
Assert.True (borderSubView.HasFocus);
|
||||
Assert.True (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.Border.CanFocus = false;
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.False (view.Border.HasFocus);
|
||||
Assert.False (borderSubView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.Border.CanFocus = true;
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.False (view.Border.HasFocus);
|
||||
Assert.False (borderSubView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.Border.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (view.Border.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
Assert.True (borderSubView.HasFocus);
|
||||
Assert.True (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.Border.CanFocus = false;
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.False (view.Border.HasFocus);
|
||||
Assert.False (borderSubView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
view.Border.CanFocus = true;
|
||||
subViewSubView1.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
Assert.True (view.Border.HasFocus);
|
||||
Assert.True (borderSubView.HasFocus);
|
||||
Assert.True (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
|
||||
subView.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.False (view.Border.HasFocus);
|
||||
Assert.False (borderSubView.HasFocus);
|
||||
Assert.False (subViewSubView1.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
Assert.False (subViewSubView3.HasFocus);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void SetFocus_Peer_LeavesOther ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subview1 = new View
|
||||
{
|
||||
Id = "subview1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subview2 = new View
|
||||
{
|
||||
Id = "subview2",
|
||||
CanFocus = true
|
||||
};
|
||||
view.Add (subview1, subview2);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.Equal (subview1, view.Focused);
|
||||
Assert.True (subview1.HasFocus);
|
||||
Assert.False (subview2.HasFocus);
|
||||
|
||||
subview2.SetFocus ();
|
||||
Assert.Equal (subview2, view.Focused);
|
||||
Assert.True (subview2.HasFocus);
|
||||
Assert.False (subview1.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetFocus_On_Peer_Moves_Focus_To_Peer ()
|
||||
{
|
||||
var top = new View
|
||||
{
|
||||
Id = "top",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var view1 = new View
|
||||
{
|
||||
Id = "view1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView1 = new View
|
||||
{
|
||||
Id = "subView1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view1.Add (subView1);
|
||||
|
||||
var subView1SubView1 = new View
|
||||
{
|
||||
Id = "subView1subView1",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
subView1.Add (subView1SubView1);
|
||||
|
||||
var view2 = new View
|
||||
{
|
||||
Id = "view2",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
top.Add (view1, view2);
|
||||
Assert.False (view1.HasFocus);
|
||||
Assert.False (view2.HasFocus);
|
||||
|
||||
view1.SetFocus ();
|
||||
Assert.True (view1.HasFocus);
|
||||
Assert.True (subView1.HasFocus);
|
||||
Assert.True (subView1SubView1.HasFocus);
|
||||
Assert.Equal (subView1, view1.Focused);
|
||||
Assert.Equal (subView1SubView1, subView1.Focused);
|
||||
|
||||
view2.SetFocus ();
|
||||
Assert.False (view1.HasFocus);
|
||||
Assert.True (view2.HasFocus);
|
||||
}
|
||||
}
|
||||
254
Tests/UnitTests/View/Navigation/VisibleTests.cs
Normal file
254
Tests/UnitTests/View/Navigation/VisibleTests.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class VisibleTests () : TestsAllViews
|
||||
{
|
||||
[Fact]
|
||||
public void Visible_False_Leaves ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
|
||||
view.Visible = false;
|
||||
Assert.False (view.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Visible_False_Leaves_Subview ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
|
||||
view.Visible = false;
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Visible_False_Leaves_Subview2 ()
|
||||
{
|
||||
var view = new Window
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
|
||||
view.Visible = false;
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Visible_False_On_Subview_Leaves_Just_Subview ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
|
||||
subView.Visible = false;
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Visible_False_Focuses_Deepest_Focusable_Subview ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView1 = new View
|
||||
{
|
||||
Id = "subViewSubView1",
|
||||
CanFocus = false
|
||||
};
|
||||
|
||||
var subViewSubView2 = new View
|
||||
{
|
||||
Id = "subViewSubView2",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subViewSubView3 = new View
|
||||
{
|
||||
Id = "subViewSubView3",
|
||||
CanFocus = true // This is the one that will be focused
|
||||
};
|
||||
subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
Assert.Equal (subViewSubView2, subView.Focused);
|
||||
|
||||
subViewSubView2.Visible = false;
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
Assert.Equal (subViewSubView3, subView.Focused);
|
||||
Assert.True (subViewSubView3.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Visible_True_Subview_Focuses_SubView ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.False (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
|
||||
view.Visible = true;
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Visible_True_On_Subview_Focuses ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.False (subView.HasFocus);
|
||||
|
||||
subView.Visible = true;
|
||||
Assert.True (view.HasFocus);
|
||||
Assert.True (subView.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Visible_True_Focuses_Deepest_Focusable_Subview ()
|
||||
{
|
||||
var view = new View
|
||||
{
|
||||
Id = "view",
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
var subView = new View
|
||||
{
|
||||
Id = "subView",
|
||||
CanFocus = true,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
var subViewSubView1 = new View
|
||||
{
|
||||
Id = "subViewSubView1",
|
||||
CanFocus = false
|
||||
};
|
||||
|
||||
var subViewSubView2 = new View
|
||||
{
|
||||
Id = "subViewSubView2",
|
||||
CanFocus = true // This is the one that will be focused
|
||||
};
|
||||
|
||||
var subViewSubView3 = new View
|
||||
{
|
||||
Id = "subViewSubView3",
|
||||
CanFocus = true
|
||||
};
|
||||
subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
|
||||
|
||||
view.Add (subView);
|
||||
|
||||
view.SetFocus ();
|
||||
Assert.False (subView.HasFocus);
|
||||
Assert.False (subViewSubView2.HasFocus);
|
||||
|
||||
subView.Visible = true;
|
||||
Assert.True (subView.HasFocus);
|
||||
Assert.Equal (subView, view.Focused);
|
||||
Assert.Equal (subViewSubView2, subView.Focused);
|
||||
Assert.True (subViewSubView2.HasFocus);
|
||||
}
|
||||
}
|
||||
107
Tests/UnitTests/View/Orientation/OrientationHelperTests.cs
Normal file
107
Tests/UnitTests/View/Orientation/OrientationHelperTests.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using Moq;
|
||||
|
||||
namespace Terminal.Gui.ViewTests.OrientationTests;
|
||||
|
||||
public class OrientationHelperTests
|
||||
{
|
||||
[Fact]
|
||||
public void Orientation_Set_NewValue_InvokesChangingAndChangedEvents ()
|
||||
{
|
||||
// Arrange
|
||||
Mock<IOrientation> mockIOrientation = new Mock<IOrientation> ();
|
||||
var orientationHelper = new OrientationHelper (mockIOrientation.Object);
|
||||
var changingEventInvoked = 0;
|
||||
var changedEventInvoked = 0;
|
||||
|
||||
orientationHelper.OrientationChanging += (sender, e) => { changingEventInvoked++; };
|
||||
orientationHelper.OrientationChanged += (sender, e) => { changedEventInvoked++; };
|
||||
|
||||
// Act
|
||||
orientationHelper.Orientation = Orientation.Vertical;
|
||||
|
||||
// Assert
|
||||
Assert.Equal (1, changingEventInvoked);
|
||||
Assert.Equal(1, changedEventInvoked);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Orientation_Set_NewValue_InvokesOnChangingAndOnChangedOverrides ()
|
||||
{
|
||||
// Arrange
|
||||
Mock<IOrientation> mockIOrientation = new Mock<IOrientation> ();
|
||||
var onChangingOverrideCalled = 0;
|
||||
var onChangedOverrideCalled = 0;
|
||||
|
||||
mockIOrientation.Setup (x => x.OnOrientationChanging (It.IsAny<Orientation> (), It.IsAny<Orientation> ()))
|
||||
.Callback (() => onChangingOverrideCalled++)
|
||||
.Returns (false); // Ensure it doesn't cancel the change
|
||||
|
||||
mockIOrientation.Setup (x => x.OnOrientationChanged (It.IsAny<Orientation> ()))
|
||||
.Callback (() => onChangedOverrideCalled++);
|
||||
|
||||
var orientationHelper = new OrientationHelper (mockIOrientation.Object);
|
||||
|
||||
// Act
|
||||
orientationHelper.Orientation = Orientation.Vertical;
|
||||
|
||||
// Assert
|
||||
Assert.Equal (1, onChangingOverrideCalled);
|
||||
Assert.Equal (1, onChangedOverrideCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Orientation_Set_SameValue_DoesNotInvokeChangingOrChangedEvents ()
|
||||
{
|
||||
// Arrange
|
||||
Mock<IOrientation> mockIOrientation = new Mock<IOrientation> ();
|
||||
var orientationHelper = new OrientationHelper (mockIOrientation.Object);
|
||||
orientationHelper.Orientation = Orientation.Horizontal; // Set initial orientation
|
||||
var changingEventInvoked = 0;
|
||||
var changedEventInvoked = 0;
|
||||
|
||||
orientationHelper.OrientationChanging += (sender, e) => { changingEventInvoked++; };
|
||||
orientationHelper.OrientationChanged += (sender, e) => { changedEventInvoked++; };
|
||||
|
||||
// Act
|
||||
orientationHelper.Orientation = Orientation.Horizontal; // Set to the same value
|
||||
|
||||
// Assert
|
||||
Assert.Equal (0, changingEventInvoked);
|
||||
Assert.Equal (0, changedEventInvoked);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Orientation_Set_NewValue_OrientationChanging_CancellationPreventsChange ()
|
||||
{
|
||||
// Arrange
|
||||
Mock<IOrientation> mockIOrientation = new Mock<IOrientation> ();
|
||||
var orientationHelper = new OrientationHelper (mockIOrientation.Object);
|
||||
orientationHelper.OrientationChanging += (sender, e) => { e.Cancel = true; }; // Cancel the change
|
||||
|
||||
// Act
|
||||
orientationHelper.Orientation = Orientation.Vertical;
|
||||
|
||||
// Assert
|
||||
Assert.Equal (Orientation.Horizontal, orientationHelper.Orientation); // Initial orientation is Horizontal
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Orientation_Set_NewValue_OnOrientationChanging_CancelsChange ()
|
||||
{
|
||||
// Arrange
|
||||
Mock<IOrientation> mockIOrientation = new Mock<IOrientation> ();
|
||||
|
||||
mockIOrientation.Setup (x => x.OnOrientationChanging (It.IsAny<Orientation> (), It.IsAny<Orientation> ()))
|
||||
.Returns (true); // Override to return true, cancelling the change
|
||||
|
||||
var orientationHelper = new OrientationHelper (mockIOrientation.Object);
|
||||
|
||||
// Act
|
||||
orientationHelper.Orientation = Orientation.Vertical;
|
||||
|
||||
// Assert
|
||||
Assert.Equal (
|
||||
Orientation.Horizontal,
|
||||
orientationHelper.Orientation); // Initial orientation is Horizontal, and it should remain unchanged due to cancellation
|
||||
}
|
||||
}
|
||||
136
Tests/UnitTests/View/Orientation/OrientationTests.cs
Normal file
136
Tests/UnitTests/View/Orientation/OrientationTests.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
namespace Terminal.Gui.ViewTests.OrientationTests;
|
||||
|
||||
public class OrientationTests
|
||||
{
|
||||
private class CustomView : View, IOrientation
|
||||
{
|
||||
private readonly OrientationHelper _orientationHelper;
|
||||
|
||||
public CustomView ()
|
||||
{
|
||||
_orientationHelper = new (this);
|
||||
Orientation = Orientation.Vertical;
|
||||
_orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
|
||||
_orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
|
||||
}
|
||||
|
||||
public Orientation Orientation
|
||||
{
|
||||
get => _orientationHelper.Orientation;
|
||||
set => _orientationHelper.Orientation = value;
|
||||
}
|
||||
|
||||
public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
|
||||
public event EventHandler<EventArgs<Orientation>> OrientationChanged;
|
||||
|
||||
public bool CancelOnOrientationChanging { get; set; }
|
||||
|
||||
public bool OnOrientationChangingCalled { get; private set; }
|
||||
public bool OnOrientationChangedCalled { get; private set; }
|
||||
|
||||
public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation)
|
||||
{
|
||||
OnOrientationChangingCalled = true;
|
||||
// Custom logic before orientation changes
|
||||
return CancelOnOrientationChanging; // Return true to cancel the change
|
||||
}
|
||||
|
||||
public void OnOrientationChanged (Orientation newOrientation)
|
||||
{
|
||||
OnOrientationChangedCalled = true;
|
||||
// Custom logic after orientation has changed
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Orientation_Change_IsSuccessful ()
|
||||
{
|
||||
// Arrange
|
||||
var customView = new CustomView ();
|
||||
var orientationChanged = false;
|
||||
customView.OrientationChanged += (sender, e) => orientationChanged = true;
|
||||
|
||||
// Act
|
||||
customView.Orientation = Orientation.Horizontal;
|
||||
|
||||
// Assert
|
||||
Assert.True (orientationChanged, "OrientationChanged event was not invoked.");
|
||||
Assert.Equal (Orientation.Horizontal, customView.Orientation);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Orientation_Change_OrientationChanging_Set_Cancel_IsCancelled ()
|
||||
{
|
||||
// Arrange
|
||||
var customView = new CustomView ();
|
||||
customView.OrientationChanging += (sender, e) => e.Cancel = true; // Cancel the orientation change
|
||||
var orientationChanged = false;
|
||||
customView.OrientationChanged += (sender, e) => orientationChanged = true;
|
||||
|
||||
// Act
|
||||
customView.Orientation = Orientation.Horizontal;
|
||||
|
||||
// Assert
|
||||
Assert.False (orientationChanged, "OrientationChanged event was invoked despite cancellation.");
|
||||
Assert.Equal (Orientation.Vertical, customView.Orientation); // Assuming Vertical is the default orientation
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Orientation_Change_OnOrientationChanging_Return_True_IsCancelled ()
|
||||
{
|
||||
// Arrange
|
||||
var customView = new CustomView ();
|
||||
customView.CancelOnOrientationChanging = true; // Cancel the orientation change
|
||||
|
||||
var orientationChanged = false;
|
||||
customView.OrientationChanged += (sender, e) => orientationChanged = true;
|
||||
|
||||
// Act
|
||||
customView.Orientation = Orientation.Horizontal;
|
||||
|
||||
// Assert
|
||||
Assert.False (orientationChanged, "OrientationChanged event was invoked despite cancellation.");
|
||||
Assert.Equal (Orientation.Vertical, customView.Orientation); // Assuming Vertical is the default orientation
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void OrientationChanging_VirtualMethodCalledBeforeEvent ()
|
||||
{
|
||||
// Arrange
|
||||
var radioGroup = new CustomView ();
|
||||
bool eventCalled = false;
|
||||
|
||||
radioGroup.OrientationChanging += (sender, e) =>
|
||||
{
|
||||
eventCalled = true;
|
||||
Assert.True (radioGroup.OnOrientationChangingCalled, "OnOrientationChanging was not called before the event.");
|
||||
};
|
||||
|
||||
// Act
|
||||
radioGroup.Orientation = Orientation.Horizontal;
|
||||
|
||||
// Assert
|
||||
Assert.True (eventCalled, "OrientationChanging event was not called.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OrientationChanged_VirtualMethodCalledBeforeEvent ()
|
||||
{
|
||||
// Arrange
|
||||
var radioGroup = new CustomView ();
|
||||
bool eventCalled = false;
|
||||
|
||||
radioGroup.OrientationChanged += (sender, e) =>
|
||||
{
|
||||
eventCalled = true;
|
||||
Assert.True (radioGroup.OnOrientationChangedCalled, "OnOrientationChanged was not called before the event.");
|
||||
};
|
||||
|
||||
// Act
|
||||
radioGroup.Orientation = Orientation.Horizontal;
|
||||
|
||||
// Assert
|
||||
Assert.True (eventCalled, "OrientationChanged event was not called.");
|
||||
}
|
||||
}
|
||||
288
Tests/UnitTests/View/SubviewTests.cs
Normal file
288
Tests/UnitTests/View/SubviewTests.cs
Normal file
@@ -0,0 +1,288 @@
|
||||
using System.Text;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class SubviewTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
public SubviewTests (ITestOutputHelper output) { _output = output; }
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void GetTopSuperView_Test ()
|
||||
{
|
||||
var v1 = new View ();
|
||||
var fv1 = new FrameView ();
|
||||
fv1.Add (v1);
|
||||
var tf1 = new TextField ();
|
||||
var w1 = new Window ();
|
||||
w1.Add (fv1, tf1);
|
||||
var top1 = new Toplevel ();
|
||||
top1.Add (w1);
|
||||
|
||||
var v2 = new View ();
|
||||
var fv2 = new FrameView ();
|
||||
fv2.Add (v2);
|
||||
var tf2 = new TextField ();
|
||||
var w2 = new Window ();
|
||||
w2.Add (fv2, tf2);
|
||||
var top2 = new Toplevel ();
|
||||
top2.Add (w2);
|
||||
|
||||
Assert.Equal (top1, v1.GetTopSuperView ());
|
||||
Assert.Equal (top2, v2.GetTopSuperView ());
|
||||
|
||||
v1.Dispose ();
|
||||
fv1.Dispose ();
|
||||
tf1.Dispose ();
|
||||
w1.Dispose ();
|
||||
top1.Dispose ();
|
||||
v2.Dispose ();
|
||||
fv2.Dispose ();
|
||||
tf2.Dispose ();
|
||||
w2.Dispose ();
|
||||
top2.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void Initialized_Event_Comparing_With_Added_Event ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
var top = new Toplevel { Id = "0" }; // Frame: 0, 0, 80, 25; Viewport: 0, 0, 80, 25
|
||||
|
||||
var winAddedToTop = new Window
|
||||
{
|
||||
Id = "t", Width = Dim.Fill (), Height = Dim.Fill ()
|
||||
}; // Frame: 0, 0, 80, 25; Viewport: 0, 0, 78, 23
|
||||
|
||||
var v1AddedToWin = new View
|
||||
{
|
||||
Id = "v1", Width = Dim.Fill (), Height = Dim.Fill ()
|
||||
}; // Frame: 1, 1, 78, 23 (because Windows has a border)
|
||||
|
||||
var v2AddedToWin = new View
|
||||
{
|
||||
Id = "v2", Width = Dim.Fill (), Height = Dim.Fill ()
|
||||
}; // Frame: 1, 1, 78, 23 (because Windows has a border)
|
||||
|
||||
var svAddedTov1 = new View
|
||||
{
|
||||
Id = "sv1", Width = Dim.Fill (), Height = Dim.Fill ()
|
||||
}; // Frame: 1, 1, 78, 23 (same as it's superview v1AddedToWin)
|
||||
|
||||
int tc = 0, wc = 0, v1c = 0, v2c = 0, sv1c = 0;
|
||||
|
||||
winAddedToTop.Added += (s, e) =>
|
||||
{
|
||||
Assert.Equal (e.SuperView.Frame.Width, winAddedToTop.Frame.Width);
|
||||
Assert.Equal (e.SuperView.Frame.Height, winAddedToTop.Frame.Height);
|
||||
};
|
||||
|
||||
v1AddedToWin.Added += (s, e) =>
|
||||
{
|
||||
Assert.Equal (e.SuperView.Frame.Width, v1AddedToWin.Frame.Width);
|
||||
Assert.Equal (e.SuperView.Frame.Height, v1AddedToWin.Frame.Height);
|
||||
};
|
||||
|
||||
v2AddedToWin.Added += (s, e) =>
|
||||
{
|
||||
Assert.Equal (e.SuperView.Frame.Width, v2AddedToWin.Frame.Width);
|
||||
Assert.Equal (e.SuperView.Frame.Height, v2AddedToWin.Frame.Height);
|
||||
};
|
||||
|
||||
svAddedTov1.Added += (s, e) =>
|
||||
{
|
||||
Assert.Equal (e.SuperView.Frame.Width, svAddedTov1.Frame.Width);
|
||||
Assert.Equal (e.SuperView.Frame.Height, svAddedTov1.Frame.Height);
|
||||
};
|
||||
|
||||
top.Initialized += (s, e) =>
|
||||
{
|
||||
tc++;
|
||||
Assert.Equal (1, tc);
|
||||
Assert.Equal (1, wc);
|
||||
Assert.Equal (1, v1c);
|
||||
Assert.Equal (1, v2c);
|
||||
Assert.Equal (1, sv1c);
|
||||
|
||||
Assert.True (top.CanFocus);
|
||||
Assert.True (winAddedToTop.CanFocus);
|
||||
Assert.False (v1AddedToWin.CanFocus);
|
||||
Assert.False (v2AddedToWin.CanFocus);
|
||||
Assert.False (svAddedTov1.CanFocus);
|
||||
|
||||
Application.LayoutAndDraw ();
|
||||
};
|
||||
|
||||
winAddedToTop.Initialized += (s, e) =>
|
||||
{
|
||||
wc++;
|
||||
Assert.Equal (top.Viewport.Width, winAddedToTop.Frame.Width);
|
||||
Assert.Equal (top.Viewport.Height, winAddedToTop.Frame.Height);
|
||||
};
|
||||
|
||||
v1AddedToWin.Initialized += (s, e) =>
|
||||
{
|
||||
v1c++;
|
||||
|
||||
// Top.Frame: 0, 0, 80, 25; Top.Viewport: 0, 0, 80, 25
|
||||
// BUGBUG: This is wrong, it should be 78, 23. This test has always been broken.
|
||||
// in no way should the v1AddedToWin.Frame be the same as the Top.Frame/Viewport
|
||||
// as it is a subview of winAddedToTop, which has a border!
|
||||
//Assert.Equal (top.Viewport.Width, v1AddedToWin.Frame.Width);
|
||||
//Assert.Equal (top.Viewport.Height, v1AddedToWin.Frame.Height);
|
||||
};
|
||||
|
||||
v2AddedToWin.Initialized += (s, e) =>
|
||||
{
|
||||
v2c++;
|
||||
|
||||
// Top.Frame: 0, 0, 80, 25; Top.Viewport: 0, 0, 80, 25
|
||||
// BUGBUG: This is wrong, it should be 78, 23. This test has always been broken.
|
||||
// in no way should the v2AddedToWin.Frame be the same as the Top.Frame/Viewport
|
||||
// as it is a subview of winAddedToTop, which has a border!
|
||||
//Assert.Equal (top.Viewport.Width, v2AddedToWin.Frame.Width);
|
||||
//Assert.Equal (top.Viewport.Height, v2AddedToWin.Frame.Height);
|
||||
};
|
||||
|
||||
svAddedTov1.Initialized += (s, e) =>
|
||||
{
|
||||
sv1c++;
|
||||
|
||||
// Top.Frame: 0, 0, 80, 25; Top.Viewport: 0, 0, 80, 25
|
||||
// BUGBUG: This is wrong, it should be 78, 23. This test has always been broken.
|
||||
// in no way should the svAddedTov1.Frame be the same as the Top.Frame/Viewport
|
||||
// because sv1AddedTov1 is a subview of v1AddedToWin, which is a subview of
|
||||
// winAddedToTop, which has a border!
|
||||
//Assert.Equal (top.Viewport.Width, svAddedTov1.Frame.Width);
|
||||
//Assert.Equal (top.Viewport.Height, svAddedTov1.Frame.Height);
|
||||
Assert.False (svAddedTov1.CanFocus);
|
||||
//Assert.Throws<InvalidOperationException> (() => svAddedTov1.CanFocus = true);
|
||||
Assert.False (svAddedTov1.CanFocus);
|
||||
};
|
||||
|
||||
v1AddedToWin.Add (svAddedTov1);
|
||||
winAddedToTop.Add (v1AddedToWin, v2AddedToWin);
|
||||
top.Add (winAddedToTop);
|
||||
|
||||
Application.Iteration += (s, a) =>
|
||||
{
|
||||
Application.LayoutAndDraw ();
|
||||
top.Running = false;
|
||||
};
|
||||
|
||||
Application.Run (top);
|
||||
top.Dispose ();
|
||||
Application.Shutdown ();
|
||||
|
||||
Assert.Equal (1, tc);
|
||||
Assert.Equal (1, wc);
|
||||
Assert.Equal (1, v1c);
|
||||
Assert.Equal (1, v2c);
|
||||
Assert.Equal (1, sv1c);
|
||||
|
||||
Assert.True (top.CanFocus);
|
||||
Assert.True (winAddedToTop.CanFocus);
|
||||
Assert.False (v1AddedToWin.CanFocus);
|
||||
Assert.False (v2AddedToWin.CanFocus);
|
||||
Assert.False (svAddedTov1.CanFocus);
|
||||
|
||||
v1AddedToWin.CanFocus = true;
|
||||
Assert.False (svAddedTov1.CanFocus); // False because sv1 was disposed and it isn't a subview of v1.
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void Initialized_Event_Will_Be_Invoked_When_Added_Dynamically ()
|
||||
{
|
||||
Application.Init (new FakeDriver ());
|
||||
|
||||
var t = new Toplevel { Id = "0" };
|
||||
|
||||
var w = new Window { Id = "t", Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
var v1 = new View { Id = "v1", Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
var v2 = new View { Id = "v2", Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
int tc = 0, wc = 0, v1c = 0, v2c = 0, sv1c = 0;
|
||||
|
||||
t.Initialized += (s, e) =>
|
||||
{
|
||||
tc++;
|
||||
Assert.Equal (1, tc);
|
||||
Assert.Equal (1, wc);
|
||||
Assert.Equal (1, v1c);
|
||||
Assert.Equal (1, v2c);
|
||||
Assert.Equal (0, sv1c); // Added after t in the Application.Iteration.
|
||||
|
||||
Assert.True (t.CanFocus);
|
||||
Assert.True (w.CanFocus);
|
||||
Assert.False (v1.CanFocus);
|
||||
Assert.False (v2.CanFocus);
|
||||
|
||||
Application.LayoutAndDraw ();
|
||||
};
|
||||
|
||||
w.Initialized += (s, e) =>
|
||||
{
|
||||
wc++;
|
||||
Assert.Equal (t.Viewport.Width, w.Frame.Width);
|
||||
Assert.Equal (t.Viewport.Height, w.Frame.Height);
|
||||
};
|
||||
|
||||
v1.Initialized += (s, e) =>
|
||||
{
|
||||
v1c++;
|
||||
|
||||
//Assert.Equal (t.Viewport.Width, v1.Frame.Width);
|
||||
//Assert.Equal (t.Viewport.Height, v1.Frame.Height);
|
||||
};
|
||||
|
||||
v2.Initialized += (s, e) =>
|
||||
{
|
||||
v2c++;
|
||||
|
||||
//Assert.Equal (t.Viewport.Width, v2.Frame.Width);
|
||||
//Assert.Equal (t.Viewport.Height, v2.Frame.Height);
|
||||
};
|
||||
w.Add (v1, v2);
|
||||
t.Add (w);
|
||||
|
||||
Application.Iteration += (s, a) =>
|
||||
{
|
||||
var sv1 = new View { Id = "sv1", Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
sv1.Initialized += (s, e) =>
|
||||
{
|
||||
sv1c++;
|
||||
Assert.NotEqual (t.Frame.Width, sv1.Frame.Width);
|
||||
Assert.NotEqual (t.Frame.Height, sv1.Frame.Height);
|
||||
Assert.False (sv1.CanFocus);
|
||||
//Assert.Throws<InvalidOperationException> (() => sv1.CanFocus = true);
|
||||
Assert.False (sv1.CanFocus);
|
||||
};
|
||||
|
||||
v1.Add (sv1);
|
||||
|
||||
Application.LayoutAndDraw ();
|
||||
t.Running = false;
|
||||
};
|
||||
|
||||
Application.Run (t);
|
||||
t.Dispose ();
|
||||
Application.Shutdown ();
|
||||
|
||||
Assert.Equal (1, tc);
|
||||
Assert.Equal (1, wc);
|
||||
Assert.Equal (1, v1c);
|
||||
Assert.Equal (1, v2c);
|
||||
Assert.Equal (1, sv1c);
|
||||
|
||||
Assert.True (t.CanFocus);
|
||||
Assert.True (w.CanFocus);
|
||||
Assert.False (v1.CanFocus);
|
||||
Assert.False (v2.CanFocus);
|
||||
}}
|
||||
1138
Tests/UnitTests/View/TextTests.cs
Normal file
1138
Tests/UnitTests/View/TextTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
446
Tests/UnitTests/View/ViewCommandTests.cs
Normal file
446
Tests/UnitTests/View/ViewCommandTests.cs
Normal file
@@ -0,0 +1,446 @@
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class ViewCommandTests
|
||||
{
|
||||
#region OnAccept/Accept tests
|
||||
|
||||
[Fact]
|
||||
public void Accept_Command_Raises_NoFocus ()
|
||||
{
|
||||
var view = new ViewEventTester ();
|
||||
Assert.False (view.HasFocus);
|
||||
|
||||
Assert.False (view.InvokeCommand (Command.Accept)); // there's no superview, so it should return true?
|
||||
|
||||
Assert.Equal (1, view.OnAcceptedCount);
|
||||
|
||||
Assert.Equal (1, view.AcceptedCount);
|
||||
|
||||
Assert.False (view.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accept_Command_Handle_OnAccept_NoEvent ()
|
||||
{
|
||||
var view = new ViewEventTester ();
|
||||
Assert.False (view.HasFocus);
|
||||
|
||||
view.HandleOnAccepted = true;
|
||||
Assert.True (view.InvokeCommand (Command.Accept));
|
||||
|
||||
Assert.Equal (1, view.OnAcceptedCount);
|
||||
|
||||
Assert.Equal (0, view.AcceptedCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accept_Handle_Event_OnAccept_Returns_True ()
|
||||
{
|
||||
var view = new View ();
|
||||
var acceptInvoked = false;
|
||||
|
||||
view.Accepting += ViewOnAccept;
|
||||
|
||||
bool? ret = view.InvokeCommand (Command.Accept);
|
||||
Assert.True (ret);
|
||||
Assert.True (acceptInvoked);
|
||||
|
||||
return;
|
||||
|
||||
void ViewOnAccept (object sender, CommandEventArgs e)
|
||||
{
|
||||
acceptInvoked = true;
|
||||
e.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accept_Command_Invokes_Accept_Event ()
|
||||
{
|
||||
var view = new View ();
|
||||
var accepted = false;
|
||||
|
||||
view.Accepting += ViewOnAccept;
|
||||
|
||||
view.InvokeCommand (Command.Accept);
|
||||
Assert.True (accepted);
|
||||
|
||||
return;
|
||||
|
||||
void ViewOnAccept (object sender, CommandEventArgs e) { accepted = true; }
|
||||
}
|
||||
|
||||
// Accept on subview should bubble up to parent
|
||||
[Fact]
|
||||
public void Accept_Command_Bubbles_Up_To_SuperView ()
|
||||
{
|
||||
var view = new ViewEventTester { Id = "view" };
|
||||
var subview = new ViewEventTester { Id = "subview" };
|
||||
view.Add (subview);
|
||||
|
||||
subview.InvokeCommand (Command.Accept);
|
||||
Assert.Equal (1, subview.OnAcceptedCount);
|
||||
Assert.Equal (1, view.OnAcceptedCount);
|
||||
|
||||
subview.HandleOnAccepted = true;
|
||||
subview.InvokeCommand (Command.Accept);
|
||||
Assert.Equal (2, subview.OnAcceptedCount);
|
||||
Assert.Equal (1, view.OnAcceptedCount);
|
||||
|
||||
subview.HandleOnAccepted = false;
|
||||
subview.HandleAccepted = true;
|
||||
subview.InvokeCommand (Command.Accept);
|
||||
Assert.Equal (3, subview.OnAcceptedCount);
|
||||
Assert.Equal (1, view.OnAcceptedCount);
|
||||
|
||||
// Add a super view to test deeper hierarchy
|
||||
var superView = new ViewEventTester { Id = "superView" };
|
||||
superView.Add (view);
|
||||
|
||||
subview.InvokeCommand (Command.Accept);
|
||||
Assert.Equal (4, subview.OnAcceptedCount);
|
||||
Assert.Equal (1, view.OnAcceptedCount);
|
||||
Assert.Equal (0, superView.OnAcceptedCount);
|
||||
|
||||
subview.HandleAccepted = false;
|
||||
subview.InvokeCommand (Command.Accept);
|
||||
Assert.Equal (5, subview.OnAcceptedCount);
|
||||
Assert.Equal (2, view.OnAcceptedCount);
|
||||
Assert.Equal (1, superView.OnAcceptedCount);
|
||||
|
||||
view.HandleAccepted = true;
|
||||
subview.InvokeCommand (Command.Accept);
|
||||
Assert.Equal (6, subview.OnAcceptedCount);
|
||||
Assert.Equal (3, view.OnAcceptedCount);
|
||||
Assert.Equal (1, superView.OnAcceptedCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MouseClick_Does_Not_Invoke_Accept_Command ()
|
||||
{
|
||||
var view = new ViewEventTester ();
|
||||
view.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked, Position = Point.Empty, View = view });
|
||||
|
||||
Assert.Equal (0, view.OnAcceptedCount);
|
||||
}
|
||||
|
||||
// See https://github.com/gui-cs/Terminal.Gui/issues/3913
|
||||
[Fact]
|
||||
public void Button_IsDefault_Raises_Accepted_Correctly ()
|
||||
{
|
||||
int A_AcceptedCount = 0;
|
||||
bool A_CancelAccepting = false;
|
||||
|
||||
int B_AcceptedCount = 0;
|
||||
bool B_CancelAccepting = false;
|
||||
|
||||
var w = new Window ()
|
||||
{
|
||||
BorderStyle = LineStyle.None,
|
||||
Width = 10,
|
||||
Height = 10
|
||||
};
|
||||
|
||||
var btnA = new Button ()
|
||||
{
|
||||
Width = 3,
|
||||
IsDefault = true
|
||||
};
|
||||
btnA.Accepting += (s, e) =>
|
||||
{
|
||||
A_AcceptedCount++;
|
||||
e.Cancel = A_CancelAccepting;
|
||||
};
|
||||
|
||||
var btnB = new Button ()
|
||||
{
|
||||
Width = 3,
|
||||
X = Pos.Right (btnA)
|
||||
};
|
||||
|
||||
btnB.Accepting += (s, e) =>
|
||||
{
|
||||
B_AcceptedCount++;
|
||||
e.Cancel = B_CancelAccepting;
|
||||
};
|
||||
w.Add (btnA, btnB);
|
||||
|
||||
w.LayoutSubviews ();
|
||||
|
||||
Application.Top = w;
|
||||
Application.TopLevels.Push(w);
|
||||
Assert.Same (Application.Top, w);
|
||||
|
||||
// Click button 2
|
||||
var btn2Frame = btnB.FrameToScreen ();
|
||||
|
||||
Application.RaiseMouseEvent (
|
||||
new MouseEventArgs ()
|
||||
{
|
||||
ScreenPosition = btn2Frame.Location,
|
||||
Flags = MouseFlags.Button1Clicked
|
||||
});
|
||||
|
||||
// Button A should have been accepted because B didn't cancel and A IsDefault
|
||||
Assert.Equal (1, A_AcceptedCount);
|
||||
Assert.Equal (1, B_AcceptedCount);
|
||||
|
||||
B_CancelAccepting = true;
|
||||
Application.RaiseMouseEvent (
|
||||
new MouseEventArgs ()
|
||||
{
|
||||
ScreenPosition = btn2Frame.Location,
|
||||
Flags = MouseFlags.Button1Clicked
|
||||
});
|
||||
|
||||
// Button A (IsDefault) should NOT have been accepted because B canceled
|
||||
Assert.Equal (1, A_AcceptedCount);
|
||||
Assert.Equal (2, B_AcceptedCount);
|
||||
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
// See: https://github.com/gui-cs/Terminal.Gui/issues/3905
|
||||
[Fact]
|
||||
public void Button_CanFocus_False_Raises_Accepted_Correctly ()
|
||||
{
|
||||
int wAcceptedCount = 0;
|
||||
bool wCancelAccepting = false;
|
||||
var w = new Window ()
|
||||
{
|
||||
Title = "Window",
|
||||
BorderStyle = LineStyle.None,
|
||||
Width = 10,
|
||||
Height = 10
|
||||
};
|
||||
|
||||
w.Accepting += (s, e) =>
|
||||
{
|
||||
wAcceptedCount++;
|
||||
e.Cancel = wCancelAccepting;
|
||||
};
|
||||
|
||||
int btnAcceptedCount = 0;
|
||||
bool btnCancelAccepting = false;
|
||||
var btn = new Button ()
|
||||
{
|
||||
Title = "Button",
|
||||
Width = 3,
|
||||
IsDefault = true,
|
||||
};
|
||||
btn.CanFocus = true;
|
||||
|
||||
btn.Accepting += (s, e) =>
|
||||
{
|
||||
btnAcceptedCount++;
|
||||
e.Cancel = btnCancelAccepting;
|
||||
};
|
||||
|
||||
w.Add (btn);
|
||||
|
||||
|
||||
Application.Top = w;
|
||||
Application.TopLevels.Push (w);
|
||||
Assert.Same (Application.Top, w);
|
||||
|
||||
w.LayoutSubviews ();
|
||||
|
||||
// Click button just like a driver would
|
||||
var btnFrame = btn.FrameToScreen ();
|
||||
Application.RaiseMouseEvent (
|
||||
new MouseEventArgs ()
|
||||
{
|
||||
ScreenPosition = btnFrame.Location,
|
||||
Flags = MouseFlags.Button1Pressed
|
||||
});
|
||||
|
||||
Application.RaiseMouseEvent (
|
||||
new MouseEventArgs ()
|
||||
{
|
||||
ScreenPosition = btnFrame.Location,
|
||||
Flags = MouseFlags.Button1Released
|
||||
});
|
||||
|
||||
Application.RaiseMouseEvent (
|
||||
new MouseEventArgs ()
|
||||
{
|
||||
ScreenPosition = btnFrame.Location,
|
||||
Flags = MouseFlags.Button1Clicked
|
||||
});
|
||||
|
||||
Assert.Equal (1, btnAcceptedCount);
|
||||
Assert.Equal (2, wAcceptedCount);
|
||||
|
||||
Application.ResetState (true);
|
||||
}
|
||||
|
||||
#endregion OnAccept/Accept tests
|
||||
|
||||
#region OnSelect/Select tests
|
||||
|
||||
[Theory]
|
||||
[CombinatorialData]
|
||||
public void Select_Command_Raises_SetsFocus (bool canFocus)
|
||||
{
|
||||
var view = new ViewEventTester
|
||||
{
|
||||
CanFocus = canFocus
|
||||
};
|
||||
|
||||
Assert.Equal (canFocus, view.CanFocus);
|
||||
Assert.False (view.HasFocus);
|
||||
|
||||
view.InvokeCommand (Command.Select);
|
||||
|
||||
Assert.Equal (1, view.OnSelectingCount);
|
||||
|
||||
Assert.Equal (1, view.SelectingCount);
|
||||
|
||||
Assert.Equal (canFocus, view.HasFocus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Select_Command_Handle_OnSelecting_NoEvent ()
|
||||
{
|
||||
var view = new ViewEventTester ();
|
||||
Assert.False (view.HasFocus);
|
||||
|
||||
view.HandleOnSelecting = true;
|
||||
Assert.True (view.InvokeCommand (Command.Select));
|
||||
|
||||
Assert.Equal (1, view.OnSelectingCount);
|
||||
|
||||
Assert.Equal (0, view.SelectingCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Select_Handle_Event_OnSelecting_Returns_True ()
|
||||
{
|
||||
var view = new View ();
|
||||
var SelectingInvoked = false;
|
||||
|
||||
view.Selecting += ViewOnSelect;
|
||||
|
||||
bool? ret = view.InvokeCommand (Command.Select);
|
||||
Assert.True (ret);
|
||||
Assert.True (SelectingInvoked);
|
||||
|
||||
return;
|
||||
|
||||
void ViewOnSelect (object sender, CommandEventArgs e)
|
||||
{
|
||||
SelectingInvoked = true;
|
||||
e.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Select_Command_Invokes_Selecting_Event ()
|
||||
{
|
||||
var view = new View ();
|
||||
var selecting = false;
|
||||
|
||||
view.Selecting += ViewOnSelecting;
|
||||
|
||||
view.InvokeCommand (Command.Select);
|
||||
Assert.True (selecting);
|
||||
|
||||
return;
|
||||
|
||||
void ViewOnSelecting (object sender, CommandEventArgs e) { selecting = true; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MouseClick_Invokes_Select_Command ()
|
||||
{
|
||||
var view = new ViewEventTester ();
|
||||
view.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked, Position = Point.Empty, View = view });
|
||||
|
||||
Assert.Equal (1, view.OnSelectingCount);
|
||||
}
|
||||
|
||||
#endregion OnSelect/Select tests
|
||||
|
||||
#region OnHotKey/HotKey tests
|
||||
|
||||
[Fact]
|
||||
public void HotKey_Command_SetsFocus ()
|
||||
{
|
||||
var view = new View ();
|
||||
|
||||
view.CanFocus = true;
|
||||
Assert.False (view.HasFocus);
|
||||
view.InvokeCommand (Command.HotKey);
|
||||
Assert.True (view.HasFocus);
|
||||
}
|
||||
|
||||
#endregion OnHotKey/HotKey tests
|
||||
|
||||
public class ViewEventTester : View
|
||||
{
|
||||
public ViewEventTester ()
|
||||
{
|
||||
CanFocus = true;
|
||||
|
||||
Accepting += (s, a) =>
|
||||
{
|
||||
a.Cancel = HandleAccepted;
|
||||
AcceptedCount++;
|
||||
};
|
||||
|
||||
HandlingHotKey += (s, a) =>
|
||||
{
|
||||
a.Cancel = HandleHandlingHotKey;
|
||||
HandlingHotKeyCount++;
|
||||
};
|
||||
|
||||
Selecting += (s, a) =>
|
||||
{
|
||||
a.Cancel = HandleSelecting;
|
||||
SelectingCount++;
|
||||
};
|
||||
}
|
||||
|
||||
public int OnAcceptedCount { get; set; }
|
||||
public int AcceptedCount { get; set; }
|
||||
public bool HandleOnAccepted { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool OnAccepting (CommandEventArgs args)
|
||||
{
|
||||
OnAcceptedCount++;
|
||||
|
||||
return HandleOnAccepted;
|
||||
}
|
||||
|
||||
public bool HandleAccepted { get; set; }
|
||||
|
||||
public int OnHandlingHotKeyCount { get; set; }
|
||||
public int HandlingHotKeyCount { get; set; }
|
||||
public bool HandleOnHandlingHotKey { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool OnHandlingHotKey (CommandEventArgs args)
|
||||
{
|
||||
OnHandlingHotKeyCount++;
|
||||
|
||||
return HandleOnHandlingHotKey;
|
||||
}
|
||||
|
||||
public bool HandleHandlingHotKey { get; set; }
|
||||
|
||||
public int OnSelectingCount { get; set; }
|
||||
public int SelectingCount { get; set; }
|
||||
public bool HandleOnSelecting { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool OnSelecting (CommandEventArgs args)
|
||||
{
|
||||
OnSelectingCount++;
|
||||
|
||||
return HandleOnSelecting;
|
||||
}
|
||||
|
||||
public bool HandleSelecting { get; set; }
|
||||
}
|
||||
}
|
||||
539
Tests/UnitTests/View/ViewTests.cs
Normal file
539
Tests/UnitTests/View/ViewTests.cs
Normal file
@@ -0,0 +1,539 @@
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using UnitTests;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
public class ViewTests
|
||||
{
|
||||
private ITestOutputHelper _output;
|
||||
|
||||
public ViewTests (ITestOutputHelper output)
|
||||
{
|
||||
output = output;
|
||||
#if DEBUG_IDISPOSABLE
|
||||
View.DebugIDisposable = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Generic lifetime (IDisposable) tests
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void Dispose_Works ()
|
||||
{
|
||||
var r = new View ();
|
||||
#if DEBUG_IDISPOSABLE
|
||||
Assert.Equal (4, View.Instances.Count);
|
||||
#endif
|
||||
|
||||
r.Dispose ();
|
||||
#if DEBUG_IDISPOSABLE
|
||||
Assert.Empty (View.Instances);
|
||||
#endif
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Disposing_Event_Notify_All_Subscribers_On_The_First_Container ()
|
||||
{
|
||||
#if DEBUG_IDISPOSABLE
|
||||
// Only clear before because need to test after assert
|
||||
View.Instances.Clear ();
|
||||
#endif
|
||||
|
||||
var container1 = new View { Id = "Container1" };
|
||||
var count = 0;
|
||||
|
||||
var view = new View { Id = "View" };
|
||||
view.Disposing += View_Disposing;
|
||||
container1.Add (view);
|
||||
Assert.Equal (container1, view.SuperView);
|
||||
|
||||
void View_Disposing (object sender, EventArgs e)
|
||||
{
|
||||
count++;
|
||||
Assert.Equal (view, sender);
|
||||
container1.Remove ((View)sender);
|
||||
}
|
||||
|
||||
Assert.Single (container1.Subviews);
|
||||
|
||||
var container2 = new View { Id = "Container2" };
|
||||
|
||||
container2.Add (view);
|
||||
Assert.Equal (container2, view.SuperView);
|
||||
Assert.Equal (container1.Subviews.Count, container2.Subviews.Count);
|
||||
container2.Dispose ();
|
||||
|
||||
Assert.Empty (container1.Subviews);
|
||||
Assert.Empty (container2.Subviews);
|
||||
Assert.Equal (1, count);
|
||||
Assert.Null (view.SuperView);
|
||||
|
||||
container1.Dispose ();
|
||||
|
||||
#if DEBUG_IDISPOSABLE
|
||||
Assert.Empty (View.Instances);
|
||||
#endif
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Disposing_Event_Notify_All_Subscribers_On_The_Second_Container ()
|
||||
{
|
||||
#if DEBUG_IDISPOSABLE
|
||||
// Only clear before because need to test after assert
|
||||
View.Instances.Clear ();
|
||||
#endif
|
||||
|
||||
var container1 = new View { Id = "Container1" };
|
||||
|
||||
var view = new View { Id = "View" };
|
||||
container1.Add (view);
|
||||
Assert.Equal (container1, view.SuperView);
|
||||
Assert.Single (container1.Subviews);
|
||||
|
||||
var container2 = new View { Id = "Container2" };
|
||||
var count = 0;
|
||||
|
||||
view.Disposing += View_Disposing;
|
||||
container2.Add (view);
|
||||
Assert.Equal (container2, view.SuperView);
|
||||
|
||||
void View_Disposing (object sender, EventArgs e)
|
||||
{
|
||||
count++;
|
||||
Assert.Equal (view, sender);
|
||||
container2.Remove ((View)sender);
|
||||
}
|
||||
|
||||
Assert.Equal (container1.Subviews.Count, container2.Subviews.Count);
|
||||
container1.Dispose ();
|
||||
|
||||
Assert.Empty (container1.Subviews);
|
||||
Assert.Empty (container2.Subviews);
|
||||
Assert.Equal (1, count);
|
||||
Assert.Null (view.SuperView);
|
||||
|
||||
container2.Dispose ();
|
||||
|
||||
#if DEBUG_IDISPOSABLE
|
||||
Assert.Empty (View.Instances);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Not_Notifying_Dispose ()
|
||||
{
|
||||
// Only clear before because need to test after assert
|
||||
#if DEBUG_IDISPOSABLE
|
||||
View.Instances.Clear ();
|
||||
#endif
|
||||
var container1 = new View { Id = "Container1" };
|
||||
|
||||
var view = new View { Id = "View" };
|
||||
container1.Add (view);
|
||||
Assert.Equal (container1, view.SuperView);
|
||||
|
||||
Assert.Single (container1.Subviews);
|
||||
|
||||
var container2 = new View { Id = "Container2" };
|
||||
|
||||
container2.Add (view);
|
||||
Assert.Equal (container2, view.SuperView);
|
||||
Assert.Equal (container1.Subviews.Count, container2.Subviews.Count);
|
||||
container1.Dispose ();
|
||||
|
||||
Assert.Empty (container1.Subviews);
|
||||
Assert.NotEmpty (container2.Subviews);
|
||||
Assert.Single (container2.Subviews);
|
||||
Assert.Null (view.SuperView);
|
||||
|
||||
// Trying access disposed properties
|
||||
#if DEBUG_IDISPOSABLE
|
||||
Assert.True (container2.Subviews [0].WasDisposed);
|
||||
#endif
|
||||
Assert.False (container2.Subviews [0].CanFocus);
|
||||
Assert.Null (container2.Subviews [0].Margin);
|
||||
Assert.Null (container2.Subviews [0].Border);
|
||||
Assert.Null (container2.Subviews [0].Padding);
|
||||
Assert.Null (view.SuperView);
|
||||
|
||||
container2.Dispose ();
|
||||
|
||||
#if DEBUG_IDISPOSABLE
|
||||
Assert.Empty (View.Instances);
|
||||
#endif
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void Dispose_View ()
|
||||
{
|
||||
var view = new View ();
|
||||
Assert.NotNull (view.Margin);
|
||||
Assert.NotNull (view.Border);
|
||||
Assert.NotNull (view.Padding);
|
||||
|
||||
#if DEBUG_IDISPOSABLE
|
||||
Assert.Equal (4, View.Instances.Count);
|
||||
#endif
|
||||
|
||||
view.Dispose ();
|
||||
Assert.Null (view.Margin);
|
||||
Assert.Null (view.Border);
|
||||
Assert.Null (view.Padding);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Internal_Tests ()
|
||||
{
|
||||
var rect = new Rectangle (1, 1, 10, 1);
|
||||
var view = new View { Frame = rect };
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void New_Initializes ()
|
||||
{
|
||||
// Parameterless
|
||||
var r = new View ();
|
||||
Assert.NotNull (r);
|
||||
Assert.True (r.Enabled);
|
||||
Assert.True (r.Visible);
|
||||
|
||||
Assert.Equal ($"View(){r.Viewport}", r.ToString ());
|
||||
Assert.False (r.CanFocus);
|
||||
Assert.False (r.HasFocus);
|
||||
Assert.Equal (new (0, 0, 0, 0), r.Viewport);
|
||||
Assert.Equal (new (0, 0, 0, 0), r.Frame);
|
||||
Assert.Null (r.Focused);
|
||||
Assert.Null (r.ColorScheme);
|
||||
Assert.Equal (0, r.Width);
|
||||
Assert.Equal (0, r.Height);
|
||||
Assert.Equal (0, r.X);
|
||||
Assert.Equal (0, r.Y);
|
||||
Assert.False (r.IsCurrentTop);
|
||||
Assert.Empty (r.Id);
|
||||
Assert.Empty (r.Subviews);
|
||||
Assert.False (r.WantContinuousButtonPressed);
|
||||
Assert.False (r.WantMousePositionReports);
|
||||
Assert.Null (r.SuperView);
|
||||
Assert.Null (r.MostFocused);
|
||||
Assert.Equal (TextDirection.LeftRight_TopBottom, r.TextDirection);
|
||||
r.Dispose ();
|
||||
|
||||
// Empty Rect
|
||||
r = new () { Frame = Rectangle.Empty };
|
||||
Assert.NotNull (r);
|
||||
Assert.Equal ($"View(){r.Viewport}", r.ToString ());
|
||||
Assert.False (r.CanFocus);
|
||||
Assert.False (r.HasFocus);
|
||||
Assert.Equal (new (0, 0, 0, 0), r.Viewport);
|
||||
Assert.Equal (new (0, 0, 0, 0), r.Frame);
|
||||
Assert.Null (r.Focused);
|
||||
Assert.Null (r.ColorScheme);
|
||||
Assert.Equal (0, r.Width);
|
||||
Assert.Equal (0, r.Height);
|
||||
Assert.Equal (0, r.X);
|
||||
Assert.Equal (0, r.Y);
|
||||
Assert.False (r.IsCurrentTop);
|
||||
Assert.Empty (r.Id);
|
||||
Assert.Empty (r.Subviews);
|
||||
Assert.False (r.WantContinuousButtonPressed);
|
||||
Assert.False (r.WantMousePositionReports);
|
||||
Assert.Null (r.SuperView);
|
||||
Assert.Null (r.MostFocused);
|
||||
Assert.Equal (TextDirection.LeftRight_TopBottom, r.TextDirection);
|
||||
r.Dispose ();
|
||||
|
||||
// Rect with values
|
||||
r = new () { Frame = new (1, 2, 3, 4) };
|
||||
Assert.NotNull (r);
|
||||
Assert.Equal ($"View(){r.Frame}", r.ToString ());
|
||||
Assert.False (r.CanFocus);
|
||||
Assert.False (r.HasFocus);
|
||||
Assert.Equal (new (0, 0, 3, 4), r.Viewport);
|
||||
Assert.Equal (new (1, 2, 3, 4), r.Frame);
|
||||
Assert.Null (r.Focused);
|
||||
Assert.Null (r.ColorScheme);
|
||||
Assert.Equal (3, r.Width);
|
||||
Assert.Equal (4, r.Height);
|
||||
Assert.Equal (1, r.X);
|
||||
Assert.Equal (2, r.Y);
|
||||
Assert.False (r.IsCurrentTop);
|
||||
Assert.Empty (r.Id);
|
||||
Assert.Empty (r.Subviews);
|
||||
Assert.False (r.WantContinuousButtonPressed);
|
||||
Assert.False (r.WantMousePositionReports);
|
||||
Assert.Null (r.SuperView);
|
||||
Assert.Null (r.MostFocused);
|
||||
Assert.Equal (TextDirection.LeftRight_TopBottom, r.TextDirection);
|
||||
r.Dispose ();
|
||||
|
||||
// Initializes a view with a vertical direction
|
||||
r = new ()
|
||||
{
|
||||
Text = "Vertical View",
|
||||
TextDirection = TextDirection.TopBottom_LeftRight,
|
||||
Width = Dim.Auto (),
|
||||
Height = Dim.Auto ()
|
||||
};
|
||||
r.TextFormatter.WordWrap = false;
|
||||
Assert.NotNull (r);
|
||||
|
||||
r.BeginInit ();
|
||||
r.EndInit ();
|
||||
Assert.False (r.CanFocus);
|
||||
Assert.False (r.HasFocus);
|
||||
Assert.Equal (new (0, 0, 1, 13), r.Viewport);
|
||||
Assert.Equal (new (0, 0, 1, 13), r.Frame);
|
||||
Assert.Null (r.Focused);
|
||||
Assert.Null (r.ColorScheme);
|
||||
Assert.False (r.IsCurrentTop);
|
||||
#if DEBUG
|
||||
Assert.Equal ("Vertical View", r.Id);
|
||||
#else
|
||||
Assert.Equal (string.Empty, r.Id);
|
||||
#endif
|
||||
Assert.Empty (r.Subviews);
|
||||
Assert.False (r.WantContinuousButtonPressed);
|
||||
Assert.False (r.WantMousePositionReports);
|
||||
Assert.Null (r.SuperView);
|
||||
Assert.Null (r.MostFocused);
|
||||
Assert.Equal (TextDirection.TopBottom_LeftRight, r.TextDirection);
|
||||
r.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void New_Methods_Return_False ()
|
||||
{
|
||||
var r = new View ();
|
||||
|
||||
Assert.False (r.NewKeyDownEvent (Key.Empty));
|
||||
|
||||
//Assert.False (r.OnKeyDown (new KeyEventArgs () { Key = Key.Unknown }));
|
||||
Assert.False (r.NewKeyUpEvent (Key.Empty));
|
||||
Assert.False (r.NewMouseEvent (new () { Flags = MouseFlags.AllEvents }));
|
||||
|
||||
r.Dispose ();
|
||||
|
||||
// TODO: Add more
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Test_Nested_Views_With_Height_Equal_To_One ()
|
||||
{
|
||||
var v = new View { Width = 11, Height = 3, ColorScheme = new () };
|
||||
|
||||
var top = new View { Width = Dim.Fill (), Height = 1 };
|
||||
var bottom = new View { Width = Dim.Fill (), Height = 1, Y = 2 };
|
||||
|
||||
top.Add (new Label { Text = "111" });
|
||||
v.Add (top);
|
||||
v.Add (new LineView (Orientation.Horizontal) { Y = 1 });
|
||||
bottom.Add (new Label { Text = "222" });
|
||||
v.Add (bottom);
|
||||
|
||||
v.BeginInit ();
|
||||
v.EndInit ();
|
||||
v.LayoutSubviews ();
|
||||
v.Draw ();
|
||||
|
||||
var looksLike =
|
||||
@"
|
||||
111
|
||||
───────────
|
||||
222";
|
||||
DriverAssert.AssertDriverContentsAre (looksLike, _output);
|
||||
v.Dispose ();
|
||||
top.Dispose ();
|
||||
bottom.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[TestRespondersDisposed]
|
||||
public void View_With_No_Difference_Between_An_Object_Initializer_Compute_And_A_Absolute ()
|
||||
{
|
||||
// Object Initializer Computed
|
||||
var view = new View { X = 1, Y = 2, Width = 3, Height = 4 };
|
||||
|
||||
// Object Initializer Absolute
|
||||
var super = new View { Frame = new (0, 0, 10, 10) };
|
||||
super.Add (view);
|
||||
super.BeginInit ();
|
||||
super.EndInit ();
|
||||
super.LayoutSubviews ();
|
||||
|
||||
Assert.Equal (1, view.X);
|
||||
Assert.Equal (2, view.Y);
|
||||
Assert.Equal (3, view.Width);
|
||||
Assert.Equal (4, view.Height);
|
||||
Assert.False (view.Frame.IsEmpty);
|
||||
Assert.Equal (new (1, 2, 3, 4), view.Frame);
|
||||
Assert.False (view.Viewport.IsEmpty);
|
||||
Assert.Equal (new (0, 0, 3, 4), view.Viewport);
|
||||
|
||||
view.LayoutSubviews ();
|
||||
|
||||
Assert.Equal (1, view.X);
|
||||
Assert.Equal (2, view.Y);
|
||||
Assert.Equal (3, view.Width);
|
||||
Assert.Equal (4, view.Height);
|
||||
Assert.False (view.Frame.IsEmpty);
|
||||
Assert.False (view.Viewport.IsEmpty);
|
||||
super.Dispose ();
|
||||
|
||||
#if DEBUG_IDISPOSABLE
|
||||
Assert.Empty (View.Instances);
|
||||
#endif
|
||||
|
||||
// Default Constructor
|
||||
view = new ();
|
||||
Assert.Equal (0, view.X);
|
||||
Assert.Equal (0, view.Y);
|
||||
Assert.Equal (0, view.Width);
|
||||
Assert.Equal (0, view.Height);
|
||||
Assert.True (view.Frame.IsEmpty);
|
||||
Assert.True (view.Viewport.IsEmpty);
|
||||
view.Dispose ();
|
||||
|
||||
// Object Initializer
|
||||
view = new () { X = 1, Y = 2, Text = "" };
|
||||
Assert.Equal (1, view.X);
|
||||
Assert.Equal (2, view.Y);
|
||||
Assert.Equal (0, view.Width);
|
||||
Assert.Equal (0, view.Height);
|
||||
Assert.False (view.Frame.IsEmpty);
|
||||
Assert.True (view.Viewport.IsEmpty);
|
||||
view.Dispose ();
|
||||
|
||||
// Default Constructor and post assignment equivalent to Object Initializer
|
||||
view = new ();
|
||||
view.X = 1;
|
||||
view.Y = 2;
|
||||
view.Width = 3;
|
||||
view.Height = 4;
|
||||
super = new () { Frame = new (0, 0, 10, 10) };
|
||||
super.Add (view);
|
||||
super.BeginInit ();
|
||||
super.EndInit ();
|
||||
super.LayoutSubviews ();
|
||||
Assert.Equal (1, view.X);
|
||||
Assert.Equal (2, view.Y);
|
||||
Assert.Equal (3, view.Width);
|
||||
Assert.Equal (4, view.Height);
|
||||
Assert.False (view.Frame.IsEmpty);
|
||||
Assert.Equal (new (1, 2, 3, 4), view.Frame);
|
||||
Assert.False (view.Viewport.IsEmpty);
|
||||
Assert.Equal (new (0, 0, 3, 4), view.Viewport);
|
||||
super.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Visible_Clear_The_View_Output ()
|
||||
{
|
||||
var view = new View { Text = "Testing visibility." }; // use View, not Label to avoid AutoSize == true
|
||||
|
||||
Assert.Equal (0, view.Frame.Width);
|
||||
Assert.Equal (0, view.Height);
|
||||
var win = new Window ();
|
||||
win.Add (view);
|
||||
Toplevel top = new ();
|
||||
top.Add (win);
|
||||
RunState rs = Application.Begin (top);
|
||||
|
||||
view.Width = Dim.Auto ();
|
||||
view.Height = Dim.Auto ();
|
||||
Application.RunIteration (ref rs);
|
||||
Assert.Equal ("Testing visibility.".Length, view.Frame.Width);
|
||||
Assert.True (view.Visible);
|
||||
((FakeDriver)Application.Driver!).SetBufferSize (30, 5);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌────────────────────────────┐
|
||||
│Testing visibility. │
|
||||
│ │
|
||||
│ │
|
||||
└────────────────────────────┘
|
||||
",
|
||||
_output
|
||||
);
|
||||
|
||||
view.Visible = false;
|
||||
|
||||
var firstIteration = false;
|
||||
Application.RunIteration (ref rs, firstIteration);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
┌────────────────────────────┐
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└────────────────────────────┘
|
||||
",
|
||||
_output
|
||||
);
|
||||
Application.End (rs);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Visible_Sets_Also_Sets_Subviews ()
|
||||
{
|
||||
var button = new Button { Text = "Click Me" };
|
||||
var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
win.Add (button);
|
||||
Toplevel top = new ();
|
||||
top.Add (win);
|
||||
|
||||
var iterations = 0;
|
||||
|
||||
Application.Iteration += (s, a) =>
|
||||
{
|
||||
iterations++;
|
||||
|
||||
Assert.True (button.Visible);
|
||||
Assert.True (button.CanFocus);
|
||||
Assert.True (button.HasFocus);
|
||||
Assert.True (win.Visible);
|
||||
Assert.True (win.CanFocus);
|
||||
Assert.True (win.HasFocus);
|
||||
|
||||
win.Visible = false;
|
||||
Assert.True (button.Visible);
|
||||
Assert.True (button.CanFocus);
|
||||
Assert.False (button.HasFocus);
|
||||
Assert.False (win.Visible);
|
||||
Assert.True (win.CanFocus);
|
||||
Assert.False (win.HasFocus);
|
||||
|
||||
button.SetFocus ();
|
||||
Assert.False (button.HasFocus);
|
||||
Assert.False (win.HasFocus);
|
||||
|
||||
win.SetFocus ();
|
||||
Assert.False (button.HasFocus);
|
||||
Assert.False (win.HasFocus);
|
||||
|
||||
win.Visible = true;
|
||||
Assert.True (button.HasFocus);
|
||||
Assert.True (win.HasFocus);
|
||||
|
||||
Application.RequestStop ();
|
||||
};
|
||||
|
||||
Application.Run (top);
|
||||
top.Dispose ();
|
||||
Assert.Equal (1, iterations);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user