Skip to content

Commit

Permalink
Fixes gui-cs#2810. Pressing Alt key on a Toplevel with only a MenuBar…
Browse files Browse the repository at this point in the history
… throws System.InvalidOperationException.
  • Loading branch information
BDisp committed Aug 31, 2023
1 parent e0365a0 commit 0820ce9
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 12 deletions.
23 changes: 12 additions & 11 deletions Terminal.Gui/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static partial class Application {

// For Unit testing - ignores UseSystemConsole
internal static bool _forceFakeConsole;

private static List<CultureInfo> _cachedSupportedCultures;

/// <summary>
Expand Down Expand Up @@ -242,7 +242,7 @@ static void ResetState ()

// BUGBUG: OverlappedTop is not cleared here, but it should be?

MainLoop?.Stop();
MainLoop?.Stop ();
MainLoop = null;
Driver?.End ();
Driver = null;
Expand Down Expand Up @@ -518,7 +518,7 @@ public static void Run (Toplevel view, Func<Exception, bool> errorHandler = null
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) {
Expand Down Expand Up @@ -657,8 +657,8 @@ public static void RunMainLoopIteration (ref RunState state, bool wait, ref bool
}
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 ();
Expand All @@ -678,9 +678,9 @@ public static void RunMainLoopIteration (ref RunState state, bool wait, ref bool
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 ();
Expand All @@ -689,9 +689,9 @@ public static void RunMainLoopIteration (ref RunState state, bool wait, ref bool
} 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 ();
}
}
Expand Down Expand Up @@ -832,7 +832,8 @@ public static void End (RunState runState)
OverlappedTop.OnAllChildClosed ();
} else {
SetCurrentOverlappedAsTop ();
Current.OnEnter (Current);
runState.Toplevel.OnLeave (Current);
Current.OnEnter (runState.Toplevel);
}
Refresh ();
}
Expand Down
1 change: 1 addition & 0 deletions Terminal.Gui/Views/Toplevel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,7 @@ void FocusNearestView (IEnumerable<View> views, Direction direction)
///<inheritdoc/>
public override void Add (View view)
{
CanFocus = true;
AddMenuStatusBar (view);
base.Add (view);
}
Expand Down
110 changes: 109 additions & 1 deletion UnitTests/Views/ToplevelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,98 @@ public void OnEnter_OnLeave_Triggered_On_Application_Begin_End ()
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]
Expand Down Expand Up @@ -1494,5 +1585,22 @@ void Current_DrawContentComplete (object sender, DrawEventArgs e)

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);
}
}
}

0 comments on commit 0820ce9

Please sign in to comment.