Browse Source

code cleanup.

Tig 11 months ago
parent
commit
4d3ec57f27

+ 0 - 5
Terminal.Gui/Application/ApplicationNavigation.cs

@@ -78,11 +78,6 @@ public class ApplicationNavigation
 
 
         _focused = value;
         _focused = value;
 
 
-        if (_focused is { } && _focused.Arrangement.HasFlag (ViewArrangement.Overlapped))
-        {
-            _focused.SuperView?.MoveSubviewToStart (_focused);
-        }
-
         FocusedChanged?.Invoke (null, EventArgs.Empty);
         FocusedChanged?.Invoke (null, EventArgs.Empty);
     }
     }
 
 

+ 16 - 10
Terminal.Gui/View/View.Navigation.cs

@@ -84,10 +84,8 @@ public partial class View // Focus and cross-view navigation management (TabStop
                     }
                     }
                 }
                 }
             }
             }
-
         }
         }
 
 
-
         int focusedIndex = index.IndexOf (Focused); // Will return -1 if Focused can't be found or is null
         int focusedIndex = index.IndexOf (Focused); // Will return -1 if Focused can't be found or is null
         var next = 0;
         var next = 0;
 
 
@@ -193,7 +191,8 @@ public partial class View // Focus and cross-view navigation management (TabStop
     public event EventHandler? CanFocusChanged;
     public event EventHandler? CanFocusChanged;
 
 
     /// <summary>
     /// <summary>
-    ///     Focuses the deepest focusable Subview if one exists. If there are no focusable Subviews then the focus is set to the view itself.
+    ///     Focuses the deepest focusable Subview if one exists. If there are no focusable Subviews then the focus is set to
+    ///     the view itself.
     /// </summary>
     /// </summary>
     /// <param name="direction"></param>
     /// <param name="direction"></param>
     /// <param name="behavior"></param>
     /// <param name="behavior"></param>
@@ -450,8 +449,12 @@ public partial class View // Focus and cross-view navigation management (TabStop
             previousFocusedView.SetHasFocusFalse (this);
             previousFocusedView.SetHasFocusFalse (this);
         }
         }
 
 
+        if (Arrangement.HasFlag (ViewArrangement.Overlapped))
+        {
+            SuperView?.MoveSubviewToStart (this);
+        }
+
         NotifyFocusChanged (HasFocus, previousFocusedView, this);
         NotifyFocusChanged (HasFocus, previousFocusedView, this);
-        SetNeedsDisplay ();
 
 
         // Post-conditions - prove correctness
         // Post-conditions - prove correctness
         if (HasFocus == previousValue)
         if (HasFocus == previousValue)
@@ -500,10 +503,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     ///     <see langword="true"/>, if the change to <see cref="View.HasFocus"/> is to be cancelled, <see langword="false"/>
     ///     <see langword="true"/>, if the change to <see cref="View.HasFocus"/> is to be cancelled, <see langword="false"/>
     ///     otherwise.
     ///     otherwise.
     /// </returns>
     /// </returns>
-    protected virtual bool OnHasFocusChanging (bool currentHasFocus, bool newHasFocus, View? currentFocused, View? newFocused)
-    {
-        return false;
-    }
+    protected virtual bool OnHasFocusChanging (bool currentHasFocus, bool newHasFocus, View? currentFocused, View? newFocused) { return false; }
 
 
     /// <summary>
     /// <summary>
     ///     Raised when <see cref="View.HasFocus"/> is about to change.
     ///     Raised when <see cref="View.HasFocus"/> is about to change.
@@ -521,8 +521,14 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// <summary>
     /// <summary>
     ///     Called when this view should stop being focused.
     ///     Called when this view should stop being focused.
     /// </summary>
     /// </summary>
-    /// <param name="newFocusedView">The new focused view. If <see langword="null"/> it is not known which view will be focused.</param>
-    /// <param name="traversingDown">Set to true to indicate method is being called recurively, traversing down the focus chain.</param>
+    /// <param name="newFocusedView">
+    ///     The new focused view. If <see langword="null"/> it is not known which view will be
+    ///     focused.
+    /// </param>
+    /// <param name="traversingDown">
+    ///     Set to true to indicate method is being called recurively, traversing down the focus
+    ///     chain.
+    /// </param>
     /// <exception cref="InvalidOperationException"></exception>
     /// <exception cref="InvalidOperationException"></exception>
     private void SetHasFocusFalse (View? newFocusedView, bool traversingDown = false)
     private void SetHasFocusFalse (View? newFocusedView, bool traversingDown = false)
     {
     {

+ 2 - 1
UICatalog/Scenarios/ShadowStyles.cs

@@ -34,7 +34,8 @@ public class ShadowStyles : Scenario
             Width = Dim.Percent (30),
             Width = Dim.Percent (30),
             Height = Dim.Percent (30),
             Height = Dim.Percent (30),
             Title = "Shadow Window",
             Title = "Shadow Window",
-            Arrangement = ViewArrangement.Movable,
+            Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped,
+            BorderStyle = LineStyle.Double,
             ShadowStyle = ShadowStyle.Transparent
             ShadowStyle = ShadowStyle.Transparent
         };
         };
 
 

+ 487 - 481
UnitTests/Application/KeyboardTests.cs

@@ -1,15 +1,12 @@
-using UICatalog;
-using Xunit.Abstractions;
+using Xunit.Abstractions;
 
 
 namespace Terminal.Gui.ApplicationTests;
 namespace Terminal.Gui.ApplicationTests;
 
 
 /// <summary>
 /// <summary>
-/// Application tests for keyboard support.
+///     Application tests for keyboard support.
 /// </summary>
 /// </summary>
 public class KeyboardTests
 public class KeyboardTests
 {
 {
-    private readonly ITestOutputHelper _output;
-
     public KeyboardTests (ITestOutputHelper output)
     public KeyboardTests (ITestOutputHelper output)
     {
     {
         _output = output;
         _output = output;
@@ -19,281 +16,360 @@ public class KeyboardTests
 #endif
 #endif
     }
     }
 
 
+    private readonly ITestOutputHelper _output;
 
 
-    [Fact]
+    private object _timeoutLock;
+
+    [Fact (Skip = "No longer valid test.")]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void QuitKey_Getter_Setter ()
+    public void EnsuresTopOnFront_CanFocus_False_By_Keyboard ()
     {
     {
         Toplevel top = new ();
         Toplevel top = new ();
-        var isQuiting = false;
 
 
-        top.Closing += (s, e) =>
-                       {
-                           isQuiting = true;
-                           e.Cancel = true;
-                       };
+        var win = new Window
+        {
+            Title = "win",
+            X = 0,
+            Y = 0,
+            Width = 20,
+            Height = 10
+        };
+        var tf = new TextField { Width = 10 };
+        win.Add (tf);
+
+        var win2 = new Window
+        {
+            Title = "win2",
+            X = 22,
+            Y = 0,
+            Width = 20,
+            Height = 10
+        };
+        var tf2 = new TextField { Width = 10 };
+        win2.Add (tf2);
+        top.Add (win, win2);
 
 
         Application.Begin (top);
         Application.Begin (top);
-        top.Running = true;
 
 
-        Key prevKey = Application.QuitKey;
+        Assert.True (win.CanFocus);
+        Assert.True (win.HasFocus);
+        Assert.True (win2.CanFocus);
+        Assert.False (win2.HasFocus);
+        Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
 
 
-        Application.OnKeyDown (Application.QuitKey);
-        Assert.True (isQuiting);
+        win.CanFocus = false;
+        Assert.False (win.CanFocus);
+        Assert.False (win.HasFocus);
+        Assert.True (win2.CanFocus);
+        Assert.True (win2.HasFocus);
+        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
 
-        isQuiting = false;
-        Application.OnKeyDown (Application.QuitKey);
-        Assert.True (isQuiting);
+        Application.OnKeyDown (Key.F6);
+        Assert.True (win2.CanFocus);
+        Assert.False (win.HasFocus);
+        Assert.True (win2.CanFocus);
+        Assert.True (win2.HasFocus);
+        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
 
-        isQuiting = false;
-        Application.QuitKey = Key.C.WithCtrl;
-        Application.OnKeyDown (prevKey); // Should not quit
-        Assert.False (isQuiting);
-        Application.OnKeyDown (Key.Q.WithCtrl);// Should not quit
-        Assert.False (isQuiting);
+        Application.OnKeyDown (Key.F6);
+        Assert.False (win.CanFocus);
+        Assert.False (win.HasFocus);
+        Assert.True (win2.CanFocus);
+        Assert.True (win2.HasFocus);
+        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
+        top.Dispose ();
+    }
 
 
-        Application.OnKeyDown (Application.QuitKey);
-        Assert.True (isQuiting);
+    [Fact (Skip = "No longer valid test.")]
+    [AutoInitShutdown]
+    public void EnsuresTopOnFront_CanFocus_True_By_Keyboard ()
+    {
+        Toplevel top = new ();
 
 
-        // Reset the QuitKey to avoid throws errors on another tests
-        Application.QuitKey = prevKey;
+        var win = new Window
+        {
+            Title = "win",
+            X = 0,
+            Y = 0,
+            Width = 20,
+            Height = 10
+        };
+        var tf = new TextField { Width = 10 };
+        win.Add (tf);
+
+        var win2 = new Window
+        {
+            Title = "win2",
+            X = 22,
+            Y = 0,
+            Width = 20,
+            Height = 10
+        };
+        var tf2 = new TextField { Width = 10 };
+        win2.Add (tf2);
+        top.Add (win, win2);
+
+        Application.Begin (top);
+
+        Assert.True (win.CanFocus);
+        Assert.True (win.HasFocus);
+        Assert.True (win2.CanFocus);
+        Assert.False (win2.HasFocus);
+        Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
+
+        Application.OnKeyDown (Key.F6);
+        Assert.True (win.CanFocus);
+        Assert.False (win.HasFocus);
+        Assert.True (win2.CanFocus);
+        Assert.True (win2.HasFocus);
+        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
+
+        Application.OnKeyDown (Key.F6);
+        Assert.True (win.CanFocus);
+        Assert.True (win.HasFocus);
+        Assert.True (win2.CanFocus);
+        Assert.False (win2.HasFocus);
+        Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
-    public void QuitKey_Default_Is_Esc ()
+    [AutoInitShutdown]
+    public void KeyBinding_Application_KeyBindings_Add_Adds ()
     {
     {
-        Application.ResetState (true);
-        // Before Init
-        Assert.Equal (Key.Esc, Application.QuitKey);
-
-        Application.Init (new FakeDriver ());
-        // After Init
-        Assert.Equal (Key.Esc, Application.QuitKey);
+        Application.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept);
+        Application.KeyBindings.Add (Key.B, KeyBindingScope.Application, Command.Accept);
 
 
-        Application.Shutdown ();
+        Assert.True (Application.KeyBindings.TryGet (Key.A, out KeyBinding binding));
+        Assert.Null (binding.BoundView);
+        Assert.True (Application.KeyBindings.TryGet (Key.B, out binding));
+        Assert.Null (binding.BoundView);
     }
     }
 
 
-    private object _timeoutLock;
+    [Fact]
+    [AutoInitShutdown]
+    public void KeyBinding_Application_RemoveKeyBinding_Removes ()
+    {
+        Application.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept);
+
+        Assert.True (Application.KeyBindings.TryGet (Key.A, out _));
+
+        Application.KeyBindings.Remove (Key.A);
+        Assert.False (Application.KeyBindings.TryGet (Key.A, out _));
+    }
 
 
     [Fact]
     [Fact]
-    public void QuitKey_Quits ()
+    [AutoInitShutdown]
+    public void KeyBinding_OnKeyDown ()
     {
     {
-        Assert.Null (_timeoutLock);
-        _timeoutLock = new object ();
+        var view = new ScopedKeyBindingView ();
+        var invoked = false;
+        view.InvokingKeyBindings += (s, e) => invoked = true;
 
 
-        uint abortTime = 500;
-        bool initialized = false;
-        int iteration = 0;
-        bool shutdown = false;
-        object timeout = null;
+        var top = new Toplevel ();
+        top.Add (view);
+        Application.Begin (top);
 
 
-        Application.InitializedChanged += OnApplicationOnInitializedChanged;
+        Application.OnKeyDown (Key.A);
+        Assert.False (invoked);
+        Assert.True (view.ApplicationCommand);
 
 
-        Application.Init (new FakeDriver ());
-        Assert.True (initialized);
-        Assert.False (shutdown);
+        invoked = false;
+        view.ApplicationCommand = false;
+        Application.KeyBindings.Remove (KeyCode.A);
+        Application.OnKeyDown (Key.A); // old
+        Assert.False (invoked);
+        Assert.False (view.ApplicationCommand);
+        Application.KeyBindings.Add (Key.A.WithCtrl, view, Command.Save);
+        Application.OnKeyDown (Key.A); // old
+        Assert.False (invoked);
+        Assert.False (view.ApplicationCommand);
+        Application.OnKeyDown (Key.A.WithCtrl); // new
+        Assert.False (invoked);
+        Assert.True (view.ApplicationCommand);
 
 
-        _output.WriteLine ("Application.Run<Toplevel> ().Dispose ()..");
-        Application.Run<Toplevel> ().Dispose ();
-        _output.WriteLine ("Back from Application.Run<Toplevel> ().Dispose ()");
+        invoked = false;
+        Application.OnKeyDown (Key.H);
+        Assert.True (invoked);
 
 
-        Assert.True (initialized);
-        Assert.False (shutdown);
+        invoked = false;
+        Assert.False (view.HasFocus);
+        Application.OnKeyDown (Key.F);
+        Assert.False (invoked);
 
 
-        Assert.Equal (1, iteration);
+        Assert.True (view.ApplicationCommand);
+        Assert.True (view.HotKeyCommand);
+        Assert.False (view.FocusedCommand);
+        top.Dispose ();
+    }
 
 
-        Application.Shutdown ();
+    [Fact]
+    [AutoInitShutdown]
+    public void KeyBinding_OnKeyDown_Negative ()
+    {
+        var view = new ScopedKeyBindingView ();
+        var invoked = false;
+        view.InvokingKeyBindings += (s, e) => invoked = true;
 
 
-        Application.InitializedChanged -= OnApplicationOnInitializedChanged;
+        var top = new Toplevel ();
+        top.Add (view);
+        Application.Begin (top);
 
 
-        lock (_timeoutLock)
-        {
-            if (timeout is { })
-            {
-                Application.RemoveTimeout (timeout);
-                timeout = null;
-            }
-        }
+        Application.OnKeyDown (Key.A.WithCtrl);
+        Assert.False (invoked);
+        Assert.False (view.ApplicationCommand);
+        Assert.False (view.HotKeyCommand);
+        Assert.False (view.FocusedCommand);
 
 
-        Assert.True (initialized);
-        Assert.True (shutdown);
+        invoked = false;
+        Assert.False (view.HasFocus);
+        Application.OnKeyDown (Key.Z);
+        Assert.False (invoked);
+        Assert.False (view.ApplicationCommand);
+        Assert.False (view.HotKeyCommand);
+        Assert.False (view.FocusedCommand);
+        top.Dispose ();
+    }
 
 
-#if DEBUG_IDISPOSABLE
-        Assert.Empty (Responder.Instances);
-#endif
-        lock (_timeoutLock)
-        {
-            _timeoutLock = null;
-        }
+    [Fact]
+    [AutoInitShutdown]
+    public void KeyBinding_View_KeyBindings_Add_Adds ()
+    {
+        View view1 = new ();
+        Application.KeyBindings.Add (Key.A, view1, Command.Accept);
 
 
-        return;
+        View view2 = new ();
+        Application.KeyBindings.Add (Key.B, view2, Command.Accept);
 
 
-        void OnApplicationOnInitializedChanged (object s, EventArgs<bool> a)
-        {
-            _output.WriteLine ("OnApplicationOnInitializedChanged: {0}", a.CurrentValue);
-            if (a.CurrentValue)
-            {
-                Application.Iteration += OnApplicationOnIteration;
-                initialized = true;
-                lock (_timeoutLock)
-                {
-                    timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
-                }
-            }
-            else
-            {
-                Application.Iteration -= OnApplicationOnIteration;
-                shutdown = true;
-            }
-        }
-
-        bool ForceCloseCallback ()
-        {
-            lock (_timeoutLock)
-            {
-                _output.WriteLine ($"ForceCloseCallback. iteration: {iteration}");
-                if (timeout is { })
-                {
-                    timeout = null;
-                }
-            }
-            Application.ResetState (true);
-            Assert.Fail ($"Failed to Quit with {Application.QuitKey} after {abortTime}ms. Force quit.");
-
-            return false;
-        }
-
-        void OnApplicationOnIteration (object s, IterationEventArgs a)
-        {
-            _output.WriteLine ("Iteration: {0}", iteration);
-            iteration++;
-            Assert.True (iteration < 2, "Too many iterations, something is wrong.");
-            if (Application.IsInitialized)
-            {
-                _output.WriteLine ("  Pressing QuitKey");
-                Application.OnKeyDown (Application.QuitKey);
-            }
-        }
+        Assert.True (Application.KeyBindings.TryGet (Key.A, out KeyBinding binding));
+        Assert.Equal (view1, binding.BoundView);
+        Assert.True (Application.KeyBindings.TryGet (Key.B, out binding));
+        Assert.Equal (view2, binding.BoundView);
     }
     }
 
 
-
     [Fact]
     [Fact]
-    public void NextTabKey_Moves_Focus_To_Next_TabStop ()
+    [AutoInitShutdown]
+    public void KeyBinding_View_KeyBindings_RemoveKeyBinding_Removes ()
     {
     {
-        // Arrange
-        Application.Navigation = new ApplicationNavigation ();
-        var top = new Toplevel ();
-        var view1 = new View () { Id = "view1", CanFocus = true };
-        var view2 = new View () { Id = "view2", CanFocus = true };
-        top.Add (view1, view2);
-        Application.Top = top;
-        Application.Current = top;
-        view1.SetFocus ();
-
-        // Act
-        Application.OnKeyDown (Application.NextTabKey);
+        View view1 = new ();
+        Application.KeyBindings.Add (Key.A, view1, Command.Accept);
 
 
-        // Assert
-        Assert.True (view2.HasFocus);
+        View view2 = new ();
+        Application.KeyBindings.Add (Key.B, view1, Command.Accept);
 
 
-        top.Dispose ();
-        Application.Navigation = null;
+        Application.KeyBindings.Remove (Key.A, view1);
+        Assert.False (Application.KeyBindings.TryGet (Key.A, out _));
     }
     }
 
 
     [Fact]
     [Fact]
-    public void PrevTabKey_Moves_Focus_To_Prev_TabStop ()
+    public void KeyUp_Event ()
     {
     {
-        // Arrange
-        Application.Navigation = new ApplicationNavigation ();
-        var top = new Toplevel ();
-        var view1 = new View () { Id = "view1", CanFocus = true };
-        var view2 = new View () { Id = "view2", CanFocus = true };
-        top.Add (view1, view2);
-        Application.Top = top;
-        Application.Current = top;
-        view1.SetFocus ();
+        Application.Init (new FakeDriver ());
 
 
-        // Act
-        Application.OnKeyDown (Application.NextTabKey);
+        // Setup some fake keypresses (This)
+        var input = "Tests";
 
 
-        // Assert
-        Assert.True (view2.HasFocus);
+        Key originalQuitKey = Application.QuitKey;
+        Application.QuitKey = Key.Q.WithCtrl;
 
 
-        top.Dispose ();
-        Application.Navigation = null;
-    }
+        // Put a control-q in at the end
+        FakeConsole.MockKeyPresses.Push (new ('Q', ConsoleKey.Q, false, false, true));
+
+        foreach (char c in input.Reverse ())
+        {
+            if (char.IsLetter (c))
+            {
+                FakeConsole.MockKeyPresses.Push (
+                                                 new (
+                                                      c,
+                                                      (ConsoleKey)char.ToUpper (c),
+                                                      char.IsUpper (c),
+                                                      false,
+                                                      false
+                                                     )
+                                                );
+            }
+            else
+            {
+                FakeConsole.MockKeyPresses.Push (
+                                                 new (
+                                                      c,
+                                                      (ConsoleKey)c,
+                                                      false,
+                                                      false,
+                                                      false
+                                                     )
+                                                );
+            }
+        }
 
 
+        int stackSize = FakeConsole.MockKeyPresses.Count;
 
 
+        var iterations = 0;
 
 
-    [Fact]
-    public void NextTabGroupKey_Moves_Focus_To_TabStop_In_Next_TabGroup ()
-    {
-        // Arrange
-        Application.Navigation = new ApplicationNavigation ();
+        Application.Iteration += (s, a) =>
+                                 {
+                                     iterations++;
+
+                                     // Stop if we run out of control...
+                                     if (iterations > 10)
+                                     {
+                                         Application.RequestStop ();
+                                     }
+                                 };
+
+        var keyUps = 0;
+        var output = string.Empty;
         var top = new Toplevel ();
         var top = new Toplevel ();
-        var view1 = new View ()
-        {
-            Id = "view1", 
-            CanFocus = true, 
-            TabStop = TabBehavior.TabGroup
-        };
 
 
-        var subView1 = new View ()
-        {
-            Id = "subView1",
-            CanFocus = true,
-            TabStop = TabBehavior.TabStop
-        };
+        top.KeyUp += (sender, args) =>
+                     {
+                         if (args.KeyCode != (KeyCode.CtrlMask | KeyCode.Q))
+                         {
+                             output += args.AsRune;
+                         }
 
 
-        view1.Add (subView1);
+                         keyUps++;
+                     };
 
 
-        var view2 = new View ()
-        {
-            Id = "view2", 
-            CanFocus = true, 
-            TabStop = TabBehavior.TabGroup
-        };
-        var subView2 = new View ()
-        {
-            Id = "subView2",
-            CanFocus = true,
-            TabStop = TabBehavior.TabStop
-        };
-        view2.Add (subView2);
+        Application.Run (top);
+        Application.QuitKey = originalQuitKey;
 
 
-        top.Add (view1, view2);
-        Application.Top = top;
-        Application.Current = top;
-        view1.SetFocus ();
-        Assert.True (view1.HasFocus);
-        Assert.True (subView1.HasFocus);
+        // Input string should match output
+        Assert.Equal (input, output);
 
 
-        // Act
-        Application.OnKeyDown (Application.NextTabGroupKey);
+        // # of key up events should match stack size
+        //Assert.Equal (stackSize, keyUps);
+        // We can't use numbers variables on the left side of an Assert.Equal/NotEqual,
+        // it must be literal (Linux only).
+        Assert.Equal (6, keyUps);
 
 
-        // Assert
-        Assert.True (view2.HasFocus);
-        Assert.True (subView2.HasFocus);
+        // # of key up events should match # of iterations
+        Assert.Equal (stackSize, iterations);
 
 
         top.Dispose ();
         top.Dispose ();
-        Application.Navigation = null;
+        Application.Shutdown ();
+        Assert.Null (Application.Current);
+        Assert.Null (Application.Top);
+        Assert.Null (Application.MainLoop);
+        Assert.Null (Application.Driver);
     }
     }
 
 
     [Fact]
     [Fact]
-    public void PrevTabGroupKey_Moves_Focus_To_TabStop_In_Prev_TabGroup ()
+    public void NextTabGroupKey_Moves_Focus_To_TabStop_In_Next_TabGroup ()
     {
     {
         // Arrange
         // Arrange
-        Application.Navigation = new ApplicationNavigation ();
+        Application.Navigation = new ();
         var top = new Toplevel ();
         var top = new Toplevel ();
-        var view1 = new View ()
+
+        var view1 = new View
         {
         {
             Id = "view1",
             Id = "view1",
             CanFocus = true,
             CanFocus = true,
             TabStop = TabBehavior.TabGroup
             TabStop = TabBehavior.TabGroup
         };
         };
 
 
-        var subView1 = new View ()
+        var subView1 = new View
         {
         {
             Id = "subView1",
             Id = "subView1",
             CanFocus = true,
             CanFocus = true,
@@ -302,13 +378,14 @@ public class KeyboardTests
 
 
         view1.Add (subView1);
         view1.Add (subView1);
 
 
-        var view2 = new View ()
+        var view2 = new View
         {
         {
             Id = "view2",
             Id = "view2",
             CanFocus = true,
             CanFocus = true,
             TabStop = TabBehavior.TabGroup
             TabStop = TabBehavior.TabGroup
         };
         };
-        var subView2 = new View ()
+
+        var subView2 = new View
         {
         {
             Id = "subView2",
             Id = "subView2",
             CanFocus = true,
             CanFocus = true,
@@ -324,7 +401,7 @@ public class KeyboardTests
         Assert.True (subView1.HasFocus);
         Assert.True (subView1.HasFocus);
 
 
         // Act
         // Act
-        Application.OnKeyDown (Application.PrevTabGroupKey);
+        Application.OnKeyDown (Application.NextTabGroupKey);
 
 
         // Assert
         // Assert
         Assert.True (view2.HasFocus);
         Assert.True (view2.HasFocus);
@@ -375,9 +452,8 @@ public class KeyboardTests
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
 
 
                                      Application.OnKeyDown (Key.F6);
                                      Application.OnKeyDown (Key.F6);
-                                     Assert.True (v1.HasFocus);
+                                     Assert.True (v2.HasFocus);
 
 
-  
                                      Application.RequestStop ();
                                      Application.RequestStop ();
                                  };
                                  };
 
 
@@ -393,343 +469,273 @@ public class KeyboardTests
         Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
         Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
 
 
         top.Dispose ();
         top.Dispose ();
+
         // Shutdown must be called to safely clean up Application if Init has been called
         // Shutdown must be called to safely clean up Application if Init has been called
         Application.Shutdown ();
         Application.Shutdown ();
     }
     }
 
 
-    [Fact(Skip = "No longer valid test.")]
-    [AutoInitShutdown]
-    public void EnsuresTopOnFront_CanFocus_False_By_Keyboard ()
+    [Fact]
+    public void NextTabKey_Moves_Focus_To_Next_TabStop ()
     {
     {
-        Toplevel top = new ();
+        // Arrange
+        Application.Navigation = new ();
+        var top = new Toplevel ();
+        var view1 = new View { Id = "view1", CanFocus = true };
+        var view2 = new View { Id = "view2", CanFocus = true };
+        top.Add (view1, view2);
+        Application.Top = top;
+        Application.Current = top;
+        view1.SetFocus ();
 
 
-        var win = new Window
+        // Act
+        Application.OnKeyDown (Application.NextTabKey);
+
+        // Assert
+        Assert.True (view2.HasFocus);
+
+        top.Dispose ();
+        Application.Navigation = null;
+    }
+
+    [Fact]
+    public void PrevTabGroupKey_Moves_Focus_To_TabStop_In_Prev_TabGroup ()
+    {
+        // Arrange
+        Application.Navigation = new ();
+        var top = new Toplevel ();
+
+        var view1 = new View
         {
         {
-            Title = "win",
-            X = 0,
-            Y = 0,
-            Width = 20,
-            Height = 10
+            Id = "view1",
+            CanFocus = true,
+            TabStop = TabBehavior.TabGroup
         };
         };
-        var tf = new TextField { Width = 10 };
-        win.Add (tf);
 
 
-        var win2 = new Window
+        var subView1 = new View
         {
         {
-            Title = "win2",
-            X = 22,
-            Y = 0,
-            Width = 20,
-            Height = 10
+            Id = "subView1",
+            CanFocus = true,
+            TabStop = TabBehavior.TabStop
         };
         };
-        var tf2 = new TextField { Width = 10 };
-        win2.Add (tf2);
-        top.Add (win, win2);
 
 
-        Application.Begin (top);
+        view1.Add (subView1);
 
 
-        Assert.True (win.CanFocus);
-        Assert.True (win.HasFocus);
-        Assert.True (win2.CanFocus);
-        Assert.False (win2.HasFocus);
-        Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
+        var view2 = new View
+        {
+            Id = "view2",
+            CanFocus = true,
+            TabStop = TabBehavior.TabGroup
+        };
 
 
-        win.CanFocus = false;
-        Assert.False (win.CanFocus);
-        Assert.False (win.HasFocus);
-        Assert.True (win2.CanFocus);
-        Assert.True (win2.HasFocus);
-        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
+        var subView2 = new View
+        {
+            Id = "subView2",
+            CanFocus = true,
+            TabStop = TabBehavior.TabStop
+        };
+        view2.Add (subView2);
 
 
-        Application.OnKeyDown (Key.F6);
-        Assert.True (win2.CanFocus);
-        Assert.False (win.HasFocus);
-        Assert.True (win2.CanFocus);
-        Assert.True (win2.HasFocus);
-        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
+        top.Add (view1, view2);
+        Application.Top = top;
+        Application.Current = top;
+        view1.SetFocus ();
+        Assert.True (view1.HasFocus);
+        Assert.True (subView1.HasFocus);
+
+        // Act
+        Application.OnKeyDown (Application.PrevTabGroupKey);
+
+        // Assert
+        Assert.True (view2.HasFocus);
+        Assert.True (subView2.HasFocus);
 
 
-        Application.OnKeyDown (Key.F6);
-        Assert.False (win.CanFocus);
-        Assert.False (win.HasFocus);
-        Assert.True (win2.CanFocus);
-        Assert.True (win2.HasFocus);
-        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
         top.Dispose ();
         top.Dispose ();
+        Application.Navigation = null;
     }
     }
 
 
-    [Fact (Skip = "No longer valid test.")]
-    [AutoInitShutdown]
-    public void EnsuresTopOnFront_CanFocus_True_By_Keyboard ()
+    [Fact]
+    public void PrevTabKey_Moves_Focus_To_Prev_TabStop ()
     {
     {
-        Toplevel top = new ();
-
-        var win = new Window
-        {
-            Title = "win",
-            X = 0,
-            Y = 0,
-            Width = 20,
-            Height = 10
-        };
-        var tf = new TextField { Width = 10 };
-        win.Add (tf);
-
-        var win2 = new Window
-        {
-            Title = "win2",
-            X = 22,
-            Y = 0,
-            Width = 20,
-            Height = 10
-        };
-        var tf2 = new TextField { Width = 10 };
-        win2.Add (tf2);
-        top.Add (win, win2);
-
-        Application.Begin (top);
+        // Arrange
+        Application.Navigation = new ();
+        var top = new Toplevel ();
+        var view1 = new View { Id = "view1", CanFocus = true };
+        var view2 = new View { Id = "view2", CanFocus = true };
+        top.Add (view1, view2);
+        Application.Top = top;
+        Application.Current = top;
+        view1.SetFocus ();
 
 
-        Assert.True (win.CanFocus);
-        Assert.True (win.HasFocus);
-        Assert.True (win2.CanFocus);
-        Assert.False (win2.HasFocus);
-        Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
+        // Act
+        Application.OnKeyDown (Application.NextTabKey);
 
 
-        Application.OnKeyDown (Key.F6);
-        Assert.True (win.CanFocus);
-        Assert.False (win.HasFocus);
-        Assert.True (win2.CanFocus);
-        Assert.True (win2.HasFocus);
-        Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
+        // Assert
+        Assert.True (view2.HasFocus);
 
 
-        Application.OnKeyDown (Key.F6);
-        Assert.True (win.CanFocus);
-        Assert.True (win.HasFocus);
-        Assert.True (win2.CanFocus);
-        Assert.False (win2.HasFocus);
-        Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
         top.Dispose ();
         top.Dispose ();
+        Application.Navigation = null;
     }
     }
 
 
     [Fact]
     [Fact]
-    public void KeyUp_Event ()
+    public void QuitKey_Default_Is_Esc ()
     {
     {
-        Application.Init (new FakeDriver ());
-
-        // Setup some fake keypresses (This)
-        var input = "Tests";
-
-        Key originalQuitKey = Application.QuitKey;
-        Application.QuitKey = Key.Q.WithCtrl;
-        // Put a control-q in at the end
-        FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo ('Q', ConsoleKey.Q, false, false, true));
+        Application.ResetState (true);
 
 
-        foreach (char c in input.Reverse ())
-        {
-            if (char.IsLetter (c))
-            {
-                FakeConsole.MockKeyPresses.Push (
-                                                 new ConsoleKeyInfo (
-                                                                     c,
-                                                                     (ConsoleKey)char.ToUpper (c),
-                                                                     char.IsUpper (c),
-                                                                     false,
-                                                                     false
-                                                                    )
-                                                );
-            }
-            else
-            {
-                FakeConsole.MockKeyPresses.Push (
-                                                 new ConsoleKeyInfo (
-                                                                     c,
-                                                                     (ConsoleKey)c,
-                                                                     false,
-                                                                     false,
-                                                                     false
-                                                                    )
-                                                );
-            }
-        }
+        // Before Init
+        Assert.Equal (Key.Esc, Application.QuitKey);
 
 
-        int stackSize = FakeConsole.MockKeyPresses.Count;
+        Application.Init (new FakeDriver ());
 
 
-        var iterations = 0;
+        // After Init
+        Assert.Equal (Key.Esc, Application.QuitKey);
 
 
-        Application.Iteration += (s, a) =>
-                                 {
-                                     iterations++;
+        Application.Shutdown ();
+    }
 
 
-                                     // Stop if we run out of control...
-                                     if (iterations > 10)
-                                     {
-                                         Application.RequestStop ();
-                                     }
-                                 };
+    [Fact]
+    [AutoInitShutdown]
+    public void QuitKey_Getter_Setter ()
+    {
+        Toplevel top = new ();
+        var isQuiting = false;
 
 
-        var keyUps = 0;
-        var output = string.Empty;
-        var top = new Toplevel ();
+        top.Closing += (s, e) =>
+                       {
+                           isQuiting = true;
+                           e.Cancel = true;
+                       };
 
 
-        top.KeyUp += (sender, args) =>
-                     {
-                         if (args.KeyCode != (KeyCode.CtrlMask | KeyCode.Q))
-                         {
-                             output += args.AsRune;
-                         }
+        Application.Begin (top);
+        top.Running = true;
 
 
-                         keyUps++;
-                     };
+        Key prevKey = Application.QuitKey;
 
 
-        Application.Run (top);
-        Application.QuitKey = originalQuitKey;
+        Application.OnKeyDown (Application.QuitKey);
+        Assert.True (isQuiting);
 
 
-        // Input string should match output
-        Assert.Equal (input, output);
+        isQuiting = false;
+        Application.OnKeyDown (Application.QuitKey);
+        Assert.True (isQuiting);
 
 
-        // # of key up events should match stack size
-        //Assert.Equal (stackSize, keyUps);
-        // We can't use numbers variables on the left side of an Assert.Equal/NotEqual,
-        // it must be literal (Linux only).
-        Assert.Equal (6, keyUps);
+        isQuiting = false;
+        Application.QuitKey = Key.C.WithCtrl;
+        Application.OnKeyDown (prevKey); // Should not quit
+        Assert.False (isQuiting);
+        Application.OnKeyDown (Key.Q.WithCtrl); // Should not quit
+        Assert.False (isQuiting);
 
 
-        // # of key up events should match # of iterations
-        Assert.Equal (stackSize, iterations);
+        Application.OnKeyDown (Application.QuitKey);
+        Assert.True (isQuiting);
 
 
+        // Reset the QuitKey to avoid throws errors on another tests
+        Application.QuitKey = prevKey;
         top.Dispose ();
         top.Dispose ();
-        Application.Shutdown ();
-        Assert.Null (Application.Current);
-        Assert.Null (Application.Top);
-        Assert.Null (Application.MainLoop);
-        Assert.Null (Application.Driver);
     }
     }
 
 
     [Fact]
     [Fact]
-    [AutoInitShutdown]
-    public void KeyBinding_OnKeyDown ()
+    public void QuitKey_Quits ()
     {
     {
-        var view = new ScopedKeyBindingView ();
-        var invoked = false;
-        view.InvokingKeyBindings += (s, e) => invoked = true;
-
-        var top = new Toplevel ();
-        top.Add (view);
-        Application.Begin (top);
+        Assert.Null (_timeoutLock);
+        _timeoutLock = new ();
 
 
-        Application.OnKeyDown (Key.A);
-        Assert.False (invoked);
-        Assert.True (view.ApplicationCommand);
+        uint abortTime = 500;
+        var initialized = false;
+        var iteration = 0;
+        var shutdown = false;
+        object timeout = null;
 
 
-        invoked = false;
-        view.ApplicationCommand = false;
-        Application.KeyBindings.Remove (KeyCode.A); 
-        Application.OnKeyDown (Key.A); // old
-        Assert.False (invoked);
-        Assert.False (view.ApplicationCommand);
-        Application.KeyBindings.Add (Key.A.WithCtrl, view, Command.Save);
-        Application.OnKeyDown (Key.A); // old
-        Assert.False (invoked);
-        Assert.False (view.ApplicationCommand);
-        Application.OnKeyDown (Key.A.WithCtrl); // new
-        Assert.False (invoked);
-        Assert.True (view.ApplicationCommand);
+        Application.InitializedChanged += OnApplicationOnInitializedChanged;
 
 
-        invoked = false;
-        Application.OnKeyDown (Key.H);
-        Assert.True (invoked);
+        Application.Init (new FakeDriver ());
+        Assert.True (initialized);
+        Assert.False (shutdown);
 
 
-        invoked = false;
-        Assert.False (view.HasFocus);
-        Application.OnKeyDown (Key.F);
-        Assert.False (invoked);
+        _output.WriteLine ("Application.Run<Toplevel> ().Dispose ()..");
+        Application.Run<Toplevel> ().Dispose ();
+        _output.WriteLine ("Back from Application.Run<Toplevel> ().Dispose ()");
 
 
-        Assert.True (view.ApplicationCommand);
-        Assert.True (view.HotKeyCommand);
-        Assert.False (view.FocusedCommand);
-        top.Dispose ();
-    }
+        Assert.True (initialized);
+        Assert.False (shutdown);
 
 
-    [Fact]
-    [AutoInitShutdown]
-    public void KeyBinding_OnKeyDown_Negative ()
-    {
-        var view = new ScopedKeyBindingView ();
-        var invoked = false;
-        view.InvokingKeyBindings += (s, e) => invoked = true;
+        Assert.Equal (1, iteration);
 
 
-        var top = new Toplevel ();
-        top.Add (view);
-        Application.Begin (top);
+        Application.Shutdown ();
 
 
-        Application.OnKeyDown (Key.A.WithCtrl);
-        Assert.False (invoked);
-        Assert.False (view.ApplicationCommand);
-        Assert.False (view.HotKeyCommand);
-        Assert.False (view.FocusedCommand);
+        Application.InitializedChanged -= OnApplicationOnInitializedChanged;
 
 
-        invoked = false;
-        Assert.False (view.HasFocus);
-        Application.OnKeyDown (Key.Z);
-        Assert.False (invoked);
-        Assert.False (view.ApplicationCommand);
-        Assert.False (view.HotKeyCommand);
-        Assert.False (view.FocusedCommand);
-        top.Dispose ();
-    }
+        lock (_timeoutLock)
+        {
+            if (timeout is { })
+            {
+                Application.RemoveTimeout (timeout);
+                timeout = null;
+            }
+        }
 
 
-    [Fact]
-    [AutoInitShutdown]
-    public void KeyBinding_Application_KeyBindings_Add_Adds ()
-    {
-        Application.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept);
-        Application.KeyBindings.Add (Key.B, KeyBindingScope.Application, Command.Accept);
+        Assert.True (initialized);
+        Assert.True (shutdown);
 
 
-        Assert.True (Application.KeyBindings.TryGet (Key.A, out var binding));
-        Assert.Null (binding.BoundView);
-        Assert.True (Application.KeyBindings.TryGet (Key.B, out binding));
-        Assert.Null (binding.BoundView);
-    }
+#if DEBUG_IDISPOSABLE
+        Assert.Empty (Responder.Instances);
+#endif
+        lock (_timeoutLock)
+        {
+            _timeoutLock = null;
+        }
 
 
-    [Fact]
-    [AutoInitShutdown]
-    public void KeyBinding_View_KeyBindings_Add_Adds ()
-    {
-        View view1 = new ();
-        Application.KeyBindings.Add (Key.A, view1, Command.Accept);
+        return;
 
 
-        View view2 = new ();
-        Application.KeyBindings.Add (Key.B, view2, Command.Accept);
+        void OnApplicationOnInitializedChanged (object s, EventArgs<bool> a)
+        {
+            _output.WriteLine ("OnApplicationOnInitializedChanged: {0}", a.CurrentValue);
 
 
-        Assert.True (Application.KeyBindings.TryGet (Key.A, out var binding));
-        Assert.Equal (view1, binding.BoundView);
-        Assert.True (Application.KeyBindings.TryGet (Key.B, out binding));
-        Assert.Equal (view2, binding.BoundView);
-    }
+            if (a.CurrentValue)
+            {
+                Application.Iteration += OnApplicationOnIteration;
+                initialized = true;
 
 
-    [Fact]
-    [AutoInitShutdown]
-    public void KeyBinding_Application_RemoveKeyBinding_Removes ()
-    {
-        Application.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept);
+                lock (_timeoutLock)
+                {
+                    timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
+                }
+            }
+            else
+            {
+                Application.Iteration -= OnApplicationOnIteration;
+                shutdown = true;
+            }
+        }
 
 
-        Assert.True (Application.KeyBindings.TryGet (Key.A, out _));
+        bool ForceCloseCallback ()
+        {
+            lock (_timeoutLock)
+            {
+                _output.WriteLine ($"ForceCloseCallback. iteration: {iteration}");
 
 
-        Application.KeyBindings.Remove (Key.A);
-        Assert.False (Application.KeyBindings.TryGet (Key.A, out _));
-    }
+                if (timeout is { })
+                {
+                    timeout = null;
+                }
+            }
 
 
-    [Fact]
-    [AutoInitShutdown]
-    public void KeyBinding_View_KeyBindings_RemoveKeyBinding_Removes ()
-    {
+            Application.ResetState (true);
+            Assert.Fail ($"Failed to Quit with {Application.QuitKey} after {abortTime}ms. Force quit.");
 
 
-        View view1 = new ();
-        Application.KeyBindings.Add (Key.A, view1, Command.Accept);
+            return false;
+        }
 
 
-        View view2 = new ();
-        Application.KeyBindings.Add (Key.B, view1, Command.Accept);
+        void OnApplicationOnIteration (object s, IterationEventArgs a)
+        {
+            _output.WriteLine ("Iteration: {0}", iteration);
+            iteration++;
+            Assert.True (iteration < 2, "Too many iterations, something is wrong.");
 
 
-        Application.KeyBindings.Remove (Key.A, view1);
-        Assert.False (Application.KeyBindings.TryGet (Key.A, out _));
+            if (Application.IsInitialized)
+            {
+                _output.WriteLine ("  Pressing QuitKey");
+                Application.OnKeyDown (Application.QuitKey);
+            }
+        }
     }
     }
 
 
     // Test View for testing Application key Bindings
     // Test View for testing Application key Bindings