Browse Source

Merge pull request #3627 from tig/v2_2491-Arrangement-Overlapped

Partially Adresses #2491. Refactors how `Focus` works
Tig 11 months ago
parent
commit
56d9c59484
100 changed files with 2588 additions and 2271 deletions
  1. 1 0
      .github/workflows/publish.yml
  2. 7 1
      CONTRIBUTING.md
  3. 1 1
      README.md
  4. 172 173
      Terminal.Gui/Application/Application.Keyboard.cs
  5. 21 1
      Terminal.Gui/Application/Application.Mouse.cs
  6. 9 3
      Terminal.Gui/Application/Application.Run.cs
  7. 2 0
      Terminal.Gui/Application/Application.cs
  8. 33 159
      Terminal.Gui/Application/ApplicationNavigation.cs
  9. 3 3
      Terminal.Gui/Application/ApplicationOverlapped.cs
  10. 1 0
      Terminal.Gui/Application/MainLoop.cs
  11. 1 0
      Terminal.Gui/Input/Responder.cs
  12. 3 6
      Terminal.Gui/Text/Autocomplete/AutocompleteBase.cs
  13. 33 36
      Terminal.Gui/Text/Autocomplete/IAutocomplete.cs
  14. 5 4
      Terminal.Gui/Text/Autocomplete/PopupAutocomplete.PopUp.cs
  15. 98 70
      Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs
  16. 1 0
      Terminal.Gui/Text/CollectionNavigatorBase.cs
  17. 1 1
      Terminal.Gui/View/Adornment/Adornment.cs
  18. 1 1
      Terminal.Gui/View/Adornment/ShadowView.cs
  19. 15 19
      Terminal.Gui/View/Navigation/FocusEventArgs.cs
  20. 10 2
      Terminal.Gui/View/Navigation/TabBehavior.cs
  21. 4 3
      Terminal.Gui/View/Orientation/IOrientation.cs
  22. 2 1
      Terminal.Gui/View/Orientation/Orientation.cs
  23. 4 3
      Terminal.Gui/View/Orientation/OrientationHelper.cs
  24. 7 9
      Terminal.Gui/View/SuperViewChangedEventArgs.cs
  25. 1 0
      Terminal.Gui/View/View.Adornments.cs
  26. 4 4
      Terminal.Gui/View/View.Content.cs
  27. 1 0
      Terminal.Gui/View/View.Cursor.cs
  28. 1 3
      Terminal.Gui/View/View.Diagnostics.cs
  29. 8 6
      Terminal.Gui/View/View.Drawing.cs
  30. 95 85
      Terminal.Gui/View/View.Hierarchy.cs
  31. 47 50
      Terminal.Gui/View/View.Keyboard.cs
  32. 7 7
      Terminal.Gui/View/View.Layout.cs
  33. 9 9
      Terminal.Gui/View/View.Mouse.cs
  34. 415 534
      Terminal.Gui/View/View.Navigation.cs
  35. 1 1
      Terminal.Gui/View/View.Text.cs
  36. 41 25
      Terminal.Gui/View/View.cs
  37. 7 6
      Terminal.Gui/Views/Bar.cs
  38. 1 1
      Terminal.Gui/Views/ColorBar.cs
  39. 4 0
      Terminal.Gui/Views/ColorModelStrategy.cs
  40. 46 13
      Terminal.Gui/Views/ColorPicker.cs
  41. 1 0
      Terminal.Gui/Views/ColorPicker16.cs
  42. 51 50
      Terminal.Gui/Views/ComboBox.cs
  43. 4 3
      Terminal.Gui/Views/DatePicker.cs
  44. 19 24
      Terminal.Gui/Views/FileDialog.cs
  45. 4 0
      Terminal.Gui/Views/HexView.cs
  46. 2 4
      Terminal.Gui/Views/ListView.cs
  47. 4 0
      Terminal.Gui/Views/Menu/ContextMenu.cs
  48. 7 1
      Terminal.Gui/Views/Menu/Menu.cs
  49. 3 3
      Terminal.Gui/Views/Menu/MenuBar.cs
  50. 1 1
      Terminal.Gui/Views/NumericUpDown.cs
  51. 16 19
      Terminal.Gui/Views/ScrollBarView.cs
  52. 5 15
      Terminal.Gui/Views/Shortcut.cs
  53. 19 47
      Terminal.Gui/Views/TabView.cs
  54. 30 5
      Terminal.Gui/Views/TextField.cs
  55. 25 3
      Terminal.Gui/Views/TextView.cs
  56. 6 1
      Terminal.Gui/Views/Tile.cs
  57. 16 8
      Terminal.Gui/Views/TileView.cs
  58. 0 22
      Terminal.Gui/Views/Toplevel.cs
  59. 10 5
      Terminal.Gui/Views/TreeView/TreeView.cs
  60. 2 2
      UICatalog/KeyBindingsDialog.cs
  61. 11 18
      UICatalog/Scenarios/ASCIICustomButton.cs
  62. 2 1
      UICatalog/Scenarios/AdornmentEditor.cs
  63. 11 4
      UICatalog/Scenarios/Adornments.cs
  64. 86 57
      UICatalog/Scenarios/AdornmentsEditor.cs
  65. 74 55
      UICatalog/Scenarios/AllViewsTester.cs
  66. 26 15
      UICatalog/Scenarios/BackgroundWorkerCollection.cs
  67. 1 1
      UICatalog/Scenarios/ChineseUI.cs
  68. 10 10
      UICatalog/Scenarios/ColorPicker.cs
  69. 27 6
      UICatalog/Scenarios/ConfigurationEditor.cs
  70. 4 0
      UICatalog/Scenarios/ContentScrolling.cs
  71. 1 1
      UICatalog/Scenarios/CsvEditor.cs
  72. 4 4
      UICatalog/Scenarios/Dialogs.cs
  73. 7 7
      UICatalog/Scenarios/DynamicMenuBar.cs
  74. 2 2
      UICatalog/Scenarios/DynamicStatusBar.cs
  75. 8 8
      UICatalog/Scenarios/Editor.cs
  76. 3 3
      UICatalog/Scenarios/GraphViewExample.cs
  77. 1 1
      UICatalog/Scenarios/HexEditor.cs
  78. 12 7
      UICatalog/Scenarios/KeyBindings.cs
  79. 1 1
      UICatalog/Scenarios/LineCanvasExperiment.cs
  80. 1 1
      UICatalog/Scenarios/LineViewExample.cs
  81. 1 1
      UICatalog/Scenarios/ListColumns.cs
  82. 267 0
      UICatalog/Scenarios/Navigation.cs
  83. 3 3
      UICatalog/Scenarios/Notepad.cs
  84. 8 9
      UICatalog/Scenarios/NumericUpDownDemo.cs
  85. 1 1
      UICatalog/Scenarios/ProgressBarStyles.cs
  86. 2 1
      UICatalog/Scenarios/RunTExample.cs
  87. 6 2
      UICatalog/Scenarios/ShadowStyles.cs
  88. 2 1
      UICatalog/Scenarios/SingleBackgroundWorker.cs
  89. 1 1
      UICatalog/Scenarios/Sliders.cs
  90. 15 8
      UICatalog/Scenarios/TabViewExample.cs
  91. 1 1
      UICatalog/Scenarios/TableEditor.cs
  92. 11 91
      UICatalog/Scenarios/ViewExperiments.cs
  93. 3 1
      UICatalog/Scenarios/Wizards.cs
  94. 2 8
      UICatalog/UICatalog.cs
  95. 111 124
      UnitTests/Application/Application.NavigationTests.cs
  96. 11 2
      UnitTests/Application/ApplicationTests.cs
  97. 500 356
      UnitTests/Application/KeyboardTests.cs
  98. 0 4
      UnitTests/FileServices/FileDialogTests.cs
  99. 2 2
      UnitTests/Input/ResponderTests.cs
  100. 1 0
      UnitTests/TestHelpers.cs

+ 1 - 0
.github/workflows/publish.yml

@@ -44,6 +44,7 @@ jobs:
       run: |
       run: |
         dotnet-gitversion /updateprojectfiles
         dotnet-gitversion /updateprojectfiles
         dotnet build --no-incremental --nologo --force --configuration Release
         dotnet build --no-incremental --nologo --force --configuration Release
+        dotnet test --configuration Release
 
 
     - name: Pack
     - name: Pack
       run: dotnet pack -c Release --include-symbols -p:Version='${{ steps.gitversion.outputs.SemVer }}' 
       run: dotnet pack -c Release --include-symbols -p:Version='${{ steps.gitversion.outputs.SemVer }}' 

+ 7 - 1
CONTRIBUTING.md

@@ -90,7 +90,13 @@ remote:
 
 
 Follow the template instructions found on Github.
 Follow the template instructions found on Github.
 
 
-## Terminal.Gui Coding Style
+## Tenets for [gui-cs](www.github.com/gui-cs) Code Style (Unless you have better ones)
+
+* **Six-Year-Old Reading Level** - Our code style is biased towards code readability and away from terseness. This is *Systems Software* and needs to stand the test of time. Code should be structured and use variable names that make it readable by a 6-year-old, and comments in code are encouraged. 
+* **Consistency, Consistency, Consistency** - We adopt and document our standards for code style and then enforce them ruthlessly. For example, we require code reviews to pay attention to code style, not just functionality. 
+* **Don't be Weird** - Like all developers we have opinions, but our opinions on code style are tempered by existing standards. We are biased towards code style that used by Microsoft and other leading dotnet developers. For example, we choose 4 spaces for indentation instead of 8.
+* **Set and Forget** - We embrace and encourage the use of technology that makes it easy for contributors to apply best-practice code-style, such as ReSharper. As we do so we are mindful that tools can cause hidden issues and merge hell.
+* **Documentation is the Spec** - We care deeply about providing delightful developer documentation and are sticklers for grammar and clarity. If the code and the docs conflict, we are biased to believe what we wrote in the API documentation. This drives a virtuous cycle of clear thinking.
 
 
 **Terminal.Gui** uses a derivative of the [Microsoft C# Coding Conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions), with any deviations from those (somewhat older) conventions codified in the .editorconfig for the solution, as well as even more specific definitions in team-shared dotsettings files, used by ReSharper and Rider.\
 **Terminal.Gui** uses a derivative of the [Microsoft C# Coding Conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions), with any deviations from those (somewhat older) conventions codified in the .editorconfig for the solution, as well as even more specific definitions in team-shared dotsettings files, used by ReSharper and Rider.\
 Before you commit code, please run the formatting rules on **only the code file(s) you have modified**, in one of the following ways, in order of most preferred to least preferred:
 Before you commit code, please run the formatting rules on **only the code file(s) you have modified**, in one of the following ways, in order of most preferred to least preferred:

+ 1 - 1
README.md

@@ -35,7 +35,7 @@ dotnet run
 * [API Documentation](https://gui-cs.github.io/Terminal.GuiV2Docs/api/Terminal.Gui.html)
 * [API Documentation](https://gui-cs.github.io/Terminal.GuiV2Docs/api/Terminal.Gui.html)
 * [Documentation Home](https://gui-cs.github.io/Terminal.GuiV2Docs)
 * [Documentation Home](https://gui-cs.github.io/Terminal.GuiV2Docs)
 
 
-_The Documentation matches the most recent Nuget release from the `v2_develop` branch. The documentation for v1 is here: ([![Version](https://img.shields.io/nuget/v/Terminal.Gui.svg)](https://www.nuget.org/packages/Terminal.Gui))_
+The above documentation matches the most recent Nuget release from the `v2_develop` branch. Get the [v1 documentation here](This is the v2 API documentation. For v1 go here: https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.html)
 
 
 See the [`Terminal.Gui/` README](https://github.com/gui-cs/Terminal.Gui/tree/master/Terminal.Gui) for an overview of how the library is structured. 
 See the [`Terminal.Gui/` README](https://github.com/gui-cs/Terminal.Gui/tree/master/Terminal.Gui) for an overview of how the library is structured. 
 
 

+ 172 - 173
Terminal.Gui/Application/Application.Keyboard.cs

@@ -1,45 +1,49 @@
 #nullable enable
 #nullable enable
-using System.Text.Json.Serialization;
-
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 public static partial class Application // Keyboard handling
 public static partial class Application // Keyboard handling
 {
 {
+    private static Key _nextTabGroupKey = Key.F6; // Resources/config.json overrrides
     private static Key _nextTabKey = Key.Tab; // Resources/config.json overrrides
     private static Key _nextTabKey = Key.Tab; // Resources/config.json overrrides
 
 
-    /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
-    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static Key NextTabKey
-    {
-        get => _nextTabKey;
-        set
-        {
-            if (_nextTabKey != value)
-            {
-                ReplaceKey (_nextTabKey, value);
-                _nextTabKey = value;
-            }
-        }
-    }
+    private static Key _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrrides
 
 
     private static Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrrides
     private static Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrrides
 
 
-    /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
-    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static Key PrevTabKey
-    {
-        get => _prevTabKey;
-        set
-        {
-            if (_prevTabKey != value)
-            {
-                ReplaceKey (_prevTabKey, value);
-                _prevTabKey = value;
-            }
-        }
-    }
+    private static Key _quitKey = Key.Esc; // Resources/config.json overrrides
 
 
-    private static Key _nextTabGroupKey = Key.F6; // Resources/config.json overrrides
+    static Application () { AddApplicationKeyBindings (); }
+
+    /// <summary>Gets the key bindings for this view.</summary>
+    public static KeyBindings KeyBindings { get; internal set; } = new ();
+
+    /// <summary>
+    ///     Event fired when the user presses a key. Fired by <see cref="OnKeyDown"/>.
+    ///     <para>
+    ///         Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
+    ///         additional processing.
+    ///     </para>
+    /// </summary>
+    /// <remarks>
+    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
+    ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
+    ///     <para>Fired after <see cref="KeyDown"/> and before <see cref="KeyUp"/>.</para>
+    /// </remarks>
+    public static event EventHandler<Key>? KeyDown;
+
+    /// <summary>
+    ///     Event fired when the user releases a key. Fired by <see cref="OnKeyUp"/>.
+    ///     <para>
+    ///         Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
+    ///         additional processing.
+    ///     </para>
+    /// </summary>
+    /// <remarks>
+    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
+    ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
+    ///     <para>Fired after <see cref="KeyDown"/>.</para>
+    /// </remarks>
+    public static event EventHandler<Key>? KeyUp;
 
 
     /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
     /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
@@ -56,71 +60,21 @@ public static partial class Application // Keyboard handling
         }
         }
     }
     }
 
 
-    private static Key _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrrides
-
-    /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
-    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static Key PrevTabGroupKey
-    {
-        get => _prevTabGroupKey;
-        set
-        {
-            if (_prevTabGroupKey != value)
-            {
-                ReplaceKey (_prevTabGroupKey, value);
-                _prevTabGroupKey = value;
-            }
-        }
-    }
-
-    private static Key _quitKey = Key.Esc; // Resources/config.json overrrides
-
-    /// <summary>Gets or sets the key to quit the application.</summary>
+    /// <summary>Alternative key to navigate forwards through views. Ctrl+Tab is the primary key.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
-    public static Key QuitKey
+    public static Key NextTabKey
     {
     {
-        get => _quitKey;
+        get => _nextTabKey;
         set
         set
         {
         {
-            if (_quitKey != value)
+            if (_nextTabKey != value)
             {
             {
-                ReplaceKey (_quitKey, value);
-                _quitKey = value;
+                ReplaceKey (_nextTabKey, value);
+                _nextTabKey = value;
             }
             }
         }
         }
     }
     }
 
 
-    private static void ReplaceKey (Key oldKey, Key newKey)
-    {
-        if (KeyBindings.Bindings.Count == 0)
-        {
-            return;
-        }
-
-        if (newKey == Key.Empty)
-        {
-            KeyBindings.Remove (oldKey);
-        }
-        else
-        {
-            KeyBindings.ReplaceKey (oldKey, newKey);
-        }
-    }
-
-    /// <summary>
-    ///     Event fired when the user presses a key. Fired by <see cref="OnKeyDown"/>.
-    ///     <para>
-    ///         Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
-    ///         additional processing.
-    ///     </para>
-    /// </summary>
-    /// <remarks>
-    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
-    ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
-    ///     <para>Fired after <see cref="KeyDown"/> and before <see cref="KeyUp"/>.</para>
-    /// </remarks>
-    public static event EventHandler<Key>? KeyDown;
-
     /// <summary>
     /// <summary>
     ///     Called by the <see cref="ConsoleDriver"/> when the user presses a key. Fires the <see cref="KeyDown"/> event
     ///     Called by the <see cref="ConsoleDriver"/> when the user presses a key. Fires the <see cref="KeyDown"/> event
     ///     then calls <see cref="View.NewKeyDownEvent"/> on all top level views. Called after <see cref="OnKeyDown"/> and
     ///     then calls <see cref="View.NewKeyDownEvent"/> on all top level views. Called after <see cref="OnKeyDown"/> and
@@ -190,24 +144,7 @@ public static partial class Application // Keyboard handling
 
 
                 foreach (Command command in appBinding.Commands)
                 foreach (Command command in appBinding.Commands)
                 {
                 {
-                    if (!CommandImplementations!.ContainsKey (command))
-                    {
-                        throw new NotSupportedException (
-                                                         @$"A KeyBinding was set up for the command {command} ({keyEvent}) but that command is not supported by Application."
-                                                        );
-                    }
-
-                    if (CommandImplementations.TryGetValue (command, out Func<CommandContext, bool?>? implementation))
-                    {
-                        var context = new CommandContext (command, keyEvent, appBinding); // Create the context here
-                        toReturn = implementation (context);
-                    }
-
-                    // if ever see a true then that's what we will return
-                    if (toReturn ?? false)
-                    {
-                        toReturn = true;
-                    }
+                    toReturn = InvokeCommand (command, keyEvent, appBinding);
                 }
                 }
 
 
                 return toReturn ?? true;
                 return toReturn ?? true;
@@ -218,18 +155,30 @@ public static partial class Application // Keyboard handling
     }
     }
 
 
     /// <summary>
     /// <summary>
-    ///     Event fired when the user releases a key. Fired by <see cref="OnKeyUp"/>.
-    ///     <para>
-    ///         Set <see cref="Key.Handled"/> to <see langword="true"/> to indicate the key was handled and to prevent
-    ///         additional processing.
-    ///     </para>
+    /// INTENRAL method to invoke one of the commands in <see cref="CommandImplementations"/>
     /// </summary>
     /// </summary>
-    /// <remarks>
-    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
-    ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
-    ///     <para>Fired after <see cref="KeyDown"/>.</para>
-    /// </remarks>
-    public static event EventHandler<Key>? KeyUp;
+    /// <param name="command"></param>
+    /// <param name="keyEvent"></param>
+    /// <param name="appBinding"></param>
+    /// <returns></returns>
+    /// <exception cref="NotSupportedException"></exception>
+    private static bool? InvokeCommand (Command command, Key keyEvent, KeyBinding appBinding)
+    {
+        if (!CommandImplementations!.ContainsKey (command))
+        {
+            throw new NotSupportedException (
+                                             @$"A KeyBinding was set up for the command {command} ({keyEvent}) but that command is not supported by Application."
+                                            );
+        }
+
+        if (CommandImplementations.TryGetValue (command, out Func<CommandContext, bool?>? implementation))
+        {
+            var context = new CommandContext (command, keyEvent, appBinding); // Create the context here
+            return implementation (context);
+        }
+
+        return false;
+    }
 
 
     /// <summary>
     /// <summary>
     ///     Called by the <see cref="ConsoleDriver"/> when the user releases a key. Fires the <see cref="KeyUp"/> event
     ///     Called by the <see cref="ConsoleDriver"/> when the user releases a key. Fires the <see cref="KeyUp"/> event
@@ -268,33 +217,50 @@ public static partial class Application // Keyboard handling
         return false;
         return false;
     }
     }
 
 
-    /// <summary>Gets the key bindings for this view.</summary>
-    public static KeyBindings KeyBindings { get; internal set; } = new ();
-
-    /// <summary>
-    ///     Commands for Application.
-    /// </summary>
-    private static Dictionary<Command, Func<CommandContext, bool?>>? CommandImplementations { get; set; }
+    /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Key PrevTabGroupKey
+    {
+        get => _prevTabGroupKey;
+        set
+        {
+            if (_prevTabGroupKey != value)
+            {
+                ReplaceKey (_prevTabGroupKey, value);
+                _prevTabGroupKey = value;
+            }
+        }
+    }
 
 
-    /// <summary>
-    ///     <para>
-    ///         Sets the function that will be invoked for a <see cref="Command"/>.
-    ///     </para>
-    ///     <para>
-    ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
-    ///         replace the old one.
-    ///     </para>
-    /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         This version of AddCommand is for commands that do not require a <see cref="CommandContext"/>.
-    ///     </para>
-    /// </remarks>
-    /// <param name="command">The command.</param>
-    /// <param name="f">The function.</param>
-    private static void AddCommand (Command command, Func<bool?> f) { CommandImplementations! [command] = ctx => f (); }
+    /// <summary>Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Key PrevTabKey
+    {
+        get => _prevTabKey;
+        set
+        {
+            if (_prevTabKey != value)
+            {
+                ReplaceKey (_prevTabKey, value);
+                _prevTabKey = value;
+            }
+        }
+    }
 
 
-    static Application () { AddApplicationKeyBindings (); }
+    /// <summary>Gets or sets the key to quit the application.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Key QuitKey
+    {
+        get => _quitKey;
+        set
+        {
+            if (_quitKey != value)
+            {
+                ReplaceKey (_quitKey, value);
+                _quitKey = value;
+            }
+        }
+    }
 
 
     internal static void AddApplicationKeyBindings ()
     internal static void AddApplicationKeyBindings ()
     {
     {
@@ -303,7 +269,7 @@ public static partial class Application // Keyboard handling
         // Things this view knows how to do
         // Things this view knows how to do
         AddCommand (
         AddCommand (
                     Command.QuitToplevel, // TODO: IRunnable: Rename to Command.Quit to make more generic.
                     Command.QuitToplevel, // TODO: IRunnable: Rename to Command.Quit to make more generic.
-                    () =>
+                    static () =>
                     {
                     {
                         if (ApplicationOverlapped.OverlappedTop is { })
                         if (ApplicationOverlapped.OverlappedTop is { })
                         {
                         {
@@ -320,7 +286,7 @@ public static partial class Application // Keyboard handling
 
 
         AddCommand (
         AddCommand (
                     Command.Suspend,
                     Command.Suspend,
-                    () =>
+                    static () =>
                     {
                     {
                         Driver?.Suspend ();
                         Driver?.Suspend ();
 
 
@@ -330,47 +296,47 @@ public static partial class Application // Keyboard handling
 
 
         AddCommand (
         AddCommand (
                     Command.NextView,
                     Command.NextView,
-                    () =>
-                    {
-                        ApplicationNavigation.MoveNextView ();
-
-                        return true;
-                    }
-                   );
+                    static () => Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop));
 
 
         AddCommand (
         AddCommand (
                     Command.PreviousView,
                     Command.PreviousView,
-                    () =>
-                    {
-                        ApplicationNavigation.MovePreviousView ();
-
-                        return true;
-                    }
-                   );
+                    static () => Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop));
 
 
         AddCommand (
         AddCommand (
                     Command.NextViewOrTop,
                     Command.NextViewOrTop,
-                    () =>
+                    static () =>
                     {
                     {
-                        ApplicationNavigation.MoveNextViewOrTop ();
+                        // TODO: This OverlapppedTop tomfoolery goes away in addressing #2491
+                        if (ApplicationOverlapped.OverlappedTop is { })
+                        {
+                            ApplicationOverlapped.OverlappedMoveNext ();
 
 
-                        return true;
+                            return true;
+                        }
+
+                        return Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
                     }
                     }
                    );
                    );
 
 
         AddCommand (
         AddCommand (
                     Command.PreviousViewOrTop,
                     Command.PreviousViewOrTop,
-                    () =>
+                    static () =>
                     {
                     {
-                        ApplicationNavigation.MovePreviousViewOrTop ();
+                        // TODO: This OverlapppedTop tomfoolery goes away in addressing #2491
+                        if (ApplicationOverlapped.OverlappedTop is { })
+                        {
+                            ApplicationOverlapped.OverlappedMovePrevious ();
 
 
-                        return true;
+                            return true;
+                        }
+
+                        return Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
                     }
                     }
                    );
                    );
 
 
         AddCommand (
         AddCommand (
                     Command.Refresh,
                     Command.Refresh,
-                    () =>
+                    static () =>
                     {
                     {
                         Refresh ();
                         Refresh ();
 
 
@@ -396,8 +362,8 @@ public static partial class Application // Keyboard handling
         KeyBindings.Add (NextTabKey, KeyBindingScope.Application, Command.NextView);
         KeyBindings.Add (NextTabKey, KeyBindingScope.Application, Command.NextView);
         KeyBindings.Add (PrevTabKey, KeyBindingScope.Application, Command.PreviousView);
         KeyBindings.Add (PrevTabKey, KeyBindingScope.Application, Command.PreviousView);
 
 
-        KeyBindings.Add (NextTabGroupKey, KeyBindingScope.Application, Command.NextViewOrTop); // Needed on Unix
-        KeyBindings.Add (PrevTabGroupKey, KeyBindingScope.Application, Command.PreviousViewOrTop); // Needed on Unix
+        KeyBindings.Add (NextTabGroupKey, KeyBindingScope.Application, Command.NextViewOrTop);
+        KeyBindings.Add (PrevTabGroupKey, KeyBindingScope.Application, Command.PreviousViewOrTop);
 
 
         // TODO: Refresh Key should be configurable
         // TODO: Refresh Key should be configurable
         KeyBindings.Add (Key.F5, KeyBindingScope.Application, Command.Refresh);
         KeyBindings.Add (Key.F5, KeyBindingScope.Application, Command.Refresh);
@@ -407,13 +373,6 @@ public static partial class Application // Keyboard handling
         {
         {
             KeyBindings.Add (Key.Z.WithCtrl, KeyBindingScope.Application, Command.Suspend);
             KeyBindings.Add (Key.Z.WithCtrl, KeyBindingScope.Application, Command.Suspend);
         }
         }
-
-#if UNIX_KEY_BINDINGS
-        KeyBindings.Add (Key.L.WithCtrl, Command.Refresh); // Unix
-        KeyBindings.Add (Key.F.WithCtrl, Command.NextView); // Unix
-        KeyBindings.Add (Key.I.WithCtrl, Command.NextView); // Unix
-        KeyBindings.Add (Key.B.WithCtrl, Command.PreviousView); // Unix
-#endif
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -432,4 +391,44 @@ public static partial class Application // Keyboard handling
                           .Distinct ()
                           .Distinct ()
                           .ToList ();
                           .ToList ();
     }
     }
+
+    /// <summary>
+    ///     <para>
+    ///         Sets the function that will be invoked for a <see cref="Command"/>.
+    ///     </para>
+    ///     <para>
+    ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
+    ///         replace the old one.
+    ///     </para>
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This version of AddCommand is for commands that do not require a <see cref="CommandContext"/>.
+    ///     </para>
+    /// </remarks>
+    /// <param name="command">The command.</param>
+    /// <param name="f">The function.</param>
+    private static void AddCommand (Command command, Func<bool?> f) { CommandImplementations! [command] = ctx => f (); }
+
+    /// <summary>
+    ///     Commands for Application.
+    /// </summary>
+    private static Dictionary<Command, Func<CommandContext, bool?>>? CommandImplementations { get; set; }
+
+    private static void ReplaceKey (Key oldKey, Key newKey)
+    {
+        if (KeyBindings.Bindings.Count == 0)
+        {
+            return;
+        }
+
+        if (newKey == Key.Empty)
+        {
+            KeyBindings.Remove (oldKey);
+        }
+        else
+        {
+            KeyBindings.ReplaceKey (oldKey, newKey);
+        }
+    }
 }
 }

+ 21 - 1
Terminal.Gui/Application/Application.Mouse.cs

@@ -143,6 +143,12 @@ public static partial class Application // Mouse handling
 
 
         if (view is { })
         if (view is { })
         {
         {
+#if DEBUG_IDISPOSABLE
+            if (view.WasDisposed)
+            {
+                throw new ObjectDisposedException (view.GetType ().FullName);
+            }
+#endif
             mouseEvent.View = view;
             mouseEvent.View = view;
         }
         }
 
 
@@ -155,6 +161,13 @@ public static partial class Application // Mouse handling
 
 
         if (MouseGrabView is { })
         if (MouseGrabView is { })
         {
         {
+
+#if DEBUG_IDISPOSABLE
+            if (MouseGrabView.WasDisposed)
+            {
+                throw new ObjectDisposedException (MouseGrabView.GetType ().FullName);
+            }
+#endif
             // If the mouse is grabbed, send the event to the view that grabbed it.
             // If the mouse is grabbed, send the event to the view that grabbed it.
             // The coordinates are relative to the Bounds of the view that grabbed the mouse.
             // The coordinates are relative to the Bounds of the view that grabbed the mouse.
             Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.Position);
             Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.Position);
@@ -174,8 +187,15 @@ public static partial class Application // Mouse handling
             }
             }
 
 
             //System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
             //System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
-            if (MouseGrabView.NewMouseEvent (viewRelativeMouseEvent) is true)
+            if (MouseGrabView?.NewMouseEvent (viewRelativeMouseEvent) is true)
+            {
+                return;
+            }
+
+            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+            if (MouseGrabView is null && view is Adornment)
             {
             {
+                // The view that grabbed the mouse has been disposed
                 return;
                 return;
             }
             }
         }
         }

+ 9 - 3
Terminal.Gui/Application/Application.Run.cs

@@ -99,7 +99,7 @@ public static partial class Application // Run (Begin, Run, End, Stop)
             else if (ApplicationOverlapped.OverlappedTop is { } && toplevel != Top && TopLevels.Contains (Top!))
             else if (ApplicationOverlapped.OverlappedTop is { } && toplevel != Top && TopLevels.Contains (Top!))
             {
             {
                 // BUGBUG: Don't call OnLeave/OnEnter directly! Set HasFocus to false and let the system handle it.
                 // BUGBUG: Don't call OnLeave/OnEnter directly! Set HasFocus to false and let the system handle it.
-                Top!.OnLeave (toplevel);
+                //Top!.OnLeave (toplevel);
             }
             }
 
 
             // BUGBUG: We should not depend on `Id` internally.
             // BUGBUG: We should not depend on `Id` internally.
@@ -186,7 +186,14 @@ public static partial class Application // Run (Begin, Run, End, Stop)
 
 
         toplevel.LayoutSubviews ();
         toplevel.LayoutSubviews ();
         toplevel.PositionToplevels ();
         toplevel.PositionToplevels ();
-        toplevel.FocusFirst (null);
+
+        // TODO: Should this use FindDeepestFocusableView instead?
+        // Try to set initial focus to any TabStop
+        if (!toplevel.HasFocus)
+        {
+            toplevel.SetFocus ();
+        }
+
         ApplicationOverlapped.BringOverlappedTopToFront ();
         ApplicationOverlapped.BringOverlappedTopToFront ();
 
 
         if (refreshDriver)
         if (refreshDriver)
@@ -858,7 +865,6 @@ public static partial class Application // Run (Begin, Run, End, Stop)
                 if (Current is { HasFocus: false })
                 if (Current is { HasFocus: false })
                 {
                 {
                     Current.SetFocus ();
                     Current.SetFocus ();
-                    Current.RestoreFocus ();
                 }
                 }
             }
             }
 
 

+ 2 - 0
Terminal.Gui/Application/Application.cs

@@ -138,6 +138,8 @@ public static partial class Application
     // starts running and after Shutdown returns.
     // starts running and after Shutdown returns.
     internal static void ResetState (bool ignoreDisposed = false)
     internal static void ResetState (bool ignoreDisposed = false)
     {
     {
+        Application.Navigation = new ApplicationNavigation ();
+
         // Shutdown is the bookend for Init. As such it needs to clean up all resources
         // Shutdown is the bookend for Init. As such it needs to clean up all resources
         // Init created. Apps that do any threading will need to code defensively for this.
         // Init created. Apps that do any threading will need to code defensively for this.
         // e.g. see Issue #537
         // e.g. see Issue #537

+ 33 - 159
Terminal.Gui/Application/ApplicationNavigation.cs

@@ -7,7 +7,6 @@ namespace Terminal.Gui;
 /// </summary>
 /// </summary>
 public class ApplicationNavigation
 public class ApplicationNavigation
 {
 {
-
     /// <summary>
     /// <summary>
     ///     Initializes a new instance of the <see cref="ApplicationNavigation"/> class.
     ///     Initializes a new instance of the <see cref="ApplicationNavigation"/> class.
     /// </summary>
     /// </summary>
@@ -16,38 +15,17 @@ public class ApplicationNavigation
         // TODO: Move navigation key bindings here from AddApplicationKeyBindings
         // TODO: Move navigation key bindings here from AddApplicationKeyBindings
     }
     }
 
 
-    private View? _focused = null;
-
-    /// <summary>
-    ///     Gets the most focused <see cref="View"/> in the application, if there is one.
-    /// </summary>
-    public View? GetFocused () { return _focused; }
-
-    /// <summary>
-    ///     INTERNAL method to record the most focused <see cref="View"/> in the application.
-    /// </summary>
-    /// <remarks>
-    ///     Raises <see cref="FocusedChanged"/>.
-    /// </remarks>
-    internal void SetFocused (View? value)
-    {
-        if (_focused == value)
-        {
-            return;
-        }
-
-        _focused = value;
-
-        FocusedChanged?.Invoke (null, EventArgs.Empty);
-
-        return;
-    }
+    private View? _focused;
 
 
     /// <summary>
     /// <summary>
     ///     Raised when the most focused <see cref="View"/> in the application has changed.
     ///     Raised when the most focused <see cref="View"/> in the application has changed.
     /// </summary>
     /// </summary>
     public event EventHandler<EventArgs>? FocusedChanged;
     public event EventHandler<EventArgs>? FocusedChanged;
 
 
+    /// <summary>
+    ///     Gets the most focused <see cref="View"/> in the application, if there is one.
+    /// </summary>
+    public View? GetFocused () { return _focused; }
 
 
     /// <summary>
     /// <summary>
     ///     Gets whether <paramref name="view"/> is in the Subview hierarchy of <paramref name="start"/>.
     ///     Gets whether <paramref name="view"/> is in the Subview hierarchy of <paramref name="start"/>.
@@ -55,14 +33,14 @@ public class ApplicationNavigation
     /// <param name="start"></param>
     /// <param name="start"></param>
     /// <param name="view"></param>
     /// <param name="view"></param>
     /// <returns></returns>
     /// <returns></returns>
-    public static bool IsInHierarchy (View start, View? view)
+    public static bool IsInHierarchy (View? start, View? view)
     {
     {
         if (view is null)
         if (view is null)
         {
         {
             return false;
             return false;
         }
         }
 
 
-        if (view == start)
+        if (view == start || start is null)
         {
         {
             return true;
             return true;
         }
         }
@@ -74,7 +52,8 @@ public class ApplicationNavigation
                 return true;
                 return true;
             }
             }
 
 
-            var found = IsInHierarchy (subView, view);
+            bool found = IsInHierarchy (subView, view);
+
             if (found)
             if (found)
             {
             {
                 return found;
                 return found;
@@ -84,146 +63,41 @@ public class ApplicationNavigation
         return false;
         return false;
     }
     }
 
 
-
-    /// <summary>
-    ///     Gets the deepest focused subview of the specified <paramref name="view"/>.
-    /// </summary>
-    /// <param name="view"></param>
-    /// <returns></returns>
-    internal static View? GetDeepestFocusedSubview (View? view)
-    {
-        if (view is null)
-        {
-            return null;
-        }
-
-        foreach (View v in view.Subviews)
-        {
-            if (v.HasFocus)
-            {
-                return GetDeepestFocusedSubview (v);
-            }
-        }
-
-        return view;
-    }
-
     /// <summary>
     /// <summary>
-    ///     Moves the focus to the next focusable view.
-    ///     Honors <see cref="ViewArrangement.Overlapped"/> and will only move to the next subview
-    ///     if the current and next subviews are not overlapped.
+    ///     INTERNAL method to record the most focused <see cref="View"/> in the application.
     /// </summary>
     /// </summary>
-    internal static void MoveNextView ()
+    /// <remarks>
+    ///     Raises <see cref="FocusedChanged"/>.
+    /// </remarks>
+    internal void SetFocused (View? value)
     {
     {
-        View? old = GetDeepestFocusedSubview (Application.Current!.Focused);
-
-        if (!Application.Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop))
-        {
-            Application.Current.AdvanceFocus (NavigationDirection.Forward, null);
-        }
-
-        if (old != Application.Current.Focused && old != Application.Current.Focused?.Focused)
-        {
-            old?.SetNeedsDisplay ();
-            Application.Current.Focused?.SetNeedsDisplay ();
-        }
-        else
+        if (_focused == value)
         {
         {
-            ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView?.TabIndexes, NavigationDirection.Forward);
+            return;
         }
         }
-    }
-
-    /// <summary>
-    ///     Moves the focus to the next <see cref="Toplevel"/> subview or the next subview that has
-    ///     <see cref="ApplicationOverlapped.OverlappedTop"/> set.
-    /// </summary>
-    internal static void MoveNextViewOrTop ()
-    {
-        if (ApplicationOverlapped.OverlappedTop is null)
-        {
-            Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
-
-            if (!Application.Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup))
-            {
-                Application.Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
 
 
-                if (Application.Current.Focused is null)
-                {
-                    Application.Current.RestoreFocus ();
-                }
-            }
-
-            if (top != Application.Current.Focused && top != Application.Current.Focused?.Focused)
-            {
-                top?.SetNeedsDisplay ();
-                Application.Current.Focused?.SetNeedsDisplay ();
-            }
-            else
-            {
-                ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView?.TabIndexes, NavigationDirection.Forward);
-            }
-
-            //top!.AdvanceFocus (NavigationDirection.Forward);
-
-            //if (top.Focused is null)
-            //{
-            //    top.AdvanceFocus (NavigationDirection.Forward);
-            //}
+        _focused = value;
 
 
-            //top.SetNeedsDisplay ();
-            ApplicationOverlapped.BringOverlappedTopToFront ();
-        }
-        else
-        {
-            ApplicationOverlapped.OverlappedMoveNext ();
-        }
+        FocusedChanged?.Invoke (null, EventArgs.Empty);
     }
     }
 
 
-    // TODO: These methods should return bool to indicate if the focus was moved or not.
-
     /// <summary>
     /// <summary>
-    ///     Moves the focus to the next view. Honors <see cref="ViewArrangement.Overlapped"/> and will only move to the next
-    ///     subview
-    ///     if the current and next subviews are not overlapped.
+    ///     Advances the focus to the next or previous view in the focus chain, based on
+    ///     <paramref name="direction"/>.
     /// </summary>
     /// </summary>
-    internal static void MovePreviousView ()
-    {
-        View? old = GetDeepestFocusedSubview (Application.Current!.Focused);
-
-        if (!Application.Current.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop))
-        {
-            Application.Current.AdvanceFocus (NavigationDirection.Backward, null);
-        }
-
-        if (old != Application.Current.Focused && old != Application.Current.Focused?.Focused)
-        {
-            old?.SetNeedsDisplay ();
-            Application.Current.Focused?.SetNeedsDisplay ();
-        }
-        else
-        {
-            ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView?.TabIndexes?.Reverse (), NavigationDirection.Backward);
-        }
-    }
-
-    internal static void MovePreviousViewOrTop ()
+    /// <remarks>
+    ///     <para>
+    ///         If there is no next/previous view, the focus remains on the current view.
+    ///     </para>
+    /// </remarks>
+    /// <param name="direction">The direction to advance.</param>
+    /// <param name="behavior">The tab behavior.</param>
+    /// <returns>
+    ///     <see langword="true"/> if focus was changed to another subview (or stayed on this one), <see langword="false"/>
+    ///     otherwise.
+    /// </returns>
+    public bool AdvanceFocus (NavigationDirection direction, TabBehavior? behavior)
     {
     {
-        if (ApplicationOverlapped.OverlappedTop is null)
-        {
-            Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
-            top!.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
-
-            if (top.Focused is null)
-            {
-                top.AdvanceFocus (NavigationDirection.Backward, null);
-            }
-
-            top.SetNeedsDisplay ();
-            ApplicationOverlapped.BringOverlappedTopToFront ();
-        }
-        else
-        {
-            ApplicationOverlapped.OverlappedMovePrevious ();
-        }
+        return Application.Current is { } && Application.Current.AdvanceFocus (direction, behavior);
     }
     }
 }
 }

+ 3 - 3
Terminal.Gui/Application/ApplicationOverlapped.cs

@@ -79,7 +79,7 @@ public static class ApplicationOverlapped
 
 
         if (top is Toplevel && Application.Top?.Subviews.Count > 1 && Application.Top.Subviews [^1] != top)
         if (top is Toplevel && Application.Top?.Subviews.Count > 1 && Application.Top.Subviews [^1] != top)
         {
         {
-            Application.Top.BringSubviewToFront (top);
+            Application.Top.MoveSubviewToStart (top);
         }
         }
     }
     }
 
 
@@ -96,12 +96,12 @@ public static class ApplicationOverlapped
 
 
         foreach (Toplevel top in OverlappedChildren)
         foreach (Toplevel top in OverlappedChildren)
         {
         {
-            if (type is { } && top.GetType () == type && exclude?.Contains (top.Data.ToString ()) == false)
+            if (type is { } && top.GetType () == type && exclude?.Contains (top.Data?.ToString ()) == false)
             {
             {
                 return top;
                 return top;
             }
             }
 
 
-            if ((type is { } && top.GetType () != type) || exclude?.Contains (top.Data.ToString ()) == true)
+            if ((type is { } && top.GetType () != type) || exclude?.Contains (top.Data?.ToString ()) == true)
             {
             {
                 continue;
                 continue;
             }
             }

+ 1 - 0
Terminal.Gui/Application/MainLoop.cs

@@ -296,6 +296,7 @@ internal class MainLoop : IDisposable
     ///     Invoked when a new timeout is added. To be used in the case when
     ///     Invoked when a new timeout is added. To be used in the case when
     ///     <see cref="Application.EndAfterFirstIteration"/> is <see langword="true"/>.
     ///     <see cref="Application.EndAfterFirstIteration"/> is <see langword="true"/>.
     /// </summary>
     /// </summary>
+    [CanBeNull]
     internal event EventHandler<TimeoutEventArgs> TimeoutAdded;
     internal event EventHandler<TimeoutEventArgs> TimeoutAdded;
 
 
     /// <summary>Wakes up the <see cref="MainLoop"/> that might be waiting on input.</summary>
     /// <summary>Wakes up the <see cref="MainLoop"/> that might be waiting on input.</summary>

+ 1 - 0
Terminal.Gui/Input/Responder.cs

@@ -26,6 +26,7 @@ public class Responder : IDisposable
     }
     }
 
 
     /// <summary>Event raised when <see cref="Dispose()"/> has been called to signal that this object is being disposed.</summary>
     /// <summary>Event raised when <see cref="Dispose()"/> has been called to signal that this object is being disposed.</summary>
+    [CanBeNull]
     public event EventHandler Disposing;
     public event EventHandler Disposing;
 
 
     /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
     /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>

+ 3 - 6
Terminal.Gui/Text/Autocomplete/AutocompleteBase.cs

@@ -36,17 +36,14 @@ public abstract class AutocompleteBase : IAutocomplete
     /// <inheritdoc/>
     /// <inheritdoc/>
     public abstract ColorScheme ColorScheme { get; set; }
     public abstract ColorScheme ColorScheme { get; set; }
 
 
-    // TODO: Update to use Key instead of KeyCode
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public virtual KeyCode SelectionKey { get; set; } = KeyCode.Enter;
+    public virtual Key SelectionKey { get; set; } = Key.Enter;
 
 
-    // TODO: Update to use Key instead of KeyCode
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public virtual KeyCode CloseKey { get; set; } = KeyCode.Esc;
+    public virtual Key CloseKey { get; set; } = Key.Esc;
 
 
-    // TODO: Update to use Key instead of KeyCode
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public virtual KeyCode Reopen { get; set; } = (KeyCode)Key.Space.WithCtrl.WithAlt;
+    public virtual Key Reopen { get; set; } = Key.Space.WithCtrl.WithAlt;
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public virtual AutocompleteContext Context { get; set; }
     public virtual AutocompleteContext Context { get; set; }

+ 33 - 36
Terminal.Gui/Text/Autocomplete/IAutocomplete.cs

@@ -8,9 +8,11 @@ namespace Terminal.Gui;
 /// </summary>
 /// </summary>
 public interface IAutocomplete
 public interface IAutocomplete
 {
 {
-    // TODO: Update to use Key instead of KeyCode
+    /// <summary>Clears <see cref="Suggestions"/></summary>
+    void ClearSuggestions ();
+
     /// <summary>The key that the user can press to close the currently popped autocomplete menu</summary>
     /// <summary>The key that the user can press to close the currently popped autocomplete menu</summary>
-    KeyCode CloseKey { get; set; }
+    Key CloseKey { get; set; }
 
 
     /// <summary>
     /// <summary>
     ///     The colors to use to render the overlay. Accessing this property before the Application has been initialized
     ///     The colors to use to render the overlay. Accessing this property before the Application has been initialized
@@ -21,6 +23,12 @@ public interface IAutocomplete
     /// <summary>The context used by the autocomplete menu.</summary>
     /// <summary>The context used by the autocomplete menu.</summary>
     AutocompleteContext Context { get; set; }
     AutocompleteContext Context { get; set; }
 
 
+    /// <summary>
+    ///     Populates <see cref="Suggestions"/> with all <see cref="Suggestion"/> proposed by
+    ///     <see cref="SuggestionGenerator"/> at the given <paramref name="context"/> (cursor position)
+    /// </summary>
+    void GenerateSuggestions (AutocompleteContext context);
+
     /// <summary>The host control that will use autocomplete.</summary>
     /// <summary>The host control that will use autocomplete.</summary>
     View HostControl { get; set; }
     View HostControl { get; set; }
 
 
@@ -30,19 +38,38 @@ public interface IAutocomplete
     /// <summary>The maximum width of the autocomplete dropdown</summary>
     /// <summary>The maximum width of the autocomplete dropdown</summary>
     int MaxWidth { get; set; }
     int MaxWidth { get; set; }
 
 
+    /// <summary>
+    ///     Handle mouse events before <see cref="HostControl"/> e.g. to make mouse events like report/click apply to the
+    ///     autocomplete control instead of changing the cursor position in the underlying text view.
+    /// </summary>
+    /// <param name="me">The mouse event.</param>
+    /// <param name="fromHost">If was called from the popup or from the host.</param>
+    /// <returns><c>true</c>if the mouse can be handled <c>false</c>otherwise.</returns>
+    bool OnMouseEvent (MouseEvent me, bool fromHost = false);
+
     /// <summary>Gets or sets where the popup will be displayed.</summary>
     /// <summary>Gets or sets where the popup will be displayed.</summary>
     bool PopupInsideContainer { get; set; }
     bool PopupInsideContainer { get; set; }
 
 
-    // TODO: Update to use Key instead of KeyCode
+    /// <summary>
+    ///     Handle key events before <see cref="HostControl"/> e.g. to make key events like up/down apply to the
+    ///     autocomplete control instead of changing the cursor position in the underlying text view.
+    /// </summary>
+    /// <param name="a">The key event.</param>
+    /// <returns><c>true</c>if the key can be handled <c>false</c>otherwise.</returns>
+    bool ProcessKey (Key a);
+
+    /// <summary>Renders the autocomplete dialog inside the given <see cref="HostControl"/> at the given point.</summary>
+    /// <param name="renderAt"></param>
+    void RenderOverlay (Point renderAt);
+
     /// <summary>The key that the user can press to reopen the currently popped autocomplete menu</summary>
     /// <summary>The key that the user can press to reopen the currently popped autocomplete menu</summary>
-    KeyCode Reopen { get; set; }
+    Key Reopen { get; set; }
 
 
     /// <summary>The currently selected index into <see cref="Suggestions"/> that the user has highlighted</summary>
     /// <summary>The currently selected index into <see cref="Suggestions"/> that the user has highlighted</summary>
     int SelectedIdx { get; set; }
     int SelectedIdx { get; set; }
 
 
-    // TODO: Update to use Key instead of KeyCode
     /// <summary>The key that the user must press to accept the currently selected autocomplete suggestion</summary>
     /// <summary>The key that the user must press to accept the currently selected autocomplete suggestion</summary>
-    KeyCode SelectionKey { get; set; }
+    Key SelectionKey { get; set; }
 
 
     /// <summary>
     /// <summary>
     ///     Gets or Sets the class responsible for generating <see cref="Suggestions"/> based on a given
     ///     Gets or Sets the class responsible for generating <see cref="Suggestions"/> based on a given
@@ -55,34 +82,4 @@ public interface IAutocomplete
 
 
     /// <summary>True if the autocomplete should be considered open and visible</summary>
     /// <summary>True if the autocomplete should be considered open and visible</summary>
     bool Visible { get; set; }
     bool Visible { get; set; }
-
-    /// <summary>Clears <see cref="Suggestions"/></summary>
-    void ClearSuggestions ();
-
-    /// <summary>
-    ///     Populates <see cref="Suggestions"/> with all <see cref="Suggestion"/> proposed by
-    ///     <see cref="SuggestionGenerator"/> at the given <paramref name="context"/> (cursor position)
-    /// </summary>
-    void GenerateSuggestions (AutocompleteContext context);
-
-    /// <summary>
-    ///     Handle mouse events before <see cref="HostControl"/> e.g. to make mouse events like report/click apply to the
-    ///     autocomplete control instead of changing the cursor position in the underlying text view.
-    /// </summary>
-    /// <param name="me">The mouse event.</param>
-    /// <param name="fromHost">If was called from the popup or from the host.</param>
-    /// <returns><c>true</c>if the mouse can be handled <c>false</c>otherwise.</returns>
-    bool OnMouseEvent (MouseEvent me, bool fromHost = false);
-
-    /// <summary>
-    ///     Handle key events before <see cref="HostControl"/> e.g. to make key events like up/down apply to the
-    ///     autocomplete control instead of changing the cursor position in the underlying text view.
-    /// </summary>
-    /// <param name="a">The key event.</param>
-    /// <returns><c>true</c>if the key can be handled <c>false</c>otherwise.</returns>
-    bool ProcessKey (Key a);
-
-    /// <summary>Renders the autocomplete dialog inside the given <see cref="HostControl"/> at the given point.</summary>
-    /// <param name="renderAt"></param>
-    void RenderOverlay (Point renderAt);
 }
 }

+ 5 - 4
Terminal.Gui/Text/Autocomplete/PopupAutocomplete.PopUp.cs

@@ -5,16 +5,15 @@ public abstract partial class PopupAutocomplete
 {
 {
     private sealed class Popup : View
     private sealed class Popup : View
     {
     {
-        private readonly PopupAutocomplete _autoComplete;
-
         public Popup (PopupAutocomplete autoComplete)
         public Popup (PopupAutocomplete autoComplete)
         {
         {
-            this._autoComplete = autoComplete;
+            _autoComplete = autoComplete;
             CanFocus = true;
             CanFocus = true;
+            TabStop = TabBehavior.NoStop;
             WantMousePositionReports = true;
             WantMousePositionReports = true;
         }
         }
 
 
-        protected internal override bool OnMouseEvent  (MouseEvent mouseEvent) { return _autoComplete.OnMouseEvent (mouseEvent); }
+        private readonly PopupAutocomplete _autoComplete;
 
 
         public override void OnDrawContent (Rectangle viewport)
         public override void OnDrawContent (Rectangle viewport)
         {
         {
@@ -25,5 +24,7 @@ public abstract partial class PopupAutocomplete
 
 
             _autoComplete.RenderOverlay (_autoComplete.LastPopupPos.Value);
             _autoComplete.RenderOverlay (_autoComplete.LastPopupPos.Value);
         }
         }
+
+        protected internal override bool OnMouseEvent (MouseEvent mouseEvent) { return _autoComplete.OnMouseEvent (mouseEvent); }
     }
     }
 }
 }

+ 98 - 70
Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs

@@ -1,3 +1,5 @@
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
@@ -6,11 +8,12 @@ namespace Terminal.Gui;
 /// </summary>
 /// </summary>
 public abstract partial class PopupAutocomplete : AutocompleteBase
 public abstract partial class PopupAutocomplete : AutocompleteBase
 {
 {
-    private bool closed;
-    private ColorScheme colorScheme;
-    private View hostControl;
-    private View top, popup;
-    private int toRenderLength;
+    private bool _closed;
+    private ColorScheme _colorScheme;
+    private View _hostControl;
+    private View _top;  // The _hostControl's SuperView
+    private View _popup;
+    private int _toRenderLength;
 
 
     /// <summary>Creates a new instance of the <see cref="PopupAutocomplete"/> class.</summary>
     /// <summary>Creates a new instance of the <see cref="PopupAutocomplete"/> class.</summary>
     public PopupAutocomplete () { PopupInsideContainer = true; }
     public PopupAutocomplete () { PopupInsideContainer = true; }
@@ -23,43 +26,59 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
     {
     {
         get
         get
         {
         {
-            if (colorScheme is null)
+            if (_colorScheme is null)
             {
             {
-                colorScheme = Colors.ColorSchemes ["Menu"];
+                _colorScheme = Colors.ColorSchemes ["Menu"];
             }
             }
 
 
-            return colorScheme;
+            return _colorScheme;
         }
         }
-        set => colorScheme = value;
+        set => _colorScheme = value;
     }
     }
 
 
     /// <summary>The host control to handle.</summary>
     /// <summary>The host control to handle.</summary>
     public override View HostControl
     public override View HostControl
     {
     {
-        get => hostControl;
+        get => _hostControl;
         set
         set
         {
         {
-            hostControl = value;
-            top = hostControl.SuperView;
+            if (value == _hostControl)
+            {
+                return;
+            }
+
+            _hostControl = value;
+
+            if (_hostControl is null)
+            {
+                RemovePopupFromTop();
+                _top.Removed -= _top_Removed;
+                _top = null;
+
+                return;
+            }
 
 
-            if (top is { })
+            _top = _hostControl.SuperView;
+
+            if (_top is { })
             {
             {
-                top.DrawContent += Top_DrawContent;
-                top.DrawContentComplete += Top_DrawContentComplete;
-                top.Removed += Top_Removed;
+                if (_top.IsInitialized)
+                {
+                    AddPopupToTop ();
+                }
+                else
+                {
+                    _top.Initialized += _top_Initialized;
+                }
+                _top.Removed += _top_Removed;
             }
             }
         }
         }
     }
     }
 
 
-    /// <summary>
-    ///     When more suggestions are available than can be rendered the user can scroll down the dropdown list. This
-    ///     indicates how far down they have gone
-    /// </summary>
-    public virtual int ScrollOffset { get; set; }
-
-    #nullable enable
-    private Point? LastPopupPos { get; set; }
-    #nullable restore
+    private void _top_Added (object sender, SuperViewChangedEventArgs e)
+    {
+        throw new NotImplementedException ();
+    }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override void EnsureSelectedIdxIsValid ()
     public override void EnsureSelectedIdxIsValid ()
@@ -73,7 +92,7 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
         }
         }
 
 
         // if user moved selection down past bottom of current scroll window
         // if user moved selection down past bottom of current scroll window
-        while (toRenderLength > 0 && SelectedIdx >= ScrollOffset + toRenderLength)
+        while (_toRenderLength > 0 && SelectedIdx >= ScrollOffset + _toRenderLength)
         {
         {
             ScrollOffset++;
             ScrollOffset++;
         }
         }
@@ -119,7 +138,7 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
             if (Visible && HostControl is { })
             if (Visible && HostControl is { })
             {
             {
                 Visible = false;
                 Visible = false;
-                closed = false;
+                _closed = false;
             }
             }
 
 
             HostControl?.SetNeedsDisplay ();
             HostControl?.SetNeedsDisplay ();
@@ -127,9 +146,10 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
             return false;
             return false;
         }
         }
 
 
-        if (popup is null || Suggestions.Count == 0)
+        if (_popup is null || Suggestions.Count == 0)
         {
         {
-            ManipulatePopup ();
+            //AddPopupToTop ();
+            //Debug.Fail ("popup is null");
 
 
             return false;
             return false;
         }
         }
@@ -176,8 +196,7 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
         if (SuggestionGenerator.IsWordChar ((Rune)key))
         if (SuggestionGenerator.IsWordChar ((Rune)key))
         {
         {
             Visible = true;
             Visible = true;
-            ManipulatePopup ();
-            closed = false;
+            _closed = false;
 
 
             return false;
             return false;
         }
         }
@@ -189,11 +208,11 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
             return ReopenSuggestions ();
             return ReopenSuggestions ();
         }
         }
 
 
-        if (closed || Suggestions.Count == 0)
+        if (_closed || Suggestions.Count == 0)
         {
         {
             Visible = false;
             Visible = false;
 
 
-            if (!closed)
+            if (!_closed)
             {
             {
                 Close ();
                 Close ();
             }
             }
@@ -249,7 +268,7 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
     {
     {
         if (!Context.Canceled && Suggestions.Count > 0 && !Visible && HostControl?.HasFocus == true)
         if (!Context.Canceled && Suggestions.Count > 0 && !Visible && HostControl?.HasFocus == true)
         {
         {
-            ProcessKey (new Key (Suggestions [0].Title [0]));
+            ProcessKey (new (Suggestions [0].Title [0]));
         }
         }
         else if (!Visible || HostControl?.HasFocus == false || Suggestions.Count == 0)
         else if (!Visible || HostControl?.HasFocus == false || Suggestions.Count == 0)
         {
         {
@@ -287,13 +306,13 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
         else
         else
         {
         {
             // don't overspill vertically
             // don't overspill vertically
-            height = Math.Min (Math.Min (top.Viewport.Height - HostControl.Frame.Bottom, MaxHeight), Suggestions.Count);
+            height = Math.Min (Math.Min (_top.Viewport.Height - HostControl.Frame.Bottom, MaxHeight), Suggestions.Count);
 
 
             // There is no space below, lets see if can popup on top
             // There is no space below, lets see if can popup on top
-            if (height < Suggestions.Count && HostControl.Frame.Y - top.Frame.Y >= height)
+            if (height < Suggestions.Count && HostControl.Frame.Y - _top.Frame.Y >= height)
             {
             {
                 // Verifies that the upper limit available is greater than the lower limit
                 // Verifies that the upper limit available is greater than the lower limit
-                if (HostControl.Frame.Y > top.Viewport.Height - HostControl.Frame.Y)
+                if (HostControl.Frame.Y > _top.Viewport.Height - HostControl.Frame.Y)
                 {
                 {
                     renderAt.Y = Math.Max (HostControl.Frame.Y - Math.Min (Suggestions.Count, MaxHeight), 0);
                     renderAt.Y = Math.Max (HostControl.Frame.Y - Math.Min (Suggestions.Count, MaxHeight), 0);
                     height = Math.Min (Math.Min (Suggestions.Count, MaxHeight), HostControl.Frame.Y);
                     height = Math.Min (Math.Min (Suggestions.Count, MaxHeight), HostControl.Frame.Y);
@@ -311,7 +330,7 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
         }
         }
 
 
         Suggestion [] toRender = Suggestions.Skip (ScrollOffset).Take (height).ToArray ();
         Suggestion [] toRender = Suggestions.Skip (ScrollOffset).Take (height).ToArray ();
-        toRenderLength = toRender.Length;
+        _toRenderLength = toRender.Length;
 
 
         if (toRender.Length == 0)
         if (toRender.Length == 0)
         {
         {
@@ -340,37 +359,37 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
         else
         else
         {
         {
             // don't overspill horizontally, let's see if it can be displayed on the left
             // don't overspill horizontally, let's see if it can be displayed on the left
-            if (width > top.Viewport.Width - (renderAt.X + HostControl.Frame.X))
+            if (width > _top.Viewport.Width - (renderAt.X + HostControl.Frame.X))
             {
             {
                 // Verifies that the left limit available is greater than the right limit
                 // Verifies that the left limit available is greater than the right limit
-                if (renderAt.X + HostControl.Frame.X > top.Viewport.Width - (renderAt.X + HostControl.Frame.X))
+                if (renderAt.X + HostControl.Frame.X > _top.Viewport.Width - (renderAt.X + HostControl.Frame.X))
                 {
                 {
                     renderAt.X -= Math.Min (width, LastPopupPos.Value.X);
                     renderAt.X -= Math.Min (width, LastPopupPos.Value.X);
                     width = Math.Min (width, LastPopupPos.Value.X);
                     width = Math.Min (width, LastPopupPos.Value.X);
                 }
                 }
                 else
                 else
                 {
                 {
-                    width = Math.Min (width, top.Viewport.Width - renderAt.X);
+                    width = Math.Min (width, _top.Viewport.Width - renderAt.X);
                 }
                 }
             }
             }
         }
         }
 
 
         if (PopupInsideContainer)
         if (PopupInsideContainer)
         {
         {
-            popup.Frame = new (
+            _popup.Frame = new (
                                new (HostControl.Frame.X + renderAt.X, HostControl.Frame.Y + renderAt.Y),
                                new (HostControl.Frame.X + renderAt.X, HostControl.Frame.Y + renderAt.Y),
                                new (width, height)
                                new (width, height)
                               );
                               );
         }
         }
         else
         else
         {
         {
-            popup.Frame = new (
+            _popup.Frame = new (
                                renderAt with { X = HostControl.Frame.X + renderAt.X },
                                renderAt with { X = HostControl.Frame.X + renderAt.X },
                                new (width, height)
                                new (width, height)
                               );
                               );
         }
         }
 
 
-        popup.Move (0, 0);
+        _popup.Move (0, 0);
 
 
         for (var i = 0; i < toRender.Length; i++)
         for (var i = 0; i < toRender.Length; i++)
         {
         {
@@ -383,7 +402,7 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
                 Application.Driver?.SetAttribute (ColorScheme.Normal);
                 Application.Driver?.SetAttribute (ColorScheme.Normal);
             }
             }
 
 
-            popup.Move (0, i);
+            _popup.Move (0, i);
 
 
             string text = TextFormatter.ClipOrPad (toRender [i].Title, width);
             string text = TextFormatter.ClipOrPad (toRender [i].Title, width);
 
 
@@ -391,6 +410,12 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
         }
         }
     }
     }
 
 
+    /// <summary>
+    ///     When more suggestions are available than can be rendered the user can scroll down the dropdown list. This
+    ///     indicates how far down they have gone
+    /// </summary>
+    public virtual int ScrollOffset { get; set; }
+
     /// <summary>
     /// <summary>
     ///     Closes the Autocomplete context menu if it is showing and <see cref="IAutocomplete.ClearSuggestions"/>
     ///     Closes the Autocomplete context menu if it is showing and <see cref="IAutocomplete.ClearSuggestions"/>
     /// </summary>
     /// </summary>
@@ -398,9 +423,9 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
     {
     {
         ClearSuggestions ();
         ClearSuggestions ();
         Visible = false;
         Visible = false;
-        closed = true;
+        _closed = true;
         HostControl?.SetNeedsDisplay ();
         HostControl?.SetNeedsDisplay ();
-        ManipulatePopup ();
+        //RemovePopupFromTop ();
     }
     }
 
 
     /// <summary>Deletes the text backwards before insert the selected text in the <see cref="HostControl"/>.</summary>
     /// <summary>Deletes the text backwards before insert the selected text in the <see cref="HostControl"/>.</summary>
@@ -486,7 +511,7 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
         if (Suggestions.Count > 0)
         if (Suggestions.Count > 0)
         {
         {
             Visible = true;
             Visible = true;
-            closed = false;
+            _closed = false;
             HostControl?.SetNeedsDisplay ();
             HostControl?.SetNeedsDisplay ();
 
 
             return true;
             return true;
@@ -516,42 +541,45 @@ public abstract partial class PopupAutocomplete : AutocompleteBase
     /// <param name="column"></param>
     /// <param name="column"></param>
     protected abstract void SetCursorPosition (int column);
     protected abstract void SetCursorPosition (int column);
 
 
-    private void ManipulatePopup ()
-    {
-        if (Visible && popup is null)
-        {
-            popup = new Popup (this) { Frame = Rectangle.Empty };
-            top?.Add (popup);
-        }
+#nullable enable
+    private Point? LastPopupPos { get; set; }
+#nullable restore
 
 
-        if (!Visible && popup is { })
+    private void AddPopupToTop ()
+    {
+        if (_popup is null)
         {
         {
-            top?.Remove (popup);
-            popup.Dispose ();
-            popup = null;
+            _popup = new Popup (this)
+            {
+                CanFocus = false
+            };
+            _top?.Add (_popup);
         }
         }
     }
     }
 
 
-    private void Top_DrawContent (object sender, DrawEventArgs e)
+    private void RemovePopupFromTop ()
     {
     {
-        if (!closed)
+        if (_popup is { } && _top.Subviews.Contains (_popup))
         {
         {
-            ReopenSuggestions ();
-        }
+            _top?.Remove (_popup);
+            _popup.Dispose ();
+            _popup = null;
 
 
-        ManipulatePopup ();
+        }
+    }
 
 
-        if (Visible)
+    private void _top_Initialized (object sender, EventArgs e)
+    {
+        if (_top is null)
         {
         {
-            top.BringSubviewToFront (popup);
+            _top = sender as View;
         }
         }
+        AddPopupToTop ();
     }
     }
 
 
-    private void Top_DrawContentComplete (object sender, DrawEventArgs e) { ManipulatePopup (); }
-
-    private void Top_Removed (object sender, SuperViewChangedEventArgs e)
+    private void _top_Removed (object sender, SuperViewChangedEventArgs e)
     {
     {
         Visible = false;
         Visible = false;
-        ManipulatePopup ();
+        RemovePopupFromTop ();
     }
     }
 }
 }

+ 1 - 0
Terminal.Gui/Text/CollectionNavigatorBase.cs

@@ -147,6 +147,7 @@ public abstract class CollectionNavigatorBase
     public virtual void OnSearchStringChanged (KeystrokeNavigatorEventArgs e) { SearchStringChanged?.Invoke (this, e); }
     public virtual void OnSearchStringChanged (KeystrokeNavigatorEventArgs e) { SearchStringChanged?.Invoke (this, e); }
 
 
     /// <summary>This event is invoked when <see cref="SearchString"/>  changes. Useful for debugging.</summary>
     /// <summary>This event is invoked when <see cref="SearchString"/>  changes. Useful for debugging.</summary>
+    [CanBeNull]
     public event EventHandler<KeystrokeNavigatorEventArgs> SearchStringChanged;
     public event EventHandler<KeystrokeNavigatorEventArgs> SearchStringChanged;
 
 
     /// <summary>Returns the collection being navigated element at <paramref name="idx"/>.</summary>
     /// <summary>Returns the collection being navigated element at <paramref name="idx"/>.</summary>

+ 1 - 1
Terminal.Gui/View/Adornment/Adornment.cs

@@ -86,7 +86,7 @@ public class Adornment : View
     ///     Adornments cannot be used as sub-views (see <see cref="Parent"/>); setting this property will throw
     ///     Adornments cannot be used as sub-views (see <see cref="Parent"/>); setting this property will throw
     ///     <see cref="InvalidOperationException"/>.
     ///     <see cref="InvalidOperationException"/>.
     /// </summary>
     /// </summary>
-    public override View SuperView
+    public override View? SuperView
     {
     {
         get => null!;
         get => null!;
         set => throw new InvalidOperationException (@"Adornments can not be Subviews or have SuperViews. Use Parent instead.");
         set => throw new InvalidOperationException (@"Adornments can not be Subviews or have SuperViews. Use Parent instead.");

+ 1 - 1
Terminal.Gui/View/Adornment/ShadowView.cs

@@ -109,7 +109,7 @@ internal class ShadowView : View
         Rectangle screen = ViewportToScreen (viewport);
         Rectangle screen = ViewportToScreen (viewport);
 
 
         // Fill the rest of the rectangle - note we skip the last since vertical will draw it
         // Fill the rest of the rectangle - note we skip the last since vertical will draw it
-        for (int i = screen.X; i < screen.X + screen.Width - 1; i++)
+        for (int i = screen.X + 1; i < screen.X + screen.Width - 1; i++)
         {
         {
             Driver.Move (i, screen.Y);
             Driver.Move (i, screen.Y);
 
 

+ 15 - 19
Terminal.Gui/View/Navigation/FocusEventArgs.cs

@@ -1,27 +1,23 @@
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-/// <summary>Defines the event arguments for <see cref="View.SetFocus()"/></summary>
-public class FocusEventArgs : EventArgs
+/// <summary>The event arguments for <see cref="View.HasFocus"/> events.</summary>
+public class HasFocusEventArgs : CancelEventArgs<bool>
 {
 {
-    /// <summary>Constructs.</summary>
-    /// <param name="leaving">The view that is losing focus.</param>
-    /// <param name="entering">The view that is gaining focus.</param>
-    public FocusEventArgs (View leaving, View entering) {
-        Leaving = leaving;
-        Entering = entering;
+    /// <summary>Initializes a new instance.</summary>
+    /// <param name="currentHasFocus">The current value of <see cref="View.HasFocus"/>.</param>
+    /// <param name="newHasFocus">The value <see cref="View.HasFocus"/> will have if the event is not cancelled.</param>
+    /// <param name="currentFocused">The view that is losing focus.</param>
+    /// <param name="newFocused">The view that is gaining focus.</param>
+    public HasFocusEventArgs (bool currentHasFocus, bool newHasFocus, View currentFocused, View newFocused) : base (ref currentHasFocus, ref newHasFocus)
+    {
+        CurrentFocused = currentFocused;
+        NewFocused = newFocused;
     }
     }
 
 
-    /// <summary>
-    ///     Indicates if the current focus event has already been processed and the driver should stop notifying any other
-    ///     event subscriber. It's important to set this value to true specially when updating any View's layout from inside the
-    ///     subscriber method.
-    /// </summary>
-    public bool Handled { get; set; }
+    /// <summary>Gets or sets the view that is losing focus.</summary>
+    public View CurrentFocused { get; set; }
 
 
-    /// <summary>Indicates the view that is losing focus.</summary>
-    public View Leaving { get; set; }
-
-    /// <summary>Indicates the view that is gaining focus.</summary>
-    public View Entering { get; set; }
+    /// <summary>Gets or sets the view that is gaining focus.</summary>
+    public View NewFocused { get; set; }
 
 
 }
 }

+ 10 - 2
Terminal.Gui/View/Navigation/TabBehavior.cs

@@ -8,6 +8,13 @@ public enum TabBehavior
     /// <summary>
     /// <summary>
     ///     The View will not be a stop-poknt for keyboard-based navigation.
     ///     The View will not be a stop-poknt for keyboard-based navigation.
     /// </summary>
     /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This flag has no impact on whether the view can be focused via means other than the keyboard. Use
+    ///         <see cref="View.CanFocus"/>
+    ///         to control whether a View can focus or not.
+    ///     </para>
+    /// </remarks>
     NoStop = 0,
     NoStop = 0,
 
 
     /// <summary>
     /// <summary>
@@ -16,7 +23,8 @@ public enum TabBehavior
     TabStop = 1,
     TabStop = 1,
 
 
     /// <summary>
     /// <summary>
-    ///     The View will be a stop-point for keyboard-based navigation across groups (e.g. if the user presses <see cref="Application.NextTabGroupKey"/> (`Ctrl-PageDown`).
+    ///     The View will be a stop-point for keyboard-based navigation across groups (e.g. if the user presses
+    ///     <see cref="Application.NextTabGroupKey"/> (`Ctrl-PageDown`).
     /// </summary>
     /// </summary>
-    TabGroup = 2,
+    TabGroup = 2
 }
 }

+ 4 - 3
Terminal.Gui/View/Orientation/IOrientation.cs

@@ -1,4 +1,5 @@
-
+#nullable enable
+
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 using System;
 using System;
 
 
@@ -18,7 +19,7 @@ public interface IOrientation
     /// <summary>
     /// <summary>
     ///     Raised when <see cref="Orientation"/> is changing. Can be cancelled.
     ///     Raised when <see cref="Orientation"/> is changing. Can be cancelled.
     /// </summary>
     /// </summary>
-    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+    public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
 
 
     /// <summary>
     /// <summary>
     ///     Called when <see cref="Orientation"/> is changing.
     ///     Called when <see cref="Orientation"/> is changing.
@@ -31,7 +32,7 @@ public interface IOrientation
     /// <summary>
     /// <summary>
     ///     Raised when <see cref="Orientation"/> has changed.
     ///     Raised when <see cref="Orientation"/> has changed.
     /// </summary>
     /// </summary>
-    public event EventHandler<EventArgs<Orientation>> OrientationChanged;
+    public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
 
 
     /// <summary>
     /// <summary>
     ///     Called when <see cref="Orientation"/> has been changed.
     ///     Called when <see cref="Orientation"/> has been changed.

+ 2 - 1
Terminal.Gui/View/Orientation/Orientation.cs

@@ -1,4 +1,5 @@
-namespace Terminal.Gui;
+#nullable enable
+namespace Terminal.Gui;
 
 
 /// <summary>Direction of an element (horizontal or vertical)</summary>
 /// <summary>Direction of an element (horizontal or vertical)</summary>
 public enum Orientation
 public enum Orientation

+ 4 - 3
Terminal.Gui/View/Orientation/OrientationHelper.cs

@@ -1,4 +1,5 @@
-namespace Terminal.Gui;
+#nullable enable
+namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
 ///     Helper class for implementing <see cref="IOrientation"/>.
 ///     Helper class for implementing <see cref="IOrientation"/>.
@@ -119,7 +120,7 @@ public class OrientationHelper
     ///         it was not canceled).
     ///         it was not canceled).
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
-    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+    public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
 
 
     /// <summary>
     /// <summary>
     ///     Raised when the orientation has changed.
     ///     Raised when the orientation has changed.
@@ -134,5 +135,5 @@ public class OrientationHelper
     ///         This event will be raised after the <see cref="IOrientation.OnOrientationChanged"/> method is called.
     ///         This event will be raised after the <see cref="IOrientation.OnOrientationChanged"/> method is called.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
-    public event EventHandler<EventArgs<Orientation>> OrientationChanged;
+    public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
 }
 }

+ 7 - 9
Terminal.Gui/View/SuperViewChangedEventArgs.cs

@@ -7,22 +7,20 @@
 public class SuperViewChangedEventArgs : EventArgs
 public class SuperViewChangedEventArgs : EventArgs
 {
 {
     /// <summary>Creates a new instance of the <see cref="SuperViewChangedEventArgs"/> class.</summary>
     /// <summary>Creates a new instance of the <see cref="SuperViewChangedEventArgs"/> class.</summary>
-    /// <param name="parent"></param>
-    /// <param name="child"></param>
-    public SuperViewChangedEventArgs (View parent, View child)
+    /// <param name="superView"></param>
+    /// <param name="subView"></param>
+    public SuperViewChangedEventArgs (View superView, View subView)
     {
     {
-        Parent = parent;
-        Child = child;
+        SuperView = superView;
+        SubView = subView;
     }
     }
 
 
-    // TODO: Child is the wrong name. It should be View.
     /// <summary>The view that is having it's <see cref="View.SuperView"/> changed</summary>
     /// <summary>The view that is having it's <see cref="View.SuperView"/> changed</summary>
-    public View Child { get; }
+    public View SubView { get; }
 
 
-    // TODO: Parent is the wrong name. It should be SuperView.
     /// <summary>
     /// <summary>
     ///     The parent.  For <see cref="View.Removed"/> this is the old parent (new parent now being null).  For
     ///     The parent.  For <see cref="View.Removed"/> this is the old parent (new parent now being null).  For
     ///     <see cref="View.Added"/> it is the new parent to whom view now belongs.
     ///     <see cref="View.Added"/> it is the new parent to whom view now belongs.
     /// </summary>
     /// </summary>
-    public View Parent { get; }
+    public View SuperView { get; }
 }
 }

+ 1 - 0
Terminal.Gui/View/View.Adornments.cs

@@ -194,6 +194,7 @@ public partial class View // Adornments
     /// <summary>
     /// <summary>
     ///     Fired when the <see cref="BorderStyle"/> is changing. Allows the event to be cancelled.
     ///     Fired when the <see cref="BorderStyle"/> is changing. Allows the event to be cancelled.
     /// </summary>
     /// </summary>
+    [CanBeNull]
     public event EventHandler<CancelEventArgs<LineStyle>> BorderStyleChanging;
     public event EventHandler<CancelEventArgs<LineStyle>> BorderStyleChanging;
 
 
     /// <summary>
     /// <summary>

+ 4 - 4
Terminal.Gui/View/View.Content.cs

@@ -1,4 +1,5 @@
-namespace Terminal.Gui;
+#nullable enable
+namespace Terminal.Gui;
 
 
 public partial class View
 public partial class View
 {
 {
@@ -153,7 +154,7 @@ public partial class View
     /// <summary>
     /// <summary>
     ///     Event raised when the <see cref="GetContentSize ()"/> changes.
     ///     Event raised when the <see cref="GetContentSize ()"/> changes.
     /// </summary>
     /// </summary>
-    public event EventHandler<SizeChangedEventArgs> ContentSizeChanged;
+    public event EventHandler<SizeChangedEventArgs>? ContentSizeChanged;
 
 
     /// <summary>
     /// <summary>
     ///     Converts a Content-relative location to a Screen-relative location.
     ///     Converts a Content-relative location to a Screen-relative location.
@@ -356,8 +357,7 @@ public partial class View
     ///     Fired when the <see cref="Viewport"/> changes. This event is fired after the <see cref="Viewport"/> has been
     ///     Fired when the <see cref="Viewport"/> changes. This event is fired after the <see cref="Viewport"/> has been
     ///     updated.
     ///     updated.
     /// </summary>
     /// </summary>
-    [CanBeNull]
-    public event EventHandler<DrawEventArgs> ViewportChanged;
+    public event EventHandler<DrawEventArgs>? ViewportChanged;
 
 
     /// <summary>
     /// <summary>
     ///     Called when the <see cref="Viewport"/> changes. Invokes the <see cref="ViewportChanged"/> event.
     ///     Called when the <see cref="Viewport"/> changes. Invokes the <see cref="ViewportChanged"/> event.

+ 1 - 0
Terminal.Gui/View/View.Cursor.cs

@@ -1,3 +1,4 @@
+#nullable enable
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 public partial class View
 public partial class View

+ 1 - 3
Terminal.Gui/View/View.Diagnostics.cs

@@ -1,6 +1,4 @@
-
-
-
+#nullable enable
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>Enables diagnostic functions for <see cref="View"/>.</summary>
 /// <summary>Enables diagnostic functions for <see cref="View"/>.</summary>

+ 8 - 6
Terminal.Gui/View/View.Drawing.cs

@@ -202,11 +202,6 @@ public partial class View // Drawing APIs
     /// </remarks>
     /// </remarks>
     public void Draw ()
     public void Draw ()
     {
     {
-        if (!CanBeVisible (this))
-        {
-            return;
-        }
-
         OnDrawAdornments ();
         OnDrawAdornments ();
 
 
         if (ColorScheme is { })
         if (ColorScheme is { })
@@ -262,6 +257,7 @@ public partial class View // Drawing APIs
     ///         <see cref="View"/> .
     ///         <see cref="View"/> .
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
+    [CanBeNull]
     public event EventHandler<DrawEventArgs> DrawContent;
     public event EventHandler<DrawEventArgs> DrawContent;
 
 
     /// <summary>Event invoked when the content area of the View is completed drawing.</summary>
     /// <summary>Event invoked when the content area of the View is completed drawing.</summary>
@@ -272,6 +268,7 @@ public partial class View // Drawing APIs
     ///         <see cref="View"/> .
     ///         <see cref="View"/> .
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
+    [CanBeNull]
     public event EventHandler<DrawEventArgs> DrawContentComplete;
     public event EventHandler<DrawEventArgs> DrawContentComplete;
 
 
     /// <summary>Utility function to draw strings that contain a hotkey.</summary>
     /// <summary>Utility function to draw strings that contain a hotkey.</summary>
@@ -475,6 +472,11 @@ public partial class View // Drawing APIs
                 Clear ();
                 Clear ();
             }
             }
 
 
+            if (!CanBeVisible (this))
+            {
+                return;
+            }
+
             if (!string.IsNullOrEmpty (TextFormatter.Text))
             if (!string.IsNullOrEmpty (TextFormatter.Text))
             {
             {
                 if (TextFormatter is { })
                 if (TextFormatter is { })
@@ -505,7 +507,7 @@ public partial class View // Drawing APIs
             if (TabStop == TabBehavior.TabGroup && _subviews.Count(v => v.Arrangement.HasFlag (ViewArrangement.Overlapped)) > 0)
             if (TabStop == TabBehavior.TabGroup && _subviews.Count(v => v.Arrangement.HasFlag (ViewArrangement.Overlapped)) > 0)
             {
             {
                 // TODO: This is a temporary hack to make overlapped non-Toplevels have a zorder. See also View.SetFocus
                 // TODO: This is a temporary hack to make overlapped non-Toplevels have a zorder. See also View.SetFocus
-                subviewsNeedingDraw = _tabIndexes.Where (
+                subviewsNeedingDraw = _subviews.Where (
                                                        view => view.Visible
                                                        view => view.Visible
                                                                && (view.NeedsDisplay || view.SubViewNeedsDisplay || view.LayoutNeeded)
                                                                && (view.NeedsDisplay || view.SubViewNeedsDisplay || view.LayoutNeeded)
                                                       ).Reverse ();
                                                       ).Reverse ();

+ 95 - 85
Terminal.Gui/View/View.Hierarchy.cs

@@ -1,3 +1,4 @@
+#nullable enable
 using System.Diagnostics;
 using System.Diagnostics;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
@@ -5,27 +6,31 @@ namespace Terminal.Gui;
 public partial class View // SuperView/SubView hierarchy management (SuperView, SubViews, Add, Remove, etc.)
 public partial class View // SuperView/SubView hierarchy management (SuperView, SubViews, Add, Remove, etc.)
 {
 {
     private static readonly IList<View> _empty = new List<View> (0).AsReadOnly ();
     private static readonly IList<View> _empty = new List<View> (0).AsReadOnly ();
-    private List<View> _subviews; // This is null, and allocated on demand.
-    private View _superView;
 
 
-    /// <summary>Indicates whether the view was added to <see cref="SuperView"/>.</summary>
-    public bool IsAdded { get; private set; }
+    private List<View>? _subviews; // This is null, and allocated on demand.
+
+    // Internally, we use InternalSubviews rather than subviews, as we do not expect us
+    // to make the same mistakes our users make when they poke at the Subviews.
+    internal IList<View> InternalSubviews => _subviews ?? _empty;
 
 
     /// <summary>This returns a list of the subviews contained by this view.</summary>
     /// <summary>This returns a list of the subviews contained by this view.</summary>
     /// <value>The subviews.</value>
     /// <value>The subviews.</value>
     public IList<View> Subviews => _subviews?.AsReadOnly () ?? _empty;
     public IList<View> Subviews => _subviews?.AsReadOnly () ?? _empty;
 
 
+    private View? _superView;
+
     /// <summary>Returns the container for this view, or null if this view has not been added to a container.</summary>
     /// <summary>Returns the container for this view, or null if this view has not been added to a container.</summary>
     /// <value>The super view.</value>
     /// <value>The super view.</value>
-    public virtual View SuperView
+    public virtual View? SuperView
     {
     {
-        get => _superView;
+        get => _superView!;
         set => throw new NotImplementedException ();
         set => throw new NotImplementedException ();
     }
     }
 
 
-    // Internally, we use InternalSubviews rather than subviews, as we do not expect us
-    // to make the same mistakes our users make when they poke at the Subviews.
-    internal IList<View> InternalSubviews => _subviews ?? _empty;
+    #region AddRemove
+
+    /// <summary>Indicates whether the view was added to <see cref="SuperView"/>.</summary>
+    public bool IsAdded { get; private set; }
 
 
     /// <summary>Adds a subview (child) to this view.</summary>
     /// <summary>Adds a subview (child) to this view.</summary>
     /// <remarks>
     /// <remarks>
@@ -42,42 +47,26 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
     /// <returns>The view that was added.</returns>
     /// <returns>The view that was added.</returns>
     public virtual View Add (View view)
     public virtual View Add (View view)
     {
     {
-        if (view is null)
-        {
-            return view;
-        }
-
         if (_subviews is null)
         if (_subviews is null)
         {
         {
-            _subviews = new ();
+            _subviews = [];
         }
         }
 
 
-        if (_tabIndexes is null)
-        {
-            _tabIndexes = new ();
-        }
+        Debug.WriteLineIf (_subviews.Contains (view), $"WARNING: {view} has already been added to {this}.");
+
+        // TileView likes to add views that were previously added and have HasFocus = true. No bueno.
+        view.HasFocus = false;
 
 
-        Debug.WriteLineIf (_subviews.Contains (view), $"BUGBUG: {view} has already been added to {this}.");
         _subviews.Add (view);
         _subviews.Add (view);
-        _tabIndexes.Add (view);
         view._superView = this;
         view._superView = this;
 
 
-        if (view.CanFocus)
+        if (view is { Enabled: true, Visible: true, CanFocus: true })
         {
         {
-            // BUGBUG: This is a poor API design. Automatic behavior like this is non-obvious and should be avoided. Instead, callers to Add should be explicit about what they want.
-            _addingViewSoCanFocusAlsoUpdatesSuperView = true;
-
-            if (SuperView?.CanFocus == false)
+            // Add will cause the newly added subview to gain focus if it's focusable
+            if (HasFocus)
             {
             {
-                SuperView._addingViewSoCanFocusAlsoUpdatesSuperView = true;
-                SuperView.CanFocus = true;
-                SuperView._addingViewSoCanFocusAlsoUpdatesSuperView = false;
+                view.SetFocus ();
             }
             }
-
-            // QUESTION: This automatic behavior of setting CanFocus to true on the SuperView is not documented, and is annoying.
-            CanFocus = true;
-            view._tabIndex = _tabIndexes.IndexOf (view);
-            _addingViewSoCanFocusAlsoUpdatesSuperView = false;
         }
         }
 
 
         if (view.Enabled && !Enabled)
         if (view.Enabled && !Enabled)
@@ -113,7 +102,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
     ///         the lifecycle of the subviews to be transferred to this View.
     ///         the lifecycle of the subviews to be transferred to this View.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
-    public void Add (params View [] views)
+    public void Add (params View []? views)
     {
     {
         if (views is null)
         if (views is null)
         {
         {
@@ -127,32 +116,13 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
     }
     }
 
 
     /// <summary>Event fired when this view is added to another.</summary>
     /// <summary>Event fired when this view is added to another.</summary>
-    public event EventHandler<SuperViewChangedEventArgs> Added;
-
-    /// <summary>Get the top superview of a given <see cref="View"/>.</summary>
-    /// <returns>The superview view.</returns>
-    public View GetTopSuperView (View view = null, View superview = null)
-    {
-        View top = superview ?? Application.Top;
-
-        for (View v = view?.SuperView ?? this?.SuperView; v != null; v = v.SuperView)
-        {
-            top = v;
-
-            if (top == superview)
-            {
-                break;
-            }
-        }
-
-        return top;
-    }
+    public event EventHandler<SuperViewChangedEventArgs>? Added;
 
 
     /// <summary>Method invoked when a subview is being added to this view.</summary>
     /// <summary>Method invoked when a subview is being added to this view.</summary>
     /// <param name="e">Event where <see cref="ViewEventArgs.View"/> is the subview being added.</param>
     /// <param name="e">Event where <see cref="ViewEventArgs.View"/> is the subview being added.</param>
     public virtual void OnAdded (SuperViewChangedEventArgs e)
     public virtual void OnAdded (SuperViewChangedEventArgs e)
     {
     {
-        View view = e.Child;
+        View view = e.SubView;
         view.IsAdded = true;
         view.IsAdded = true;
         view.OnResizeNeeded ();
         view.OnResizeNeeded ();
         view.Added?.Invoke (this, e);
         view.Added?.Invoke (this, e);
@@ -162,7 +132,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
     /// <param name="e">Event args describing the subview being removed.</param>
     /// <param name="e">Event args describing the subview being removed.</param>
     public virtual void OnRemoved (SuperViewChangedEventArgs e)
     public virtual void OnRemoved (SuperViewChangedEventArgs e)
     {
     {
-        View view = e.Child;
+        View view = e.SubView;
         view.IsAdded = false;
         view.IsAdded = false;
         view.Removed?.Invoke (this, e);
         view.Removed?.Invoke (this, e);
     }
     }
@@ -175,18 +145,27 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
     ///         lifecycle to be transferred to the caller; the caller muse call <see cref="Dispose"/>.
     ///         lifecycle to be transferred to the caller; the caller muse call <see cref="Dispose"/>.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
-    public virtual View Remove (View view)
+    /// <returns>
+    ///     The removed View. <see langword="null"/> if the View could not be removed.
+    /// </returns>
+    public virtual View? Remove (View view)
     {
     {
-        if (view is null || _subviews is null)
+        if (_subviews is null)
         {
         {
             return view;
             return view;
         }
         }
 
 
         Rectangle touched = view.Frame;
         Rectangle touched = view.Frame;
+
+        // If a view being removed is focused, it should lose focus.
+        if (view.HasFocus)
+        {
+            view.HasFocus = false;
+        }
+
         _subviews.Remove (view);
         _subviews.Remove (view);
-        _tabIndexes.Remove (view);
-        view._superView = null;
-        //view._tabIndex = -1;
+        view._superView = null; // Null this AFTER removing focus
+
         SetNeedsLayout ();
         SetNeedsLayout ();
         SetNeedsDisplay ();
         SetNeedsDisplay ();
 
 
@@ -198,13 +177,13 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
             }
             }
         }
         }
 
 
-        OnRemoved (new (this, view));
-
-        if (Focused == view)
+        if (HasFocus)
         {
         {
-            Focused = null;
+            FocusDeepest (NavigationDirection.Forward, TabStop);
         }
         }
 
 
+        OnRemoved (new (this, view));
+
         return view;
         return view;
     }
     }
 
 
@@ -233,18 +212,43 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
     }
     }
 
 
     /// <summary>Event fired when this view is removed from another.</summary>
     /// <summary>Event fired when this view is removed from another.</summary>
-    public event EventHandler<SuperViewChangedEventArgs> Removed;
+    public event EventHandler<SuperViewChangedEventArgs>? Removed;
 
 
+    #endregion AddRemove
 
 
-    /// <summary>Moves <paramref name="subview"/> one position towards the start of the <see cref="Subviews"/> list</summary>
-    /// <param name="subview">The subview to move forward.</param>
-    public void BringSubviewForward (View subview)
+    // TODO: Mark as internal. Or nuke.
+    /// <summary>Get the top superview of a given <see cref="View"/>.</summary>
+    /// <returns>The superview view.</returns>
+    public View? GetTopSuperView (View? view = null, View? superview = null)
+    {
+        View? top = superview ?? Application.Top;
+
+        for (View? v = view?.SuperView ?? this?.SuperView; v != null; v = v.SuperView)
+        {
+            top = v;
+
+            if (top == superview)
+            {
+                break;
+            }
+        }
+
+        return top;
+    }
+
+    #region SubViewOrdering
+
+    /// <summary>
+    ///     Moves <paramref name="subview"/> one position towards the end of the <see cref="Subviews"/> list.
+    /// </summary>
+    /// <param name="subview">The subview to move.</param>
+    public void MoveSubviewTowardsEnd (View subview)
     {
     {
         PerformActionForSubview (
         PerformActionForSubview (
                                  subview,
                                  subview,
                                  x =>
                                  x =>
                                  {
                                  {
-                                     int idx = _subviews.IndexOf (x);
+                                     int idx = _subviews!.IndexOf (x);
 
 
                                      if (idx + 1 < _subviews.Count)
                                      if (idx + 1 < _subviews.Count)
                                      {
                                      {
@@ -255,30 +259,33 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
                                 );
                                 );
     }
     }
 
 
-    /// <summary>Moves <paramref name="subview"/> to the start of the <see cref="Subviews"/> list.</summary>
-    /// <param name="subview">The subview to send to the start.</param>
-    public void BringSubviewToFront (View subview)
+    /// <summary>
+    ///     Moves <paramref name="subview"/> to the end of the <see cref="Subviews"/> list.
+    /// </summary>
+    /// <param name="subview">The subview to move.</param>
+    public void MoveSubviewToEnd (View subview)
     {
     {
         PerformActionForSubview (
         PerformActionForSubview (
                                  subview,
                                  subview,
                                  x =>
                                  x =>
                                  {
                                  {
-                                     _subviews.Remove (x);
+                                     _subviews!.Remove (x);
                                      _subviews.Add (x);
                                      _subviews.Add (x);
                                  }
                                  }
                                 );
                                 );
     }
     }
 
 
-
-    /// <summary>Moves <paramref name="subview"/> one position towards the end of the <see cref="Subviews"/> list</summary>
-    /// <param name="subview">The subview to move backwards.</param>
-    public void SendSubviewBackwards (View subview)
+    /// <summary>
+    ///     Moves <paramref name="subview"/> one position towards the start of the <see cref="Subviews"/> list.
+    /// </summary>
+    /// <param name="subview">The subview to move.</param>
+    public void MoveSubviewTowardsStart (View subview)
     {
     {
         PerformActionForSubview (
         PerformActionForSubview (
                                  subview,
                                  subview,
                                  x =>
                                  x =>
                                  {
                                  {
-                                     int idx = _subviews.IndexOf (x);
+                                     int idx = _subviews!.IndexOf (x);
 
 
                                      if (idx > 0)
                                      if (idx > 0)
                                      {
                                      {
@@ -289,15 +296,17 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
                                 );
                                 );
     }
     }
 
 
-    /// <summary>Moves <paramref name="subview"/> to the end of the <see cref="Subviews"/> list.</summary>
-    /// <param name="subview">The subview to send to the end.</param>
-    public void SendSubviewToBack (View subview)
+    /// <summary>
+    ///     Moves <paramref name="subview"/> to the start of the <see cref="Subviews"/> list.
+    /// </summary>
+    /// <param name="subview">The subview to move.</param>
+    public void MoveSubviewToStart (View subview)
     {
     {
         PerformActionForSubview (
         PerformActionForSubview (
                                  subview,
                                  subview,
                                  x =>
                                  x =>
                                  {
                                  {
-                                     _subviews.Remove (x);
+                                     _subviews!.Remove (x);
                                      _subviews.Insert (0, subview);
                                      _subviews.Insert (0, subview);
                                  }
                                  }
                                 );
                                 );
@@ -310,7 +319,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
     /// <param name="action"></param>
     /// <param name="action"></param>
     private void PerformActionForSubview (View subview, Action<View> action)
     private void PerformActionForSubview (View subview, Action<View> action)
     {
     {
-        if (_subviews.Contains (subview))
+        if (_subviews!.Contains (subview))
         {
         {
             action (subview);
             action (subview);
         }
         }
@@ -320,4 +329,5 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
         subview.SetNeedsDisplay ();
         subview.SetNeedsDisplay ();
     }
     }
 
 
+    #endregion SubViewOrdering
 }
 }

+ 47 - 50
Terminal.Gui/View/View.Keyboard.cs

@@ -1,12 +1,12 @@
-using System.ComponentModel;
+#nullable enable
 using System.Diagnostics;
 using System.Diagnostics;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
-public partial class View  // Keyboard APIs
+public partial class View // Keyboard APIs
 {
 {
     /// <summary>
     /// <summary>
-    ///  Helper to configure all things keyboard related for a View. Called from the View constructor.
+    ///     Helper to configure all things keyboard related for a View. Called from the View constructor.
     /// </summary>
     /// </summary>
     private void SetupKeyboard ()
     private void SetupKeyboard ()
     {
     {
@@ -22,17 +22,14 @@ public partial class View  // Keyboard APIs
     }
     }
 
 
     /// <summary>
     /// <summary>
-    ///    Helper to dispose all things keyboard related for a View. Called from the View Dispose method.
+    ///     Helper to dispose all things keyboard related for a View. Called from the View Dispose method.
     /// </summary>
     /// </summary>
-    private void DisposeKeyboard ()
-    {
-        TitleTextFormatter.HotKeyChanged -= TitleTextFormatter_HotKeyChanged;
-    }
+    private void DisposeKeyboard () { TitleTextFormatter.HotKeyChanged -= TitleTextFormatter_HotKeyChanged; }
 
 
     #region HotKey Support
     #region HotKey Support
 
 
     /// <summary>
     /// <summary>
-    /// Called when the HotKey command (<see cref="Command.HotKey"/>) is invoked. Causes this view to be focused.
+    ///     Called when the HotKey command (<see cref="Command.HotKey"/>) is invoked. Causes this view to be focused.
     /// </summary>
     /// </summary>
     /// <returns>If <see langword="true"/> the command was canceled.</returns>
     /// <returns>If <see langword="true"/> the command was canceled.</returns>
     private bool? OnHotKey ()
     private bool? OnHotKey ()
@@ -40,6 +37,7 @@ public partial class View  // Keyboard APIs
         if (CanFocus)
         if (CanFocus)
         {
         {
             SetFocus ();
             SetFocus ();
+
             return true;
             return true;
         }
         }
 
 
@@ -47,10 +45,10 @@ public partial class View  // Keyboard APIs
     }
     }
 
 
     /// <summary>Invoked when the <see cref="HotKey"/> is changed.</summary>
     /// <summary>Invoked when the <see cref="HotKey"/> is changed.</summary>
-    public event EventHandler<KeyChangedEventArgs> HotKeyChanged;
+    public event EventHandler<KeyChangedEventArgs>? HotKeyChanged;
 
 
     private Key _hotKey = new ();
     private Key _hotKey = new ();
-    private void TitleTextFormatter_HotKeyChanged (object sender, KeyChangedEventArgs e) { HotKeyChanged?.Invoke (this, e); }
+    private void TitleTextFormatter_HotKeyChanged (object? sender, KeyChangedEventArgs e) { HotKeyChanged?.Invoke (this, e); }
 
 
     /// <summary>
     /// <summary>
     ///     Gets or sets the hot key defined for this view. Pressing the hot key on the keyboard while this view has focus will
     ///     Gets or sets the hot key defined for this view. Pressing the hot key on the keyboard while this view has focus will
@@ -117,7 +115,8 @@ public partial class View  // Keyboard APIs
     /// <remarks>
     /// <remarks>
     ///     <para>
     ///     <para>
     ///         By default, key bindings are added for both the base key (e.g. <see cref="Key.D3"/>) and the Alt-shifted key
     ///         By default, key bindings are added for both the base key (e.g. <see cref="Key.D3"/>) and the Alt-shifted key
-    ///         (e.g. <c>Key.D3.WithAlt</c>) This behavior can be overriden by overriding <see cref="AddKeyBindingsForHotKey"/>.
+    ///         (e.g. <c>Key.D3.WithAlt</c>) This behavior can be overriden by overriding <see cref="AddKeyBindingsForHotKey"/>
+    ///         .
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         By default, when <paramref name="hotKey"/> is <see cref="Key.A"/> through <see cref="Key.Z"/> key bindings
     ///         By default, when <paramref name="hotKey"/> is <see cref="Key.A"/> through <see cref="Key.Z"/> key bindings
@@ -131,7 +130,7 @@ public partial class View  // Keyboard APIs
     /// <param name="context">Arbitrary context that can be associated with this key binding.</param>
     /// <param name="context">Arbitrary context that can be associated with this key binding.</param>
     /// <returns><see langword="true"/> if the HotKey bindings were added.</returns>
     /// <returns><see langword="true"/> if the HotKey bindings were added.</returns>
     /// <exception cref="ArgumentException"></exception>
     /// <exception cref="ArgumentException"></exception>
-    public virtual bool AddKeyBindingsForHotKey (Key prevHotKey, Key hotKey, [CanBeNull] object context = null)
+    public virtual bool AddKeyBindingsForHotKey (Key prevHotKey, Key hotKey, object? context = null)
     {
     {
         if (_hotKey == hotKey)
         if (_hotKey == hotKey)
         {
         {
@@ -195,6 +194,7 @@ public partial class View  // Keyboard APIs
         if (newKey != Key.Empty)
         if (newKey != Key.Empty)
         {
         {
             KeyBinding keyBinding = new ([Command.HotKey], KeyBindingScope.HotKey, context);
             KeyBinding keyBinding = new ([Command.HotKey], KeyBindingScope.HotKey, context);
+
             // Add the base and Alt key
             // Add the base and Alt key
             KeyBindings.Remove (newKey);
             KeyBindings.Remove (newKey);
             KeyBindings.Add (newKey, keyBinding);
             KeyBindings.Add (newKey, keyBinding);
@@ -220,10 +220,7 @@ public partial class View  // Keyboard APIs
     /// </summary>
     /// </summary>
     public virtual Rune HotKeySpecifier
     public virtual Rune HotKeySpecifier
     {
     {
-        get
-        {
-            return TitleTextFormatter.HotKeySpecifier;
-        }
+        get => TitleTextFormatter.HotKeySpecifier;
         set
         set
         {
         {
             TitleTextFormatter.HotKeySpecifier = TextFormatter.HotKeySpecifier = value;
             TitleTextFormatter.HotKeySpecifier = TextFormatter.HotKeySpecifier = value;
@@ -363,7 +360,7 @@ public partial class View  // Keyboard APIs
     ///     </para>
     ///     </para>
     ///     <para>See <see href="../docs/keyboard.md">for an overview of Terminal.Gui keyboard APIs.</see></para>
     ///     <para>See <see href="../docs/keyboard.md">for an overview of Terminal.Gui keyboard APIs.</see></para>
     /// </remarks>
     /// </remarks>
-    public event EventHandler<Key> KeyDown;
+    public event EventHandler<Key>? KeyDown;
 
 
     /// <summary>
     /// <summary>
     ///     Low-level API called when the user presses a key, allowing views do things during key down events. This is
     ///     Low-level API called when the user presses a key, allowing views do things during key down events. This is
@@ -411,7 +408,7 @@ public partial class View  // Keyboard APIs
     ///     </para>
     ///     </para>
     ///     <para>See <see href="../docs/keyboard.md">for an overview of Terminal.Gui keyboard APIs.</see></para>
     ///     <para>See <see href="../docs/keyboard.md">for an overview of Terminal.Gui keyboard APIs.</see></para>
     /// </remarks>
     /// </remarks>
-    public event EventHandler<Key> ProcessKeyDown;
+    public event EventHandler<Key>? ProcessKeyDown;
 
 
     #endregion KeyDown Event
     #endregion KeyDown Event
 
 
@@ -502,7 +499,7 @@ public partial class View  // Keyboard APIs
     ///         <para>See <see href="../docs/keyboard.md">for an overview of Terminal.Gui keyboard APIs.</see></para>
     ///         <para>See <see href="../docs/keyboard.md">for an overview of Terminal.Gui keyboard APIs.</see></para>
     ///     </remarks>
     ///     </remarks>
     /// </summary>
     /// </summary>
-    public event EventHandler<Key> KeyUp;
+    public event EventHandler<Key>? KeyUp;
 
 
     #endregion KeyUp Event
     #endregion KeyUp Event
 
 
@@ -511,7 +508,7 @@ public partial class View  // Keyboard APIs
     #region Key Bindings
     #region Key Bindings
 
 
     /// <summary>Gets the key bindings for this view.</summary>
     /// <summary>Gets the key bindings for this view.</summary>
-    public KeyBindings KeyBindings { get; internal set; }
+    public KeyBindings KeyBindings { get; internal set; } = null!;
 
 
     private Dictionary<Command, Func<CommandContext, bool?>> CommandImplementations { get; } = new ();
     private Dictionary<Command, Func<CommandContext, bool?>> CommandImplementations { get; } = new ();
 
 
@@ -535,11 +532,11 @@ public partial class View  // Keyboard APIs
         if (KeyBindings.TryGet (keyEvent, scope, out KeyBinding kb))
         if (KeyBindings.TryGet (keyEvent, scope, out KeyBinding kb))
         {
         {
             InvokingKeyBindings?.Invoke (this, keyEvent);
             InvokingKeyBindings?.Invoke (this, keyEvent);
+
             if (keyEvent.Handled)
             if (keyEvent.Handled)
             {
             {
                 return true;
                 return true;
             }
             }
-
         }
         }
 
 
         // * If no key binding was found, `InvokeKeyBindings` returns `null`.
         // * If no key binding was found, `InvokeKeyBindings` returns `null`.
@@ -614,6 +611,7 @@ public partial class View  // Keyboard APIs
             {
             {
                 continue;
                 continue;
             }
             }
+
             if (subview.KeyBindings.TryGet (keyEvent, scope, out KeyBinding binding))
             if (subview.KeyBindings.TryGet (keyEvent, scope, out KeyBinding binding))
             {
             {
                 if (binding.Scope == KeyBindingScope.Focused && !subview.HasFocus)
                 if (binding.Scope == KeyBindingScope.Focused && !subview.HasFocus)
@@ -631,6 +629,7 @@ public partial class View  // Keyboard APIs
                 if (subViewHandled is { })
                 if (subViewHandled is { })
                 {
                 {
                     handled = subViewHandled;
                     handled = subViewHandled;
+
                     if ((bool)subViewHandled)
                     if ((bool)subViewHandled)
                     {
                     {
                         return true;
                         return true;
@@ -639,6 +638,7 @@ public partial class View  // Keyboard APIs
             }
             }
 
 
             bool recurse = subview.ProcessSubViewKeyBindings (keyEvent, scope, ref handled, invoke);
             bool recurse = subview.ProcessSubViewKeyBindings (keyEvent, scope, ref handled, invoke);
+
             if (recurse || (handled is { } && (bool)handled))
             if (recurse || (handled is { } && (bool)handled))
             {
             {
                 return true;
                 return true;
@@ -652,12 +652,12 @@ public partial class View  // Keyboard APIs
     // TODO: A better approach would be to have Application hold a list of bound Hotkeys, similar to
     // TODO: A better approach would be to have Application hold a list of bound Hotkeys, similar to
     // TODO: how Application holds a list of Application Scoped key bindings and then check that list.
     // TODO: how Application holds a list of Application Scoped key bindings and then check that list.
     /// <summary>
     /// <summary>
-    /// Returns true if Key is bound in this view hierarchy. For debugging
+    ///     Returns true if Key is bound in this view hierarchy. For debugging
     /// </summary>
     /// </summary>
     /// <param name="key">The key to test.</param>
     /// <param name="key">The key to test.</param>
     /// <param name="boundView">Returns the view the key is bound to.</param>
     /// <param name="boundView">Returns the view the key is bound to.</param>
     /// <returns></returns>
     /// <returns></returns>
-    public bool IsHotKeyKeyBound (Key key, out View boundView)
+    public bool IsHotKeyKeyBound (Key key, out View? boundView)
     {
     {
         // recurse through the subviews to find the views that has the key bound
         // recurse through the subviews to find the views that has the key bound
         boundView = null;
         boundView = null;
@@ -667,6 +667,7 @@ public partial class View  // Keyboard APIs
             if (subview.KeyBindings.TryGet (key, KeyBindingScope.HotKey, out _))
             if (subview.KeyBindings.TryGet (key, KeyBindingScope.HotKey, out _))
             {
             {
                 boundView = subview;
                 boundView = subview;
+
                 return true;
                 return true;
             }
             }
 
 
@@ -674,8 +675,8 @@ public partial class View  // Keyboard APIs
             {
             {
                 return true;
                 return true;
             }
             }
-
         }
         }
+
         return false;
         return false;
     }
     }
 
 
@@ -683,7 +684,7 @@ public partial class View  // Keyboard APIs
     ///     Invoked when a key is pressed that may be mapped to a key binding. Set <see cref="Key.Handled"/> to true to
     ///     Invoked when a key is pressed that may be mapped to a key binding. Set <see cref="Key.Handled"/> to true to
     ///     stop the key from being processed by other views.
     ///     stop the key from being processed by other views.
     /// </summary>
     /// </summary>
-    public event EventHandler<Key> InvokingKeyBindings;
+    public event EventHandler<Key>? InvokingKeyBindings;
 
 
     /// <summary>
     /// <summary>
     ///     Invokes any binding that is registered on this <see cref="View"/> and matches the <paramref name="key"/>
     ///     Invokes any binding that is registered on this <see cref="View"/> and matches the <paramref name="key"/>
@@ -713,20 +714,19 @@ public partial class View  // Keyboard APIs
             //var boundView = views [0];
             //var boundView = views [0];
             //var commandBinding = boundView.KeyBindings.Get (key);
             //var commandBinding = boundView.KeyBindings.Get (key);
             Debug.WriteLine (
             Debug.WriteLine (
-                             $"WARNING: InvokeKeyBindings ({key}) - An Application scope binding exists for this key. The registered view will not invoke Command.");//{commandBinding.Commands [0]}: {boundView}.");
+                             $"WARNING: InvokeKeyBindings ({key}) - An Application scope binding exists for this key. The registered view will not invoke Command."); //{commandBinding.Commands [0]}: {boundView}.");
         }
         }
 
 
         // TODO: This is a "prototype" debug check. It may be too annoying vs. useful.
         // TODO: This is a "prototype" debug check. It may be too annoying vs. useful.
         // Scour the bindings up our View hierarchy
         // Scour the bindings up our View hierarchy
         // to ensure that the key is not already bound to a different set of commands.
         // to ensure that the key is not already bound to a different set of commands.
-        if (SuperView?.IsHotKeyKeyBound (key, out View previouslyBoundView) ?? false)
+        if (SuperView?.IsHotKeyKeyBound (key, out View? previouslyBoundView) ?? false)
         {
         {
             Debug.WriteLine ($"WARNING: InvokeKeyBindings ({key}) - A subview or peer has bound this Key and will not see it: {previouslyBoundView}.");
             Debug.WriteLine ($"WARNING: InvokeKeyBindings ({key}) - A subview or peer has bound this Key and will not see it: {previouslyBoundView}.");
         }
         }
 
 
 #endif
 #endif
 
 
-
         foreach (Command command in binding.Commands)
         foreach (Command command in binding.Commands)
         {
         {
             if (!CommandImplementations.ContainsKey (command))
             if (!CommandImplementations.ContainsKey (command))
@@ -763,7 +763,7 @@ public partial class View  // Keyboard APIs
     ///     <see langword="true"/> if the command was invoked the command was handled.
     ///     <see langword="true"/> if the command was invoked the command was handled.
     ///     <see langword="false"/> if the command was invoked and the command was not handled.
     ///     <see langword="false"/> if the command was invoked and the command was not handled.
     /// </returns>
     /// </returns>
-    public bool? InvokeCommands (Command [] commands, [CanBeNull] Key key = null, [CanBeNull] KeyBinding? keyBinding = null)
+    public bool? InvokeCommands (Command [] commands, Key? key = null, KeyBinding? keyBinding = null)
     {
     {
         bool? toReturn = null;
         bool? toReturn = null;
 
 
@@ -798,11 +798,12 @@ public partial class View  // Keyboard APIs
     ///     <see langword="null"/> if no command was found. <see langword="true"/> if the command was invoked, and it
     ///     <see langword="null"/> if no command was found. <see langword="true"/> if the command was invoked, and it
     ///     handled the command. <see langword="false"/> if the command was invoked, and it did not handle the command.
     ///     handled the command. <see langword="false"/> if the command was invoked, and it did not handle the command.
     /// </returns>
     /// </returns>
-    public bool? InvokeCommand (Command command, [CanBeNull] Key key = null, [CanBeNull] KeyBinding? keyBinding = null)
+    public bool? InvokeCommand (Command command, Key? key = null, KeyBinding? keyBinding = null)
     {
     {
-        if (CommandImplementations.TryGetValue (command, out Func<CommandContext, bool?> implementation))
+        if (CommandImplementations.TryGetValue (command, out Func<CommandContext, bool?>? implementation))
         {
         {
             var context = new CommandContext (command, key, keyBinding); // Create the context here
             var context = new CommandContext (command, key, keyBinding); // Create the context here
+
             return implementation (context);
             return implementation (context);
         }
         }
 
 
@@ -812,7 +813,7 @@ public partial class View  // Keyboard APIs
     /// <summary>
     /// <summary>
     ///     <para>
     ///     <para>
     ///         Sets the function that will be invoked for a <see cref="Command"/>. Views should call
     ///         Sets the function that will be invoked for a <see cref="Command"/>. Views should call
-    ///        AddCommand for each command they support.
+    ///         AddCommand for each command they support.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
     ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
@@ -820,22 +821,20 @@ public partial class View  // Keyboard APIs
     ///     </para>
     ///     </para>
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
-    /// <para>
-    ///     This version of AddCommand is for commands that require <see cref="CommandContext"/>. Use <see cref="AddCommand(Command,Func{System.Nullable{bool}})"/>
-    ///     in cases where the command does not require a <see cref="CommandContext"/>.
-    /// </para>
+    ///     <para>
+    ///         This version of AddCommand is for commands that require <see cref="CommandContext"/>. Use
+    ///         <see cref="AddCommand(Command,Func{System.Nullable{bool}})"/>
+    ///         in cases where the command does not require a <see cref="CommandContext"/>.
+    ///     </para>
     /// </remarks>
     /// </remarks>
     /// <param name="command">The command.</param>
     /// <param name="command">The command.</param>
     /// <param name="f">The function.</param>
     /// <param name="f">The function.</param>
-    protected void AddCommand (Command command, Func<CommandContext, bool?> f)
-    {
-        CommandImplementations [command] = f;
-    }
+    protected void AddCommand (Command command, Func<CommandContext, bool?> f) { CommandImplementations [command] = f; }
 
 
     /// <summary>
     /// <summary>
     ///     <para>
     ///     <para>
     ///         Sets the function that will be invoked for a <see cref="Command"/>. Views should call
     ///         Sets the function that will be invoked for a <see cref="Command"/>. Views should call
-    ///        AddCommand for each command they support.
+    ///         AddCommand for each command they support.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
     ///         If AddCommand has already been called for <paramref name="command"/> <paramref name="f"/> will
@@ -843,17 +842,15 @@ public partial class View  // Keyboard APIs
     ///     </para>
     ///     </para>
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
-    /// <para>
-    ///     This version of AddCommand is for commands that do not require a <see cref="CommandContext"/>.
-    ///     If the command requires context, use <see cref="AddCommand(Command,Func{CommandContext,System.Nullable{bool}})"/>
-    /// </para>
+    ///     <para>
+    ///         This version of AddCommand is for commands that do not require a <see cref="CommandContext"/>.
+    ///         If the command requires context, use
+    ///         <see cref="AddCommand(Command,Func{CommandContext,System.Nullable{bool}})"/>
+    ///     </para>
     /// </remarks>
     /// </remarks>
     /// <param name="command">The command.</param>
     /// <param name="command">The command.</param>
     /// <param name="f">The function.</param>
     /// <param name="f">The function.</param>
-    protected void AddCommand (Command command, Func<bool?> f)
-    {
-        CommandImplementations [command] = ctx => f ();
-    }
+    protected void AddCommand (Command command, Func<bool?> f) { CommandImplementations [command] = ctx => f (); }
 
 
     /// <summary>Returns all commands that are supported by this <see cref="View"/>.</summary>
     /// <summary>Returns all commands that are supported by this <see cref="View"/>.</summary>
     /// <returns></returns>
     /// <returns></returns>

+ 7 - 7
Terminal.Gui/View/View.Layout.cs

@@ -121,7 +121,7 @@ public partial class View // Layout APIs
         View? superView;
         View? superView;
         statusBar = null!;
         statusBar = null!;
 
 
-        if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
+        if (viewToMove is not Toplevel || viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
         {
         {
             maxDimension = Driver.Cols;
             maxDimension = Driver.Cols;
             superView = Application.Top;
             superView = Application.Top;
@@ -163,7 +163,7 @@ public partial class View // Layout APIs
         }
         }
         else
         else
         {
         {
-            View t = viewToMove!.SuperView;
+            View? t = viewToMove!.SuperView;
 
 
             while (t is { } and not Toplevel)
             while (t is { } and not Toplevel)
             {
             {
@@ -194,7 +194,7 @@ public partial class View // Layout APIs
         }
         }
         else
         else
         {
         {
-            View t = viewToMove!.SuperView;
+            View? t = viewToMove!.SuperView;
 
 
             while (t is { } and not Toplevel)
             while (t is { } and not Toplevel)
             {
             {
@@ -311,7 +311,7 @@ public partial class View // Layout APIs
     public virtual Rectangle FrameToScreen ()
     public virtual Rectangle FrameToScreen ()
     {
     {
         Rectangle screen = Frame;
         Rectangle screen = Frame;
-        View current = SuperView;
+        View? current = SuperView;
 
 
         while (current is { })
         while (current is { })
         {
         {
@@ -547,14 +547,14 @@ public partial class View // Layout APIs
     ///     Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has
     ///     Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has
     ///     otherwise changed.
     ///     otherwise changed.
     /// </remarks>
     /// </remarks>
-    public event EventHandler<LayoutEventArgs> LayoutComplete;
+    public event EventHandler<LayoutEventArgs>? LayoutComplete;
 
 
     /// <summary>Fired after the View's <see cref="LayoutSubviews"/> method has completed.</summary>
     /// <summary>Fired after the View's <see cref="LayoutSubviews"/> method has completed.</summary>
     /// <remarks>
     /// <remarks>
     ///     Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has
     ///     Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has
     ///     otherwise changed.
     ///     otherwise changed.
     /// </remarks>
     /// </remarks>
-    public event EventHandler<LayoutEventArgs> LayoutStarted;
+    public event EventHandler<LayoutEventArgs>? LayoutStarted;
 
 
     /// <summary>
     /// <summary>
     ///     Adjusts <see cref="Frame"/> given the SuperView's ContentSize (nominally the same as
     ///     Adjusts <see cref="Frame"/> given the SuperView's ContentSize (nominally the same as
@@ -694,7 +694,7 @@ public partial class View // Layout APIs
         HashSet<View> nodes = new ();
         HashSet<View> nodes = new ();
         HashSet<(View, View)> edges = new ();
         HashSet<(View, View)> edges = new ();
         CollectAll (this, ref nodes, ref edges);
         CollectAll (this, ref nodes, ref edges);
-        List<View> ordered = TopologicalSort (SuperView, nodes, edges);
+        List<View> ordered = TopologicalSort (SuperView!, nodes, edges);
 
 
         foreach (View v in ordered)
         foreach (View v in ordered)
         {
         {

+ 9 - 9
Terminal.Gui/View/View.Mouse.cs

@@ -1,17 +1,17 @@
-using System.ComponentModel;
+#nullable enable
+using System.ComponentModel;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 public partial class View // Mouse APIs
 public partial class View // Mouse APIs
 {
 {
-    [CanBeNull]
-    private ColorScheme _savedHighlightColorScheme;
+    private ColorScheme? _savedHighlightColorScheme;
 
 
     /// <summary>
     /// <summary>
     ///     Fired when the view is highlighted. Set <see cref="CancelEventArgs.Cancel"/> to <see langword="true"/>
     ///     Fired when the view is highlighted. Set <see cref="CancelEventArgs.Cancel"/> to <see langword="true"/>
     ///     to implement a custom highlight scheme or prevent the view from being highlighted.
     ///     to implement a custom highlight scheme or prevent the view from being highlighted.
     /// </summary>
     /// </summary>
-    public event EventHandler<CancelEventArgs<HighlightStyle>> Highlight;
+    public event EventHandler<CancelEventArgs<HighlightStyle>>? Highlight;
 
 
     /// <summary>
     /// <summary>
     ///     Gets or sets whether the <see cref="View"/> will be highlighted visually while the mouse button is
     ///     Gets or sets whether the <see cref="View"/> will be highlighted visually while the mouse button is
@@ -29,10 +29,10 @@ public partial class View // Mouse APIs
     ///         The coordinates are relative to <see cref="View.Viewport"/>.
     ///         The coordinates are relative to <see cref="View.Viewport"/>.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
-    public event EventHandler<MouseEventEventArgs> MouseClick;
+    public event EventHandler<MouseEventEventArgs>? MouseClick;
 
 
     /// <summary>Event fired when the mouse moves into the View's <see cref="Viewport"/>.</summary>
     /// <summary>Event fired when the mouse moves into the View's <see cref="Viewport"/>.</summary>
-    public event EventHandler<MouseEventEventArgs> MouseEnter;
+    public event EventHandler<MouseEventEventArgs>? MouseEnter;
 
 
     /// <summary>Event fired when a mouse event occurs.</summary>
     /// <summary>Event fired when a mouse event occurs.</summary>
     /// <remarks>
     /// <remarks>
@@ -40,10 +40,10 @@ public partial class View // Mouse APIs
     ///         The coordinates are relative to <see cref="View.Viewport"/>.
     ///         The coordinates are relative to <see cref="View.Viewport"/>.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
-    public event EventHandler<MouseEventEventArgs> MouseEvent;
+    public event EventHandler<MouseEventEventArgs>? MouseEvent;
 
 
     /// <summary>Event fired when the mouse leaves the View's <see cref="Viewport"/>.</summary>
     /// <summary>Event fired when the mouse leaves the View's <see cref="Viewport"/>.</summary>
-    public event EventHandler<MouseEventEventArgs> MouseLeave;
+    public event EventHandler<MouseEventEventArgs>? MouseLeave;
 
 
     /// <summary>
     /// <summary>
     ///     Processes a <see cref="MouseEvent"/>. This method is called by <see cref="Application.OnMouseEvent"/> when a mouse
     ///     Processes a <see cref="MouseEvent"/>. This method is called by <see cref="Application.OnMouseEvent"/> when a mouse
@@ -244,7 +244,7 @@ public partial class View // Mouse APIs
         if (!Enabled)
         if (!Enabled)
         {
         {
             // QUESTION: Is this right? Should a disabled view eat mouse clicks?
             // QUESTION: Is this right? Should a disabled view eat mouse clicks?
-            return args.Handled = true;
+            return args.Handled = false;
         }
         }
 
 
         MouseClick?.Invoke (this, args);
         MouseClick?.Invoke (this, args);

File diff suppressed because it is too large
+ 415 - 534
Terminal.Gui/View/View.Navigation.cs


+ 1 - 1
Terminal.Gui/View/View.Text.cs

@@ -4,7 +4,7 @@ namespace Terminal.Gui;
 
 
 public partial class View // Text Property APIs
 public partial class View // Text Property APIs
 {
 {
-    private string _text;
+    private string _text = null!;
 
 
     /// <summary>
     /// <summary>
     ///     Called when the <see cref="Text"/> has changed. Fires the <see cref="TextChanged"/> event.
     ///     Called when the <see cref="Text"/> has changed. Fires the <see cref="TextChanged"/> event.

+ 41 - 25
Terminal.Gui/View/View.cs

@@ -1,4 +1,5 @@
-using System.ComponentModel;
+#nullable enable
+using System.ComponentModel;
 using System.Diagnostics;
 using System.Diagnostics;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
@@ -112,11 +113,11 @@ public partial class View : Responder, ISupportInitializeNotification
     ///     <see cref="HandledEventArgs.Handled"/>
     ///     <see cref="HandledEventArgs.Handled"/>
     ///     to cancel the event.
     ///     to cancel the event.
     /// </summary>
     /// </summary>
-    public event EventHandler<HandledEventArgs> Accept;
+    public event EventHandler<HandledEventArgs>? Accept;
 
 
     /// <summary>Gets or sets arbitrary data for the view.</summary>
     /// <summary>Gets or sets arbitrary data for the view.</summary>
     /// <remarks>This property is not used internally.</remarks>
     /// <remarks>This property is not used internally.</remarks>
-    public object Data { get; set; }
+    public object? Data { get; set; }
 
 
     /// <summary>Gets or sets an identifier for the view;</summary>
     /// <summary>Gets or sets an identifier for the view;</summary>
     /// <value>The identifier.</value>
     /// <value>The identifier.</value>
@@ -168,7 +169,7 @@ public partial class View : Responder, ISupportInitializeNotification
     ///     Points to the current driver in use by the view, it is a convenience property for simplifying the development
     ///     Points to the current driver in use by the view, it is a convenience property for simplifying the development
     ///     of new views.
     ///     of new views.
     /// </summary>
     /// </summary>
-    public static ConsoleDriver Driver => Application.Driver;
+    public static ConsoleDriver Driver => Application.Driver!;
 
 
     /// <summary>Initializes a new instance of <see cref="View"/>.</summary>
     /// <summary>Initializes a new instance of <see cref="View"/>.</summary>
     /// <remarks>
     /// <remarks>
@@ -191,7 +192,7 @@ public partial class View : Responder, ISupportInitializeNotification
     ///     configurations and assignments to be performed before the <see cref="View"/> being shown.
     ///     configurations and assignments to be performed before the <see cref="View"/> being shown.
     ///     View implements <see cref="ISupportInitializeNotification"/> to allow for more sophisticated initialization.
     ///     View implements <see cref="ISupportInitializeNotification"/> to allow for more sophisticated initialization.
     /// </summary>
     /// </summary>
-    public event EventHandler Initialized;
+    public event EventHandler? Initialized;
 
 
     /// <summary>
     /// <summary>
     ///     Get or sets if  the <see cref="View"/> has been initialized (via <see cref="ISupportInitialize.BeginInit"/>
     ///     Get or sets if  the <see cref="View"/> has been initialized (via <see cref="ISupportInitialize.BeginInit"/>
@@ -228,9 +229,10 @@ public partial class View : Responder, ISupportInitializeNotification
         {
         {
             throw new InvalidOperationException ("The view is already initialized.");
             throw new InvalidOperationException ("The view is already initialized.");
         }
         }
-
+#if AUTO_CANFOCUS
         _oldCanFocus = CanFocus;
         _oldCanFocus = CanFocus;
         _oldTabIndex = _tabIndex;
         _oldTabIndex = _tabIndex;
+#endif
 
 
         BeginInitAdornments ();
         BeginInitAdornments ();
 
 
@@ -285,15 +287,17 @@ public partial class View : Responder, ISupportInitializeNotification
         Initialized?.Invoke (this, EventArgs.Empty);
         Initialized?.Invoke (this, EventArgs.Empty);
     }
     }
 
 
-    #endregion Constructors and Initialization
+#endregion Constructors and Initialization
 
 
     #region Visibility
     #region Visibility
 
 
     private bool _enabled = true;
     private bool _enabled = true;
+
+    // This is a cache of the Enabled property so that we can restore it when the superview is re-enabled.
     private bool _oldEnabled;
     private bool _oldEnabled;
 
 
     /// <summary>Gets or sets a value indicating whether this <see cref="Responder"/> can respond to user interaction.</summary>
     /// <summary>Gets or sets a value indicating whether this <see cref="Responder"/> can respond to user interaction.</summary>
-    public virtual bool Enabled
+    public bool Enabled
     {
     {
         get => _enabled;
         get => _enabled;
         set
         set
@@ -307,7 +311,13 @@ public partial class View : Responder, ISupportInitializeNotification
 
 
             if (!_enabled && HasFocus)
             if (!_enabled && HasFocus)
             {
             {
-                SetHasFocus (false, this);
+                HasFocus = false;
+            }
+
+            if (_enabled && CanFocus && Visible && !HasFocus
+                && SuperView is null or { HasFocus: true, Visible: true, Enabled: true, Focused: null })
+            {
+                SetFocus ();
             }
             }
 
 
             OnEnabledChanged ();
             OnEnabledChanged ();
@@ -328,14 +338,16 @@ public partial class View : Responder, ISupportInitializeNotification
                 else
                 else
                 {
                 {
                     view.Enabled = view._oldEnabled;
                     view.Enabled = view._oldEnabled;
+#if AUTO_CANFOCUS
                     view._addingViewSoCanFocusAlsoUpdatesSuperView = _enabled;
                     view._addingViewSoCanFocusAlsoUpdatesSuperView = _enabled;
+#endif
                 }
                 }
             }
             }
         }
         }
     }
     }
 
 
     /// <summary>Event fired when the <see cref="Enabled"/> value is being changed.</summary>
     /// <summary>Event fired when the <see cref="Enabled"/> value is being changed.</summary>
-    public event EventHandler EnabledChanged;
+    public event EventHandler? EnabledChanged;
 
 
     /// <summary>Method invoked when the <see cref="Enabled"/> property from a view is changed.</summary>
     /// <summary>Method invoked when the <see cref="Enabled"/> property from a view is changed.</summary>
     public virtual void OnEnabledChanged () { EnabledChanged?.Invoke (this, EventArgs.Empty); }
     public virtual void OnEnabledChanged () { EnabledChanged?.Invoke (this, EventArgs.Empty); }
@@ -359,13 +371,14 @@ public partial class View : Responder, ISupportInitializeNotification
             {
             {
                 if (HasFocus)
                 if (HasFocus)
                 {
                 {
-                    SetHasFocus (false, this);
+                    HasFocus = false;
                 }
                 }
+            }
 
 
-                if (IsInitialized && ClearOnVisibleFalse)
-                {
-                    Clear ();
-                }
+            if (_visible && CanFocus && Enabled && !HasFocus
+                && SuperView is null or { HasFocus: true, Visible: true, Enabled: true, Focused: null })
+            {
+                SetFocus ();
             }
             }
 
 
             OnVisibleChanged ();
             OnVisibleChanged ();
@@ -376,20 +389,23 @@ public partial class View : Responder, ISupportInitializeNotification
     /// <summary>Method invoked when the <see cref="Visible"/> property from a view is changed.</summary>
     /// <summary>Method invoked when the <see cref="Visible"/> property from a view is changed.</summary>
     public virtual void OnVisibleChanged () { VisibleChanged?.Invoke (this, EventArgs.Empty); }
     public virtual void OnVisibleChanged () { VisibleChanged?.Invoke (this, EventArgs.Empty); }
 
 
-    /// <summary>Gets or sets whether a view is cleared if the <see cref="Visible"/> property is <see langword="false"/>.</summary>
-    public bool ClearOnVisibleFalse { get; set; } = true;
-
     /// <summary>Event fired when the <see cref="Visible"/> value is being changed.</summary>
     /// <summary>Event fired when the <see cref="Visible"/> value is being changed.</summary>
-    public event EventHandler VisibleChanged;
+    public event EventHandler? VisibleChanged;
 
 
-    private static bool CanBeVisible (View view)
+    // TODO: This API is a hack. We should make Visible propogate automatically, no? See https://github.com/gui-cs/Terminal.Gui/issues/3703
+    /// <summary>
+    ///     INTERNAL Indicates whether all views up the Superview hierarchy are visible.
+    /// </summary>
+    /// <param name="view">The view to test.</param>
+    /// <returns> <see langword="false"/> if `view.Visible` is  <see langword="false"/> or any Superview is not visible, <see langword="true"/> otherwise.</returns>
+    internal static bool CanBeVisible (View view)
     {
     {
         if (!view.Visible)
         if (!view.Visible)
         {
         {
             return false;
             return false;
         }
         }
 
 
-        for (View c = view.SuperView; c != null; c = c.SuperView)
+        for (View? c = view.SuperView; c != null; c = c.SuperView)
         {
         {
             if (!c.Visible)
             if (!c.Visible)
             {
             {
@@ -400,7 +416,7 @@ public partial class View : Responder, ISupportInitializeNotification
         return true;
         return true;
     }
     }
 
 
-    #endregion Visibility
+#endregion Visibility
 
 
     #region Title
     #region Title
 
 
@@ -463,7 +479,7 @@ public partial class View : Responder, ISupportInitializeNotification
                 SetHotKeyFromTitle ();
                 SetHotKeyFromTitle ();
                 SetNeedsDisplay ();
                 SetNeedsDisplay ();
 #if DEBUG
 #if DEBUG
-                if (_title is { } && string.IsNullOrEmpty (Id))
+                if (string.IsNullOrEmpty (Id))
                 {
                 {
                     Id = _title;
                     Id = _title;
                 }
                 }
@@ -504,13 +520,13 @@ public partial class View : Responder, ISupportInitializeNotification
     }
     }
 
 
     /// <summary>Event fired after the <see cref="View.Title"/> has been changed.</summary>
     /// <summary>Event fired after the <see cref="View.Title"/> has been changed.</summary>
-    public event EventHandler<EventArgs<string>> TitleChanged;
+    public event EventHandler<EventArgs<string>>? TitleChanged;
 
 
     /// <summary>
     /// <summary>
     ///     Event fired when the <see cref="View.Title"/> is changing. Set <see cref="CancelEventArgs.Cancel"/> to `true`
     ///     Event fired when the <see cref="View.Title"/> is changing. Set <see cref="CancelEventArgs.Cancel"/> to `true`
     ///     to cancel the Title change.
     ///     to cancel the Title change.
     /// </summary>
     /// </summary>
-    public event EventHandler<CancelEventArgs<string>> TitleChanging;
+    public event EventHandler<CancelEventArgs<string>>? TitleChanging;
 
 
     #endregion
     #endregion
 }
 }

+ 7 - 6
Terminal.Gui/Views/Bar.cs

@@ -1,3 +1,4 @@
+#nullable enable
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
@@ -43,7 +44,7 @@ public class Bar : View, IOrientation, IDesignable
         }
         }
     }
     }
 
 
-    private void Bar_Initialized (object sender, EventArgs e) { ColorScheme = Colors.ColorSchemes ["Menu"]; }
+    private void Bar_Initialized (object? sender, EventArgs e) { ColorScheme = Colors.ColorSchemes ["Menu"]; }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override void SetBorderStyle (LineStyle value)
     public override void SetBorderStyle (LineStyle value)
@@ -72,10 +73,10 @@ public class Bar : View, IOrientation, IDesignable
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+    public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public event EventHandler<EventArgs<Orientation>> OrientationChanged;
+    public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
 
 
     /// <summary>Called when <see cref="Orientation"/> has changed.</summary>
     /// <summary>Called when <see cref="Orientation"/> has changed.</summary>
     /// <param name="newOrientation"></param>
     /// <param name="newOrientation"></param>
@@ -132,9 +133,9 @@ public class Bar : View, IOrientation, IDesignable
     /// <summary>Removes a <see cref="Shortcut"/> at specified index of <see cref="View.Subviews"/>.</summary>
     /// <summary>Removes a <see cref="Shortcut"/> at specified index of <see cref="View.Subviews"/>.</summary>
     /// <param name="index">The zero-based index of the item to remove.</param>
     /// <param name="index">The zero-based index of the item to remove.</param>
     /// <returns>The <see cref="Shortcut"/> removed.</returns>
     /// <returns>The <see cref="Shortcut"/> removed.</returns>
-    public Shortcut RemoveShortcut (int index)
+    public Shortcut? RemoveShortcut (int index)
     {
     {
-        View toRemove = null;
+        View? toRemove = null;
 
 
         for (var i = 0; i < Subviews.Count; i++)
         for (var i = 0; i < Subviews.Count; i++)
         {
         {
@@ -158,7 +159,7 @@ public class Bar : View, IOrientation, IDesignable
     {
     {
         base.OnLayoutStarted (args);
         base.OnLayoutStarted (args);
 
 
-        View prevBarItem = null;
+        View? prevBarItem = null;
 
 
         switch (Orientation)
         switch (Orientation)
         {
         {

+ 1 - 1
Terminal.Gui/Views/ColorBar.cs

@@ -121,7 +121,7 @@ internal abstract class ColorBar : View, IColorBar
             }
             }
 
 
             mouseEvent.Handled = true;
             mouseEvent.Handled = true;
-            FocusFirst (null);
+            SetFocus ();
 
 
             return true;
             return true;
         }
         }

+ 4 - 0
Terminal.Gui/Views/ColorModelStrategy.cs

@@ -43,6 +43,10 @@ internal class ColorModelStrategy
 
 
     public void SetBarsToColor (IList<IColorBar> bars, Color newValue, ColorModel model)
     public void SetBarsToColor (IList<IColorBar> bars, Color newValue, ColorModel model)
     {
     {
+        if (bars.Count == 0)
+        {
+            return;
+        }
         switch (model)
         switch (model)
         {
         {
             case ColorModel.RGB:
             case ColorModel.RGB:

+ 46 - 13
Terminal.Gui/Views/ColorPicker.cs

@@ -1,5 +1,7 @@
 #nullable enable
 #nullable enable
 
 
+using System;
+
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>
 /// <summary>
@@ -15,6 +17,7 @@ public class ColorPicker : View
     public ColorPicker ()
     public ColorPicker ()
     {
     {
         CanFocus = true;
         CanFocus = true;
+        TabStop = TabBehavior.TabStop;
         Height = Dim.Auto ();
         Height = Dim.Auto ();
         Width = Dim.Auto ();
         Width = Dim.Auto ();
         ApplyStyleChanges ();
         ApplyStyleChanges ();
@@ -51,17 +54,17 @@ public class ColorPicker : View
             bar.Y = y;
             bar.Y = y;
             bar.Width = Dim.Fill (Style.ShowTextFields ? textFieldWidth : 0);
             bar.Width = Dim.Fill (Style.ShowTextFields ? textFieldWidth : 0);
 
 
+            TextField? tfValue = null;
             if (Style.ShowTextFields)
             if (Style.ShowTextFields)
             {
             {
-                var tfValue = new TextField
+                tfValue = new TextField
                 {
                 {
                     X = Pos.AnchorEnd (textFieldWidth),
                     X = Pos.AnchorEnd (textFieldWidth),
                     Y = y,
                     Y = y,
                     Width = textFieldWidth
                     Width = textFieldWidth
                 };
                 };
-                tfValue.Leave += UpdateSingleBarValueFromTextField;
+                tfValue.HasFocusChanged += UpdateSingleBarValueFromTextField;
                 _textFields.Add (bar, tfValue);
                 _textFields.Add (bar, tfValue);
-                Add (tfValue);
             }
             }
 
 
             y++;
             y++;
@@ -71,6 +74,11 @@ public class ColorPicker : View
             _bars.Add (bar);
             _bars.Add (bar);
 
 
             Add (bar);
             Add (bar);
+
+            if (tfValue is { })
+            {
+                Add (tfValue);
+            }
         }
         }
 
 
         if (Style.ShowColorName)
         if (Style.ShowColorName)
@@ -81,7 +89,10 @@ public class ColorPicker : View
         CreateTextField ();
         CreateTextField ();
         SelectedColor = oldValue;
         SelectedColor = oldValue;
 
 
-        LayoutSubviews ();
+        if (IsInitialized)
+        {
+            LayoutSubviews ();
+        }
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -141,7 +152,7 @@ public class ColorPicker : View
         };
         };
         _tfName.Autocomplete = auto;
         _tfName.Autocomplete = auto;
 
 
-        _tfName.Leave += UpdateValueFromName;
+        _tfName.HasFocusChanged += UpdateValueFromName;
     }
     }
 
 
     private void CreateTextField ()
     private void CreateTextField ()
@@ -164,13 +175,13 @@ public class ColorPicker : View
         {
         {
             Y = y,
             Y = y,
             X = 4,
             X = 4,
-            Width = 8
+            Width = 8,
         };
         };
 
 
         Add (_lbHex);
         Add (_lbHex);
         Add (_tfHex);
         Add (_tfHex);
 
 
-        _tfHex.Leave += UpdateValueFromTextField;
+        _tfHex.HasFocusChanged += UpdateValueFromTextField;
     }
     }
 
 
     private void DisposeOldViews ()
     private void DisposeOldViews ()
@@ -181,7 +192,7 @@ public class ColorPicker : View
 
 
             if (_textFields.TryGetValue (bar, out TextField? tf))
             if (_textFields.TryGetValue (bar, out TextField? tf))
             {
             {
-                tf.Leave -= UpdateSingleBarValueFromTextField;
+                tf.HasFocusChanged -= UpdateSingleBarValueFromTextField;
                 Remove (tf);
                 Remove (tf);
                 tf.Dispose ();
                 tf.Dispose ();
             }
             }
@@ -203,7 +214,7 @@ public class ColorPicker : View
         if (_tfHex != null)
         if (_tfHex != null)
         {
         {
             Remove (_tfHex);
             Remove (_tfHex);
-            _tfHex.Leave -= UpdateValueFromTextField;
+            _tfHex.HasFocusChanged -= UpdateValueFromTextField;
             _tfHex.Dispose ();
             _tfHex.Dispose ();
             _tfHex = null;
             _tfHex = null;
         }
         }
@@ -218,7 +229,7 @@ public class ColorPicker : View
         if (_tfName != null)
         if (_tfName != null)
         {
         {
             Remove (_tfName);
             Remove (_tfName);
-            _tfName.Leave -= UpdateValueFromName;
+            _tfName.HasFocusChanged -= UpdateValueFromName;
             _tfName.Dispose ();
             _tfName.Dispose ();
             _tfName = null;
             _tfName = null;
         }
         }
@@ -266,8 +277,13 @@ public class ColorPicker : View
         }
         }
     }
     }
 
 
-    private void UpdateSingleBarValueFromTextField (object? sender, FocusEventArgs e)
+    private void UpdateSingleBarValueFromTextField (object? sender, HasFocusEventArgs e)
     {
     {
+        if (e.NewValue)
+        {
+            return;
+        }
+
         foreach (KeyValuePair<IColorBar, TextField> kvp in _textFields)
         foreach (KeyValuePair<IColorBar, TextField> kvp in _textFields)
         {
         {
             if (kvp.Value == sender)
             if (kvp.Value == sender)
@@ -280,8 +296,13 @@ public class ColorPicker : View
         }
         }
     }
     }
 
 
-    private void UpdateValueFromName (object? sender, FocusEventArgs e)
+    private void UpdateValueFromName (object? sender, HasFocusEventArgs e)
     {
     {
+        if (e.NewValue)
+        {
+            return;
+        }
+
         if (_tfName == null)
         if (_tfName == null)
         {
         {
             return;
             return;
@@ -298,8 +319,13 @@ public class ColorPicker : View
         }
         }
     }
     }
 
 
-    private void UpdateValueFromTextField (object? sender, FocusEventArgs e)
+    private void UpdateValueFromTextField (object? sender, HasFocusEventArgs e)
     {
     {
+            if (e.NewValue)
+        {
+            return;
+        }
+
         if (_tfHex == null)
         if (_tfHex == null)
         {
         {
             return;
             return;
@@ -315,4 +341,11 @@ public class ColorPicker : View
             SyncSubViewValues (false);
             SyncSubViewValues (false);
         }
         }
     }
     }
+
+
+    protected override void Dispose (bool disposing)
+    {
+        DisposeOldViews ();
+        base.Dispose (disposing);
+    }
 }
 }

+ 1 - 0
Terminal.Gui/Views/ColorPicker16.cs

@@ -51,6 +51,7 @@ public class ColorPicker16 : View
     }
     }
 
 
     /// <summary>Fired when a color is picked.</summary>
     /// <summary>Fired when a color is picked.</summary>
+    [CanBeNull]
     public event EventHandler<ColorEventArgs> ColorChanged;
     public event EventHandler<ColorEventArgs> ColorChanged;
 
 
     /// <summary>Cursor for the selected color.</summary>
     /// <summary>Cursor for the selected color.</summary>

+ 51 - 50
Terminal.Gui/Views/ComboBox.cs

@@ -8,6 +8,7 @@
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Diagnostics;
+using System.Threading.Channels;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -28,9 +29,10 @@ public class ComboBox : View, IDesignable
     /// <summary>Public constructor</summary>
     /// <summary>Public constructor</summary>
     public ComboBox ()
     public ComboBox ()
     {
     {
+        CanFocus = true;
         _search = new TextField () { CanFocus = true, TabStop = TabBehavior.NoStop };
         _search = new TextField () { CanFocus = true, TabStop = TabBehavior.NoStop };
 
 
-        _listview = new ComboListView (this, HideDropdownListOnClick) { CanFocus = true, TabStop = TabBehavior.NoStop};
+        _listview = new ComboListView (this, HideDropdownListOnClick) { CanFocus = true, TabStop = TabBehavior.NoStop };
 
 
         _search.TextChanged += Search_Changed;
         _search.TextChanged += Search_Changed;
         _search.Accept += Search_Accept;
         _search.Accept += Search_Accept;
@@ -298,44 +300,41 @@ public class ComboBox : View, IDesignable
         Driver.AddRune (Glyphs.DownArrow);
         Driver.AddRune (Glyphs.DownArrow);
     }
     }
 
 
-    /// <inheritdoc/>
-    public override bool OnEnter (View view)
-    {
-        if (!_search.HasFocus && !_listview.HasFocus)
-        {
-            _search.SetFocus ();
-        }
-
-        _search.CursorPosition = _search.Text.GetRuneCount ();
-
-        return base.OnEnter (view);
-    }
 
 
     /// <summary>Virtual method which invokes the <see cref="Expanded"/> event.</summary>
     /// <summary>Virtual method which invokes the <see cref="Expanded"/> event.</summary>
     public virtual void OnExpanded () { Expanded?.Invoke (this, EventArgs.Empty); }
     public virtual void OnExpanded () { Expanded?.Invoke (this, EventArgs.Empty); }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public override bool OnLeave (View view)
+    protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
     {
     {
-        if (_source?.Count > 0
-            && _selectedItem > -1
-            && _selectedItem < _source.Count - 1
-            && _text != _source.ToList () [_selectedItem].ToString ())
+        if (newHasFocus)
         {
         {
-            SetValue (_source.ToList () [_selectedItem].ToString ());
+            if (!_search.HasFocus && !_listview.HasFocus)
+            {
+                _search.SetFocus ();
+            }
+            _search.CursorPosition = _search.Text.GetRuneCount ();
         }
         }
+        else
+        { 
+            if (_source?.Count > 0
+              && _selectedItem > -1
+              && _selectedItem < _source.Count - 1
+              && _text != _source.ToList () [_selectedItem].ToString ())
+            {
+                SetValue (_source.ToList () [_selectedItem].ToString ());
+            }
 
 
-        if (_autoHide && IsShow && view != this && view != _search && view != _listview)
-        {
-            IsShow = false;
-            HideList ();
-        }
-        else if (_listview.TabStop?.HasFlag (TabBehavior.TabStop) ?? false)
-        {
-            _listview.TabStop = TabBehavior.NoStop;
+            if (_autoHide && IsShow && view != this && view != _search && view != _listview)
+            {
+                IsShow = false;
+                HideList ();
+            }
+            else if (_listview.TabStop?.HasFlag (TabBehavior.TabStop) ?? false)
+            {
+                _listview.TabStop = TabBehavior.NoStop;
+            }
         }
         }
-
-        return base.OnLeave (view);
     }
     }
 
 
     /// <summary>Invokes the OnOpenSelectedItem event if it is defined.</summary>
     /// <summary>Invokes the OnOpenSelectedItem event if it is defined.</summary>
@@ -415,7 +414,10 @@ public class ComboBox : View, IDesignable
 
 
     private bool CancelSelected ()
     private bool CancelSelected ()
     {
     {
-        _search.SetFocus ();
+        if (HasFocus)
+        {
+            _search.SetFocus ();
+        }
 
 
         if (ReadOnly || HideDropdownListOnClick)
         if (ReadOnly || HideDropdownListOnClick)
         {
         {
@@ -493,7 +495,7 @@ public class ComboBox : View, IDesignable
         Reset (true);
         Reset (true);
         _listview.Clear ();
         _listview.Clear ();
         _listview.TabStop = TabBehavior.NoStop;
         _listview.TabStop = TabBehavior.NoStop;
-        SuperView?.SendSubviewToBack (this);
+        SuperView?.MoveSubviewToStart (this);
         Rectangle rect = _listview.ViewportToScreen (_listview.IsInitialized ? _listview.Viewport : Rectangle.Empty);
         Rectangle rect = _listview.ViewportToScreen (_listview.IsInitialized ? _listview.Viewport : Rectangle.Empty);
         SuperView?.SetNeedsDisplay (rect);
         SuperView?.SetNeedsDisplay (rect);
         OnCollapsed ();
         OnCollapsed ();
@@ -563,7 +565,7 @@ public class ComboBox : View, IDesignable
     {
     {
         if (HasItems ())
         if (HasItems ())
         {
         {
-           return  _listview.MoveUp ();
+            return _listview.MoveUp ();
         }
         }
 
 
         return false;
         return false;
@@ -793,7 +795,7 @@ public class ComboBox : View, IDesignable
 
 
         _listview.Clear ();
         _listview.Clear ();
         _listview.Height = CalculateHeight ();
         _listview.Height = CalculateHeight ();
-        SuperView?.BringSubviewToFront (this);
+        SuperView?.MoveSubviewToStart (this);
     }
     }
 
 
     private bool UnixEmulation ()
     private bool UnixEmulation ()
@@ -824,6 +826,7 @@ public class ComboBox : View, IDesignable
             set => _hideDropdownListOnClick = WantContinuousButtonPressed = value;
             set => _hideDropdownListOnClick = WantContinuousButtonPressed = value;
         }
         }
 
 
+        // BUGBUG: OnMouseEvent is internal!
         protected internal override bool OnMouseEvent (MouseEvent me)
         protected internal override bool OnMouseEvent (MouseEvent me)
         {
         {
             var res = false;
             var res = false;
@@ -940,28 +943,26 @@ public class ComboBox : View, IDesignable
             }
             }
         }
         }
 
 
-        public override bool OnEnter (View view)
+        protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View previousFocusedView, [CanBeNull] View focusedVew)
         {
         {
-            if (_hideDropdownListOnClick)
+            if (newHasFocus)
             {
             {
-                _isFocusing = true;
-                _highlighted = _container.SelectedItem;
-                Application.GrabMouse (this);
+                if (_hideDropdownListOnClick)
+                {
+                    _isFocusing = true;
+                    _highlighted = _container.SelectedItem;
+                    Application.GrabMouse (this);
+                }
             }
             }
-
-            return base.OnEnter (view);
-        }
-
-        public override bool OnLeave (View view)
-        {
-            if (_hideDropdownListOnClick)
+            else
             {
             {
-                _isFocusing = false;
-                _highlighted = _container.SelectedItem;
-                Application.UngrabMouse ();
+                if (_hideDropdownListOnClick)
+                {
+                    _isFocusing = false;
+                    _highlighted = _container.SelectedItem;
+                    Application.UngrabMouse ();
+                }
             }
             }
-
-            return base.OnLeave (view);
         }
         }
 
 
         public override bool OnSelectedChanged ()
         public override bool OnSelectedChanged ()

+ 4 - 3
Terminal.Gui/Views/DatePicker.cs

@@ -13,7 +13,7 @@ namespace Terminal.Gui;
 public class DatePicker : View
 public class DatePicker : View
 {
 {
     private TableView _calendar;
     private TableView _calendar;
-    private DateTime _date = DateTime.Now;
+    private DateTime _date;
     private DateField _dateField;
     private DateField _dateField;
     private Label _dateLabel;
     private Label _dateLabel;
     private Button _nextMonthButton;
     private Button _nextMonthButton;
@@ -21,7 +21,7 @@ public class DatePicker : View
     private DataTable _table;
     private DataTable _table;
 
 
     /// <summary>Initializes a new instance of <see cref="DatePicker"/>.</summary>
     /// <summary>Initializes a new instance of <see cref="DatePicker"/>.</summary>
-    public DatePicker () { SetInitialProperties (_date); }
+    public DatePicker () { SetInitialProperties (DateTime.Now); }
 
 
     /// <summary>Initializes a new instance of <see cref="DatePicker"/> with the specified date.</summary>
     /// <summary>Initializes a new instance of <see cref="DatePicker"/> with the specified date.</summary>
     public DatePicker (DateTime date) { SetInitialProperties (date); }
     public DatePicker (DateTime date) { SetInitialProperties (date); }
@@ -183,11 +183,12 @@ public class DatePicker : View
 
 
     private void SetInitialProperties (DateTime date)
     private void SetInitialProperties (DateTime date)
     {
     {
+        _date = date;
         Title = "Date Picker";
         Title = "Date Picker";
         BorderStyle = LineStyle.Single;
         BorderStyle = LineStyle.Single;
         Date = date;
         Date = date;
         _dateLabel = new Label { X = 0, Y = 0, Text = "Date: " };
         _dateLabel = new Label { X = 0, Y = 0, Text = "Date: " };
-        TabStop = TabBehavior.TabGroup;
+        CanFocus = true;
 
 
         _calendar = new TableView
         _calendar = new TableView
         {
         {

+ 19 - 24
Terminal.Gui/Views/FileDialog.cs

@@ -89,10 +89,11 @@ public class FileDialog : Dialog
                                   NavigateIf (k, KeyCode.CursorUp, _tableView);
                                   NavigateIf (k, KeyCode.CursorUp, _tableView);
                                   NavigateIf (k, KeyCode.CursorRight, _btnOk);
                                   NavigateIf (k, KeyCode.CursorRight, _btnOk);
                               };
                               };
-        _btnCancel.Accept += (s, e) => {
-                                 Canceled = true;
-                                 Application.RequestStop ();
-                             };
+        _btnCancel.Accept += (s, e) =>
+        {
+            Canceled = true;
+            Application.RequestStop ();
+        };
 
 
         _btnUp = new Button { X = 0, Y = 1, NoPadding = true };
         _btnUp = new Button { X = 0, Y = 1, NoPadding = true };
         _btnUp.Text = GetUpButtonText ();
         _btnUp.Text = GetUpButtonText ();
@@ -290,6 +291,8 @@ public class FileDialog : Dialog
 
 
         UpdateNavigationVisibility ();
         UpdateNavigationVisibility ();
 
 
+        // BUGBUG: This TabOrder is counter-intuitive. The tab order for a dialog should match the
+        // order the Views' are presented, left to right, top to bottom.
         // Determines tab order
         // Determines tab order
         Add (_btnToggleSplitterCollapse);
         Add (_btnToggleSplitterCollapse);
         Add (_tbFind);
         Add (_tbFind);
@@ -458,19 +461,6 @@ public class FileDialog : Dialog
         _btnForward.Text = GetForwardButtonText ();
         _btnForward.Text = GetForwardButtonText ();
         _btnToggleSplitterCollapse.Text = GetToggleSplitterText (false);
         _btnToggleSplitterCollapse.Text = GetToggleSplitterText (false);
 
 
-        if (Style.FlipOkCancelButtonLayoutOrder)
-        {
-            _btnCancel.X = Pos.Func (CalculateOkButtonPosX);
-            _btnOk.X = Pos.Right (_btnCancel) + 1;
-
-            // Flip tab order too for consistency
-            int? p1 = _btnOk.TabIndex;
-            int? p2 = _btnCancel.TabIndex;
-
-            _btnOk.TabIndex = p2;
-            _btnCancel.TabIndex = p1;
-        }
-
         _tbPath.Caption = Style.PathCaption;
         _tbPath.Caption = Style.PathCaption;
         _tbFind.Caption = Style.SearchCaption;
         _tbFind.Caption = Style.SearchCaption;
 
 
@@ -518,8 +508,7 @@ public class FileDialog : Dialog
             };
             };
             AllowedTypeMenuClicked (0);
             AllowedTypeMenuClicked (0);
 
 
-            _allowedTypeMenuBar.Enter += (s, e) => { _allowedTypeMenuBar.OpenMenu (0); };
-
+            // TODO: Using v1's menu bar here is a hack. Need to upgrade this.
             _allowedTypeMenuBar.DrawContentComplete += (s, e) =>
             _allowedTypeMenuBar.DrawContentComplete += (s, e) =>
                                                        {
                                                        {
                                                            _allowedTypeMenuBar.Move (e.NewViewport.Width - 1, 0);
                                                            _allowedTypeMenuBar.Move (e.NewViewport.Width - 1, 0);
@@ -538,7 +527,7 @@ public class FileDialog : Dialog
         // to streamline user experience and allow direct typing of paths
         // to streamline user experience and allow direct typing of paths
         // with zero navigation we start with focus in the text box and any
         // with zero navigation we start with focus in the text box and any
         // default/current path fully selected and ready to be overwritten
         // default/current path fully selected and ready to be overwritten
-        _tbPath.FocusFirst (null);
+        _tbPath.SetFocus ();
         _tbPath.SelectAll ();
         _tbPath.SelectAll ();
 
 
         if (string.IsNullOrEmpty (Title))
         if (string.IsNullOrEmpty (Title))
@@ -546,6 +535,12 @@ public class FileDialog : Dialog
             Title = GetDefaultTitle ();
             Title = GetDefaultTitle ();
         }
         }
 
 
+        if (Style.FlipOkCancelButtonLayoutOrder)
+        {
+            _btnCancel.X = Pos.Func (CalculateOkButtonPosX);
+            _btnOk.X = Pos.Right (_btnCancel) + 1;
+            MoveSubviewTowardsStart (_btnCancel);
+        }
         LayoutSubviews ();
         LayoutSubviews ();
     }
     }
 
 
@@ -588,7 +583,7 @@ public class FileDialog : Dialog
 
 
     internal void ApplySort ()
     internal void ApplySort ()
     {
     {
-        FileSystemInfoStats [] stats = State?.Children ?? new FileSystemInfoStats[0];
+        FileSystemInfoStats [] stats = State?.Children ?? new FileSystemInfoStats [0];
 
 
         // This portion is never reordered (always .. at top then folders)
         // This portion is never reordered (always .. at top then folders)
         IOrderedEnumerable<FileSystemInfoStats> forcedOrder = stats
         IOrderedEnumerable<FileSystemInfoStats> forcedOrder = stats
@@ -1050,7 +1045,7 @@ public class FileDialog : Dialog
     {
     {
         if (keyEvent.KeyCode == isKey)
         if (keyEvent.KeyCode == isKey)
         {
         {
-            to.FocusFirst (null);
+            to.FocusDeepest (NavigationDirection.Forward, null);
 
 
             if (to == _tbPath)
             if (to == _tbPath)
             {
             {
@@ -1439,7 +1434,7 @@ public class FileDialog : Dialog
     {
     {
         if (_treeView.HasFocus && Separators.Contains ((char)keyEvent))
         if (_treeView.HasFocus && Separators.Contains ((char)keyEvent))
         {
         {
-            _tbPath.FocusFirst (null);
+            _tbPath.FocusDeepest (NavigationDirection.Forward, null);
 
 
             // let that keystroke go through on the tbPath instead
             // let that keystroke go through on the tbPath instead
             return true;
             return true;
@@ -1546,7 +1541,7 @@ public class FileDialog : Dialog
         public SearchState (IDirectoryInfo dir, FileDialog parent, string searchTerms) : base (dir, parent)
         public SearchState (IDirectoryInfo dir, FileDialog parent, string searchTerms) : base (dir, parent)
         {
         {
             parent.SearchMatcher.Initialize (searchTerms);
             parent.SearchMatcher.Initialize (searchTerms);
-            Children = new FileSystemInfoStats[0];
+            Children = new FileSystemInfoStats [0];
             BeginSearch ();
             BeginSearch ();
         }
         }
 
 

+ 4 - 0
Terminal.Gui/Views/HexView.cs

@@ -760,6 +760,10 @@ public class HexView : View
 
 
     private void RedisplayLine (long pos)
     private void RedisplayLine (long pos)
     {
     {
+        if (bytesPerLine == 0)
+        {
+            return;
+        }
         var delta = (int)(pos - DisplayStart);
         var delta = (int)(pos - DisplayStart);
         int line = delta / bytesPerLine;
         int line = delta / bytesPerLine;
 
 

+ 2 - 4
Terminal.Gui/Views/ListView.cs

@@ -739,14 +739,12 @@ public class ListView : View, IDesignable
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public override bool OnEnter (View view)
+    protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View currentFocused, [CanBeNull] View newFocused)
     {
     {
-        if (_lastSelectedItem != _selected)
+        if (newHasFocus && _lastSelectedItem != _selected)
         {
         {
             EnsureSelectedItemVisible ();
             EnsureSelectedItemVisible ();
         }
         }
-
-        return base.OnEnter (view);
     }
     }
 
 
     // TODO: This should be cancelable
     // TODO: This should be cancelable

+ 4 - 0
Terminal.Gui/Views/Menu/ContextMenu.cs

@@ -105,6 +105,10 @@ public sealed class ContextMenu : IDisposable
     /// <summary>Disposes the context menu object.</summary>
     /// <summary>Disposes the context menu object.</summary>
     public void Dispose ()
     public void Dispose ()
     {
     {
+        if (_menuBar is null)
+        {
+            return;
+        }
         _menuBar.MenuAllClosed -= MenuBar_MenuAllClosed;
         _menuBar.MenuAllClosed -= MenuBar_MenuAllClosed;
         Application.UngrabMouse ();
         Application.UngrabMouse ();
         _menuBar?.Dispose ();
         _menuBar?.Dispose ();

+ 7 - 1
Terminal.Gui/Views/Menu/Menu.cs

@@ -587,7 +587,13 @@ internal sealed class Menu : View
         _host.Run (action);
         _host.Run (action);
     }
     }
 
 
-    public override bool OnLeave (View view) { return _host.OnLeave (view); }
+    protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
+    {
+        if (!newHasFocus)
+        {
+            _host.LostFocus (previousFocusedView);
+        }
+    }
 
 
     private void RunSelected ()
     private void RunSelected ()
     {
     {

+ 3 - 3
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -1339,14 +1339,14 @@ public class MenuBar : View, IDesignable
     #region Mouse Handling
     #region Mouse Handling
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public override bool OnLeave (View view)
+    internal void LostFocus (View view)
     {
     {
-        if (((!(view is MenuBar) && !(view is Menu)) || (!(view is MenuBar) && !(view is Menu) && _openMenu is { })) && !_isCleaning && !_reopen)
+        if (((!(view is MenuBar) && !(view is Menu))) && !_isCleaning && !_reopen)
         {
         {
             CleanUp ();
             CleanUp ();
         }
         }
 
 
-        return base.OnLeave (view);
+        return;
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>

+ 1 - 1
Terminal.Gui/Views/NumericUpDown.cs

@@ -67,7 +67,7 @@ public class NumericUpDown<T> : View where T : notnull
             Width = Dim.Auto (minimumContentDim: Dim.Func (() => string.Format (Format, Value).Length)),
             Width = Dim.Auto (minimumContentDim: Dim.Func (() => string.Format (Format, Value).Length)),
             Height = 1,
             Height = 1,
             TextAlignment = Alignment.Center,
             TextAlignment = Alignment.Center,
-            CanFocus = true
+            CanFocus = true,
         };
         };
 
 
         _up = new ()
         _up = new ()

+ 16 - 19
Terminal.Gui/Views/ScrollBarView.cs

@@ -38,9 +38,8 @@ public class ScrollBarView : View
     public ScrollBarView ()
     public ScrollBarView ()
     {
     {
         WantContinuousButtonPressed = true;
         WantContinuousButtonPressed = true;
-        ClearOnVisibleFalse = false;
 
 
-        Added += (s, e) => CreateBottomRightCorner (e.Parent);
+        Added += (s, e) => CreateBottomRightCorner (e.SuperView);
         Initialized += ScrollBarView_Initialized;
         Initialized += ScrollBarView_Initialized;
     }
     }
 
 
@@ -103,7 +102,6 @@ public class ScrollBarView : View
 
 
         ShowScrollIndicator = true;
         ShowScrollIndicator = true;
         CreateBottomRightCorner (Host);
         CreateBottomRightCorner (Host);
-        ClearOnVisibleFalse = false;
     }
     }
 
 
     /// <summary>If true the vertical/horizontal scroll bars won't be showed if it's not needed.</summary>
     /// <summary>If true the vertical/horizontal scroll bars won't be showed if it's not needed.</summary>
@@ -216,7 +214,7 @@ public class ScrollBarView : View
     /// <value><c>true</c> if show vertical or horizontal scroll indicator; otherwise, <c>false</c>.</value>
     /// <value><c>true</c> if show vertical or horizontal scroll indicator; otherwise, <c>false</c>.</value>
     public bool ShowScrollIndicator
     public bool ShowScrollIndicator
     {
     {
-        get => _showScrollIndicator;
+        get => _showScrollIndicator && Visible;
         set
         set
         {
         {
             //if (value == showScrollIndicator) {
             //if (value == showScrollIndicator) {
@@ -266,7 +264,7 @@ public class ScrollBarView : View
         }
         }
     }
     }
 
 
-    private bool _showBothScrollIndicator => OtherScrollBarView?._showScrollIndicator == true && _showScrollIndicator;
+    private bool _showBothScrollIndicator => OtherScrollBarView?.ShowScrollIndicator == true && ShowScrollIndicator;
 
 
     /// <summary>This event is raised when the position on the scrollbar has changed.</summary>
     /// <summary>This event is raised when the position on the scrollbar has changed.</summary>
     public event EventHandler ChangedPosition;
     public event EventHandler ChangedPosition;
@@ -316,7 +314,7 @@ public class ScrollBarView : View
             return true;
             return true;
         }
         }
 
 
-        if (_showScrollIndicator
+        if (ShowScrollIndicator
             && (mouseEvent.Flags == MouseFlags.WheeledDown
             && (mouseEvent.Flags == MouseFlags.WheeledDown
                 || mouseEvent.Flags == MouseFlags.WheeledUp
                 || mouseEvent.Flags == MouseFlags.WheeledUp
                 || mouseEvent.Flags == MouseFlags.WheeledRight
                 || mouseEvent.Flags == MouseFlags.WheeledRight
@@ -448,9 +446,9 @@ public class ScrollBarView : View
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override void OnDrawContent (Rectangle viewport)
     public override void OnDrawContent (Rectangle viewport)
     {
     {
-        if (ColorScheme is null || ((!_showScrollIndicator || Size == 0) && AutoHideScrollBars && Visible))
+        if (ColorScheme is null || ((!ShowScrollIndicator || Size == 0) && AutoHideScrollBars && Visible))
         {
         {
-            if ((!_showScrollIndicator || Size == 0) && AutoHideScrollBars && Visible)
+            if ((!ShowScrollIndicator || Size == 0) && AutoHideScrollBars && Visible)
             {
             {
                 ShowHideScrollBars (false);
                 ShowHideScrollBars (false);
             }
             }
@@ -696,7 +694,7 @@ public class ScrollBarView : View
 
 
         if (barsize == 0 || barsize >= scrollBarView._size)
         if (barsize == 0 || barsize >= scrollBarView._size)
         {
         {
-            if (scrollBarView._showScrollIndicator)
+            if (scrollBarView.ShowScrollIndicator)
             {
             {
                 scrollBarView.ShowScrollIndicator = false;
                 scrollBarView.ShowScrollIndicator = false;
             }
             }
@@ -708,7 +706,7 @@ public class ScrollBarView : View
         }
         }
         else if (barsize > 0 && barsize == scrollBarView._size && scrollBarView.OtherScrollBarView is { } && pending)
         else if (barsize > 0 && barsize == scrollBarView._size && scrollBarView.OtherScrollBarView is { } && pending)
         {
         {
-            if (scrollBarView._showScrollIndicator)
+            if (scrollBarView.ShowScrollIndicator)
             {
             {
                 scrollBarView.ShowScrollIndicator = false;
                 scrollBarView.ShowScrollIndicator = false;
             }
             }
@@ -747,7 +745,7 @@ public class ScrollBarView : View
                 }
                 }
             }
             }
 
 
-            if (!scrollBarView._showScrollIndicator)
+            if (!scrollBarView.ShowScrollIndicator)
             {
             {
                 scrollBarView.ShowScrollIndicator = true;
                 scrollBarView.ShowScrollIndicator = true;
             }
             }
@@ -953,13 +951,13 @@ public class ScrollBarView : View
                                              ? Host != SuperView ? Dim.Height (Host) - 1 : Dim.Fill () - 1
                                              ? Host != SuperView ? Dim.Height (Host) - 1 : Dim.Fill () - 1
                                              : 1;
                                              : 1;
         }
         }
-        else if (_showScrollIndicator)
+        else if (ShowScrollIndicator)
         {
         {
             Width = _vertical ? 1 :
             Width = _vertical ? 1 :
                     Host != SuperView ? Dim.Width (Host) : Dim.Fill ();
                     Host != SuperView ? Dim.Width (Host) : Dim.Fill ();
             Height = _vertical ? Host != SuperView ? Dim.Height (Host) : Dim.Fill () : 1;
             Height = _vertical ? Host != SuperView ? Dim.Height (Host) : Dim.Fill () : 1;
         }
         }
-        else if (_otherScrollBarView?._showScrollIndicator == true)
+        else if (_otherScrollBarView?.ShowScrollIndicator == true)
         {
         {
             _otherScrollBarView.Width = _otherScrollBarView._vertical ? 1 :
             _otherScrollBarView.Width = _otherScrollBarView._vertical ? 1 :
                                         Host != SuperView ? Dim.Width (Host) : Dim.Fill () - 0;
                                         Host != SuperView ? Dim.Width (Host) : Dim.Fill () - 0;
@@ -1014,7 +1012,7 @@ public class ScrollBarView : View
                 _otherScrollBarView._contentBottomRightCorner.Visible = true;
                 _otherScrollBarView._contentBottomRightCorner.Visible = true;
             }
             }
         }
         }
-        else if (!_showScrollIndicator)
+        else if (!ShowScrollIndicator)
         {
         {
             if (_contentBottomRightCorner is { })
             if (_contentBottomRightCorner is { })
             {
             {
@@ -1039,12 +1037,12 @@ public class ScrollBarView : View
             _otherScrollBarView._contentBottomRightCorner.Visible = false;
             _otherScrollBarView._contentBottomRightCorner.Visible = false;
         }
         }
 
 
-        if (Host?.Visible == true && _showScrollIndicator && !Visible)
+        if (Host?.Visible == true && ShowScrollIndicator && !Visible)
         {
         {
             Visible = true;
             Visible = true;
         }
         }
 
 
-        if (Host?.Visible == true && _otherScrollBarView?._showScrollIndicator == true && !_otherScrollBarView.Visible)
+        if (Host?.Visible == true && _otherScrollBarView?.ShowScrollIndicator == true && !_otherScrollBarView.Visible)
         {
         {
             _otherScrollBarView.Visible = true;
             _otherScrollBarView.Visible = true;
         }
         }
@@ -1054,12 +1052,12 @@ public class ScrollBarView : View
             return;
             return;
         }
         }
 
 
-        if (_showScrollIndicator)
+        if (ShowScrollIndicator)
         {
         {
             Draw ();
             Draw ();
         }
         }
 
 
-        if (_otherScrollBarView is { } && _otherScrollBarView._showScrollIndicator)
+        if (_otherScrollBarView is { } && _otherScrollBarView.ShowScrollIndicator)
         {
         {
             _otherScrollBarView.Draw ();
             _otherScrollBarView.Draw ();
         }
         }
@@ -1078,7 +1076,6 @@ public class ScrollBarView : View
     {
     {
         public ContentBottomRightCorner ()
         public ContentBottomRightCorner ()
         {
         {
-            ClearOnVisibleFalse = false;
             ColorScheme = ColorScheme;
             ColorScheme = ColorScheme;
         }
         }
     }
     }

+ 5 - 15
Terminal.Gui/Views/Shortcut.cs

@@ -742,6 +742,10 @@ public class Shortcut : View, IOrientation, IDesignable
                 break;
                 break;
 
 
             case KeyBindingScope.HotKey:
             case KeyBindingScope.HotKey:
+                //if (!CanBeVisible(this))
+                //{
+                //    return true;
+                //}
                 cancel = base.OnAccept () == true;
                 cancel = base.OnAccept () == true;
 
 
                 if (CanFocus)
                 if (CanFocus)
@@ -834,24 +838,10 @@ public class Shortcut : View, IOrientation, IDesignable
         }
         }
     }
     }
 
 
-    private View _lastFocusedView;
-
-    /// <inheritdoc/>
-    public override bool OnEnter (View view)
-    {
-        SetColors ();
-        _lastFocusedView = view;
-
-        return base.OnEnter (view);
-    }
-
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public override bool OnLeave (View view)
+    protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
     {
     {
         SetColors ();
         SetColors ();
-        _lastFocusedView = this;
-
-        return base.OnLeave (view);
     }
     }
 
 
     #endregion Focus
     #endregion Focus

+ 19 - 47
Terminal.Gui/Views/TabView.cs

@@ -1,3 +1,5 @@
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 /// <summary>Control that hosts multiple sub views, presenting a single one at once.</summary>
 /// <summary>Control that hosts multiple sub views, presenting a single one at once.</summary>
@@ -25,13 +27,12 @@ public class TabView : View
     public TabView ()
     public TabView ()
     {
     {
         CanFocus = true;
         CanFocus = true;
-        TabStop = TabBehavior.TabStop;
+        TabStop = TabBehavior.TabStop; // Because TabView has focusable subviews, it must be a TabGroup
         _tabsBar = new TabRowView (this);
         _tabsBar = new TabRowView (this);
         _contentView = new View ()
         _contentView = new View ()
         {
         {
-            Id = "TabView._contentView"
+            //Id = "TabView._contentView",
         };
         };
-
         ApplyStyleChanges ();
         ApplyStyleChanges ();
 
 
         base.Add (_tabsBar);
         base.Add (_tabsBar);
@@ -64,42 +65,6 @@ public class TabView : View
                     }
                     }
                    );
                    );
 
 
-        AddCommand (
-                    Command.NextView,
-                    () =>
-                    {
-                        if (Style.TabsOnBottom)
-                        {
-                            return false;
-                        }
-
-                        if (_contentView is { HasFocus: false })
-                        {
-                            _contentView.SetFocus ();
-
-                            return _contentView.Focused is { };
-                        }
-
-                        return false;
-                    }
-                   );
-
-        AddCommand (Command.PreviousView, () =>
-                                          {
-                                              if (!Style.TabsOnBottom)
-                                              {
-                                                  return false;
-                                              }
-                                              if (_contentView is { HasFocus: false })
-                                              {
-                                                  _contentView.SetFocus ();
-
-                                                  return _contentView.Focused is { };
-                                              }
-
-                                              return false;
-                                          });
-
         AddCommand (
         AddCommand (
                     Command.PageDown,
                     Command.PageDown,
                     () =>
                     () =>
@@ -127,8 +92,6 @@ public class TabView : View
         KeyBindings.Add (Key.CursorRight, Command.Right);
         KeyBindings.Add (Key.CursorRight, Command.Right);
         KeyBindings.Add (Key.Home, Command.LeftHome);
         KeyBindings.Add (Key.Home, Command.LeftHome);
         KeyBindings.Add (Key.End, Command.RightEnd);
         KeyBindings.Add (Key.End, Command.RightEnd);
-        KeyBindings.Add (Key.CursorDown, Command.NextView);
-        KeyBindings.Add (Key.CursorUp, Command.PreviousView);
         KeyBindings.Add (Key.PageDown, Command.PageDown);
         KeyBindings.Add (Key.PageDown, Command.PageDown);
         KeyBindings.Add (Key.PageUp, Command.PageUp);
         KeyBindings.Add (Key.PageUp, Command.PageUp);
     }
     }
@@ -167,9 +130,12 @@ public class TabView : View
                 if (_selectedTab.View is { })
                 if (_selectedTab.View is { })
                 {
                 {
                     _contentView.Add (_selectedTab.View);
                     _contentView.Add (_selectedTab.View);
+                   // _contentView.Id = $"_contentView for {_selectedTab.DisplayText}";
                 }
                 }
             }
             }
 
 
+            _contentView.CanFocus = _contentView.Subviews.Count (v => v.CanFocus) > 0;
+
             EnsureSelectedTabIsVisible ();
             EnsureSelectedTabIsVisible ();
 
 
             if (old != value)
             if (old != value)
@@ -267,7 +233,7 @@ public class TabView : View
             int tabHeight = GetTabHeight (true);
             int tabHeight = GetTabHeight (true);
 
 
             //move content down to make space for tabs
             //move content down to make space for tabs
-            _contentView.Y = Pos.Bottom (_tabsBar);
+            _contentView.Y = Pos.Bottom (_tabsBar) ;
 
 
             // Fill client area leaving space at bottom for border
             // Fill client area leaving space at bottom for border
             _contentView.Height = Dim.Fill ();
             _contentView.Height = Dim.Fill ();
@@ -439,7 +405,10 @@ public class TabView : View
     }
     }
 
 
     /// <summary>Raises the <see cref="SelectedTabChanged"/> event.</summary>
     /// <summary>Raises the <see cref="SelectedTabChanged"/> event.</summary>
-    protected virtual void OnSelectedTabChanged (Tab oldTab, Tab newTab) { SelectedTabChanged?.Invoke (this, new TabChangedEventArgs (oldTab, newTab)); }
+    protected virtual void OnSelectedTabChanged (Tab oldTab, Tab newTab)
+    {
+        SelectedTabChanged?.Invoke (this, new TabChangedEventArgs (oldTab, newTab));
+    }
 
 
     /// <summary>Returns which tabs to render at each x location.</summary>
     /// <summary>Returns which tabs to render at each x location.</summary>
     /// <returns></returns>
     /// <returns></returns>
@@ -539,7 +508,10 @@ public class TabView : View
         return Style.ShowTopLine ? 3 : 2;
         return Style.ShowTopLine ? 3 : 2;
     }
     }
 
 
-    private void Tab_MouseClick (object sender, MouseEventEventArgs e) { e.Handled = _tabsBar.NewMouseEvent (e.MouseEvent) == true; }
+    private void Tab_MouseClick (object sender, MouseEventEventArgs e)
+    {
+        e.Handled = _tabsBar.NewMouseEvent (e.MouseEvent) == true;
+    }
 
 
     private void UnSetCurrentTabs ()
     private void UnSetCurrentTabs ()
     {
     {
@@ -568,9 +540,9 @@ public class TabView : View
         public TabRowView (TabView host)
         public TabRowView (TabView host)
         {
         {
             _host = host;
             _host = host;
+            Id = "tabRowView";
 
 
             CanFocus = true;
             CanFocus = true;
-            TabStop = TabBehavior.TabStop;
             Height = 1; // BUGBUG: Views should avoid setting Height as doing so implies Frame.Size == GetContentSize ().
             Height = 1; // BUGBUG: Views should avoid setting Height as doing so implies Frame.Size == GetContentSize ().
             Width = Dim.Fill ();
             Width = Dim.Fill ();
 
 
@@ -1349,7 +1321,7 @@ public class TabView : View
                 _leftScrollIndicator.Visible = true;
                 _leftScrollIndicator.Visible = true;
 
 
                 // Ensures this is clicked instead of the first tab
                 // Ensures this is clicked instead of the first tab
-                BringSubviewToFront (_leftScrollIndicator);
+                MoveSubviewToEnd (_leftScrollIndicator);
                 _leftScrollIndicator.Draw ();
                 _leftScrollIndicator.Draw ();
             }
             }
             else
             else
@@ -1367,7 +1339,7 @@ public class TabView : View
                 _rightScrollIndicator.Visible = true;
                 _rightScrollIndicator.Visible = true;
 
 
                 // Ensures this is clicked instead of the last tab if under this
                 // Ensures this is clicked instead of the last tab if under this
-                BringSubviewToFront (_rightScrollIndicator);
+                MoveSubviewToStart (_rightScrollIndicator);
                 _rightScrollIndicator.Draw ();
                 _rightScrollIndicator.Draw ();
             }
             }
             else
             else

+ 30 - 5
Terminal.Gui/Views/TextField.cs

@@ -39,10 +39,17 @@ public class TextField : View
         Used = true;
         Used = true;
         WantMousePositionReports = true;
         WantMousePositionReports = true;
 
 
+        // By default, disable hotkeys (in case someome sets Title)
+        HotKeySpecifier = new ('\xffff');
+
         _historyText.ChangeText += HistoryText_ChangeText;
         _historyText.ChangeText += HistoryText_ChangeText;
 
 
         Initialized += TextField_Initialized;
         Initialized += TextField_Initialized;
 
 
+        Added += TextField_Added;
+
+        Removed += TextField_Removed;
+
         // Things this view knows how to do
         // Things this view knows how to do
         AddCommand (
         AddCommand (
                     Command.DeleteCharRight,
                     Command.DeleteCharRight,
@@ -134,7 +141,7 @@ public class TextField : View
                     }
                     }
                    );
                    );
 
 
-        AddCommand (Command.Left,  () => MoveLeft ());
+        AddCommand (Command.Left, () => MoveLeft ());
 
 
         AddCommand (
         AddCommand (
                     Command.RightEnd,
                     Command.RightEnd,
@@ -405,6 +412,7 @@ public class TextField : View
         KeyBindings.Add (Key.Enter, Command.Accept);
         KeyBindings.Add (Key.Enter, Command.Accept);
     }
     }
 
 
+
     /// <summary>
     /// <summary>
     ///     Provides autocomplete context menu based on suggestions at the current cursor position. Configure
     ///     Provides autocomplete context menu based on suggestions at the current cursor position. Configure
     ///     <see cref="ISuggestionGenerator"/> to enable this feature.
     ///     <see cref="ISuggestionGenerator"/> to enable this feature.
@@ -1033,7 +1041,7 @@ public class TextField : View
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public override bool OnLeave (View view)
+    protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
     {
     {
         if (Application.MouseGrabView is { } && Application.MouseGrabView == this)
         if (Application.MouseGrabView is { } && Application.MouseGrabView == this)
         {
         {
@@ -1043,7 +1051,7 @@ public class TextField : View
         //if (SelectedLength != 0 && !(Application.MouseGrabView is MenuBar))
         //if (SelectedLength != 0 && !(Application.MouseGrabView is MenuBar))
         //	ClearAllSelection ();
         //	ClearAllSelection ();
 
 
-        return base.OnLeave (view);
+        return;
     }
     }
 
 
     /// TODO: Flush out these docs
     /// TODO: Flush out these docs
@@ -1855,6 +1863,20 @@ public class TextField : View
         ContextMenu.Show ();
         ContextMenu.Show ();
     }
     }
 
 
+    private void TextField_Added (object sender, SuperViewChangedEventArgs e)
+    {
+        if (Autocomplete.HostControl is null)
+        {
+            Autocomplete.HostControl = this;
+            Autocomplete.PopupInsideContainer = false;
+        }
+    }
+
+    private void TextField_Removed (object sender, SuperViewChangedEventArgs e)
+    {
+        Autocomplete.HostControl = null;
+    }
+
     private void TextField_Initialized (object sender, EventArgs e)
     private void TextField_Initialized (object sender, EventArgs e)
     {
     {
         _cursorPosition = Text.GetRuneCount ();
         _cursorPosition = Text.GetRuneCount ();
@@ -1864,8 +1886,11 @@ public class TextField : View
             ScrollOffset = _cursorPosition > Viewport.Width + 1 ? _cursorPosition - Viewport.Width + 1 : 0;
             ScrollOffset = _cursorPosition > Viewport.Width + 1 ? _cursorPosition - Viewport.Width + 1 : 0;
         }
         }
 
 
-        Autocomplete.HostControl = this;
-        Autocomplete.PopupInsideContainer = false;
+        if (Autocomplete.HostControl is null)
+        {
+            Autocomplete.HostControl = this;
+            Autocomplete.PopupInsideContainer = false;
+        }
     }
     }
 }
 }
 
 

+ 25 - 3
Terminal.Gui/Views/TextView.cs

@@ -1997,11 +1997,16 @@ public class TextView : View
         CursorVisibility = CursorVisibility.Default;
         CursorVisibility = CursorVisibility.Default;
         Used = true;
         Used = true;
 
 
+        // By default, disable hotkeys (in case someome sets Title)
+        HotKeySpecifier = new ('\xffff');
+
         _model.LinesLoaded += Model_LinesLoaded!;
         _model.LinesLoaded += Model_LinesLoaded!;
         _historyText.ChangeText += HistoryText_ChangeText!;
         _historyText.ChangeText += HistoryText_ChangeText!;
 
 
         Initialized += TextView_Initialized!;
         Initialized += TextView_Initialized!;
 
 
+        Added += TextView_Added!;
+
         LayoutComplete += TextView_LayoutComplete;
         LayoutComplete += TextView_LayoutComplete;
 
 
         // Things this view knows how to do
         // Things this view knows how to do
@@ -2499,6 +2504,11 @@ public class TextView : View
         KeyBindings.Add ((KeyCode)ContextMenu.Key, KeyBindingScope.HotKey, Command.ShowContextMenu);
         KeyBindings.Add ((KeyCode)ContextMenu.Key, KeyBindingScope.HotKey, Command.ShowContextMenu);
     }
     }
 
 
+    private void TextView_Added1 (object? sender, SuperViewChangedEventArgs e)
+    {
+        throw new NotImplementedException ();
+    }
+
     // BUGBUG: AllowsReturn is mis-named. It should be EnterKeyAccepts.
     // BUGBUG: AllowsReturn is mis-named. It should be EnterKeyAccepts.
     /// <summary>
     /// <summary>
     ///     Gets or sets whether pressing ENTER in a <see cref="TextView"/> creates a new line of text
     ///     Gets or sets whether pressing ENTER in a <see cref="TextView"/> creates a new line of text
@@ -3650,14 +3660,14 @@ public class TextView : View
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
-    public override bool OnLeave (View view)
+    protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? view)
     {
     {
         if (Application.MouseGrabView is { } && Application.MouseGrabView == this)
         if (Application.MouseGrabView is { } && Application.MouseGrabView == this)
         {
         {
             Application.UngrabMouse ();
             Application.UngrabMouse ();
         }
         }
 
 
-        return base.OnLeave (view);
+        return;
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
@@ -6334,9 +6344,21 @@ public class TextView : View
         return StringExtensions.ToString (encoded);
         return StringExtensions.ToString (encoded);
     }
     }
 
 
+    private void TextView_Added (object sender, SuperViewChangedEventArgs e)
+    {
+        if (Autocomplete.HostControl is null)
+        {
+            Autocomplete.HostControl = this;
+        }
+    }
+
+
     private void TextView_Initialized (object sender, EventArgs e)
     private void TextView_Initialized (object sender, EventArgs e)
     {
     {
-        Autocomplete.HostControl = this;
+        if (Autocomplete.HostControl is null)
+        {
+            Autocomplete.HostControl = this;
+        }
         OnContentsChanged ();
         OnContentsChanged ();
     }
     }
 
 

+ 6 - 1
Terminal.Gui/Views/Tile.cs

@@ -14,7 +14,12 @@ public class Tile
     /// <summary>Creates a new instance of the <see cref="Tile"/> class.</summary>
     /// <summary>Creates a new instance of the <see cref="Tile"/> class.</summary>
     public Tile ()
     public Tile ()
     {
     {
-        ContentView = new View { Width = Dim.Fill (), Height = Dim.Fill () };
+        ContentView = new View
+        {
+            Width = Dim.Fill (),
+            Height = Dim.Fill (),
+            CanFocus = true
+        };
 #if DEBUG_IDISPOSABLE
 #if DEBUG_IDISPOSABLE
         ContentView.Data = "Tile.ContentView";
         ContentView.Data = "Tile.ContentView";
 #endif
 #endif

+ 16 - 8
Terminal.Gui/Views/TileView.cs

@@ -10,14 +10,20 @@ public class TileView : View
     private List<Pos> _splitterDistances;
     private List<Pos> _splitterDistances;
     private List<TileViewLineView> _splitterLines;
     private List<TileViewLineView> _splitterLines;
     private List<Tile> _tiles;
     private List<Tile> _tiles;
-    private TileView parentTileView;
+    private TileView _parentTileView;
 
 
     /// <summary>Creates a new instance of the <see cref="TileView"/> class with 2 tiles (i.e. left and right).</summary>
     /// <summary>Creates a new instance of the <see cref="TileView"/> class with 2 tiles (i.e. left and right).</summary>
-    public TileView () : this (2) { }
+    public TileView () : this (2)
+    {
+    }
 
 
     /// <summary>Creates a new instance of the <see cref="TileView"/> class with <paramref name="tiles"/> number of tiles.</summary>
     /// <summary>Creates a new instance of the <see cref="TileView"/> class with <paramref name="tiles"/> number of tiles.</summary>
     /// <param name="tiles"></param>
     /// <param name="tiles"></param>
-    public TileView (int tiles) { RebuildForTileCount (tiles); }
+    public TileView (int tiles)
+    {
+        CanFocus = true;
+        RebuildForTileCount (tiles);
+    }
 
 
     /// <summary>The line style to use when drawing the splitter lines.</summary>
     /// <summary>The line style to use when drawing the splitter lines.</summary>
     public LineStyle LineStyle { get; set; } = LineStyle.None;
     public LineStyle LineStyle { get; set; } = LineStyle.None;
@@ -57,7 +63,7 @@ public class TileView : View
     /// </summary>
     /// </summary>
     /// <remarks>Use <see cref="IsRootTileView"/> to determine if the returned value is the root.</remarks>
     /// <remarks>Use <see cref="IsRootTileView"/> to determine if the returned value is the root.</remarks>
     /// <returns></returns>
     /// <returns></returns>
-    public TileView GetParentTileView () { return parentTileView; }
+    public TileView GetParentTileView () { return _parentTileView; }
 
 
     /// <summary>
     /// <summary>
     ///     Returns the index of the first <see cref="Tile"/> in <see cref="Tiles"/> which contains
     ///     Returns the index of the first <see cref="Tile"/> in <see cref="Tiles"/> which contains
@@ -116,6 +122,7 @@ public class TileView : View
 
 
                 // restore old Tile and View
                 // restore old Tile and View
                 _tiles [i] = oldTile;
                 _tiles [i] = oldTile;
+                _tiles [i].ContentView.TabStop = TabStop;
                 Add (_tiles [i].ContentView);
                 Add (_tiles [i].ContentView);
             }
             }
             else
             else
@@ -146,7 +153,7 @@ public class TileView : View
     ///     if you want to subdivide a <see cref="TileView"/>.
     ///     if you want to subdivide a <see cref="TileView"/>.
     /// </remarks>
     /// </remarks>
     /// <returns></returns>
     /// <returns></returns>
-    public bool IsRootTileView () { return parentTileView == null; }
+    public bool IsRootTileView () { return _parentTileView == null; }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>
     public override void LayoutSubviews ()
     public override void LayoutSubviews ()
@@ -354,6 +361,7 @@ public class TileView : View
 
 
             var tile = new Tile ();
             var tile = new Tile ();
             _tiles.Add (tile);
             _tiles.Add (tile);
+            tile.ContentView.Id = $"Tile.ContentView {i}";
             Add (tile.ContentView);
             Add (tile.ContentView);
             tile.TitleChanged += (s, e) => SetNeedsDisplay ();
             tile.TitleChanged += (s, e) => SetNeedsDisplay ();
         }
         }
@@ -475,7 +483,7 @@ public class TileView : View
 
 
         var newContainer = new TileView (numberOfPanels)
         var newContainer = new TileView (numberOfPanels)
         {
         {
-            Width = Dim.Fill (), Height = Dim.Fill (), parentTileView = this
+            Width = Dim.Fill (), Height = Dim.Fill (), _parentTileView = this
         };
         };
 
 
         // Take everything out of the View we are moving
         // Take everything out of the View we are moving
@@ -583,9 +591,9 @@ public class TileView : View
     {
     {
         TileView root = this;
         TileView root = this;
 
 
-        while (root.parentTileView is { })
+        while (root._parentTileView is { })
         {
         {
-            root = root.parentTileView;
+            root = root._parentTileView;
         }
         }
 
 
         return root;
         return root;

+ 0 - 22
Terminal.Gui/Views/Toplevel.cs

@@ -76,13 +76,6 @@ public partial class Toplevel : View
     /// <summary>Gets the latest <see cref="StatusBar"/> added into this Toplevel.</summary>
     /// <summary>Gets the latest <see cref="StatusBar"/> added into this Toplevel.</summary>
     public StatusBar? StatusBar => (StatusBar?)Subviews?.LastOrDefault (s => s is StatusBar);
     public StatusBar? StatusBar => (StatusBar?)Subviews?.LastOrDefault (s => s is StatusBar);
 
 
-    /// <inheritdoc/>
-    public override View Add (View view)
-    {
-        CanFocus = true;
-
-        return base.Add (view);
-    }
 
 
     // TODO: Overlapped - Rename to AllSubviewsClosed - Move to View?
     // TODO: Overlapped - Rename to AllSubviewsClosed - Move to View?
     /// <summary>
     /// <summary>
@@ -355,16 +348,6 @@ public partial class Toplevel : View
 
 
     #endregion
     #endregion
 
 
-    #region Navigation
-
-    /// <inheritdoc/>
-    public override bool OnEnter (View view) { return MostFocused?.OnEnter (view) ?? base.OnEnter (view); }
-
-    /// <inheritdoc/>
-    public override bool OnLeave (View view) { return MostFocused?.OnLeave (view) ?? base.OnLeave (view); }
-
-    #endregion
-
     #region Size / Position Management
     #region Size / Position Management
 
 
     // TODO: Make cancelable?
     // TODO: Make cancelable?
@@ -375,11 +358,6 @@ public partial class Toplevel : View
     {
     {
         if (!IsOverlappedContainer)
         if (!IsOverlappedContainer)
         {
         {
-            if (Focused is null)
-            {
-                RestoreFocus ();
-            }
-
             return null;
             return null;
         }
         }
 
 

+ 10 - 5
Terminal.Gui/Views/TreeView/TreeView.cs

@@ -34,6 +34,8 @@ public class TreeView : TreeView<ITreeNode>
     /// </summary>
     /// </summary>
     public TreeView ()
     public TreeView ()
     {
     {
+        CanFocus = true;
+
         TreeBuilder = new TreeNodeBuilder ();
         TreeBuilder = new TreeNodeBuilder ();
         AspectGetter = o => o is null ? "Null" : o.Text ?? o?.ToString () ?? "Unnamed Node";
         AspectGetter = o => o is null ? "Null" : o.Text ?? o?.ToString () ?? "Unnamed Node";
     }
     }
@@ -975,6 +977,7 @@ public class TreeView<T> : View, ITreeView where T : class
     /// <returns></returns>
     /// <returns></returns>
     public bool IsSelected (T model) { return Equals (SelectedObject, model) || (MultiSelect && multiSelectedRegions.Any (s => s.Contains (model))); }
     public bool IsSelected (T model) { return Equals (SelectedObject, model) || (MultiSelect && multiSelectedRegions.Any (s => s.Contains (model))); }
 
 
+    // BUGBUG: OnMouseEvent is internal. TreeView should not be overriding.
     ///<inheritdoc/>
     ///<inheritdoc/>
     protected internal override bool OnMouseEvent (MouseEvent me)
     protected internal override bool OnMouseEvent (MouseEvent me)
     {
     {
@@ -1155,14 +1158,16 @@ public class TreeView<T> : View, ITreeView where T : class
     }
     }
 
 
     ///<inheritdoc/>
     ///<inheritdoc/>
-    public override bool OnEnter (View view)
+    protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View currentFocused, [CanBeNull] View newFocused)
     {
     {
-        if (SelectedObject is null && Objects.Any ())
+        if (newHasFocus)
         {
         {
-            SelectedObject = Objects.First ();
+            // If there is no selected object and there are objects in the tree, select the first one
+            if (SelectedObject is null && Objects.Any ())
+            {
+                SelectedObject = Objects.First ();
+            }
         }
         }
-
-        return base.OnEnter (view);
     }
     }
 
 
     /// <inheritdoc/>
     /// <inheritdoc/>

+ 2 - 2
UICatalog/KeyBindingsDialog.cs

@@ -60,7 +60,7 @@ internal class KeyBindingsDialog : Dialog
         AddButton (cancel);
         AddButton (cancel);
 
 
         // Register event handler as the last thing in constructor to prevent early calls
         // Register event handler as the last thing in constructor to prevent early calls
-        // before it is even shown (e.g. OnEnter)
+        // before it is even shown (e.g. OnHasFocusChanging)
         _commandsListView.SelectedItemChanged += CommandsListView_SelectedItemChanged;
         _commandsListView.SelectedItemChanged += CommandsListView_SelectedItemChanged;
 
 
         // Setup to show first ListView entry
         // Setup to show first ListView entry
@@ -209,7 +209,7 @@ internal class KeyBindingsDialog : Dialog
             // (and always was wrong). Parents don't get to be told when new views are added
             // (and always was wrong). Parents don't get to be told when new views are added
             // to them
             // to them
 
 
-            view.Added += (s, e) => RecordView (e.Child);
+            view.Added += (s, e) => RecordView (e.SubView);
         }
         }
     }
     }
 }
 }

+ 11 - 18
UICatalog/Scenarios/ASCIICustomButton.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Text;
 using System.Text;
+using JetBrains.Annotations;
 using Terminal.Gui;
 using Terminal.Gui;
 
 
 namespace UICatalog.Scenarios;
 namespace UICatalog.Scenarios;
@@ -110,27 +111,19 @@ public class ASCIICustomButtonTest : Scenario
             Add (_border, _fill, title);
             Add (_border, _fill, title);
         }
         }
 
 
-        public override bool OnEnter (View view)
+        protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View previousFocusedView, [CanBeNull] View focusedVew)
         {
         {
-            _border.Visible = false;
-            _fill.Visible = true;
-            PointerEnter.Invoke (this);
-            view = this;
-
-            return base.OnEnter (view);
-        }
-
-        public override bool OnLeave (View view)
-        {
-            _border.Visible = true;
-            _fill.Visible = false;
-
-            if (view == null)
+            if (newHasFocus)
             {
             {
-                view = this;
+                _border.Visible = false;
+                _fill.Visible = true;
+                PointerEnter?.Invoke (this);
+            }
+            else
+            {
+                _border.Visible = true;
+                _fill.Visible = false;
             }
             }
-
-            return base.OnLeave (view);
         }
         }
 
 
         public event Action<ASCIICustomButton> PointerEnter;
         public event Action<ASCIICustomButton> PointerEnter;

+ 2 - 1
UICatalog/Scenarios/AdornmentEditor.cs

@@ -91,7 +91,8 @@ public class AdornmentEditor : View
         BorderStyle = LineStyle.Dashed;
         BorderStyle = LineStyle.Dashed;
         Initialized += AdornmentEditor_Initialized;
         Initialized += AdornmentEditor_Initialized;
 
 
-        TabStop = TabBehavior.TabGroup;
+        CanFocus = true;
+        TabStop = TabBehavior.TabStop;
     }
     }
 
 
     private void AdornmentEditor_Initialized (object sender, EventArgs e)
     private void AdornmentEditor_Initialized (object sender, EventArgs e)

+ 11 - 4
UICatalog/Scenarios/Adornments.cs

@@ -4,7 +4,7 @@ namespace UICatalog.Scenarios;
 
 
 [ScenarioMetadata ("Adornments Demo", "Demonstrates Margin, Border, and Padding on Views.")]
 [ScenarioMetadata ("Adornments Demo", "Demonstrates Margin, Border, and Padding on Views.")]
 [ScenarioCategory ("Layout")]
 [ScenarioCategory ("Layout")]
-[ScenarioCategory ("Borders")]
+[ScenarioCategory ("Adornments")]
 public class Adornments : Scenario
 public class Adornments : Scenario
 {
 {
     public override void Main ()
     public override void Main ()
@@ -19,11 +19,13 @@ public class Adornments : Scenario
         var editor = new AdornmentsEditor
         var editor = new AdornmentsEditor
         {
         {
             AutoSelectViewToEdit = true,
             AutoSelectViewToEdit = true,
+
             // This is for giggles, to show that the editor can be moved around.
             // This is for giggles, to show that the editor can be moved around.
             Arrangement = ViewArrangement.Movable,
             Arrangement = ViewArrangement.Movable,
-            X = Pos.AnchorEnd(),
+            X = Pos.AnchorEnd ()
         };
         };
-        editor.Border.Thickness = new Thickness (1, 2, 1, 1);
+
+        editor.Border.Thickness = new (1, 2, 1, 1);
 
 
         app.Add (editor);
         app.Add (editor);
 
 
@@ -31,7 +33,8 @@ public class Adornments : Scenario
         {
         {
             Title = "The _Window",
             Title = "The _Window",
             Arrangement = ViewArrangement.Movable,
             Arrangement = ViewArrangement.Movable,
-           // X = Pos.Center (),
+
+            // X = Pos.Center (),
             Width = Dim.Percent (60),
             Width = Dim.Percent (60),
             Height = Dim.Percent (80)
             Height = Dim.Percent (80)
         };
         };
@@ -127,6 +130,10 @@ public class Adornments : Scenario
 #endif
 #endif
                               };
                               };
 
 
+        editor.AutoSelectViewToEdit = true;
+        editor.AutoSelectSuperView = window;
+        editor.AutoSelectAdornments = true;
+
         Application.Run (app);
         Application.Run (app);
         app.Dispose ();
         app.Dispose ();
 
 

+ 86 - 57
UICatalog/Scenarios/AdornmentsEditor.cs

@@ -1,4 +1,5 @@
-using System;
+#nullable enable
+using System;
 using System.Text;
 using System.Text;
 using Terminal.Gui;
 using Terminal.Gui;
 
 
@@ -19,33 +20,43 @@ public class AdornmentsEditor : View
 
 
         //SuperViewRendersLineCanvas = true;
         //SuperViewRendersLineCanvas = true;
 
 
+        CanFocus = true;
+
         TabStop = TabBehavior.TabGroup;
         TabStop = TabBehavior.TabGroup;
 
 
-        //Application.MouseEvent += Application_MouseEvent;
-        Application.Navigation!.FocusedChanged += ApplicationNavigationOnFocusedChanged;
         Initialized += AdornmentsEditor_Initialized;
         Initialized += AdornmentsEditor_Initialized;
     }
     }
 
 
     private readonly ViewDiagnosticFlags _savedDiagnosticFlags = Diagnostics;
     private readonly ViewDiagnosticFlags _savedDiagnosticFlags = Diagnostics;
-    private View _viewToEdit;
+    private View? _viewToEdit;
 
 
-    private Label _lblView; // Text describing the vi
+    private Label? _lblView; // Text describing the vi
 
 
-    private MarginEditor _marginEditor;
-    private BorderEditor _borderEditor;
-    private PaddingEditor _paddingEditor;
+    private MarginEditor? _marginEditor;
+    private BorderEditor? _borderEditor;
+    private PaddingEditor? _paddingEditor;
 
 
     // TODO: Move Diagnostics to a separate Editor class (DiagnosticsEditor?).
     // TODO: Move Diagnostics to a separate Editor class (DiagnosticsEditor?).
-    private CheckBox _diagPaddingCheckBox;
-    private CheckBox _diagRulerCheckBox;
+    private CheckBox? _diagPaddingCheckBox;
+    private CheckBox? _diagRulerCheckBox;
 
 
     /// <summary>
     /// <summary>
-    ///     Gets or sets whether the AdornmentsEditor should automatically select the View to edit when the mouse is clicked
-    ///     anywhere outside the editor.
+    ///     Gets or sets whether the AdornmentsEditor should automatically select the View to edit
+    ///     based on the values of <see cref="AutoSelectSuperView"/> and <see cref="AutoSelectAdornments"/>.
     /// </summary>
     /// </summary>
     public bool AutoSelectViewToEdit { get; set; }
     public bool AutoSelectViewToEdit { get; set; }
 
 
-    public View ViewToEdit
+    /// <summary>
+    ///     Gets or sets the View that will scope the behavior of <see cref="AutoSelectViewToEdit"/>.
+    /// </summary>
+    public View? AutoSelectSuperView { get; set; }
+
+    /// <summary>
+    ///     Gets or sets whether auto select with the mouse will select Adornments or just Views.
+    /// </summary>
+    public bool AutoSelectAdornments { get; set; }
+
+    public View? ViewToEdit
     {
     {
         get => _viewToEdit;
         get => _viewToEdit;
         set
         set
@@ -57,11 +68,66 @@ public class AdornmentsEditor : View
 
 
             _viewToEdit = value;
             _viewToEdit = value;
 
 
-            _marginEditor.AdornmentToEdit = _viewToEdit?.Margin ?? null;
-            _borderEditor.AdornmentToEdit = _viewToEdit?.Border ?? null;
-            _paddingEditor.AdornmentToEdit = _viewToEdit?.Padding ?? null;
+            if (_viewToEdit is not Adornment)
+            {
+                _marginEditor!.AdornmentToEdit = _viewToEdit?.Margin ?? null;
+                _borderEditor!.AdornmentToEdit = _viewToEdit?.Border ?? null;
+                _paddingEditor!.AdornmentToEdit = _viewToEdit?.Padding ?? null;
+            }
+
+            if (_lblView is { })
+            {
+                _lblView.Text = $"{_viewToEdit?.GetType ().Name}: {_viewToEdit?.Id}" ?? string.Empty;
+            }
+        }
+    }
+
+
+    private void NavigationOnFocusedChanged (object? sender, EventArgs e)
+    {
+        if (AutoSelectSuperView is null)
+        {
+            return;
+        }
+
+        if (ApplicationNavigation.IsInHierarchy (this, Application.Navigation!.GetFocused ()))
+        {
+            return;
+        }
 
 
-            _lblView.Text = $"{_viewToEdit?.GetType ().Name}: {_viewToEdit?.Id}" ?? string.Empty;
+        if (!ApplicationNavigation.IsInHierarchy (AutoSelectSuperView, Application.Navigation!.GetFocused ()))
+        {
+            return;
+        }
+
+        ViewToEdit = Application.Navigation!.GetFocused ();
+    }
+
+    private void ApplicationOnMouseEvent (object? sender, MouseEvent e)
+    {
+        if (e.Flags != MouseFlags.Button1Clicked || !AutoSelectViewToEdit)
+        {
+            return;
+        }
+
+        if ((AutoSelectSuperView is { } && !AutoSelectSuperView.FrameToScreen ().Contains (e.Position))
+            || FrameToScreen ().Contains (e.Position))
+        {
+            return;
+        }
+
+        View view = e.View;
+
+        if (view is { })
+        {
+            if (view is Adornment adornment)
+            {
+                ViewToEdit = AutoSelectAdornments ? adornment : adornment.Parent;
+            }
+            else
+            {
+                ViewToEdit = view;
+            }
         }
         }
     }
     }
 
 
@@ -72,7 +138,7 @@ public class AdornmentsEditor : View
         base.Dispose (disposing);
         base.Dispose (disposing);
     }
     }
 
 
-    private void AdornmentsEditor_Initialized (object sender, EventArgs e)
+    private void AdornmentsEditor_Initialized (object? sender, EventArgs e)
     {
     {
         BorderStyle = LineStyle.Dotted;
         BorderStyle = LineStyle.Dotted;
 
 
@@ -154,45 +220,8 @@ public class AdornmentsEditor : View
 
 
         Add (_diagRulerCheckBox);
         Add (_diagRulerCheckBox);
         _diagRulerCheckBox.Y = Pos.Bottom (_diagPaddingCheckBox);
         _diagRulerCheckBox.Y = Pos.Bottom (_diagPaddingCheckBox);
-    }
-
-    private void Application_MouseEvent (object sender, MouseEvent e)
-    {
-        if (!AutoSelectViewToEdit || FrameToScreen ().Contains (e.Position))
-        {
-            return;
-        }
-
-        // TODO: Add a setting (property) so only subviews of a specified view are considered.
-        View view = e.View;
 
 
-        if (view is { } && e.Flags == MouseFlags.Button1Clicked)
-        {
-            if (view is Adornment adornment)
-            {
-                ViewToEdit = adornment.Parent;
-            }
-            else
-            {
-                ViewToEdit = view;
-            }
-        }
-    }
-
-    private void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e)
-    {
-        if (ApplicationNavigation.IsInHierarchy (this, Application.Navigation!.GetFocused ()))
-        {
-            return;
-        }
-
-        if (Application.Navigation!.GetFocused () is Adornment adornment)
-        {
-            ViewToEdit = adornment.Parent;
-        }
-        else
-        {
-            ViewToEdit = Application.Navigation.GetFocused ();
-        }
+        Application.MouseEvent += ApplicationOnMouseEvent;
+        Application.Navigation!.FocusedChanged += NavigationOnFocusedChanged;
     }
     }
 }
 }

+ 74 - 55
UICatalog/Scenarios/AllViewsTester.cs

@@ -11,7 +11,8 @@ namespace UICatalog.Scenarios;
 [ScenarioMetadata ("All Views Tester", "Provides a test UI for all classes derived from View.")]
 [ScenarioMetadata ("All Views Tester", "Provides a test UI for all classes derived from View.")]
 [ScenarioCategory ("Layout")]
 [ScenarioCategory ("Layout")]
 [ScenarioCategory ("Tests")]
 [ScenarioCategory ("Tests")]
-[ScenarioCategory ("Top Level Windows")]
+[ScenarioCategory ("Controls")]
+[ScenarioCategory ("Adornments")]
 public class AllViewsTester : Scenario
 public class AllViewsTester : Scenario
 {
 {
     private readonly List<string> _dimNames = new () { "Auto", "Percent", "Fill", "Absolute" };
     private readonly List<string> _dimNames = new () { "Auto", "Percent", "Fill", "Absolute" };
@@ -68,7 +69,7 @@ public class AllViewsTester : Scenario
             Y = 0,
             Y = 0,
             Width = Dim.Auto (DimAutoStyle.Content),
             Width = Dim.Auto (DimAutoStyle.Content),
             Height = Dim.Fill (),
             Height = Dim.Fill (),
-            CanFocus = false,
+            CanFocus = true,
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
             Title = "Classes"
             Title = "Classes"
         };
         };
@@ -84,7 +85,6 @@ public class AllViewsTester : Scenario
             SelectedItem = 0,
             SelectedItem = 0,
             Source = new ListWrapper<string> (new (_viewClasses.Keys.ToList ()))
             Source = new ListWrapper<string> (new (_viewClasses.Keys.ToList ()))
         };
         };
-        _classListView.OpenSelectedItem += (s, a) => { _settingsPane.SetFocus (); };
 
 
         _classListView.SelectedItemChanged += (s, args) =>
         _classListView.SelectedItemChanged += (s, args) =>
                                               {
                                               {
@@ -95,10 +95,19 @@ public class AllViewsTester : Scenario
                                                       _hostPane.Remove (_curView);
                                                       _hostPane.Remove (_curView);
                                                       _curView.Dispose ();
                                                       _curView.Dispose ();
                                                       _curView = null;
                                                       _curView = null;
-                                                      _hostPane.Clear ();
                                                   }
                                                   }
 
 
                                                   _curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
                                                   _curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
+                                                  // Add
+                                                  _hostPane.Add (_curView);
+
+                                                  // Force ViewToEdit to be the view and not a subview
+                                                  if (_adornmentsEditor is { })
+                                                  {
+                                                      _adornmentsEditor.AutoSelectSuperView = _curView;
+
+                                                      _adornmentsEditor.ViewToEdit = _curView;
+                                                  }
                                               };
                                               };
         _leftPane.Add (_classListView);
         _leftPane.Add (_classListView);
 
 
@@ -109,7 +118,9 @@ public class AllViewsTester : Scenario
             Width = Dim.Auto (),
             Width = Dim.Auto (),
             Height = Dim.Fill (),
             Height = Dim.Fill (),
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
-            BorderStyle = LineStyle.Single
+            BorderStyle = LineStyle.Single,
+            AutoSelectViewToEdit = true,
+            AutoSelectAdornments = false,
         };
         };
 
 
         var expandButton = new ExpanderButton
         var expandButton = new ExpanderButton
@@ -125,7 +136,7 @@ public class AllViewsTester : Scenario
             Y = 0, // for menu
             Y = 0, // for menu
             Width = Dim.Fill (),
             Width = Dim.Fill (),
             Height = Dim.Auto (),
             Height = Dim.Auto (),
-            CanFocus = false,
+            CanFocus = true,
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
             Title = "Settings"
             Title = "Settings"
         };
         };
@@ -138,14 +149,15 @@ public class AllViewsTester : Scenario
             Y = 0,
             Y = 0,
             Height = Dim.Auto (),
             Height = Dim.Auto (),
             Width = Dim.Auto (),
             Width = Dim.Auto (),
-            Title = "Location (Pos)"
+            Title = "Location (Pos)",
+            TabStop = TabBehavior.TabStop,
         };
         };
         _settingsPane.Add (_locationFrame);
         _settingsPane.Add (_locationFrame);
 
 
         var label = new Label { X = 0, Y = 0, Text = "X:" };
         var label = new Label { X = 0, Y = 0, Text = "X:" };
         _locationFrame.Add (label);
         _locationFrame.Add (label);
         _xRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
         _xRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
-        _xRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
+        _xRadioGroup.SelectedItemChanged += OnXRadioGroupOnSelectedItemChanged;
         _xText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_xVal}" };
         _xText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_xVal}" };
 
 
         _xText.Accept += (s, args) =>
         _xText.Accept += (s, args) =>
@@ -179,7 +191,7 @@ public class AllViewsTester : Scenario
                          };
                          };
         _locationFrame.Add (_yText);
         _locationFrame.Add (_yText);
         _yRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
         _yRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
-        _yRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
+        _yRadioGroup.SelectedItemChanged += OnYRadioGroupOnSelectedItemChanged;
         _locationFrame.Add (_yRadioGroup);
         _locationFrame.Add (_yRadioGroup);
 
 
         _sizeFrame = new ()
         _sizeFrame = new ()
@@ -188,14 +200,15 @@ public class AllViewsTester : Scenario
             Y = Pos.Y (_locationFrame),
             Y = Pos.Y (_locationFrame),
             Height = Dim.Auto (),
             Height = Dim.Auto (),
             Width = Dim.Auto (),
             Width = Dim.Auto (),
-            Title = "Size (Dim)"
+            Title = "Size (Dim)",
+            TabStop = TabBehavior.TabStop,
         };
         };
 
 
         radioItems = new [] { "Auto", "_Percent(width)", "_Fill(width)", "A_bsolute(width)" };
         radioItems = new [] { "Auto", "_Percent(width)", "_Fill(width)", "A_bsolute(width)" };
         label = new () { X = 0, Y = 0, Text = "Width:" };
         label = new () { X = 0, Y = 0, Text = "Width:" };
         _sizeFrame.Add (label);
         _sizeFrame.Add (label);
         _wRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
         _wRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
-        _wRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
+        _wRadioGroup.SelectedItemChanged += OnWRadioGroupOnSelectedItemChanged;
         _wText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_wVal}" };
         _wText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_wVal}" };
 
 
         _wText.Accept += (s, args) =>
         _wText.Accept += (s, args) =>
@@ -255,7 +268,7 @@ public class AllViewsTester : Scenario
         _sizeFrame.Add (_hText);
         _sizeFrame.Add (_hText);
 
 
         _hRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
         _hRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
-        _hRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
+        _hRadioGroup.SelectedItemChanged += OnHRadioGroupOnSelectedItemChanged;
         _sizeFrame.Add (_hRadioGroup);
         _sizeFrame.Add (_hRadioGroup);
 
 
         _settingsPane.Add (_sizeFrame);
         _settingsPane.Add (_sizeFrame);
@@ -308,18 +321,29 @@ public class AllViewsTester : Scenario
             Y = Pos.Bottom (_settingsPane),
             Y = Pos.Bottom (_settingsPane),
             Width = Dim.Fill (),
             Width = Dim.Fill (),
             Height = Dim.Fill (), // + 1 for status bar
             Height = Dim.Fill (), // + 1 for status bar
+            CanFocus = true,
+            TabStop = TabBehavior.TabGroup,
             ColorScheme = Colors.ColorSchemes ["Dialog"]
             ColorScheme = Colors.ColorSchemes ["Dialog"]
         };
         };
 
 
         app.Add (_leftPane, _adornmentsEditor, _settingsPane, _hostPane);
         app.Add (_leftPane, _adornmentsEditor, _settingsPane, _hostPane);
 
 
         _classListView.SelectedItem = 0;
         _classListView.SelectedItem = 0;
+        _leftPane.SetFocus ();
 
 
         Application.Run (app);
         Application.Run (app);
         app.Dispose ();
         app.Dispose ();
         Application.Shutdown ();
         Application.Shutdown ();
     }
     }
 
 
+    private void OnHRadioGroupOnSelectedItemChanged (object s, SelectedItemChangedArgs selected) { DimPosChanged (_curView); }
+
+    private void OnWRadioGroupOnSelectedItemChanged (object s, SelectedItemChangedArgs selected) { DimPosChanged (_curView); }
+
+    private void OnYRadioGroupOnSelectedItemChanged (object s, SelectedItemChangedArgs selected) { DimPosChanged (_curView); }
+
+    private void OnXRadioGroupOnSelectedItemChanged (object s, SelectedItemChangedArgs selected) { DimPosChanged (_curView); }
+
     // TODO: Add Command.HotKey handler (pop a message box?)
     // TODO: Add Command.HotKey handler (pop a message box?)
     private View CreateClass (Type type)
     private View CreateClass (Type type)
     {
     {
@@ -342,12 +366,6 @@ public class AllViewsTester : Scenario
         // Instantiate view
         // Instantiate view
         var view = (View)Activator.CreateInstance (type);
         var view = (View)Activator.CreateInstance (type);
 
 
-        // Set the colorscheme to make it stand out if is null by default
-        if (view.ColorScheme == null)
-        {
-            view.ColorScheme = Colors.ColorSchemes ["Base"];
-        }
-
         if (view is IDesignable designable)
         if (view is IDesignable designable)
         {
         {
             designable.EnableForDesign (ref _demoText);
             designable.EnableForDesign (ref _demoText);
@@ -370,10 +388,6 @@ public class AllViewsTester : Scenario
 
 
         view.Initialized += View_Initialized;
         view.Initialized += View_Initialized;
 
 
-        // Add
-        _hostPane.Add (view);
-        _hostPane.SetNeedsDisplay ();
-
         return view;
         return view;
     }
     }
 
 
@@ -387,40 +401,40 @@ public class AllViewsTester : Scenario
         try
         try
         {
         {
             view.X = _xRadioGroup.SelectedItem switch
             view.X = _xRadioGroup.SelectedItem switch
-                     {
-                         0 => Pos.Percent (_xVal),
-                         1 => Pos.AnchorEnd (),
-                         2 => Pos.Center (),
-                         3 => Pos.Absolute (_xVal),
-                         _ => view.X
-                     };
+            {
+                0 => Pos.Percent (_xVal),
+                1 => Pos.AnchorEnd (),
+                2 => Pos.Center (),
+                3 => Pos.Absolute (_xVal),
+                _ => view.X
+            };
 
 
             view.Y = _yRadioGroup.SelectedItem switch
             view.Y = _yRadioGroup.SelectedItem switch
-                     {
-                         0 => Pos.Percent (_yVal),
-                         1 => Pos.AnchorEnd (),
-                         2 => Pos.Center (),
-                         3 => Pos.Absolute (_yVal),
-                         _ => view.Y
-                     };
+            {
+                0 => Pos.Percent (_yVal),
+                1 => Pos.AnchorEnd (),
+                2 => Pos.Center (),
+                3 => Pos.Absolute (_yVal),
+                _ => view.Y
+            };
 
 
             view.Width = _wRadioGroup.SelectedItem switch
             view.Width = _wRadioGroup.SelectedItem switch
-                         {
-                             0 => Dim.Auto (),
-                             1 => Dim.Percent (_wVal),
-                             2 => Dim.Fill (_wVal),
-                             3 => Dim.Absolute (_wVal),
-                             _ => view.Width
-                         };
+            {
+                0 => Dim.Auto (),
+                1 => Dim.Percent (_wVal),
+                2 => Dim.Fill (_wVal),
+                3 => Dim.Absolute (_wVal),
+                _ => view.Width
+            };
 
 
             view.Height = _hRadioGroup.SelectedItem switch
             view.Height = _hRadioGroup.SelectedItem switch
-                          {
-                              0 => Dim.Auto (),
-                              1 => Dim.Percent (_hVal),
-                              2 => Dim.Fill (_hVal),
-                              3 => Dim.Absolute (_hVal),
-                              _ => view.Height
-                          };
+            {
+                0 => Dim.Auto (),
+                1 => Dim.Percent (_hVal),
+                2 => Dim.Fill (_hVal),
+                3 => Dim.Absolute (_hVal),
+                _ => view.Height
+            };
         }
         }
         catch (Exception e)
         catch (Exception e)
         {
         {
@@ -476,12 +490,8 @@ public class AllViewsTester : Scenario
         UpdateTitle (_curView);
         UpdateTitle (_curView);
     }
     }
 
 
-    private void Quit () { Application.RequestStop (); }
-
     private void UpdateSettings (View view)
     private void UpdateSettings (View view)
     {
     {
-        _adornmentsEditor.ViewToEdit = view;
-
         var x = view.X.ToString ();
         var x = view.X.ToString ();
         var y = view.Y.ToString ();
         var y = view.Y.ToString ();
 
 
@@ -493,7 +503,7 @@ public class AllViewsTester : Scenario
         catch (InvalidOperationException e)
         catch (InvalidOperationException e)
         {
         {
             // This is a hack to work around the fact that the Pos enum doesn't have an "Align" value yet
             // This is a hack to work around the fact that the Pos enum doesn't have an "Align" value yet
-            Debug.WriteLine($"{e}");
+            Debug.WriteLine ($"{e}");
         }
         }
 
 
         _xText.Text = $"{view.Frame.X}";
         _xText.Text = $"{view.Frame.X}";
@@ -546,7 +556,16 @@ public class AllViewsTester : Scenario
             view.Height = Dim.Fill ();
             view.Height = Dim.Fill ();
         }
         }
 
 
+        _xRadioGroup.SelectedItemChanged -= OnXRadioGroupOnSelectedItemChanged;
+        _yRadioGroup.SelectedItemChanged -= OnYRadioGroupOnSelectedItemChanged;
+        _hRadioGroup.SelectedItemChanged -= OnHRadioGroupOnSelectedItemChanged;
+        _wRadioGroup.SelectedItemChanged -= OnWRadioGroupOnSelectedItemChanged;
         UpdateSettings (view);
         UpdateSettings (view);
+        _xRadioGroup.SelectedItemChanged += OnXRadioGroupOnSelectedItemChanged;
+        _yRadioGroup.SelectedItemChanged += OnYRadioGroupOnSelectedItemChanged;
+        _hRadioGroup.SelectedItemChanged += OnHRadioGroupOnSelectedItemChanged;
+        _wRadioGroup.SelectedItemChanged += OnWRadioGroupOnSelectedItemChanged;
+
         UpdateTitle (view);
         UpdateTitle (view);
     }
     }
 }
 }

+ 26 - 15
UICatalog/Scenarios/BackgroundWorkerCollection.cs

@@ -10,7 +10,8 @@ namespace UICatalog.Scenarios;
 
 
 [ScenarioMetadata ("BackgroundWorker Collection", "A persisting multi Toplevel BackgroundWorker threading")]
 [ScenarioMetadata ("BackgroundWorker Collection", "A persisting multi Toplevel BackgroundWorker threading")]
 [ScenarioCategory ("Threading")]
 [ScenarioCategory ("Threading")]
-[ScenarioCategory ("Top Level Windows")]
+[ScenarioCategory ("Overlapped")]
+[ScenarioCategory ("Runnable")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Controls")]
 [ScenarioCategory ("Controls")]
 public class BackgroundWorkerCollection : Scenario
 public class BackgroundWorkerCollection : Scenario
@@ -77,7 +78,7 @@ public class BackgroundWorkerCollection : Scenario
                                               () => Quit (),
                                               () => Quit (),
                                               null,
                                               null,
                                               null,
                                               null,
-                                              (KeyCode)Application.QuitKey
+                                              Application.QuitKey
                                              )
                                              )
                                      }
                                      }
                                     ),
                                     ),
@@ -281,7 +282,7 @@ public class BackgroundWorkerCollection : Scenario
             _listView = new ListView { X = 0, Y = 2, Width = Dim.Fill (), Height = Dim.Fill (2), Enabled = false };
             _listView = new ListView { X = 0, Y = 2, Width = Dim.Fill (), Height = Dim.Fill (2), Enabled = false };
             Add (_listView);
             Add (_listView);
 
 
-            _start = new Button { Text = "Start", IsDefault = true, ClearOnVisibleFalse = false };
+            _start = new Button { Text = "Start", IsDefault = true };
 
 
             _start.Accept += (s, e) =>
             _start.Accept += (s, e) =>
                               {
                               {
@@ -302,19 +303,28 @@ public class BackgroundWorkerCollection : Scenario
                            }
                            }
                        };
                        };
 
 
-            LayoutStarted += (s, e) =>
-                             {
-                                 int btnsWidth = _start.Frame.Width + _close.Frame.Width + 2 - 1;
-                                 int shiftLeft = Math.Max ((Viewport.Width - btnsWidth) / 2 - 2, 0);
+            LayoutStarted += StagingUIController_LayoutStarted;
+            Disposing += StagingUIController_Disposing;
+        }
 
 
-                                 shiftLeft += _close.Frame.Width + 1;
-                                 _close.X = Pos.AnchorEnd (shiftLeft);
-                                 _close.Y = Pos.AnchorEnd (1);
+        private void StagingUIController_Disposing (object sender, EventArgs e)
+        {
+            LayoutStarted -= StagingUIController_LayoutStarted;
+            Disposing -= StagingUIController_Disposing;
+        }
 
 
-                                 shiftLeft += _start.Frame.Width + 1;
-                                 _start.X = Pos.AnchorEnd (shiftLeft);
-                                 _start.Y = Pos.AnchorEnd (1);
-                             };
+        private void StagingUIController_LayoutStarted (object sender, LayoutEventArgs e)
+        {
+            int btnsWidth = _start.Frame.Width + _close.Frame.Width + 2 - 1;
+            int shiftLeft = Math.Max ((Viewport.Width - btnsWidth) / 2 - 2, 0);
+
+            shiftLeft += _close.Frame.Width + 1;
+            _close.X = Pos.AnchorEnd (shiftLeft);
+            _close.Y = Pos.AnchorEnd (1);
+
+            shiftLeft += _start.Frame.Width + 1;
+            _start.X = Pos.AnchorEnd (shiftLeft);
+            _start.Y = Pos.AnchorEnd (1);
         }
         }
 
 
         public Staging Staging { get; private set; }
         public Staging Staging { get; private set; }
@@ -371,11 +381,12 @@ public class BackgroundWorkerCollection : Scenario
         {
         {
             CancelWorker ();
             CancelWorker ();
         }
         }
+
         private void WorkerApp_Closing (object sender, ToplevelClosingEventArgs e)
         private void WorkerApp_Closing (object sender, ToplevelClosingEventArgs e)
         {
         {
             Toplevel top = ApplicationOverlapped.OverlappedChildren!.Find (x => x.Data.ToString () == "WorkerApp");
             Toplevel top = ApplicationOverlapped.OverlappedChildren!.Find (x => x.Data.ToString () == "WorkerApp");
 
 
-            if (Visible && top == this)
+            if (e.RequestingTop == this && Visible && top == this)
             {
             {
                 Visible = false;
                 Visible = false;
                 e.Cancel = true;
                 e.Cancel = true;

+ 1 - 1
UICatalog/Scenarios/ChineseUI.cs

@@ -3,7 +3,7 @@
 namespace UICatalog.Scenarios;
 namespace UICatalog.Scenarios;
 
 
 [ScenarioMetadata ("ChineseUI", "Chinese UI")]
 [ScenarioMetadata ("ChineseUI", "Chinese UI")]
-[ScenarioCategory ("Unicode")]
+[ScenarioCategory ("Text and Formatting")]
 public class ChineseUI : Scenario
 public class ChineseUI : Scenario
 {
 {
     public override void Main ()
     public override void Main ()

+ 10 - 10
UICatalog/Scenarios/ColorPicker.cs

@@ -45,7 +45,7 @@ public class ColorPickers : Scenario
 
 
         // Foreground ColorPicker.
         // Foreground ColorPicker.
         foregroundColorPicker = new ColorPicker {
         foregroundColorPicker = new ColorPicker {
-            Title = "Foreground Color",
+            Title = "_Foreground Color",
             BorderStyle = LineStyle.Single,
             BorderStyle = LineStyle.Single,
             Width = Dim.Percent (50)
             Width = Dim.Percent (50)
         };
         };
@@ -61,7 +61,7 @@ public class ColorPickers : Scenario
         // Background ColorPicker.
         // Background ColorPicker.
         backgroundColorPicker = new ColorPicker
         backgroundColorPicker = new ColorPicker
         {
         {
-            Title = "Background Color",
+            Title = "_Background Color",
             X = Pos.AnchorEnd (),
             X = Pos.AnchorEnd (),
             Width = Dim.Percent (50),
             Width = Dim.Percent (50),
             BorderStyle = LineStyle.Single
             BorderStyle = LineStyle.Single
@@ -86,7 +86,7 @@ public class ColorPickers : Scenario
         // Foreground ColorPicker 16.
         // Foreground ColorPicker 16.
         foregroundColorPicker16 = new ColorPicker16
         foregroundColorPicker16 = new ColorPicker16
         {
         {
-            Title = "Foreground Color",
+            Title = "_Foreground Color",
             BorderStyle = LineStyle.Single,
             BorderStyle = LineStyle.Single,
             Width = Dim.Percent (50),
             Width = Dim.Percent (50),
             Visible = false  // We default to HSV so hide old one
             Visible = false  // We default to HSV so hide old one
@@ -97,7 +97,7 @@ public class ColorPickers : Scenario
         // Background ColorPicker 16.
         // Background ColorPicker 16.
         backgroundColorPicker16 = new ColorPicker16
         backgroundColorPicker16 = new ColorPicker16
         {
         {
-            Title = "Background Color",
+            Title = "_Background Color",
             X = Pos.AnchorEnd (),
             X = Pos.AnchorEnd (),
             Width = Dim.Percent (50),
             Width = Dim.Percent (50),
             BorderStyle = LineStyle.Single,
             BorderStyle = LineStyle.Single,
@@ -132,10 +132,10 @@ public class ColorPickers : Scenario
             Height = Dim.Auto (),
             Height = Dim.Auto (),
             RadioLabels = new []
             RadioLabels = new []
             {
             {
-                "RGB",
-                "HSV",
-                "HSL",
-                "16 Colors"
+                "_RGB",
+                "_HSV",
+                "H_SL",
+                "_16 Colors"
             },
             },
             SelectedItem = (int)foregroundColorPicker.Style.ColorModel,
             SelectedItem = (int)foregroundColorPicker.Style.ColorModel,
         };
         };
@@ -180,7 +180,7 @@ public class ColorPickers : Scenario
         // Checkbox for switching show text fields on and off
         // Checkbox for switching show text fields on and off
         var cbShowTextFields = new CheckBox ()
         var cbShowTextFields = new CheckBox ()
         {
         {
-            Text = "Show Text Fields",
+            Text = "Show _Text Fields",
             Y = Pos.Bottom (rgColorModel)+1,
             Y = Pos.Bottom (rgColorModel)+1,
             Width = Dim.Auto (),
             Width = Dim.Auto (),
             Height = Dim.Auto (),
             Height = Dim.Auto (),
@@ -199,7 +199,7 @@ public class ColorPickers : Scenario
         // Checkbox for switching show text fields on and off
         // Checkbox for switching show text fields on and off
         var cbShowName = new CheckBox ()
         var cbShowName = new CheckBox ()
         {
         {
-            Text = "Show Color Name",
+            Text = "Show Color _Name",
             Y = Pos.Bottom (cbShowTextFields) + 1,
             Y = Pos.Bottom (cbShowTextFields) + 1,
             Width = Dim.Auto (),
             Width = Dim.Auto (),
             Height = Dim.Auto (),
             Height = Dim.Auto (),

+ 27 - 6
UICatalog/Scenarios/ConfigurationEditor.cs

@@ -44,7 +44,11 @@ public class ConfigurationEditor : Scenario
 
 
         _tileView = new TileView (0)
         _tileView = new TileView (0)
         {
         {
-            Width = Dim.Fill (), Height = Dim.Fill (1), Orientation = Orientation.Vertical, LineStyle = LineStyle.Single
+            Width = Dim.Fill (),
+            Height = Dim.Fill (1),
+            Orientation = Orientation.Vertical,
+            LineStyle = LineStyle.Single,
+            TabStop = TabBehavior.TabGroup
         };
         };
 
 
         top.Add (_tileView);
         top.Add (_tileView);
@@ -79,7 +83,11 @@ public class ConfigurationEditor : Scenario
 
 
         top.Add (statusBar);
         top.Add (statusBar);
 
 
-        top.Loaded += (s, a) => Open ();
+        top.Loaded += (s, a) =>
+                      {
+                          Open ();
+                          //_tileView.AdvanceFocus (NavigationDirection.Forward, null);
+                      };
 
 
         _editorColorSchemeChanged += () =>
         _editorColorSchemeChanged += () =>
                                      {
                                      {
@@ -133,7 +141,18 @@ public class ConfigurationEditor : Scenario
 
 
             textView.Read ();
             textView.Read ();
 
 
-            textView.Enter += (s, e) => { _lenShortcut.Title = $"Len:{textView.Text.Length}"; };
+            textView.HasFocusChanged += (s, e) =>
+                                        {
+                                            if (e.NewValue)
+                                            {
+                                                _lenShortcut.Title = $"Len:{textView.Text.Length}";
+                                            }
+                                        };
+        }
+
+        if (_tileView.Tiles.Count > 2)
+        {
+            _tileView.Tiles.ToArray () [1].ContentView.SetFocus ();
         }
         }
 
 
         Application.Top.LayoutSubviews ();
         Application.Top.LayoutSubviews ();
@@ -150,9 +169,9 @@ public class ConfigurationEditor : Scenario
                 int result = MessageBox.Query (
                 int result = MessageBox.Query (
                                                "Save Changes",
                                                "Save Changes",
                                                $"Save changes to {editor.FileInfo.FullName}",
                                                $"Save changes to {editor.FileInfo.FullName}",
-                                               "Yes",
-                                               "No",
-                                               "Cancel"
+                                               "_Yes",
+                                               "_No",
+                                               "_Cancel"
                                               );
                                               );
 
 
                 if (result == -1 || result == 2)
                 if (result == -1 || result == 2)
@@ -196,6 +215,8 @@ public class ConfigurationEditor : Scenario
                                        }
                                        }
                                    }
                                    }
                                };
                                };
+            TabStop = TabBehavior.TabGroup;
+
         }
         }
 
 
         internal FileInfo FileInfo { get; set; }
         internal FileInfo FileInfo { get; set; }

+ 4 - 0
UICatalog/Scenarios/ContentScrolling.cs

@@ -407,6 +407,10 @@ public class ContentScrolling : Scenario
 
 
         app.Closed += (s, e) => View.Diagnostics = _diagnosticFlags;
         app.Closed += (s, e) => View.Diagnostics = _diagnosticFlags;
 
 
+        editor.AutoSelectViewToEdit = true;
+        editor.AutoSelectSuperView = view;
+        editor.AutoSelectAdornments = false;
+
         Application.Run (app);
         Application.Run (app);
         app.Dispose ();
         app.Dispose ();
         Application.Shutdown ();
         Application.Shutdown ();

+ 1 - 1
UICatalog/Scenarios/CsvEditor.cs

@@ -16,7 +16,7 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Text and Formatting")]
 [ScenarioCategory ("Text and Formatting")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Dialogs")]
-[ScenarioCategory ("Top Level Windows")]
+[ScenarioCategory ("Overlapped")]
 [ScenarioCategory ("Files and IO")]
 [ScenarioCategory ("Files and IO")]
 public class CsvEditor : Scenario
 public class CsvEditor : Scenario
 {
 {

+ 4 - 4
UICatalog/Scenarios/Dialogs.cs

@@ -311,10 +311,10 @@ public class Dialogs : Scenario
                               buttons.Add (button);
                               buttons.Add (button);
                               dialog.AddButton (button);
                               dialog.AddButton (button);
 
 
-                              if (buttons.Count > 1)
-                              {
-                                  button.TabIndex = buttons [buttons.Count - 2].TabIndex + 1;
-                              }
+                              //if (buttons.Count > 1)
+                              //{
+                              //    button.TabIndex = buttons [buttons.Count - 2].TabIndex + 1;
+                              //}
                           };
                           };
             dialog.Add (add);
             dialog.Add (add);
 
 

+ 7 - 7
UICatalog/Scenarios/DynamicMenuBar.cs

@@ -9,7 +9,7 @@ using Terminal.Gui;
 namespace UICatalog.Scenarios;
 namespace UICatalog.Scenarios;
 
 
 [ScenarioMetadata ("Dynamic MenuBar", "Demonstrates how to change a MenuBar dynamically.")]
 [ScenarioMetadata ("Dynamic MenuBar", "Demonstrates how to change a MenuBar dynamically.")]
-[ScenarioCategory ("Top Level Windows")]
+[ScenarioCategory ("Overlapped")]
 [ScenarioCategory ("Menus")]
 [ScenarioCategory ("Menus")]
 public class DynamicMenuBar : Scenario
 public class DynamicMenuBar : Scenario
 {
 {
@@ -640,10 +640,10 @@ public class DynamicMenuBar : Scenario
             };
             };
             frmMenu.Add (_lstMenus);
             frmMenu.Add (_lstMenus);
 
 
-            lblMenuBar.TabIndex = btnPrevious.TabIndex + 1;
-            _lstMenus.TabIndex = lblMenuBar.TabIndex + 1;
-            btnNext.TabIndex = _lstMenus.TabIndex + 1;
-            btnAdd.TabIndex = btnNext.TabIndex + 1;
+            //lblMenuBar.TabIndex = btnPrevious.TabIndex + 1;
+            //_lstMenus.TabIndex = lblMenuBar.TabIndex + 1;
+            //btnNext.TabIndex = _lstMenus.TabIndex + 1;
+            //btnAdd.TabIndex = btnNext.TabIndex + 1;
 
 
             var btnRemove = new Button { X = Pos.Left (btnAdd), Y = Pos.Top (btnAdd) + 1, Text = "Remove" };
             var btnRemove = new Button { X = Pos.Left (btnAdd), Y = Pos.Top (btnAdd) + 1, Text = "Remove" };
             frmMenu.Add (btnRemove);
             frmMenu.Add (btnRemove);
@@ -938,7 +938,7 @@ public class DynamicMenuBar : Scenario
                                               SetFrameDetails (menuBarItem);
                                               SetFrameDetails (menuBarItem);
                                           };
                                           };
 
 
-            _lstMenus.Enter += (s, e) =>
+            _lstMenus.HasFocusChanging += (s, e) =>
                                {
                                {
                                    MenuItem menuBarItem = _lstMenus.SelectedItem > -1 && DataContext.Menus.Count > 0
                                    MenuItem menuBarItem = _lstMenus.SelectedItem > -1 && DataContext.Menus.Count > 0
                                                               ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem
                                                               ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem
@@ -966,7 +966,7 @@ public class DynamicMenuBar : Scenario
                                        SelectCurrentMenuBarItem ();
                                        SelectCurrentMenuBarItem ();
                                    };
                                    };
 
 
-            lblMenuBar.Enter += (s, e) =>
+            lblMenuBar.HasFocusChanging += (s, e) =>
                                  {
                                  {
                                      if (_menuBar?.Menus != null)
                                      if (_menuBar?.Menus != null)
                                      {
                                      {

+ 2 - 2
UICatalog/Scenarios/DynamicStatusBar.cs

@@ -10,7 +10,7 @@ using Terminal.Gui;
 namespace UICatalog.Scenarios;
 namespace UICatalog.Scenarios;
 
 
 [ScenarioMetadata ("Dynamic StatusBar", "Demonstrates how to add and remove a StatusBar and change items dynamically.")]
 [ScenarioMetadata ("Dynamic StatusBar", "Demonstrates how to add and remove a StatusBar and change items dynamically.")]
-[ScenarioCategory ("Top Level Windows")]
+[ScenarioCategory ("Overlapped")]
 public class DynamicStatusBar : Scenario
 public class DynamicStatusBar : Scenario
 {
 {
     public override void Main ()
     public override void Main ()
@@ -440,7 +440,7 @@ public class DynamicStatusBar : Scenario
                                       }
                                       }
                                   };
                                   };
 
 
-            _lstItems.Enter += (s, e) =>
+            _lstItems.HasFocusChanging += (s, e) =>
                                {
                                {
                                    Shortcut statusItem = DataContext.Items.Count > 0
                                    Shortcut statusItem = DataContext.Items.Count > 0
                                                                ? DataContext.Items [_lstItems.SelectedItem].Shortcut
                                                                ? DataContext.Items [_lstItems.SelectedItem].Shortcut

+ 8 - 8
UICatalog/Scenarios/Editor.cs

@@ -16,7 +16,7 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Controls")]
 [ScenarioCategory ("Controls")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Text and Formatting")]
 [ScenarioCategory ("Text and Formatting")]
-[ScenarioCategory ("Top Level Windows")]
+[ScenarioCategory ("Overlapped")]
 [ScenarioCategory ("Files and IO")]
 [ScenarioCategory ("Files and IO")]
 [ScenarioCategory ("TextView")]
 [ScenarioCategory ("TextView")]
 [ScenarioCategory ("Menus")]
 [ScenarioCategory ("Menus")]
@@ -722,7 +722,7 @@ public class Editor : Scenario
             }
             }
             else
             else
             {
             {
-                FocusFirst (null);
+                FocusDeepest (NavigationDirection.Forward, null);
             }
             }
         }
         }
 
 
@@ -736,10 +736,10 @@ public class Editor : Scenario
     private void ShowFindReplace (bool isFind = true)
     private void ShowFindReplace (bool isFind = true)
     {
     {
         _findReplaceWindow.Visible = true;
         _findReplaceWindow.Visible = true;
-        _findReplaceWindow.SuperView.BringSubviewToFront (_findReplaceWindow);
+        _findReplaceWindow.SuperView.MoveSubviewToStart (_findReplaceWindow);
         _tabView.SetFocus ();
         _tabView.SetFocus ();
         _tabView.SelectedTab = isFind ? _tabView.Tabs.ToArray () [0] : _tabView.Tabs.ToArray () [1];
         _tabView.SelectedTab = isFind ? _tabView.Tabs.ToArray () [0] : _tabView.Tabs.ToArray () [1];
-        _tabView.SelectedTab.View.FocusFirst (null);
+        _tabView.SelectedTab.View.FocusDeepest (NavigationDirection.Forward, null);
     }
     }
 
 
     private void CreateFindReplace ()
     private void CreateFindReplace ()
@@ -753,10 +753,10 @@ public class Editor : Scenario
 
 
         _tabView.AddTab (new () { DisplayText = "Find", View = CreateFindTab () }, true);
         _tabView.AddTab (new () { DisplayText = "Find", View = CreateFindTab () }, true);
         _tabView.AddTab (new () { DisplayText = "Replace", View = CreateReplaceTab () }, false);
         _tabView.AddTab (new () { DisplayText = "Replace", View = CreateReplaceTab () }, false);
-        _tabView.SelectedTabChanged += (s, e) => _tabView.SelectedTab.View.FocusFirst (null);
+        _tabView.SelectedTabChanged += (s, e) => _tabView.SelectedTab.View.FocusDeepest (NavigationDirection.Forward, null);
         _findReplaceWindow.Add (_tabView);
         _findReplaceWindow.Add (_tabView);
 
 
-        _tabView.SelectedTab.View.FocusLast (null); // Hack to get the first tab to be focused
+//        _tabView.SelectedTab.View.FocusLast (null); // Hack to get the first tab to be focused
         _findReplaceWindow.Visible = false;
         _findReplaceWindow.Visible = false;
         _appWindow.Add (_findReplaceWindow);
         _appWindow.Add (_findReplaceWindow);
     }
     }
@@ -860,7 +860,7 @@ public class Editor : Scenario
             Width = Dim.Fill (1),
             Width = Dim.Fill (1),
             Text = _textToFind
             Text = _textToFind
         };
         };
-        txtToFind.Enter += (s, e) => txtToFind.Text = _textToFind;
+        txtToFind.HasFocusChanging += (s, e) => txtToFind.Text = _textToFind;
         d.Add (txtToFind);
         d.Add (txtToFind);
 
 
         var btnFindNext = new Button
         var btnFindNext = new Button
@@ -1088,7 +1088,7 @@ public class Editor : Scenario
             Width = Dim.Fill (1),
             Width = Dim.Fill (1),
             Text = _textToFind
             Text = _textToFind
         };
         };
-        txtToFind.Enter += (s, e) => txtToFind.Text = _textToFind;
+        txtToFind.HasFocusChanging += (s, e) => txtToFind.Text = _textToFind;
         d.Add (txtToFind);
         d.Add (txtToFind);
 
 
         var btnFindNext = new Button
         var btnFindNext = new Button

+ 3 - 3
UICatalog/Scenarios/GraphViewExample.cs

@@ -161,7 +161,7 @@ public class GraphViewExample : Scenario
         };
         };
 
 
         frameRight.Add (
         frameRight.Add (
-                        _about = new() { Width = Dim.Fill (), Height = Dim.Fill () }
+                        _about = new() { Width = Dim.Fill (), Height = Dim.Fill (), ReadOnly = true }
                        );
                        );
 
 
         app.Add (frameRight);
         app.Add (frameRight);
@@ -170,8 +170,8 @@ public class GraphViewExample : Scenario
                                        new Shortcut []
                                        new Shortcut []
                                        {
                                        {
                                            new (Key.G.WithCtrl, "Next Graph", () => _graphs [_currentGraph++ % _graphs.Length] ()),
                                            new (Key.G.WithCtrl, "Next Graph", () => _graphs [_currentGraph++ % _graphs.Length] ()),
-                                           new (Key.CursorUp, "Zoom In", () => Zoom (0.5f)),
-                                           new (Key.CursorDown, "Zoom Out", () => Zoom (2f))
+                                           new (Key.PageUp, "Zoom In", () => Zoom (0.5f)),
+                                           new (Key.PageDown, "Zoom Out", () => Zoom (2f))
                                        }
                                        }
                                       );
                                       );
         app.Add (statusBar);
         app.Add (statusBar);

+ 1 - 1
UICatalog/Scenarios/HexEditor.cs

@@ -8,7 +8,7 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Controls")]
 [ScenarioCategory ("Controls")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Text and Formatting")]
 [ScenarioCategory ("Text and Formatting")]
-[ScenarioCategory ("Top Level Windows")]
+[ScenarioCategory ("Overlapped")]
 [ScenarioCategory ("Files and IO")]
 [ScenarioCategory ("Files and IO")]
 public class HexEditor : Scenario
 public class HexEditor : Scenario
 {
 {

+ 12 - 7
UICatalog/Scenarios/KeyBindings.cs

@@ -82,8 +82,8 @@ public sealed class KeyBindings : Scenario
 
 
         foreach (var appBinding in Application.KeyBindings.Bindings)
         foreach (var appBinding in Application.KeyBindings.Bindings)
         {
         {
-                var commands = Application.KeyBindings.GetCommands (appBinding.Key);
-                appBindings.Add ($"{appBinding.Key} -> {appBinding.Value.BoundView?.GetType ().Name} - {commands [0]}");
+            var commands = Application.KeyBindings.GetCommands (appBinding.Key);
+            appBindings.Add ($"{appBinding.Key} -> {appBinding.Value.BoundView?.GetType ().Name} - {commands [0]}");
         }
         }
 
 
         ObservableCollection<string> hotkeyBindings = new ();
         ObservableCollection<string> hotkeyBindings = new ();
@@ -125,8 +125,7 @@ public sealed class KeyBindings : Scenario
         };
         };
         appWindow.Add (_focusedBindingsListView);
         appWindow.Add (_focusedBindingsListView);
 
 
-        appWindow.Leave += AppWindow_Leave;
-        appWindow.Enter += AppWindow_Leave;
+        appWindow.HasFocusChanged += AppWindow_HasFocusChanged;
         appWindow.DrawContent += AppWindow_DrawContent;
         appWindow.DrawContent += AppWindow_DrawContent;
 
 
         // Run - Start the application.
         // Run - Start the application.
@@ -148,11 +147,17 @@ public sealed class KeyBindings : Scenario
         }
         }
     }
     }
 
 
-    private void AppWindow_Leave (object sender, FocusEventArgs e)
+    private void AppWindow_HasFocusChanged (object sender, HasFocusEventArgs e)
     {
     {
-        foreach (var binding in Application.Top.MostFocused.KeyBindings.Bindings.Where (b => b.Value.Scope == KeyBindingScope.Focused))
+        if (e.NewValue)
         {
         {
-            _focusedBindings.Add ($"{binding.Key} -> {binding.Value.Commands [0]}");
+            if (Application.Top is { MostFocused: {} })
+            {
+                foreach (var binding in Application.Top.MostFocused.KeyBindings.Bindings.Where (b => b.Value.Scope == KeyBindingScope.Focused))
+                {
+                    _focusedBindings.Add ($"{binding.Key} -> {binding.Value.Commands [0]}");
+                }
+            }
         }
         }
     }
     }
 }
 }

+ 1 - 1
UICatalog/Scenarios/LineCanvasExperiment.cs

@@ -4,7 +4,7 @@ namespace UICatalog.Scenarios;
 
 
 [ScenarioMetadata ("LineCanvas Experiments", "Experiments with LineCanvas")]
 [ScenarioMetadata ("LineCanvas Experiments", "Experiments with LineCanvas")]
 [ScenarioCategory ("Drawing")]
 [ScenarioCategory ("Drawing")]
-[ScenarioCategory ("Borders")]
+[ScenarioCategory ("Adornments")]
 [ScenarioCategory ("Proof of Concept")]
 [ScenarioCategory ("Proof of Concept")]
 public class LineCanvasExperiment : Scenario
 public class LineCanvasExperiment : Scenario
 {
 {

+ 1 - 1
UICatalog/Scenarios/LineViewExample.cs

@@ -6,7 +6,7 @@ namespace UICatalog.Scenarios;
 [ScenarioMetadata ("Line View", "Demonstrates drawing lines using the LineView control.")]
 [ScenarioMetadata ("Line View", "Demonstrates drawing lines using the LineView control.")]
 [ScenarioCategory ("Controls")]
 [ScenarioCategory ("Controls")]
 [ScenarioCategory ("LineView")]
 [ScenarioCategory ("LineView")]
-[ScenarioCategory ("Borders")]
+[ScenarioCategory ("Adornments")]
 public class LineViewExample : Scenario
 public class LineViewExample : Scenario
 {
 {
     public override void Main ()
     public override void Main ()

+ 1 - 1
UICatalog/Scenarios/ListColumns.cs

@@ -11,7 +11,7 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Controls")]
 [ScenarioCategory ("Controls")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Text and Formatting")]
 [ScenarioCategory ("Text and Formatting")]
-[ScenarioCategory ("Top Level Windows")]
+[ScenarioCategory ("Overlapped")]
 [ScenarioCategory ("Scrolling")]
 [ScenarioCategory ("Scrolling")]
 public class ListColumns : Scenario
 public class ListColumns : Scenario
 {
 {

+ 267 - 0
UICatalog/Scenarios/Navigation.cs

@@ -0,0 +1,267 @@
+using System.Timers;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+[ScenarioMetadata ("Navigation", "Navigation Tester")]
+[ScenarioCategory ("Mouse and Keyboard")]
+[ScenarioCategory ("Layout")]
+[ScenarioCategory ("Overlapped")]
+public class Navigation : Scenario
+{
+    private int _hotkeyCount;
+
+    public override void Main ()
+    {
+        Application.Init ();
+
+        Window app = new ()
+        {
+            Title = GetQuitKeyAndName (),
+            TabStop = TabBehavior.TabGroup
+        };
+
+        var editor = new AdornmentsEditor
+        {
+            X = 0,
+            Y = 0,
+            AutoSelectViewToEdit = true,
+            TabStop = TabBehavior.NoStop
+        };
+        app.Add (editor);
+
+        FrameView testFrame = new ()
+        {
+            Title = "_1 Test Frame",
+            X = Pos.Right (editor),
+            Width = Dim.Fill (),
+            Height = Dim.Fill ()
+        };
+
+        app.Add (testFrame);
+
+        Button button = new ()
+        {
+            X = 0,
+            Y = 0,
+            Title = $"TopButton _{GetNextHotKey ()}"
+        };
+
+        testFrame.Add (button);
+
+        View tiledView1 = CreateTiledView (0, 2, 2);
+        View tiledView2 = CreateTiledView (1, Pos.Right (tiledView1), Pos.Top (tiledView1));
+
+        testFrame.Add (tiledView1);
+        testFrame.Add (tiledView2);
+
+        View tiledView3 = CreateTiledView (1, Pos.Right (tiledView2), Pos.Top (tiledView2));
+        tiledView3.TabStop = TabBehavior.TabGroup;
+        tiledView3.BorderStyle = LineStyle.Double;
+        testFrame.Add (tiledView3);
+
+        View overlappedView1 = CreateOverlappedView (2, 10, Pos.Center ());
+        View tiledSubView = CreateTiledView (4, 0, 2);
+        overlappedView1.Add (tiledSubView);
+
+        ProgressBar progressBar = new ()
+        {
+            X = Pos.AnchorEnd (),
+            Y = Pos.AnchorEnd (),
+            Width = Dim.Fill (),
+            Id = "progressBar"
+        };
+        overlappedView1.Add (progressBar);
+
+        Timer timer = new (10)
+        {
+            AutoReset = true
+        };
+
+        timer.Elapsed += (o, args) =>
+                         {
+                             if (progressBar.Fraction == 1.0)
+                             {
+                                 progressBar.Fraction = 0;
+                             }
+
+                             progressBar.Fraction += 0.01f;
+
+                             Application.Wakeup ();
+
+                             progressBar.SetNeedsDisplay ();
+                         };
+        timer.Start ();
+
+        View overlappedView2 = CreateOverlappedView (3, 8, 10);
+
+        View overlappedInOverlapped1 = CreateOverlappedView (4, 1, 4);
+        overlappedView2.Add (overlappedInOverlapped1);
+
+        View overlappedInOverlapped2 = CreateOverlappedView (5, 10, 7);
+        overlappedView2.Add (overlappedInOverlapped2);
+
+        StatusBar statusBar = new ();
+
+        statusBar.Add (
+                       new Shortcut
+                       {
+                           Title = "Hide",
+                           Text = "Hotkey",
+                           Key = Key.F4,
+                           Action = () =>
+                                    {
+                                        // TODO: move this logic into `View.ShowHide()` or similar
+                                        overlappedView2.Visible = false;
+                                        overlappedView2.Enabled = overlappedView2.Visible;
+                                    }
+                       });
+
+        statusBar.Add (
+                       new Shortcut
+                       {
+                           Title = "Toggle Hide",
+                           Text = "App",
+                           KeyBindingScope = KeyBindingScope.Application,
+                           Key = Key.F4.WithCtrl,
+                           Action = () =>
+                                    {
+                                        // TODO: move this logic into `View.ShowHide()` or similar
+                                        overlappedView2.Visible = !overlappedView2.Visible;
+                                        overlappedView2.Enabled = overlappedView2.Visible;
+
+                                        if (overlappedView2.Visible)
+                                        {
+                                            overlappedView2.SetFocus ();
+                                        }
+                                    }
+                       });
+        overlappedView2.Add (statusBar);
+
+        ColorPicker colorPicker = new ()
+        {
+            Y = 12,
+            Width = Dim.Fill (),
+            Id = "colorPicker",
+            Style = new ()
+            {
+                ShowTextFields = true,
+                ShowColorName = true
+            }
+        };
+        colorPicker.ApplyStyleChanges ();
+
+        colorPicker.SelectedColor = testFrame.ColorScheme.Normal.Background;
+        colorPicker.ColorChanged += ColorPicker_ColorChanged;
+        overlappedView2.Add (colorPicker);
+        overlappedView2.Width = 50;
+
+        testFrame.Add (overlappedView1);
+        testFrame.Add (overlappedView2);
+
+        DatePicker datePicker = new ()
+        {
+            X = 1,
+            Y = 7,
+            Id = "datePicker",
+            ColorScheme = Colors.ColorSchemes ["Toplevel"],
+            ShadowStyle = ShadowStyle.Transparent,
+            BorderStyle = LineStyle.Double,
+            CanFocus = true, // Can't drag without this? BUGBUG
+            TabStop = TabBehavior.TabGroup,
+            Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped
+        };
+        testFrame.Add (datePicker);
+
+        button = new ()
+        {
+            X = Pos.AnchorEnd (),
+            Y = Pos.AnchorEnd (),
+            Title = $"TopButton _{GetNextHotKey ()}"
+        };
+
+        testFrame.Add (button);
+
+        editor.AutoSelectSuperView = testFrame;
+        testFrame.SetFocus ();
+        Application.Run (app);
+        timer.Close ();
+        app.Dispose ();
+        Application.Shutdown ();
+
+        return;
+
+        void ColorPicker_ColorChanged (object sender, ColorEventArgs e)
+        {
+            testFrame.ColorScheme = testFrame.ColorScheme with { Normal = new (testFrame.ColorScheme.Normal.Foreground, e.CurrentValue) };
+        }
+    }
+
+    private View CreateOverlappedView (int id, Pos x, Pos y)
+    {
+        var overlapped = new View
+        {
+            X = x,
+            Y = y,
+            Height = Dim.Auto (),
+            Width = Dim.Auto (),
+            Title = $"Overlapped{id} _{GetNextHotKey ()}",
+            ColorScheme = Colors.ColorSchemes ["Toplevel"],
+            Id = $"Overlapped{id}",
+            ShadowStyle = ShadowStyle.Transparent,
+            BorderStyle = LineStyle.Double,
+            CanFocus = true, // Can't drag without this? BUGBUG
+            TabStop = TabBehavior.TabGroup,
+            Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped
+        };
+
+        Button button = new ()
+        {
+            Title = $"Button{id} _{GetNextHotKey ()}"
+        };
+        overlapped.Add (button);
+
+        button = new ()
+        {
+            Y = Pos.Bottom (button),
+            Title = $"Button{id} _{GetNextHotKey ()}"
+        };
+        overlapped.Add (button);
+
+        return overlapped;
+    }
+
+    private View CreateTiledView (int id, Pos x, Pos y)
+    {
+        var overlapped = new View
+        {
+            X = x,
+            Y = y,
+            Height = Dim.Auto (),
+            Width = Dim.Auto (),
+            Title = $"Tiled{id} _{GetNextHotKey ()}",
+            Id = $"Tiled{id}",
+            BorderStyle = LineStyle.Single,
+            CanFocus = true, // Can't drag without this? BUGBUG
+            TabStop = TabBehavior.TabStop,
+            Arrangement = ViewArrangement.Fixed
+        };
+
+        Button button = new ()
+        {
+            Title = $"Tiled Button{id} _{GetNextHotKey ()}"
+        };
+        overlapped.Add (button);
+
+        button = new ()
+        {
+            Y = Pos.Bottom (button),
+            Title = $"Tiled Button{id} _{GetNextHotKey ()}"
+        };
+        overlapped.Add (button);
+
+        return overlapped;
+    }
+
+    private char GetNextHotKey () { return (char)('A' + _hotkeyCount++); }
+}

+ 3 - 3
UICatalog/Scenarios/Notepad.cs

@@ -84,7 +84,7 @@ public class Notepad : Scenario
 
 
         _focusedTabView = _tabView;
         _focusedTabView = _tabView;
         _tabView.SelectedTabChanged += TabView_SelectedTabChanged;
         _tabView.SelectedTabChanged += TabView_SelectedTabChanged;
-        _tabView.Enter += (s, e) => _focusedTabView = _tabView;
+        _tabView.HasFocusChanging += (s, e) => _focusedTabView = _tabView;
 
 
         top.Ready += (s, e) =>
         top.Ready += (s, e) =>
                      {
                      {
@@ -241,7 +241,7 @@ public class Notepad : Scenario
 
 
         tv.TabClicked += TabView_TabClicked;
         tv.TabClicked += TabView_TabClicked;
         tv.SelectedTabChanged += TabView_SelectedTabChanged;
         tv.SelectedTabChanged += TabView_SelectedTabChanged;
-        tv.Enter += (s, e) => _focusedTabView = tv;
+        tv.HasFocusChanging += (s, e) => _focusedTabView = tv;
 
 
         return tv;
         return tv;
     }
     }
@@ -309,7 +309,7 @@ public class Notepad : Scenario
         tab.CloneTo (newTabView);
         tab.CloneTo (newTabView);
         newTile.ContentView.Add (newTabView);
         newTile.ContentView.Add (newTabView);
 
 
-        newTabView.FocusFirst (null);
+        newTabView.FocusDeepest (NavigationDirection.Forward, null);
         newTabView.AdvanceFocus (NavigationDirection.Forward, null);
         newTabView.AdvanceFocus (NavigationDirection.Forward, null);
     }
     }
 
 

+ 8 - 9
UICatalog/Scenarios/NumericUpDownDemo.cs

@@ -15,7 +15,6 @@ public class NumericUpDownDemo : Scenario
         Window app = new ()
         Window app = new ()
         {
         {
             Title = GetQuitKeyAndName (),
             Title = GetQuitKeyAndName (),
-            TabStop = TabBehavior.TabGroup
         };
         };
 
 
         var editor = new AdornmentsEditor
         var editor = new AdornmentsEditor
@@ -23,7 +22,6 @@ public class NumericUpDownDemo : Scenario
             X = 0,
             X = 0,
             Y = 0,
             Y = 0,
             AutoSelectViewToEdit = true,
             AutoSelectViewToEdit = true,
-            TabStop = TabBehavior.NoStop
         };
         };
         app.Add (editor);
         app.Add (editor);
 
 
@@ -50,15 +48,15 @@ public class NumericUpDownDemo : Scenario
         {
         {
             floatEditor!.NumericUpDown!.Increment = 0.1F;
             floatEditor!.NumericUpDown!.Increment = 0.1F;
             floatEditor!.NumericUpDown!.Format = "{0:0.0}";
             floatEditor!.NumericUpDown!.Format = "{0:0.0}";
-
         }
         }
 
 
+        editor.AutoSelectSuperView = app;
+        intEditor.SetFocus ();
+
         Application.Run (app);
         Application.Run (app);
         app.Dispose ();
         app.Dispose ();
         Application.Shutdown ();
         Application.Shutdown ();
-
     }
     }
-
 }
 }
 
 
 internal class NumericUpDownEditor<T> : View where T : notnull
 internal class NumericUpDownEditor<T> : View where T : notnull
@@ -95,6 +93,7 @@ internal class NumericUpDownEditor<T> : View where T : notnull
         Width = Dim.Auto (DimAutoStyle.Content);
         Width = Dim.Auto (DimAutoStyle.Content);
         Height = Dim.Auto (DimAutoStyle.Content);
         Height = Dim.Auto (DimAutoStyle.Content);
         TabStop = TabBehavior.TabGroup;
         TabStop = TabBehavior.TabGroup;
+        CanFocus = true;
 
 
         Initialized += NumericUpDownEditorInitialized;
         Initialized += NumericUpDownEditorInitialized;
 
 
@@ -137,7 +136,7 @@ internal class NumericUpDownEditor<T> : View where T : notnull
                         _numericUpDown.Value = (T)Convert.ChangeType (_value.Text, typeof (T));
                         _numericUpDown.Value = (T)Convert.ChangeType (_value.Text, typeof (T));
                     }
                     }
 
 
-                    _value.ColorScheme = SuperView.ColorScheme;
+                    _value.ColorScheme = SuperView!.ColorScheme;
 
 
                 }
                 }
                 catch (System.FormatException)
                 catch (System.FormatException)
@@ -184,8 +183,8 @@ internal class NumericUpDownEditor<T> : View where T : notnull
                     // Test format to ensure it's valid
                     // Test format to ensure it's valid
                     _ = string.Format (_format.Text, _value);
                     _ = string.Format (_format.Text, _value);
                     _numericUpDown.Format = _format.Text;
                     _numericUpDown.Format = _format.Text;
-                    
-                    _format.ColorScheme = SuperView.ColorScheme;
+
+                    _format.ColorScheme = SuperView!.ColorScheme;
 
 
                 }
                 }
                 catch (System.FormatException)
                 catch (System.FormatException)
@@ -240,7 +239,7 @@ internal class NumericUpDownEditor<T> : View where T : notnull
                         _numericUpDown.Increment = (T)Convert.ChangeType (_increment.Text, typeof (T));
                         _numericUpDown.Increment = (T)Convert.ChangeType (_increment.Text, typeof (T));
                     }
                     }
 
 
-                    _increment.ColorScheme = SuperView.ColorScheme;
+                    _increment.ColorScheme = SuperView!.ColorScheme;
 
 
                 }
                 }
                 catch (System.FormatException)
                 catch (System.FormatException)

+ 1 - 1
UICatalog/Scenarios/ProgressBarStyles.cs

@@ -35,7 +35,7 @@ public class ProgressBarStyles : Scenario
 
 
         var editor = new AdornmentsEditor ()
         var editor = new AdornmentsEditor ()
         {
         {
-            AutoSelectViewToEdit = false
+            AutoSelectViewToEdit = true
         };
         };
         app.Add (editor);
         app.Add (editor);
 
 

+ 2 - 1
UICatalog/Scenarios/RunTExample.cs

@@ -3,7 +3,8 @@
 namespace UICatalog.Scenarios;
 namespace UICatalog.Scenarios;
 
 
 [ScenarioMetadata ("Run<T> Example", "Illustrates using Application.Run<T> to run a custom class")]
 [ScenarioMetadata ("Run<T> Example", "Illustrates using Application.Run<T> to run a custom class")]
-[ScenarioCategory ("Top Level Windows")]
+[ScenarioCategory ("Runnable")]
+[ScenarioCategory ("Overlapped")]
 public class RunTExample : Scenario
 public class RunTExample : Scenario
 {
 {
     public override void Main ()
     public override void Main ()

+ 6 - 2
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
         };
         };
 
 
@@ -55,11 +56,14 @@ public class ShadowStyles : Scenario
         };
         };
         app.Add (button);
         app.Add (button);
 
 
+        editor.AutoSelectViewToEdit = true;
+        editor.AutoSelectSuperView = app;
+        editor.AutoSelectAdornments = false;
+
         Application.Run (app);
         Application.Run (app);
         app.Dispose ();
         app.Dispose ();
 
 
         Application.Shutdown ();
         Application.Shutdown ();
 
 
-        return;
     }
     }
 }
 }

+ 2 - 1
UICatalog/Scenarios/SingleBackgroundWorker.cs

@@ -9,7 +9,8 @@ namespace UICatalog.Scenarios;
 
 
 [ScenarioMetadata ("Single BackgroundWorker", "A single BackgroundWorker threading opening another Toplevel")]
 [ScenarioMetadata ("Single BackgroundWorker", "A single BackgroundWorker threading opening another Toplevel")]
 [ScenarioCategory ("Threading")]
 [ScenarioCategory ("Threading")]
-[ScenarioCategory ("Top Level Windows")]
+[ScenarioCategory ("Overlapped")]
+[ScenarioCategory ("Runnable")]
 public class SingleBackgroundWorker : Scenario
 public class SingleBackgroundWorker : Scenario
 {
 {
     public override void Main ()
     public override void Main ()

+ 1 - 1
UICatalog/Scenarios/Sliders.cs

@@ -609,7 +609,7 @@ public class Sliders : Scenario
                              };
                              };
         }
         }
 
 
-        app.FocusFirst (null);
+        app.FocusDeepest (NavigationDirection.Forward, null);
 
 
         Application.Run (app);
         Application.Run (app);
         app.Dispose ();
         app.Dispose ();

+ 15 - 8
UICatalog/Scenarios/TabViewExample.cs

@@ -73,6 +73,7 @@ public class TabViewExample : Scenario
 
 
         _tabView = new()
         _tabView = new()
         {
         {
+            Title = "_Tab View",
             X = 0,
             X = 0,
             Y = 1,
             Y = 1,
             Width = 60,
             Width = 60,
@@ -80,9 +81,9 @@ public class TabViewExample : Scenario
             BorderStyle = LineStyle.Single
             BorderStyle = LineStyle.Single
         };
         };
 
 
-        _tabView.AddTab (new() { DisplayText = "Tab1", View = new Label { Text = "hodor!" } }, false);
-        _tabView.AddTab (new() { DisplayText = "Tab2", View = new TextField { Text = "durdur" } }, false);
-        _tabView.AddTab (new() { DisplayText = "Interactive Tab", View = GetInteractiveTab () }, false);
+        _tabView.AddTab (new() { DisplayText = "Tab_1", View = new Label { Text = "hodor!" } }, false);
+        _tabView.AddTab (new() { DisplayText = "Tab_2", View = new TextField { Text = "durdur", Width = 10 } }, false);
+        _tabView.AddTab (new() { DisplayText = "_Interactive Tab", View = GetInteractiveTab () }, false);
         _tabView.AddTab (new() { DisplayText = "Big Text", View = GetBigTextFileTab () }, false);
         _tabView.AddTab (new() { DisplayText = "Big Text", View = GetBigTextFileTab () }, false);
 
 
         _tabView.AddTab (
         _tabView.AddTab (
@@ -138,9 +139,10 @@ public class TabViewExample : Scenario
             Y = 1,
             Y = 1,
             Width = Dim.Fill (),
             Width = Dim.Fill (),
             Height = Dim.Fill (1),
             Height = Dim.Fill (1),
-            Title = "About",
+            Title = "_About",
             BorderStyle = LineStyle.Single,
             BorderStyle = LineStyle.Single,
-            TabStop = TabBehavior.TabStop
+            TabStop = TabBehavior.TabStop,
+            CanFocus = true
         };
         };
 
 
         frameRight.Add (
         frameRight.Add (
@@ -161,9 +163,10 @@ public class TabViewExample : Scenario
             Y = Pos.Bottom (_tabView),
             Y = Pos.Bottom (_tabView),
             Width = _tabView.Width,
             Width = _tabView.Width,
             Height = Dim.Fill (1),
             Height = Dim.Fill (1),
-            Title = "Bottom Frame",
+            Title = "B_ottom Frame",
             BorderStyle = LineStyle.Single,
             BorderStyle = LineStyle.Single,
-            TabStop = TabBehavior.TabStop
+            TabStop = TabBehavior.TabStop,
+            CanFocus = true
 
 
         };
         };
 
 
@@ -216,7 +219,11 @@ public class TabViewExample : Scenario
 
 
     private View GetInteractiveTab ()
     private View GetInteractiveTab ()
     {
     {
-        var interactiveTab = new View { Width = Dim.Fill (), Height = Dim.Fill () };
+        var interactiveTab = new View
+        {
+            Width = Dim.Fill (), Height = Dim.Fill (),
+            CanFocus = true
+        };
         var lblName = new Label { Text = "Name:" };
         var lblName = new Label { Text = "Name:" };
         interactiveTab.Add (lblName);
         interactiveTab.Add (lblName);
 
 

+ 1 - 1
UICatalog/Scenarios/TableEditor.cs

@@ -14,7 +14,7 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Controls")]
 [ScenarioCategory ("Controls")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Text and Formatting")]
 [ScenarioCategory ("Text and Formatting")]
-[ScenarioCategory ("Top Level Windows")]
+[ScenarioCategory ("Overlapped")]
 public class TableEditor : Scenario
 public class TableEditor : Scenario
 {
 {
     private readonly HashSet<FileSystemInfo> _checkedFileSystemInfos = new ();
     private readonly HashSet<FileSystemInfo> _checkedFileSystemInfos = new ();

+ 11 - 91
UICatalog/Scenarios/ViewExperiments.cs

@@ -1,10 +1,11 @@
-using Terminal.Gui;
+using System;
+using Terminal.Gui;
 
 
 namespace UICatalog.Scenarios;
 namespace UICatalog.Scenarios;
 
 
 [ScenarioMetadata ("View Experiments", "v2 View Experiments")]
 [ScenarioMetadata ("View Experiments", "v2 View Experiments")]
 [ScenarioCategory ("Controls")]
 [ScenarioCategory ("Controls")]
-[ScenarioCategory ("Borders")]
+[ScenarioCategory ("Adornments")]
 [ScenarioCategory ("Layout")]
 [ScenarioCategory ("Layout")]
 [ScenarioCategory ("Proof of Concept")]
 [ScenarioCategory ("Proof of Concept")]
 public class ViewExperiments : Scenario
 public class ViewExperiments : Scenario
@@ -23,7 +24,6 @@ public class ViewExperiments : Scenario
         {
         {
             X = 0,
             X = 0,
             Y = 0,
             Y = 0,
-            AutoSelectViewToEdit = true,
             TabStop = TabBehavior.NoStop
             TabStop = TabBehavior.NoStop
         };
         };
         app.Add (editor);
         app.Add (editor);
@@ -42,31 +42,11 @@ public class ViewExperiments : Scenario
         {
         {
             X = 0,
             X = 0,
             Y = 0,
             Y = 0,
-            Title = $"TopButton _{GetNextHotKey()}",
+            Title = $"TopButton _{GetNextHotKey ()}",
         };
         };
 
 
         testFrame.Add (button);
         testFrame.Add (button);
 
 
-        var tiledView1 = CreateTiledView (0, 2, 2);
-        var tiledView2 = CreateTiledView (1, Pos.Right (tiledView1), Pos.Top (tiledView1));
-
-        testFrame.Add (tiledView1);
-        testFrame.Add (tiledView2);
-
-        var overlappedView1 = CreateOverlappedView (2, Pos.Center(), Pos.Center());
-        var tiledSubView = CreateTiledView (4, 0, 2);
-        overlappedView1.Add (tiledSubView);
-        
-        var overlappedView2 = CreateOverlappedView (3, Pos.Center() + 5, Pos.Center() + 5);
-        tiledSubView = CreateTiledView (4, 0, 2);
-        overlappedView2.Add (tiledSubView);
-
-        tiledSubView = CreateTiledView (5, 0, Pos.Bottom(tiledSubView));
-        overlappedView2.Add (tiledSubView);
-
-        testFrame.Add (overlappedView1);
-        testFrame.Add (overlappedView2);
-
         button = new ()
         button = new ()
         {
         {
             X = Pos.AnchorEnd (),
             X = Pos.AnchorEnd (),
@@ -76,9 +56,16 @@ public class ViewExperiments : Scenario
 
 
         testFrame.Add (button);
         testFrame.Add (button);
 
 
+        editor.AutoSelectViewToEdit = true;
+        editor.AutoSelectSuperView = testFrame;
+        editor.AutoSelectAdornments = true;
+
         Application.Run (app);
         Application.Run (app);
         app.Dispose ();
         app.Dispose ();
+
         Application.Shutdown ();
         Application.Shutdown ();
+
+        return;
     }
     }
 
 
     private int _hotkeyCount;
     private int _hotkeyCount;
@@ -87,71 +74,4 @@ public class ViewExperiments : Scenario
     {
     {
         return (char)((int)'A' + _hotkeyCount++);
         return (char)((int)'A' + _hotkeyCount++);
     }
     }
-
-    private View CreateTiledView (int id, Pos x, Pos y)
-    {
-        View overlapped = new View
-        {
-            X = x,
-            Y = y,
-            Height = Dim.Auto (),
-            Width = Dim.Auto (),
-            Title = $"Tiled{id} _{GetNextHotKey ()}",
-            Id = $"Tiled{id}",
-            BorderStyle = LineStyle.Single,
-            CanFocus = true, // Can't drag without this? BUGBUG
-            TabStop = TabBehavior.TabGroup,
-            Arrangement = ViewArrangement.Fixed
-        };
-
-        Button button = new ()
-        {
-            Title = $"Tiled Button{id} _{GetNextHotKey ()}"
-        };
-        overlapped.Add (button);
-
-        button = new ()
-        {
-            Y = Pos.Bottom (button),
-            Title = $"Tiled Button{id} _{GetNextHotKey ()}"
-        };
-        overlapped.Add (button);
-
-        return overlapped;
-    }
-
-
-    private View CreateOverlappedView (int id, Pos x, Pos y)
-    {
-        View overlapped = new View
-        {
-            X = x,
-            Y = y,
-            Height = Dim.Auto (),
-            Width = Dim.Auto (),
-            Title = $"Overlapped{id} _{GetNextHotKey ()}",
-            ColorScheme = Colors.ColorSchemes ["Toplevel"],
-            Id = $"Overlapped{id}",
-            ShadowStyle = ShadowStyle.Transparent,
-            BorderStyle = LineStyle.Double,
-            CanFocus = true, // Can't drag without this? BUGBUG
-            TabStop = TabBehavior.TabGroup,
-            Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped
-        };
-
-        Button button = new ()
-        {
-            Title = $"Button{id} _{GetNextHotKey ()}"
-        };
-        overlapped.Add (button);
-
-        button = new ()
-        {
-            Y = Pos.Bottom (button),
-            Title = $"Button{id} _{GetNextHotKey ()}"
-        };
-        overlapped.Add (button);
-
-        return overlapped;
-    }
 }
 }

+ 3 - 1
UICatalog/Scenarios/Wizards.cs

@@ -5,8 +5,10 @@ namespace UICatalog.Scenarios;
 
 
 [ScenarioMetadata ("Wizards", "Demonstrates the Wizard class")]
 [ScenarioMetadata ("Wizards", "Demonstrates the Wizard class")]
 [ScenarioCategory ("Dialogs")]
 [ScenarioCategory ("Dialogs")]
-[ScenarioCategory ("Top Level Windows")]
+[ScenarioCategory ("Overlapped")]
 [ScenarioCategory ("Wizards")]
 [ScenarioCategory ("Wizards")]
+[ScenarioCategory ("Runnable")]
+
 public class Wizards : Scenario
 public class Wizards : Scenario
 {
 {
     public override void Main ()
     public override void Main ()

+ 2 - 8
UICatalog/UICatalog.cs

@@ -502,7 +502,8 @@ public class UICatalogApp
                         CanFocus = false
                         CanFocus = false
                     },
                     },
                     HelpText = "",
                     HelpText = "",
-                    Key = Key.F6
+                    KeyBindingScope = KeyBindingScope.Application,
+                    Key = Key.F7
                 };
                 };
 
 
                 ((CheckBox)ShForce16Colors.CommandView).CheckedStateChanging += (sender, args) =>
                 ((CheckBox)ShForce16Colors.CommandView).CheckedStateChanging += (sender, args) =>
@@ -643,13 +644,6 @@ public class UICatalogApp
             Add (CategoryList);
             Add (CategoryList);
             Add (ScenarioList);
             Add (ScenarioList);
 
 
-            Add (MenuBar!);
-
-            if (StatusBar is { })
-            {
-                Add (StatusBar);
-            }
-
             Loaded += LoadedHandler;
             Loaded += LoadedHandler;
             Unloaded += UnloadedHandler;
             Unloaded += UnloadedHandler;
 
 

+ 111 - 124
UnitTests/Application/Application.NavigationTests.cs

@@ -1,170 +1,157 @@
-using Moq;
-using Xunit.Abstractions;
+using Xunit.Abstractions;
 
 
-namespace Terminal.Gui.ApplicationTests;
+namespace Terminal.Gui.ApplicationTests.NavigationTests;
 
 
 public class ApplicationNavigationTests (ITestOutputHelper output)
 public class ApplicationNavigationTests (ITestOutputHelper output)
 {
 {
     private readonly ITestOutputHelper _output = output;
     private readonly ITestOutputHelper _output = output;
 
 
-    [Fact]
-    public void GetDeepestFocusedSubview_ShouldReturnNull_WhenViewIsNull ()
-    {
-        // Act
-        var result = ApplicationNavigation.GetDeepestFocusedSubview (null);
-
-        // Assert
-        Assert.Null (result);
-    }
-
-    [Fact]
-    public void GetDeepestFocusedSubview_ShouldReturnSameView_WhenNoSubviewsHaveFocus ()
+    [Theory]
+    [InlineData (TabBehavior.NoStop)]
+    [InlineData (TabBehavior.TabStop)]
+    [InlineData (TabBehavior.TabGroup)]
+    public void Begin_SetsFocus_On_Deepest_Focusable_View (TabBehavior behavior)
     {
     {
-        // Arrange
-        var view = new View () { Id = "view", CanFocus = true }; ;
+        Application.Init (new FakeDriver ());
+
+        var top = new Toplevel
+        {
+            TabStop = behavior
+        };
+        Assert.False (top.HasFocus);
+
+        View subView = new ()
+        {
+            CanFocus = true,
+            TabStop = behavior
+        };
+        top.Add (subView);
+
+        View subSubView = new ()
+        {
+            CanFocus = true,
+            TabStop = TabBehavior.NoStop
+        };
+        subView.Add (subSubView);
+
+        RunState rs = Application.Begin (top);
+        Assert.True (top.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.True (subSubView.HasFocus);
 
 
-        // Act
-        var result = ApplicationNavigation.GetDeepestFocusedSubview (view);
+        top.Dispose ();
 
 
-        // Assert
-        Assert.Equal (view, result);
+        Application.Shutdown ();
     }
     }
 
 
     [Fact]
     [Fact]
-    public void GetDeepestFocusedSubview_ShouldReturnFocusedSubview ()
+    public void Begin_SetsFocus_On_Top ()
     {
     {
-        // Arrange
-        var parentView = new View () { Id = "parentView", CanFocus = true }; ;
-        var childView1 = new View () { Id = "childView1", CanFocus = true }; ;
-        var childView2 = new View () { Id = "childView2", CanFocus = true }; ;
-        var grandChildView = new View () { Id = "grandChildView", CanFocus = true }; ;
-
-        parentView.Add (childView1, childView2);
-        childView2.Add (grandChildView);
+        Application.Init (new FakeDriver ());
 
 
-        grandChildView.SetFocus ();
+        var top = new Toplevel ();
+        Assert.False (top.HasFocus);
 
 
-        // Act
-        var result = ApplicationNavigation.GetDeepestFocusedSubview (parentView);
+        RunState rs = Application.Begin (top);
+        Assert.True (top.HasFocus);
 
 
-        // Assert
-        Assert.Equal (grandChildView, result);
+        top.Dispose ();
+        Application.Shutdown ();
     }
     }
 
 
     [Fact]
     [Fact]
-    public void GetDeepestFocusedSubview_ShouldReturnDeepestFocusedSubview ()
+    public void Focused_Change_Raises_FocusedChanged ()
     {
     {
-        // Arrange
-        var parentView = new View () { Id = "parentView", CanFocus = true }; ;
-        var childView1 = new View () { Id = "childView1", CanFocus = true }; ;
-        var childView2 = new View () { Id = "childView2", CanFocus = true }; ;
-        var grandChildView = new View () { Id = "grandChildView", CanFocus = true }; ;
-        var greatGrandChildView = new View () { Id = "greatGrandChildView", CanFocus = true }; ;
+        var raised = false;
 
 
-        parentView.Add (childView1, childView2);
-        childView2.Add (grandChildView);
-        grandChildView.Add (greatGrandChildView);
+        Application.Navigation = new ();
 
 
-        grandChildView.SetFocus ();
+        Application.Navigation.FocusedChanged += ApplicationNavigationOnFocusedChanged;
 
 
-        // Act
-        var result = ApplicationNavigation.GetDeepestFocusedSubview (parentView);
+        Application.Navigation.SetFocused (new ());
 
 
-        // Assert
-        Assert.Equal (greatGrandChildView, result);
+        Assert.True (raised);
 
 
-        // Arrange
-        greatGrandChildView.CanFocus = false;
-        grandChildView.SetFocus ();
+        Application.Navigation.GetFocused ().Dispose ();
+        Application.Navigation.SetFocused (null);
 
 
-        // Act
-        result = ApplicationNavigation.GetDeepestFocusedSubview (parentView);
-
-        // Assert
-        Assert.Equal (grandChildView, result);
-    }
+        Application.Navigation.FocusedChanged -= ApplicationNavigationOnFocusedChanged;
 
 
-    [Fact]
-    public void MoveNextView_ShouldMoveFocusToNextView ()
-    {
-        // Arrange
-        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.Navigation = null;
 
 
-        // Act
-        ApplicationNavigation.MoveNextView ();
+        return;
 
 
-        // Assert
-        Assert.True (view2.HasFocus);
-
-        top.Dispose ();
+        void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e) { raised = true; }
     }
     }
 
 
     [Fact]
     [Fact]
-    public void MoveNextViewOrTop_ShouldMoveFocusToNextViewOrTop ()
+    public void GetFocused_Returns_Focused_View ()
     {
     {
-        // Arrange
-        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
-        ApplicationNavigation.MoveNextViewOrTop ();
-
-        // Assert
-        Assert.True (view2.HasFocus);
-
-        top.Dispose ();
+        Application.Navigation = new ();
+
+        Application.Current = new()
+        {
+            Id = "top",
+            CanFocus = true
+        };
+
+        var subView1 = new View
+        {
+            Id = "subView1",
+            CanFocus = true
+        };
+
+        var subView2 = new View
+        {
+            Id = "subView2",
+            CanFocus = true
+        };
+        Application.Current.Add (subView1, subView2);
+        Assert.False (Application.Current.HasFocus);
+
+        Application.Current.SetFocus ();
+        Assert.True (subView1.HasFocus);
+        Assert.Equal (subView1, Application.Navigation.GetFocused ());
+
+        Application.Navigation.AdvanceFocus (NavigationDirection.Forward, null);
+        Assert.Equal (subView2, Application.Navigation.GetFocused ());
+
+        Application.ResetState ();
     }
     }
 
 
     [Fact]
     [Fact]
-    public void MovePreviousView_ShouldMoveFocusToPreviousView ()
+    public void GetFocused_Returns_Null_If_No_Focused_View ()
     {
     {
-        // Arrange
-        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;
-        view2.SetFocus ();
+        Application.Navigation = new ();
 
 
-        // Act
-        ApplicationNavigation.MovePreviousView ();
+        Application.Current = new()
+        {
+            Id = "top",
+            CanFocus = true
+        };
 
 
-        // Assert
-        Assert.True (view1.HasFocus);
+        var subView1 = new View
+        {
+            Id = "subView1",
+            CanFocus = true
+        };
 
 
-        top.Dispose ();
-    }
+        Application.Current.Add (subView1);
+        Assert.False (Application.Current.HasFocus);
 
 
-    [Fact]
-    public void MovePreviousViewOrTop_ShouldMoveFocusToPreviousViewOrTop ()
-    {
-        // Arrange
-        var top = new Toplevel ();
-        var view1 = new View () { Id = "view1", CanFocus = true, TabStop = TabBehavior.TabGroup };
-        var view2 = new View () { Id = "view2", CanFocus = true, TabStop = TabBehavior.TabGroup };
-        top.Add (view1, view2);
-        Application.Top = top;
-        Application.Current = top;
-        view2.SetFocus ();
+        Application.Current.SetFocus ();
+        Assert.True (subView1.HasFocus);
+        Assert.Equal (subView1, Application.Navigation.GetFocused ());
 
 
-        // Act
-        ApplicationNavigation.MovePreviousViewOrTop ();
+        subView1.HasFocus = false;
+        Assert.False (subView1.HasFocus);
+        Assert.True (Application.Current.HasFocus);
+        Assert.Equal (Application.Current, Application.Navigation.GetFocused ());
 
 
-        // Assert
-        Assert.True (view1.HasFocus);
+        Application.Current.HasFocus = false;
+        Assert.False (Application.Current.HasFocus);
+        Assert.Null (Application.Navigation.GetFocused ());
 
 
-        top.Dispose ();
+        Application.ResetState ();
     }
     }
 }
 }

+ 11 - 2
UnitTests/Application/ApplicationTests.cs

@@ -463,7 +463,7 @@ public class ApplicationTests
     }
     }
 
 
     [Fact]
     [Fact]
-    public void InitWithoutTopLevelFactory_Begin_End_Cleans_Up ()
+    public void Init_WithoutTopLevelFactory_Begin_End_Cleans_Up ()
     {
     {
         // Begin will cause Run() to be called, which will call Begin(). Thus will block the tests
         // Begin will cause Run() to be called, which will call Begin(). Thus will block the tests
         // if we don't stop
         // if we don't stop
@@ -507,6 +507,15 @@ public class ApplicationTests
         Assert.Null (Application.Driver);
         Assert.Null (Application.Driver);
     }
     }
 
 
+    [Fact]
+    public void Init_NoParam_ForceDriver_Works ()
+    {
+        Application.ForceDriver = "FakeDriver";
+        Application.Init ();
+        Assert.IsType<FakeDriver> (Application.Driver);
+        Application.ResetState ();
+    }
+
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
     public void Internal_Properties_Correct ()
     public void Internal_Properties_Correct ()
@@ -955,7 +964,7 @@ public class ApplicationTests
     }
     }
 
 
     // TODO: All Toplevel layout tests should be moved to ToplevelTests.cs
     // TODO: All Toplevel layout tests should be moved to ToplevelTests.cs
-    [Fact]
+    [Fact (Skip = "#2491 - Changing focus should cause NeedsDispay = true, so bogus test?")]
     public void Run_Toplevel_With_Modal_View_Does_Not_Refresh_If_Not_Dirty ()
     public void Run_Toplevel_With_Modal_View_Does_Not_Refresh_If_Not_Dirty ()
     {
     {
         Init ();
         Init ();

+ 500 - 356
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,247 +16,11 @@ public class KeyboardTests
 #endif
 #endif
     }
     }
 
 
-
-    [Fact]
-    [AutoInitShutdown]
-    public void QuitKey_Getter_Setter ()
-    {
-        Toplevel top = new ();
-        var isQuiting = false;
-
-        top.Closing += (s, e) =>
-                       {
-                           isQuiting = true;
-                           e.Cancel = true;
-                       };
-
-        Application.Begin (top);
-        top.Running = true;
-
-        Key prevKey = Application.QuitKey;
-
-        Application.OnKeyDown (Application.QuitKey);
-        Assert.True (isQuiting);
-
-        isQuiting = false;
-        Application.OnKeyDown (Application.QuitKey);
-        Assert.True (isQuiting);
-
-        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 (Application.QuitKey);
-        Assert.True (isQuiting);
-
-        // Reset the QuitKey to avoid throws errors on another tests
-        Application.QuitKey = prevKey;
-        top.Dispose ();
-    }
-
-    [Fact]
-    public void QuitKey_Default_Is_Esc ()
-    {
-        Application.ResetState (true);
-        // Before Init
-        Assert.Equal (Key.Esc, Application.QuitKey);
-
-        Application.Init (new FakeDriver ());
-        // After Init
-        Assert.Equal (Key.Esc, Application.QuitKey);
-
-        Application.Shutdown ();
-    }
+    private readonly ITestOutputHelper _output;
 
 
     private object _timeoutLock;
     private object _timeoutLock;
 
 
-    [Fact]
-    public void QuitKey_Quits ()
-    {
-        Assert.Null (_timeoutLock);
-        _timeoutLock = new object ();
-
-        uint abortTime = 500;
-        bool initialized = false;
-        int iteration = 0;
-        bool shutdown = false;
-        object timeout = null;
-
-        Application.InitializedChanged += OnApplicationOnInitializedChanged;
-
-        Application.Init (new FakeDriver ());
-        Assert.True (initialized);
-        Assert.False (shutdown);
-
-        _output.WriteLine ("Application.Run<Toplevel> ().Dispose ()..");
-        Application.Run<Toplevel> ().Dispose ();
-        _output.WriteLine ("Back from Application.Run<Toplevel> ().Dispose ()");
-
-        Assert.True (initialized);
-        Assert.False (shutdown);
-
-        Assert.Equal (1, iteration);
-
-        Application.Shutdown ();
-
-        Application.InitializedChanged -= OnApplicationOnInitializedChanged;
-
-        lock (_timeoutLock)
-        {
-            if (timeout is { })
-            {
-                Application.RemoveTimeout (timeout);
-                timeout = null;
-            }
-        }
-
-        Assert.True (initialized);
-        Assert.True (shutdown);
-
-#if DEBUG_IDISPOSABLE
-        Assert.Empty (Responder.Instances);
-#endif
-        lock (_timeoutLock)
-        {
-            _timeoutLock = null;
-        }
-
-        return;
-
-        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);
-            }
-        }
-    }
-
-    [Fact (Skip = "Replace when new key statics are added.")]
-    public void NextTabGroupKey_PrevTabGroupKey_Tests ()
-    {
-        Application.Init (new FakeDriver ());
-
-        Toplevel top = new ();
-        var w1 = new Window ();
-        var v1 = new TextField ();
-        var v2 = new TextView ();
-        w1.Add (v1, v2);
-
-        var w2 = new Window ();
-        var v3 = new CheckBox ();
-        var v4 = new Button ();
-        w2.Add (v3, v4);
-
-        top.Add (w1, w2);
-
-        Application.Iteration += (s, a) =>
-                                 {
-                                     Assert.True (v1.HasFocus);
-
-                                     // Using default keys.
-                                     Application.OnKeyDown (Key.F6);
-                                     Assert.True (v2.HasFocus);
-                                     Application.OnKeyDown (Key.F6);
-                                     Assert.True (v3.HasFocus);
-                                     Application.OnKeyDown (Key.F6);
-                                     Assert.True (v4.HasFocus);
-                                     Application.OnKeyDown (Key.F6);
-                                     Assert.True (v1.HasFocus);
-
-                                     Application.OnKeyDown (Key.F6.WithShift);
-                                     Assert.True (v4.HasFocus);
-                                     Application.OnKeyDown (Key.F6.WithShift);
-                                     Assert.True (v3.HasFocus);
-                                     Application.OnKeyDown (Key.F6.WithShift);
-                                     Assert.True (v2.HasFocus);
-                                     Application.OnKeyDown (Key.F6.WithShift);
-                                     Assert.True (v1.HasFocus);
-                                     
-                                     // Using alternate keys.
-                                     Application.NextTabGroupKey = Key.F7;
-                                     Application.PrevTabGroupKey = Key.F8;
-
-                                     Application.OnKeyDown (Key.F7);
-                                     Assert.True (v2.HasFocus);
-                                     Application.OnKeyDown (Key.F7);
-                                     Assert.True (v3.HasFocus);
-                                     Application.OnKeyDown (Key.F7);
-                                     Assert.True (v4.HasFocus);
-                                     Application.OnKeyDown (Key.F7);
-                                     Assert.True (v1.HasFocus);
-
-                                     Application.OnKeyDown (Key.F8);
-                                     Assert.True (v4.HasFocus);
-                                     Application.OnKeyDown (Key.F8);
-                                     Assert.True (v3.HasFocus);
-                                     Application.OnKeyDown (Key.F8);
-                                     Assert.True (v2.HasFocus);
-                                     Application.OnKeyDown (Key.F8);
-                                     Assert.True (v1.HasFocus);
-
-                                     Application.RequestStop ();
-                                 };
-
-        Application.Run (top);
-
-        // Replacing the defaults keys to avoid errors on others unit tests that are using it.
-        Application.NextTabGroupKey = Key.PageDown.WithCtrl;
-        Application.PrevTabGroupKey = Key.PageUp.WithCtrl;
-        Application.QuitKey = Key.Q.WithCtrl;
-
-        Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.NextTabGroupKey.KeyCode);
-        Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.PrevTabGroupKey.KeyCode);
-        Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
-
-        top.Dispose ();
-        // Shutdown must be called to safely clean up Application if Init has been called
-        Application.Shutdown ();
-    }
-
-    [Fact]
+    [Fact (Skip = "No longer valid test.")]
     [AutoInitShutdown]
     [AutoInitShutdown]
     public void EnsuresTopOnFront_CanFocus_False_By_Keyboard ()
     public void EnsuresTopOnFront_CanFocus_False_By_Keyboard ()
     {
     {
@@ -319,7 +80,7 @@ public class KeyboardTests
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
-    [Fact]
+    [Fact (Skip = "No longer valid test.")]
     [AutoInitShutdown]
     [AutoInitShutdown]
     public void EnsuresTopOnFront_CanFocus_True_By_Keyboard ()
     public void EnsuresTopOnFront_CanFocus_True_By_Keyboard ()
     {
     {
@@ -373,97 +134,29 @@ public class KeyboardTests
     }
     }
 
 
     [Fact]
     [Fact]
-    public void KeyUp_Event ()
+    [AutoInitShutdown]
+    public void KeyBinding_Application_KeyBindings_Add_Adds ()
     {
     {
-        Application.Init (new FakeDriver ());
-
-        // Setup some fake keypresses (This)
-        var input = "Tests";
+        Application.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept);
+        Application.KeyBindings.Add (Key.B, KeyBindingScope.Application, Command.Accept);
 
 
-        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));
+        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);
+    }
 
 
-        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
-                                                                    )
-                                                );
-            }
-        }
+    [Fact]
+    [AutoInitShutdown]
+    public void KeyBinding_Application_RemoveKeyBinding_Removes ()
+    {
+        Application.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept);
 
 
-        int stackSize = FakeConsole.MockKeyPresses.Count;
+        Assert.True (Application.KeyBindings.TryGet (Key.A, out _));
 
 
-        var iterations = 0;
-
-        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 ();
-
-        top.KeyUp += (sender, args) =>
-                     {
-                         if (args.KeyCode != (KeyCode.CtrlMask | KeyCode.Q))
-                         {
-                             output += args.AsRune;
-                         }
-
-                         keyUps++;
-                     };
-
-        Application.Run (top);
-        Application.QuitKey = originalQuitKey;
-
-        // Input string should match output
-        Assert.Equal (input, output);
-
-        // # 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);
-
-        // # of key up events should match # of iterations
-        Assert.Equal (stackSize, iterations);
-
-        top.Dispose ();
-        Application.Shutdown ();
-        Assert.Null (Application.Current);
-        Assert.Null (Application.Top);
-        Assert.Null (Application.MainLoop);
-        Assert.Null (Application.Driver);
-    }
+        Application.KeyBindings.Remove (Key.A);
+        Assert.False (Application.KeyBindings.TryGet (Key.A, out _));
+    }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
@@ -483,7 +176,7 @@ public class KeyboardTests
 
 
         invoked = false;
         invoked = false;
         view.ApplicationCommand = false;
         view.ApplicationCommand = false;
-        Application.KeyBindings.Remove (KeyCode.A); 
+        Application.KeyBindings.Remove (KeyCode.A);
         Application.OnKeyDown (Key.A); // old
         Application.OnKeyDown (Key.A); // old
         Assert.False (invoked);
         Assert.False (invoked);
         Assert.False (view.ApplicationCommand);
         Assert.False (view.ApplicationCommand);
@@ -538,19 +231,6 @@ public class KeyboardTests
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
-    [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 (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);
-    }
-
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
     public void KeyBinding_View_KeyBindings_Add_Adds ()
     public void KeyBinding_View_KeyBindings_Add_Adds ()
@@ -561,7 +241,7 @@ public class KeyboardTests
         View view2 = new ();
         View view2 = new ();
         Application.KeyBindings.Add (Key.B, view2, Command.Accept);
         Application.KeyBindings.Add (Key.B, view2, Command.Accept);
 
 
-        Assert.True (Application.KeyBindings.TryGet (Key.A, out var binding));
+        Assert.True (Application.KeyBindings.TryGet (Key.A, out KeyBinding binding));
         Assert.Equal (view1, binding.BoundView);
         Assert.Equal (view1, binding.BoundView);
         Assert.True (Application.KeyBindings.TryGet (Key.B, out binding));
         Assert.True (Application.KeyBindings.TryGet (Key.B, out binding));
         Assert.Equal (view2, binding.BoundView);
         Assert.Equal (view2, binding.BoundView);
@@ -569,29 +249,493 @@ public class KeyboardTests
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void KeyBinding_Application_RemoveKeyBinding_Removes ()
+    public void KeyBinding_View_KeyBindings_RemoveKeyBinding_Removes ()
     {
     {
-        Application.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept);
+        View view1 = new ();
+        Application.KeyBindings.Add (Key.A, view1, Command.Accept);
 
 
-        Assert.True (Application.KeyBindings.TryGet (Key.A, out _));
+        View view2 = new ();
+        Application.KeyBindings.Add (Key.B, view1, Command.Accept);
 
 
-        Application.KeyBindings.Remove (Key.A);
+        Application.KeyBindings.Remove (Key.A, view1);
         Assert.False (Application.KeyBindings.TryGet (Key.A, out _));
         Assert.False (Application.KeyBindings.TryGet (Key.A, out _));
     }
     }
 
 
+    [Fact]
+    public void KeyUp_Event ()
+    {
+        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 ('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;
+
+        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 ();
+
+        top.KeyUp += (sender, args) =>
+                     {
+                         if (args.KeyCode != (KeyCode.CtrlMask | KeyCode.Q))
+                         {
+                             output += args.AsRune;
+                         }
+
+                         keyUps++;
+                     };
+
+        Application.Run (top);
+        Application.QuitKey = originalQuitKey;
+
+        // Input string should match output
+        Assert.Equal (input, output);
+
+        // # 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);
+
+        // # of key up events should match # of iterations
+        Assert.Equal (stackSize, iterations);
+
+        top.Dispose ();
+        Application.Shutdown ();
+        Assert.Null (Application.Current);
+        Assert.Null (Application.Top);
+        Assert.Null (Application.MainLoop);
+        Assert.Null (Application.Driver);
+    }
+
+    [Fact]
+    public void NextTabGroupKey_Moves_Focus_To_TabStop_In_Next_TabGroup ()
+    {
+        // Arrange
+        Application.Navigation = new ();
+        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
+        };
+
+        view1.Add (subView1);
+
+        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);
+
+        top.Add (view1, view2);
+        Application.Top = top;
+        Application.Current = top;
+        view1.SetFocus ();
+        Assert.True (view1.HasFocus);
+        Assert.True (subView1.HasFocus);
+
+        // Act
+        Application.OnKeyDown (Application.NextTabGroupKey);
+
+        // Assert
+        Assert.True (view2.HasFocus);
+        Assert.True (subView2.HasFocus);
+
+        top.Dispose ();
+        Application.Navigation = null;
+    }
+
+    [Fact]
+    public void NextTabGroupKey_PrevTabGroupKey_Tests ()
+    {
+        Application.Init (new FakeDriver ());
+
+        Toplevel top = new (); // TabGroup
+        var w1 = new Window (); // TabGroup
+        var v1 = new TextField (); // TabStop
+        var v2 = new TextView (); // TabStop
+        w1.Add (v1, v2);
+
+        var w2 = new Window (); // TabGroup
+        var v3 = new CheckBox (); // TabStop
+        var v4 = new Button (); // TabStop
+        w2.Add (v3, v4);
+
+        top.Add (w1, w2);
+
+        Application.Iteration += (s, a) =>
+                                 {
+                                     Assert.True (v1.HasFocus);
+
+                                     // Across TabGroups
+                                     Application.OnKeyDown (Key.F6);
+                                     Assert.True (v3.HasFocus);
+                                     Application.OnKeyDown (Key.F6);
+                                     Assert.True (v1.HasFocus);
+
+                                     Application.OnKeyDown (Key.F6.WithShift);
+                                     Assert.True (v3.HasFocus);
+                                     Application.OnKeyDown (Key.F6.WithShift);
+                                     Assert.True (v1.HasFocus);
+
+                                     // Restore?
+                                     Application.OnKeyDown (Key.Tab);
+                                     Assert.True (v2.HasFocus);
+
+                                     Application.OnKeyDown (Key.F6);
+                                     Assert.True (v3.HasFocus);
+
+                                     Application.OnKeyDown (Key.F6);
+                                     Assert.True (v2.HasFocus);
+
+                                     Application.RequestStop ();
+                                 };
+
+        Application.Run (top);
+
+        // Replacing the defaults keys to avoid errors on others unit tests that are using it.
+        Application.NextTabGroupKey = Key.PageDown.WithCtrl;
+        Application.PrevTabGroupKey = Key.PageUp.WithCtrl;
+        Application.QuitKey = Key.Q.WithCtrl;
+
+        Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.NextTabGroupKey.KeyCode);
+        Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.PrevTabGroupKey.KeyCode);
+        Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
+
+        top.Dispose ();
+
+        // Shutdown must be called to safely clean up Application if Init has been called
+        Application.Shutdown ();
+    }
+
+    [Fact]
+    public void NextTabKey_Moves_Focus_To_Next_TabStop ()
+    {
+        // 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 ();
+
+        // 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
+        {
+            Id = "view1",
+            CanFocus = true,
+            TabStop = TabBehavior.TabGroup
+        };
+
+        var subView1 = new View
+        {
+            Id = "subView1",
+            CanFocus = true,
+            TabStop = TabBehavior.TabStop
+        };
+
+        view1.Add (subView1);
+
+        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);
+
+        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);
+
+        top.Dispose ();
+        Application.Navigation = null;
+    }
+
+    [Fact]
+    public void PrevTabKey_Moves_Focus_To_Prev_TabStop ()
+    {
+        // 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 ();
+
+        // Act
+        Application.OnKeyDown (Application.NextTabKey);
+
+        // Assert
+        Assert.True (view2.HasFocus);
+
+        top.Dispose ();
+        Application.Navigation = null;
+    }
+
+    [Fact]
+    public void QuitKey_Default_Is_Esc ()
+    {
+        Application.ResetState (true);
+
+        // Before Init
+        Assert.Equal (Key.Esc, Application.QuitKey);
+
+        Application.Init (new FakeDriver ());
+
+        // After Init
+        Assert.Equal (Key.Esc, Application.QuitKey);
+
+        Application.Shutdown ();
+    }
+
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void KeyBinding_View_KeyBindings_RemoveKeyBinding_Removes ()
+    public void QuitKey_Getter_Setter ()
     {
     {
+        Toplevel top = new ();
+        var isQuiting = false;
 
 
-        View view1 = new ();
-        Application.KeyBindings.Add (Key.A, view1, Command.Accept);
+        top.Closing += (s, e) =>
+                       {
+                           isQuiting = true;
+                           e.Cancel = true;
+                       };
 
 
-        View view2 = new ();
-        Application.KeyBindings.Add (Key.B, view1, Command.Accept);
+        Application.Begin (top);
+        top.Running = true;
 
 
-        Application.KeyBindings.Remove (Key.A, view1);
-        Assert.False (Application.KeyBindings.TryGet (Key.A, out _));
+        Key prevKey = Application.QuitKey;
+
+        Application.OnKeyDown (Application.QuitKey);
+        Assert.True (isQuiting);
+
+        isQuiting = false;
+        Application.OnKeyDown (Application.QuitKey);
+        Assert.True (isQuiting);
+
+        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 (Application.QuitKey);
+        Assert.True (isQuiting);
+
+        // Reset the QuitKey to avoid throws errors on another tests
+        Application.QuitKey = prevKey;
+        top.Dispose ();
+    }
+
+    [Fact]
+    public void QuitKey_Quits ()
+    {
+        Assert.Null (_timeoutLock);
+        _timeoutLock = new ();
+
+        uint abortTime = 500;
+        var initialized = false;
+        var iteration = 0;
+        var shutdown = false;
+        object timeout = null;
+
+        Application.InitializedChanged += OnApplicationOnInitializedChanged;
+
+        Application.Init (new FakeDriver ());
+        Assert.True (initialized);
+        Assert.False (shutdown);
+
+        _output.WriteLine ("Application.Run<Toplevel> ().Dispose ()..");
+        Application.Run<Toplevel> ().Dispose ();
+        _output.WriteLine ("Back from Application.Run<Toplevel> ().Dispose ()");
+
+        Assert.True (initialized);
+        Assert.False (shutdown);
+
+        Assert.Equal (1, iteration);
+
+        Application.Shutdown ();
+
+        Application.InitializedChanged -= OnApplicationOnInitializedChanged;
+
+        lock (_timeoutLock)
+        {
+            if (timeout is { })
+            {
+                Application.RemoveTimeout (timeout);
+                timeout = null;
+            }
+        }
+
+        Assert.True (initialized);
+        Assert.True (shutdown);
+
+#if DEBUG_IDISPOSABLE
+        Assert.Empty (Responder.Instances);
+#endif
+        lock (_timeoutLock)
+        {
+            _timeoutLock = null;
+        }
+
+        return;
+
+        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);
+            }
+        }
     }
     }
 
 
     // Test View for testing Application key Bindings
     // Test View for testing Application key Bindings

+ 0 - 4
UnitTests/FileServices/FileDialogTests.cs

@@ -99,13 +99,9 @@ public class FileDialogTests (ITestOutputHelper output)
         string openIn = Path.Combine (Environment.CurrentDirectory, "zz");
         string openIn = Path.Combine (Environment.CurrentDirectory, "zz");
         Directory.CreateDirectory (openIn);
         Directory.CreateDirectory (openIn);
         dlg.Path = openIn + Path.DirectorySeparatorChar;
         dlg.Path = openIn + Path.DirectorySeparatorChar;
-#if BROKE_IN_2927
-        Send ('f', ConsoleKey.F, false, true, false);
-#else
         Application.OnKeyDown (Key.Tab);
         Application.OnKeyDown (Key.Tab);
         Application.OnKeyDown (Key.Tab);
         Application.OnKeyDown (Key.Tab);
         Application.OnKeyDown (Key.Tab);
         Application.OnKeyDown (Key.Tab);
-#endif
 
 
         Assert.IsType<TextField> (dlg.MostFocused);
         Assert.IsType<TextField> (dlg.MostFocused);
         var tf = (TextField)dlg.MostFocused;
         var tf = (TextField)dlg.MostFocused;

+ 2 - 2
UnitTests/Input/ResponderTests.cs

@@ -239,11 +239,11 @@ public class ResponderTests
         Assert.False (r.NewMouseLeaveEvent (new MouseEvent { Flags = MouseFlags.AllEvents }));
         Assert.False (r.NewMouseLeaveEvent (new MouseEvent { Flags = MouseFlags.AllEvents }));
 
 
         var v = new View ();
         var v = new View ();
-        Assert.False (r.OnEnter (v));
+        //Assert.False (r.OnEnter (v));
         v.Dispose ();
         v.Dispose ();
 
 
         v = new View ();
         v = new View ();
-        Assert.False (r.OnLeave (v));
+        //Assert.False (r.OnLeave (v));
         v.Dispose ();
         v.Dispose ();
 
 
         r.Dispose ();
         r.Dispose ();

+ 1 - 0
UnitTests/TestHelpers.cs

@@ -240,6 +240,7 @@ public class SetupFakeDriverAttribute : BeforeAfterTestAttribute
     public override void Before (MethodInfo methodUnderTest)
     public override void Before (MethodInfo methodUnderTest)
     {
     {
         Debug.WriteLine ($"Before: {methodUnderTest.Name}");
         Debug.WriteLine ($"Before: {methodUnderTest.Name}");
+        Application.ResetState ();
         Assert.Null (Application.Driver);
         Assert.Null (Application.Driver);
         Application.Driver = new FakeDriver { Rows = 25, Cols = 25 };
         Application.Driver = new FakeDriver { Rows = 25, Cols = 25 };
         base.Before (methodUnderTest);
         base.Before (methodUnderTest);

Some files were not shown because too many files changed in this diff