From 42f5b160220a0591ae654a95ded37ec2e2523d38 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 15 Aug 2023 11:20:19 +0100 Subject: [PATCH 1/6] Prevents throw exception if Border is null. --- Terminal.Gui/Core/Border.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Terminal.Gui/Core/Border.cs b/Terminal.Gui/Core/Border.cs index abf4438ce..08e4b614c 100644 --- a/Terminal.Gui/Core/Border.cs +++ b/Terminal.Gui/Core/Border.cs @@ -284,7 +284,7 @@ namespace Terminal.Gui { /// public override void OnCanFocusChanged () { - if (Border.Child != null) { + if (Border?.Child != null) { Border.Child.CanFocus = CanFocus; } base.OnCanFocusChanged (); From 65851ad5271c577324f974c6123a00eb5c510952 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 15 Aug 2023 13:07:06 +0100 Subject: [PATCH 2/6] Fixes #2810. Pressing Alt key on a Toplevel with only a MenuBar throws System.InvalidOperationException. --- Terminal.Gui/Core/Toplevel.cs | 6 ++++++ UnitTests/TopLevels/ToplevelTests.cs | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/Terminal.Gui/Core/Toplevel.cs b/Terminal.Gui/Core/Toplevel.cs index f06345152..23fb0df1f 100644 --- a/Terminal.Gui/Core/Toplevel.cs +++ b/Terminal.Gui/Core/Toplevel.cs @@ -552,6 +552,9 @@ namespace Terminal.Gui { /// public override void Add (View view) { + if (!CanFocus) { + CanFocus = true; + } AddMenuStatusBar (view); base.Add (view); } @@ -569,6 +572,9 @@ namespace Terminal.Gui { /// public override void Remove (View view) { + if (InternalSubviews.Count < 1) { + CanFocus = false; + } if (this is Toplevel toplevel && toplevel.MenuBar != null) { RemoveMenuStatusBar (view); } diff --git a/UnitTests/TopLevels/ToplevelTests.cs b/UnitTests/TopLevels/ToplevelTests.cs index 120ed6b35..c76e0ad04 100644 --- a/UnitTests/TopLevels/ToplevelTests.cs +++ b/UnitTests/TopLevels/ToplevelTests.cs @@ -1031,5 +1031,22 @@ namespace Terminal.Gui.TopLevelTests { Application.Driver.GetCursorVisibility (out cursor); Assert.Equal (CursorVisibility.Invisible, cursor); } + + [Fact, AutoInitShutdown] + public void Activating_MenuBar_By_Alt_Key_Does_Not_Throw () + { + var menu = new MenuBar (new MenuBarItem [] { + new MenuBarItem ("Child", new MenuItem [] { + new MenuItem ("_Create Child", "", null) + }) + }); + var topChild = new Toplevel (); + topChild.Add (menu); + Application.Top.Add (topChild); + Application.Begin (Application.Top); + + var exception = Record.Exception (() => topChild.ProcessHotKey (new KeyEvent (Key.AltMask, new KeyModifiers { Alt = true }))); + Assert.Null (exception); + } } } \ No newline at end of file From aa4f9c108f679c9ac46be3c8e2f4b3f77aea2364 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 15 Aug 2023 14:46:24 +0100 Subject: [PATCH 3/6] Fix CanFocus being set to false on background after adding all the subviews. --- Terminal.Gui/Core/Toplevel.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Terminal.Gui/Core/Toplevel.cs b/Terminal.Gui/Core/Toplevel.cs index 23fb0df1f..3b4689a47 100644 --- a/Terminal.Gui/Core/Toplevel.cs +++ b/Terminal.Gui/Core/Toplevel.cs @@ -552,9 +552,7 @@ namespace Terminal.Gui { /// public override void Add (View view) { - if (!CanFocus) { - CanFocus = true; - } + CanFocus = true; AddMenuStatusBar (view); base.Add (view); } From de909b86cdbc95c025f80c149f65580f68b1ff83 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 15 Aug 2023 16:29:07 +0100 Subject: [PATCH 4/6] Fix a bug where a focused view was receiving the Leave event. --- Terminal.Gui/Core/Toplevel.cs | 3 --- Terminal.Gui/Core/Window.cs | 3 --- UnitTests/TopLevels/ToplevelTests.cs | 3 ++- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Terminal.Gui/Core/Toplevel.cs b/Terminal.Gui/Core/Toplevel.cs index 3b4689a47..04f8995e8 100644 --- a/Terminal.Gui/Core/Toplevel.cs +++ b/Terminal.Gui/Core/Toplevel.cs @@ -570,9 +570,6 @@ namespace Terminal.Gui { /// public override void Remove (View view) { - if (InternalSubviews.Count < 1) { - CanFocus = false; - } if (this is Toplevel toplevel && toplevel.MenuBar != null) { RemoveMenuStatusBar (view); } diff --git a/Terminal.Gui/Core/Window.cs b/Terminal.Gui/Core/Window.cs index 53925f258..febb7517e 100644 --- a/Terminal.Gui/Core/Window.cs +++ b/Terminal.Gui/Core/Window.cs @@ -270,9 +270,6 @@ namespace Terminal.Gui { SetNeedsDisplay (); contentView.Remove (view); - if (contentView.InternalSubviews.Count < 1) { - CanFocus = false; - } RemoveMenuStatusBar (view); if (view != contentView && Focused == null) { FocusFirst (); diff --git a/UnitTests/TopLevels/ToplevelTests.cs b/UnitTests/TopLevels/ToplevelTests.cs index c76e0ad04..db3921d07 100644 --- a/UnitTests/TopLevels/ToplevelTests.cs +++ b/UnitTests/TopLevels/ToplevelTests.cs @@ -1009,7 +1009,8 @@ namespace Terminal.Gui.TopLevelTests { Application.End (rs); Assert.True (isEnter); - Assert.True (isLeave); // Leave event is now also invoked on Application.End allowing preform same output action + Assert.False (isLeave); // Leave event cannot be trigger because it v.Enter was performed and v is focused + Assert.True (v.HasFocus); } [Fact, AutoInitShutdown] From 4f635ffab480224a99e933df5b376c9cab553e19 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 15 Aug 2023 17:03:39 +0100 Subject: [PATCH 5/6] Add unit test to use the Enter and Leave events between two views on two toplevels. --- UnitTests/TopLevels/ToplevelTests.cs | 57 ++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/UnitTests/TopLevels/ToplevelTests.cs b/UnitTests/TopLevels/ToplevelTests.cs index db3921d07..10316b92e 100644 --- a/UnitTests/TopLevels/ToplevelTests.cs +++ b/UnitTests/TopLevels/ToplevelTests.cs @@ -1013,6 +1013,63 @@ namespace Terminal.Gui.TopLevelTests { Assert.True (v.HasFocus); } + [Fact, AutoInitShutdown] + public void OnEnter_OnLeave_Triggered_On_Application_Begin_End_With_More_Toplevels () + { + var isEnterTop = false; + var isLeaveTop = false; + var vt = new View (); + vt.Enter += (_) => isEnterTop = true; + vt.Leave += (_) => isLeaveTop = true; + var top = Application.Top; + top.Add (vt); + + Assert.False (vt.CanFocus); + var exception = Record.Exception (() => top.OnEnter (top)); + Assert.Null (exception); + exception = Record.Exception (() => top.OnLeave (top)); + Assert.Null (exception); + + vt.CanFocus = true; + Application.Begin (top); + + Assert.True (isEnterTop); + Assert.False (isLeaveTop); + + isEnterTop = false; + var isEnterDiag = false; + var isLeaveDiag = false; + var vd = new View (); + vd.Enter += (_) => isEnterDiag = true; + vd.Leave += (_) => isLeaveDiag = true; + var d = new Dialog (); + d.Add (vd); + + Assert.False (vd.CanFocus); + exception = Record.Exception (() => d.OnEnter (d)); + Assert.Null (exception); + exception = Record.Exception (() => d.OnLeave (d)); + Assert.Null (exception); + + vd.CanFocus = true; + var rs = Application.Begin (d); + + Assert.True (isEnterDiag); + Assert.False (isLeaveDiag); + Assert.False (isEnterTop); + Assert.True (isLeaveTop); + + isEnterDiag = false; + isLeaveTop = false; + Application.End (rs); + + Assert.False (isEnterDiag); + Assert.True (isLeaveDiag); + Assert.True (isEnterTop); + Assert.False (isLeaveTop); // Leave event cannot be trigger because it v.Enter was performed and v is focused + Assert.True (vt.HasFocus); + } + [Fact, AutoInitShutdown] public void PositionCursor_SetCursorVisibility_To_Invisible_If_Focused_Is_Null () { From 6e458559e259f48e59f18e773a8aa2c3f3dce421 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 15 Aug 2023 17:56:57 +0100 Subject: [PATCH 6/6] Finally the Enter and Leave events has the correct sequence. --- Terminal.Gui/Core/Application.cs | 3 +- Terminal.Gui/Core/View.cs | 3 -- UnitTests/TopLevels/ToplevelTests.cs | 51 +++++++++++++++++++++++----- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index 233b0c1fa..5d8cc1fdf 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -1053,7 +1053,8 @@ namespace Terminal.Gui { MdiTop.OnAllChildClosed (); } else { SetCurrentAsTop (); - Current.OnEnter (Current); + runState.Toplevel.OnLeave (Current); + Current.OnEnter (runState.Toplevel); } Refresh (); } diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index ed6439a8b..b2805320b 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -995,9 +995,6 @@ namespace Terminal.Gui { view.tabIndex = -1; SetNeedsLayout (); SetNeedsDisplay (); - if (subviews.Count < 1) { - CanFocus = false; - } foreach (var v in subviews) { if (v.Frame.IntersectsWith (touched)) view.SetNeedsDisplay (); diff --git a/UnitTests/TopLevels/ToplevelTests.cs b/UnitTests/TopLevels/ToplevelTests.cs index 10316b92e..89960d5ce 100644 --- a/UnitTests/TopLevels/ToplevelTests.cs +++ b/UnitTests/TopLevels/ToplevelTests.cs @@ -1016,12 +1016,31 @@ namespace Terminal.Gui.TopLevelTests { [Fact, AutoInitShutdown] public void OnEnter_OnLeave_Triggered_On_Application_Begin_End_With_More_Toplevels () { + var iterations = 0; + var steps = new int [5]; var isEnterTop = false; var isLeaveTop = false; var vt = new View (); - vt.Enter += (_) => isEnterTop = true; - vt.Leave += (_) => isLeaveTop = true; var top = Application.Top; + var diag = new Dialog (); + + vt.Enter += (e) => { + iterations++; + isEnterTop = true; + if (iterations == 1) { + steps [0] = iterations; + Assert.Null (e.View); + } else { + steps [4] = iterations; + Assert.Equal (diag, e.View); + } + }; + vt.Leave += (e) => { + iterations++; + steps [1] = iterations; + isLeaveTop = true; + Assert.Equal (diag, e.View); + }; top.Add (vt); Assert.False (vt.CanFocus); @@ -1040,19 +1059,28 @@ namespace Terminal.Gui.TopLevelTests { var isEnterDiag = false; var isLeaveDiag = false; var vd = new View (); - vd.Enter += (_) => isEnterDiag = true; - vd.Leave += (_) => isLeaveDiag = true; - var d = new Dialog (); - d.Add (vd); + vd.Enter += (e) => { + iterations++; + steps [2] = iterations; + isEnterDiag = true; + Assert.Null (e.View); + }; + vd.Leave += (e) => { + iterations++; + steps [3] = iterations; + isLeaveDiag = true; + Assert.Equal (top, e.View); + }; + diag.Add (vd); Assert.False (vd.CanFocus); - exception = Record.Exception (() => d.OnEnter (d)); + exception = Record.Exception (() => diag.OnEnter (diag)); Assert.Null (exception); - exception = Record.Exception (() => d.OnLeave (d)); + exception = Record.Exception (() => diag.OnLeave (diag)); Assert.Null (exception); vd.CanFocus = true; - var rs = Application.Begin (d); + var rs = Application.Begin (diag); Assert.True (isEnterDiag); Assert.False (isLeaveDiag); @@ -1068,6 +1096,11 @@ namespace Terminal.Gui.TopLevelTests { Assert.True (isEnterTop); Assert.False (isLeaveTop); // Leave event cannot be trigger because it v.Enter was performed and v is focused Assert.True (vt.HasFocus); + Assert.Equal (1, steps [0]); + Assert.Equal (2, steps [1]); + Assert.Equal (3, steps [2]); + Assert.Equal (4, steps [3]); + Assert.Equal (5, steps [^1]); } [Fact, AutoInitShutdown]