From e507df7b986f65f1d7620686a9dac006dbc98e01 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 15 Aug 2023 00:11:47 +0100 Subject: [PATCH 1/5] Fixes #2808. v2 ColorTests unit test is failing on Windows when testing the CursesDriver instance. --- Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index a35656f6b..389e36442 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -666,6 +666,9 @@ internal class CursesDriver : ConsoleDriver { TerminalResized = terminalResized; + if (Environment.OSVersion.Platform == PlatformID.Win32NT) { + Clipboard = new FakeDriver.FakeClipboard (); + } else { if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { Clipboard = new MacOSXClipboard (); } else { @@ -675,6 +678,7 @@ internal class CursesDriver : ConsoleDriver { Clipboard = new CursesClipboard (); } } + } ClearContents (); StartReportingMouseMoves (); From 59b5b40b64c21fd4f99f8f851a8490c108be29d1 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 15 Aug 2023 00:18:29 +0100 Subject: [PATCH 2/5] Reformatting. --- .../ConsoleDrivers/CursesDriver/CursesDriver.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 389e36442..bafa729d0 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -669,16 +669,16 @@ internal class CursesDriver : ConsoleDriver { if (Environment.OSVersion.Platform == PlatformID.Win32NT) { Clipboard = new FakeDriver.FakeClipboard (); } else { - if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { - Clipboard = new MacOSXClipboard (); - } else { - if (Is_WSL_Platform ()) { - Clipboard = new WSLClipboard (); + if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { + Clipboard = new MacOSXClipboard (); } else { - Clipboard = new CursesClipboard (); + if (Is_WSL_Platform ()) { + Clipboard = new WSLClipboard (); + } else { + Clipboard = new CursesClipboard (); + } } } - } ClearContents (); StartReportingMouseMoves (); From 5a11b88f52626af994713baed4f9c5da5885e3e4 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 15 Aug 2023 18:31:54 +0100 Subject: [PATCH 3/5] v2 Fixes #2810. Pressing Alt key on a Toplevel with only a MenuBar throws System.InvalidOperationException. --- Terminal.Gui/Application.cs | 3 +- Terminal.Gui/View/ViewSubViews.cs | 2 +- Terminal.Gui/Views/Toplevel.cs | 1 + Terminal.Gui/Views/Window.cs | 24 ------ UnitTests/View/NavigationTests.cs | 49 ++++++++++++- UnitTests/Views/ToplevelTests.cs | 117 +++++++++++++++++++++++++++++- UnitTests/Views/WindowTests.cs | 17 +++++ 7 files changed, 181 insertions(+), 32 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index b571894a8..3f4968ecf 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -832,7 +832,8 @@ namespace Terminal.Gui { OverlappedTop.OnAllChildClosed (); } else { SetCurrentOverlappedAsTop (); - Current.OnEnter (Current); + runState.Toplevel.OnLeave (Current); + Current.OnEnter (runState.Toplevel); } Refresh (); } diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs index 1657e09ed..a04d60b04 100644 --- a/Terminal.Gui/View/ViewSubViews.cs +++ b/Terminal.Gui/View/ViewSubViews.cs @@ -383,7 +383,7 @@ namespace Terminal.Gui { SuperView?.EnsureFocus (); if (SuperView != null && SuperView.Focused == null) { SuperView.FocusNext (); - if (SuperView.Focused == null) { + if (SuperView.Focused == null && Application.Current != null) { Application.Current.FocusNext (); } Application.BringOverlappedTopToFront (); diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs index 159774984..779961523 100644 --- a/Terminal.Gui/Views/Toplevel.cs +++ b/Terminal.Gui/Views/Toplevel.cs @@ -553,6 +553,7 @@ namespace Terminal.Gui { /// public override void Add (View view) { + CanFocus = true; AddMenuStatusBar (view); base.Add (view); } diff --git a/Terminal.Gui/Views/Window.cs b/Terminal.Gui/Views/Window.cs index 0358c12a8..8d4c3a2cd 100644 --- a/Terminal.Gui/Views/Window.cs +++ b/Terminal.Gui/Views/Window.cs @@ -56,29 +56,5 @@ namespace Terminal.Gui { ColorScheme = Colors.Base; // TODO: make this a theme property BorderStyle = DefaultBorderStyle; } - - // TODO: Are these overrides really needed? - /// - public override void Add (View view) - { - base.Add (view); - if (view.CanFocus) { - CanFocus = true; - } - AddMenuStatusBar (view); - } - - /// - public override void Remove (View view) - { - if (view == null) { - return; - } - - SetNeedsDisplay (); - base.Remove (view); - RemoveMenuStatusBar (view); - - } } } diff --git a/UnitTests/View/NavigationTests.cs b/UnitTests/View/NavigationTests.cs index d7f7ba809..f632fccd2 100644 --- a/UnitTests/View/NavigationTests.cs +++ b/UnitTests/View/NavigationTests.cs @@ -764,11 +764,25 @@ namespace Terminal.Gui.ViewTests { Assert.True (view1.CanFocus); Assert.True (view1.HasFocus); Assert.True (view2.CanFocus); - Assert.False (view2.HasFocus); + Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus + + Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab, new KeyModifiers ()))); + Assert.True (view1.CanFocus); + Assert.False (view1.HasFocus); // Only one of the most focused toplevels view can have focus + Assert.True (view2.CanFocus); + Assert.True (view2.HasFocus); + + Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab, new KeyModifiers ()))); + Assert.True (view1.CanFocus); + Assert.True (view1.HasFocus); + Assert.True (view2.CanFocus); + Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus view1.CanFocus = false; Assert.False (view1.CanFocus); Assert.False (view1.HasFocus); + Assert.True (view2.CanFocus); + Assert.True (view2.HasFocus); Assert.Equal (win2, Application.Current.Focused); Assert.Equal (view2, Application.Current.MostFocused); } @@ -790,11 +804,26 @@ namespace Terminal.Gui.ViewTests { Assert.True (view1.CanFocus); Assert.True (view1.HasFocus); Assert.True (view2.CanFocus); - Assert.False (view2.HasFocus); + Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus + + Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers ()))); + Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers ()))); + Assert.True (view1.CanFocus); + Assert.False (view1.HasFocus); // Only one of the most focused toplevels view can have focus + Assert.True (view2.CanFocus); + Assert.True (view2.HasFocus); + + Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers ()))); + Assert.True (view1.CanFocus); + Assert.True (view1.HasFocus); + Assert.True (view2.CanFocus); + Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus view1.CanFocus = false; Assert.False (view1.CanFocus); Assert.False (view1.HasFocus); + Assert.True (view2.CanFocus); + Assert.False (view2.HasFocus); Assert.Equal (win1, Application.Current.Focused); Assert.Equal (view12, Application.Current.MostFocused); } @@ -815,13 +844,27 @@ namespace Terminal.Gui.ViewTests { Assert.True (view1.CanFocus); Assert.True (view1.HasFocus); Assert.True (view2.CanFocus); - Assert.False (view2.HasFocus); + Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus + + Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers ()))); + Assert.True (view1.CanFocus); + Assert.False (view1.HasFocus); // Only one of the most focused toplevels view can have focus + Assert.True (view2.CanFocus); + Assert.True (view2.HasFocus); + + Assert.True (Application.Top.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers ()))); + Assert.True (view1.CanFocus); + Assert.True (view1.HasFocus); + Assert.True (view2.CanFocus); + Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus win1.CanFocus = false; Assert.False (view1.CanFocus); Assert.False (view1.HasFocus); Assert.False (win1.CanFocus); Assert.False (win1.HasFocus); + Assert.True (view2.CanFocus); + Assert.True (view2.HasFocus); Assert.Equal (win2, Application.Current.Focused); Assert.Equal (view2, Application.Current.MostFocused); } diff --git a/UnitTests/Views/ToplevelTests.cs b/UnitTests/Views/ToplevelTests.cs index b33de3975..9252fac35 100644 --- a/UnitTests/Views/ToplevelTests.cs +++ b/UnitTests/Views/ToplevelTests.cs @@ -962,10 +962,104 @@ namespace Terminal.Gui.ViewsTests { Application.End (rs); Assert.True (isEnter); - Assert.False (isLeave); - } + Assert.False(isLeave); // Leave event cannot be trigger because it v.Enter was performed and v is focused + Assert.True(v.HasFocus); + } - [Fact, AutoInitShutdown] + [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(); + var top = Application.Top; + var diag = new Dialog(); + + vt.Enter += (s, 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 += (s, e) => { + iterations++; + steps[1] = iterations; + isLeaveTop = true; + Assert.Equal(diag, e.View); + }; + 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 += (s, e) => { + iterations++; + steps[2] = iterations; + isEnterDiag = true; + Assert.Null(e.View); + }; + vd.Leave += (s, e) => { + iterations++; + steps[3] = iterations; + isLeaveDiag = true; + Assert.Equal(top, e.View); + }; + diag.Add(vd); + + Assert.False(vd.CanFocus); + exception = Record.Exception(() => diag.OnEnter(diag)); + Assert.Null(exception); + exception = Record.Exception(() => diag.OnLeave(diag)); + Assert.Null(exception); + + vd.CanFocus = true; + var rs = Application.Begin(diag); + + 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); + 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] public void PositionCursor_SetCursorVisibility_To_Invisible_If_Focused_Is_Null () { var tf = new TextField ("test") { Width = 5 }; @@ -1494,5 +1588,22 @@ namespace Terminal.Gui.ViewsTests { Application.End (rs); } + + [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 diff --git a/UnitTests/Views/WindowTests.cs b/UnitTests/Views/WindowTests.cs index 242e34a67..955be94ff 100644 --- a/UnitTests/Views/WindowTests.cs +++ b/UnitTests/Views/WindowTests.cs @@ -193,5 +193,22 @@ namespace Terminal.Gui.ViewsTests { Assert.False (win2.HasFocus); Assert.False (view2.HasFocus); } + + [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 win = new Window (); + win.Add (menu); + Application.Top.Add (win); + Application.Begin (Application.Top); + + var exception = Record.Exception (() => win.ProcessHotKey (new KeyEvent (Key.AltMask, new KeyModifiers { Alt = true }))); + Assert.Null (exception); + } } } From c50f92bf8ef2d947976c83611767d34b43db025f Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 15 Aug 2023 18:52:01 +0100 Subject: [PATCH 4/5] Reformatting. --- UnitTests/Views/ToplevelTests.cs | 169 +++++++++++++++---------------- 1 file changed, 83 insertions(+), 86 deletions(-) diff --git a/UnitTests/Views/ToplevelTests.cs b/UnitTests/Views/ToplevelTests.cs index 9252fac35..1d4241292 100644 --- a/UnitTests/Views/ToplevelTests.cs +++ b/UnitTests/Views/ToplevelTests.cs @@ -962,104 +962,101 @@ namespace Terminal.Gui.ViewsTests { Application.End (rs); Assert.True (isEnter); - Assert.False(isLeave); // Leave event cannot be trigger because it v.Enter was performed and v is focused - Assert.True(v.HasFocus); - } + Assert.False (isLeave); // Leave event cannot be trigger because it v.Enter was performed and v is focused + Assert.True (v.HasFocus); + } - [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(); - var top = Application.Top; - var diag = new Dialog(); + [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 (); + var top = Application.Top; + var diag = new Dialog (); - vt.Enter += (s, 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 += (s, e) => { - iterations++; - steps[1] = iterations; - isLeaveTop = true; - Assert.Equal(diag, e.View); - }; - top.Add(vt); + vt.Enter += (s, 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 += (s, e) => { + iterations++; + steps [1] = iterations; + isLeaveTop = true; + Assert.Equal (diag, e.View); + }; + 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); + 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); + vt.CanFocus = true; + Application.Begin (top); - Assert.True(isEnterTop); - Assert.False(isLeaveTop); + Assert.True (isEnterTop); + Assert.False (isLeaveTop); - isEnterTop = false; - var isEnterDiag = false; - var isLeaveDiag = false; - var vd = new View(); - vd.Enter += (s, e) => { - iterations++; - steps[2] = iterations; - isEnterDiag = true; - Assert.Null(e.View); - }; - vd.Leave += (s, e) => { - iterations++; - steps[3] = iterations; - isLeaveDiag = true; - Assert.Equal(top, e.View); - }; - diag.Add(vd); + isEnterTop = false; + var isEnterDiag = false; + var isLeaveDiag = false; + var vd = new View (); + vd.Enter += (s, e) => { + iterations++; + steps [2] = iterations; + isEnterDiag = true; + Assert.Null (e.View); + }; + vd.Leave += (s, e) => { + iterations++; + steps [3] = iterations; + isLeaveDiag = true; + Assert.Equal (top, e.View); + }; + diag.Add (vd); - Assert.False(vd.CanFocus); - exception = Record.Exception(() => diag.OnEnter(diag)); - Assert.Null(exception); - exception = Record.Exception(() => diag.OnLeave(diag)); - Assert.Null(exception); + Assert.False (vd.CanFocus); + exception = Record.Exception (() => diag.OnEnter (diag)); + Assert.Null (exception); + exception = Record.Exception (() => diag.OnLeave (diag)); + Assert.Null (exception); - vd.CanFocus = true; - var rs = Application.Begin(diag); + vd.CanFocus = true; + var rs = Application.Begin (diag); - Assert.True(isEnterDiag); - Assert.False(isLeaveDiag); - Assert.False(isEnterTop); - Assert.True(isLeaveTop); + Assert.True (isEnterDiag); + Assert.False (isLeaveDiag); + Assert.False (isEnterTop); + Assert.True (isLeaveTop); - isEnterDiag = false; - isLeaveTop = false; - Application.End(rs); + 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); - 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]); - } + 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); + 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] + [Fact, AutoInitShutdown] public void PositionCursor_SetCursorVisibility_To_Invisible_If_Focused_Is_Null () { var tf = new TextField ("test") { Width = 5 }; From 0820ce985f9761b5d20fa80e0510f8f42771307f Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 31 Aug 2023 14:24:54 +0100 Subject: [PATCH 5/5] Fixes #2810. Pressing Alt key on a Toplevel with only a MenuBar throws System.InvalidOperationException. --- Terminal.Gui/Application.cs | 23 +++---- Terminal.Gui/Views/Toplevel.cs | 1 + UnitTests/Views/ToplevelTests.cs | 110 ++++++++++++++++++++++++++++++- 3 files changed, 122 insertions(+), 12 deletions(-) diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs index b571894a8..49c1abff7 100644 --- a/Terminal.Gui/Application.cs +++ b/Terminal.Gui/Application.cs @@ -54,7 +54,7 @@ namespace Terminal.Gui { // For Unit testing - ignores UseSystemConsole internal static bool _forceFakeConsole; - + private static List _cachedSupportedCultures; /// @@ -242,7 +242,7 @@ namespace Terminal.Gui { // BUGBUG: OverlappedTop is not cleared here, but it should be? - MainLoop?.Stop(); + MainLoop?.Stop (); MainLoop = null; Driver?.End (); Driver = null; @@ -518,7 +518,7 @@ namespace Terminal.Gui { public static void Refresh () { // TODO: Figure out how to remove this call to ClearContents. Refresh should just repaint damaged areas, not clear - Driver.ClearContents(); + Driver.ClearContents (); View last = null; foreach (var v in _toplevels.Reverse ()) { if (v.Visible) { @@ -657,8 +657,8 @@ namespace Terminal.Gui { } firstIteration = false; - if (state.Toplevel != Top && - (Top.NeedsDisplay|| Top.SubViewNeedsDisplay || Top.LayoutNeeded)) { + if (state.Toplevel != Top && + (Top.NeedsDisplay || Top.SubViewNeedsDisplay || Top.LayoutNeeded)) { state.Toplevel.SetNeedsDisplay (state.Toplevel.Frame); Top.Clear (); Top.Draw (); @@ -678,9 +678,9 @@ namespace Terminal.Gui { state.Toplevel.Clear (); } - if (state.Toplevel.NeedsDisplay || - state.Toplevel.SubViewNeedsDisplay || - state.Toplevel.LayoutNeeded || + if (state.Toplevel.NeedsDisplay || + state.Toplevel.SubViewNeedsDisplay || + state.Toplevel.LayoutNeeded || OverlappedChildNeedsDisplay ()) { state.Toplevel.Clear (); state.Toplevel.Draw (); @@ -689,9 +689,9 @@ namespace Terminal.Gui { } else { Driver.UpdateCursor (); } - if (state.Toplevel != Top && + if (state.Toplevel != Top && !state.Toplevel.Modal && - (Top.NeedsDisplay|| Top.SubViewNeedsDisplay || Top.LayoutNeeded)) { + (Top.NeedsDisplay || Top.SubViewNeedsDisplay || Top.LayoutNeeded)) { Top.Draw (); } } @@ -832,7 +832,8 @@ namespace Terminal.Gui { OverlappedTop.OnAllChildClosed (); } else { SetCurrentOverlappedAsTop (); - Current.OnEnter (Current); + runState.Toplevel.OnLeave (Current); + Current.OnEnter (runState.Toplevel); } Refresh (); } diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs index 159774984..779961523 100644 --- a/Terminal.Gui/Views/Toplevel.cs +++ b/Terminal.Gui/Views/Toplevel.cs @@ -553,6 +553,7 @@ namespace Terminal.Gui { /// public override void Add (View view) { + CanFocus = true; AddMenuStatusBar (view); base.Add (view); } diff --git a/UnitTests/Views/ToplevelTests.cs b/UnitTests/Views/ToplevelTests.cs index b33de3975..1d4241292 100644 --- a/UnitTests/Views/ToplevelTests.cs +++ b/UnitTests/Views/ToplevelTests.cs @@ -962,7 +962,98 @@ namespace Terminal.Gui.ViewsTests { Application.End (rs); Assert.True (isEnter); - Assert.False (isLeave); + Assert.False (isLeave); // Leave event cannot be trigger because it v.Enter was performed and v is focused + Assert.True (v.HasFocus); + } + + [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 (); + var top = Application.Top; + var diag = new Dialog (); + + vt.Enter += (s, 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 += (s, e) => { + iterations++; + steps [1] = iterations; + isLeaveTop = true; + Assert.Equal (diag, e.View); + }; + 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 += (s, e) => { + iterations++; + steps [2] = iterations; + isEnterDiag = true; + Assert.Null (e.View); + }; + vd.Leave += (s, e) => { + iterations++; + steps [3] = iterations; + isLeaveDiag = true; + Assert.Equal (top, e.View); + }; + diag.Add (vd); + + Assert.False (vd.CanFocus); + exception = Record.Exception (() => diag.OnEnter (diag)); + Assert.Null (exception); + exception = Record.Exception (() => diag.OnLeave (diag)); + Assert.Null (exception); + + vd.CanFocus = true; + var rs = Application.Begin (diag); + + 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); + 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] @@ -1494,5 +1585,22 @@ namespace Terminal.Gui.ViewsTests { Application.End (rs); } + + [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