diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 8a443133e..d79f5007b 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -391,6 +391,7 @@ namespace Terminal.Gui { keyHandler (new KeyEvent (map, keyModifiers)); keyUpHandler (new KeyEvent (map, keyModifiers)); + keyModifiers = new KeyModifiers (); }; } diff --git a/Terminal.Gui/Core/Event.cs b/Terminal.Gui/Core/Event.cs index 04ae3b961..83ccffe61 100644 --- a/Terminal.Gui/Core/Event.cs +++ b/Terminal.Gui/Core/Event.cs @@ -392,7 +392,7 @@ namespace Terminal.Gui { /// Gets a value indicating whether the Shift key was pressed. /// /// true if is shift; otherwise, false. - public bool IsShift => keyModifiers.Shift; + public bool IsShift => keyModifiers.Shift || Key == Key.BackTab; /// /// Gets a value indicating whether the Alt key was pressed (real or synthesized) diff --git a/Terminal.Gui/Core/Toplevel.cs b/Terminal.Gui/Core/Toplevel.cs index 19b133ef9..69f75e3a3 100644 --- a/Terminal.Gui/Core/Toplevel.cs +++ b/Terminal.Gui/Core/Toplevel.cs @@ -195,7 +195,7 @@ namespace Terminal.Gui { if (base.ProcessKey (keyEvent)) return true; - switch (keyEvent.Key) { + switch (ShortcutHelper.GetModifiersKey (keyEvent)) { case Key.Q | Key.CtrlMask: // FIXED: stop current execution of this container Application.RequestStop (); @@ -217,27 +217,32 @@ namespace Terminal.Gui { var old = GetDeepestFocusedSubview (Focused); if (!FocusNext ()) FocusNext (); - if (old != Focused) { + if (old != Focused && old != Focused?.Focused) { old?.SetNeedsDisplay (); Focused?.SetNeedsDisplay (); } else { - FocusNearestView (GetToplevelSubviews (true)); + FocusNearestView (SuperView?.TabIndexes, Direction.Forward); } return true; + case Key.BackTab | Key.ShiftMask: case Key.CursorLeft: case Key.CursorUp: - case Key.BackTab: old = GetDeepestFocusedSubview (Focused); if (!FocusPrev ()) FocusPrev (); - if (old != Focused) { + if (old != Focused && old != Focused?.Focused) { old?.SetNeedsDisplay (); Focused?.SetNeedsDisplay (); } else { - FocusNearestView (GetToplevelSubviews (false)); + FocusNearestView (SuperView?.TabIndexes?.Reverse(), Direction.Backward); } return true; - + case Key.Tab | Key.CtrlMask: + Application.Top.FocusNext (); + return true; + case Key.Tab | Key.ShiftMask | Key.CtrlMask: + Application.Top.FocusPrev (); + return true; case Key.L | Key.CtrlMask: Application.Refresh (); return true; @@ -272,39 +277,34 @@ namespace Terminal.Gui { return view; } - IEnumerable GetToplevelSubviews (bool isForward) - { - if (SuperView == null) { - return null; - } - - HashSet views = new HashSet (); - - foreach (var v in SuperView.Subviews) { - views.Add (v); - } - - return isForward ? views : views.Reverse (); - } - - void FocusNearestView (IEnumerable views) + void FocusNearestView (IEnumerable views, Direction direction) { if (views == null) { return; } bool found = false; + bool focusProcessed = false; + int idx = 0; foreach (var v in views) { if (v == this) { found = true; } if (found && v != this) { - v.EnsureFocus (); + if (direction == Direction.Forward) { + SuperView?.FocusNext (); + } else { + SuperView?.FocusPrev (); + } + focusProcessed = true; if (SuperView.Focused != null && SuperView.Focused != this) { return; } + } else if (found && !focusProcessed && idx == views.Count () - 1) { + views.ToList () [0].SetFocus (); } + idx++; } } diff --git a/UICatalog/Scenarios/WindowsAndFrameViews.cs b/UICatalog/Scenarios/WindowsAndFrameViews.cs index 0d7f108c3..df590328c 100644 --- a/UICatalog/Scenarios/WindowsAndFrameViews.cs +++ b/UICatalog/Scenarios/WindowsAndFrameViews.cs @@ -108,7 +108,7 @@ namespace UICatalog { var frameView = new FrameView ("This is a Sub-FrameView") { X = Pos.Percent (50), Y = 1, - Width = Dim.Percent (100), + Width = Dim.Percent (100, true), // Or Dim.Percent (50) Height = 5, ColorScheme = Colors.Base, Text = "The Text in the FrameView", diff --git a/UnitTests/ViewTests.cs b/UnitTests/ViewTests.cs index dfb45df8b..990e3f9c9 100644 --- a/UnitTests/ViewTests.cs +++ b/UnitTests/ViewTests.cs @@ -6,7 +6,7 @@ using System.Linq; using Terminal.Gui; using Xunit; -// Alais Console to MockConsole so we don't accidentally use Console +// Alias Console to MockConsole so we don't accidentally use Console using Console = Terminal.Gui.FakeConsole; namespace Terminal.Gui { @@ -27,9 +27,9 @@ namespace Terminal.Gui { Assert.Null (r.ColorScheme); Assert.Equal (Dim.Sized (0), r.Width); Assert.Equal (Dim.Sized (0), r.Height); - // BUGBUG: Pos needs eqality implemented - //Assert.Equal (Pos.At (0), r.X); - //Assert.Equal (Pos.At (0), r.Y); + // FIXED: Pos needs equality implemented + Assert.Equal (Pos.At (0), r.X); + Assert.Equal (Pos.At (0), r.Y); Assert.False (r.IsCurrentTop); Assert.Empty (r.Id); Assert.Empty (r.Subviews); @@ -1077,5 +1077,37 @@ namespace Terminal.Gui { Assert.True (view.Frame.IsEmpty); Assert.True (view.Bounds.IsEmpty); } + + [Fact] + public void FocusNearestView_Ensure_Focus_Ordered () + { + var top = new Toplevel (); + + var win = new Window (); + var winSubview = new View ("WindowSubview") { + CanFocus = true + }; + win.Add (winSubview); + top.Add (win); + + var frm = new FrameView (); + var frmSubview = new View ("FrameSubview") { + CanFocus = true + }; + frm.Add (frmSubview); + top.Add (frm); + + top.ProcessKey (new KeyEvent (Key.Tab, new KeyModifiers ())); + Assert.Equal ($"WindowSubview", top.MostFocused.Text); + top.ProcessKey (new KeyEvent (Key.Tab, new KeyModifiers ())); + Assert.Equal ("FrameSubview", top.MostFocused.Text); + top.ProcessKey (new KeyEvent (Key.Tab, new KeyModifiers ())); + Assert.Equal ($"WindowSubview", top.MostFocused.Text); + + top.ProcessKey (new KeyEvent (Key.BackTab | Key.ShiftMask, new KeyModifiers ())); + Assert.Equal ("FrameSubview", top.MostFocused.Text); + top.ProcessKey (new KeyEvent (Key.BackTab | Key.ShiftMask, new KeyModifiers ())); + Assert.Equal ($"WindowSubview", top.MostFocused.Text); + } } }