Browse Source

Merge branch 'v2_develop' into sixel-encoder-tinkering

Thomas Nind 10 months ago
parent
commit
050db7a924
100 changed files with 3636 additions and 3023 deletions
  1. 1 1
      NativeAot/Properties/launchSettings.json
  2. 3 3
      README.md
  3. 1 1
      Terminal.Gui/Application/Application.Driver.cs
  4. 9 3
      Terminal.Gui/Application/Application.Initialization.cs
  5. 59 54
      Terminal.Gui/Application/Application.Keyboard.cs
  6. 137 104
      Terminal.Gui/Application/Application.Mouse.cs
  7. 51 300
      Terminal.Gui/Application/Application.Run.cs
  8. 0 6
      Terminal.Gui/Application/Application.Screen.cs
  9. 1 43
      Terminal.Gui/Application/Application.Toplevel.cs
  10. 1 2
      Terminal.Gui/Application/Application.cs
  11. 3 1
      Terminal.Gui/Application/ApplicationNavigation.cs
  12. 0 444
      Terminal.Gui/Application/ApplicationOverlapped.cs
  13. 1 1
      Terminal.Gui/Configuration/AppScope.cs
  14. 18 5
      Terminal.Gui/Configuration/ColorJsonConverter.cs
  15. 1 1
      Terminal.Gui/Configuration/ConfigurationManager.cs
  16. 24 17
      Terminal.Gui/Configuration/RuneJsonConverter.cs
  17. 25 8
      Terminal.Gui/Configuration/SettingsScope.cs
  18. 3 1
      Terminal.Gui/Configuration/SourceGenerationContext.cs
  19. 8 1
      Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
  20. 39 39
      Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
  21. 3 3
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
  22. 2 2
      Terminal.Gui/ConsoleDrivers/NetDriver.cs
  23. 1 1
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  24. 55 53
      Terminal.Gui/Drawing/Alignment.cs
  25. 8 14
      Terminal.Gui/Drawing/AlignmentModes.cs
  26. 6 6
      Terminal.Gui/Drawing/Attribute.cs
  27. 47 44
      Terminal.Gui/Drawing/Color.ColorExtensions.cs
  28. 2 2
      Terminal.Gui/Drawing/Color.ColorName.cs
  29. 180 177
      Terminal.Gui/Drawing/Color.Formatting.cs
  30. 3 3
      Terminal.Gui/Drawing/Color.Operators.cs
  31. 40 40
      Terminal.Gui/Drawing/Color.cs
  32. 43 55
      Terminal.Gui/Drawing/ColorScheme.cs
  33. 30 27
      Terminal.Gui/Drawing/ColorStrings.cs
  34. 28 5
      Terminal.Gui/Drawing/Glyphs.cs
  35. 164 142
      Terminal.Gui/Input/Command.cs
  36. 1 1
      Terminal.Gui/Input/GrabMouseEventArgs.cs
  37. 93 47
      Terminal.Gui/Input/Key.cs
  38. 10 17
      Terminal.Gui/Input/KeyBindingScope.cs
  39. 63 0
      Terminal.Gui/Resources/Strings.Designer.cs
  40. 21 0
      Terminal.Gui/Resources/Strings.resx
  41. 324 38
      Terminal.Gui/Resources/config.json
  42. 1 0
      Terminal.Gui/Terminal.Gui.csproj
  43. 32 32
      Terminal.Gui/View/Adornment/Adornment.cs
  44. 910 172
      Terminal.Gui/View/Adornment/Border.cs
  45. 7 4
      Terminal.Gui/View/Adornment/Margin.cs
  46. 1 1
      Terminal.Gui/View/Adornment/Padding.cs
  47. 1 1
      Terminal.Gui/View/Adornment/ShadowView.cs
  48. 9 8
      Terminal.Gui/View/HighlightStyle.cs
  49. 1 1
      Terminal.Gui/View/Layout/DimAutoStyle.cs
  50. 5 7
      Terminal.Gui/View/Navigation/TabBehavior.cs
  51. 29 18
      Terminal.Gui/View/View.Adornments.cs
  52. 2 3
      Terminal.Gui/View/View.Arrangement.cs
  53. 12 0
      Terminal.Gui/View/View.Content.cs
  54. 2 3
      Terminal.Gui/View/View.Diagnostics.cs
  55. 117 63
      Terminal.Gui/View/View.Drawing.cs
  56. 11 6
      Terminal.Gui/View/View.Hierarchy.cs
  57. 7 0
      Terminal.Gui/View/View.Keyboard.cs
  58. 41 78
      Terminal.Gui/View/View.Layout.cs
  59. 362 225
      Terminal.Gui/View/View.Mouse.cs
  60. 150 56
      Terminal.Gui/View/View.Navigation.cs
  61. 15 12
      Terminal.Gui/View/ViewArrangement.cs
  62. 14 23
      Terminal.Gui/View/ViewportSettings.cs
  63. 8 5
      Terminal.Gui/Views/Button.cs
  64. 7 1
      Terminal.Gui/Views/CheckBox.cs
  65. 2 2
      Terminal.Gui/Views/ColorBar.cs
  66. 9 9
      Terminal.Gui/Views/ColorPicker16.cs
  67. 11 11
      Terminal.Gui/Views/ComboBox.cs
  68. 3 3
      Terminal.Gui/Views/DateField.cs
  69. 0 1
      Terminal.Gui/Views/DatePicker.cs
  70. 1 12
      Terminal.Gui/Views/Dialog.cs
  71. 4 4
      Terminal.Gui/Views/FileDialog.cs
  72. 13 13
      Terminal.Gui/Views/HexView.cs
  73. 12 12
      Terminal.Gui/Views/ListView.cs
  74. 1 1
      Terminal.Gui/Views/Menu/ContextMenu.cs
  75. 14 13
      Terminal.Gui/Views/Menu/Menu.cs
  76. 21 21
      Terminal.Gui/Views/Menu/MenuBar.cs
  77. 1 1
      Terminal.Gui/Views/Menu/MenuItem.cs
  78. 22 0
      Terminal.Gui/Views/Menuv2.cs
  79. 2 2
      Terminal.Gui/Views/NumericUpDown.cs
  80. 6 4
      Terminal.Gui/Views/ProgressBar.cs
  81. 12 12
      Terminal.Gui/Views/RadioGroup.cs
  82. 10 8
      Terminal.Gui/Views/ScrollView.cs
  83. 15 33
      Terminal.Gui/Views/Shortcut.cs
  84. 37 7
      Terminal.Gui/Views/Slider.cs
  85. 5 1
      Terminal.Gui/Views/Tab.cs
  86. 2 2
      Terminal.Gui/Views/TabView.cs
  87. 25 21
      Terminal.Gui/Views/TableView/TableView.cs
  88. 11 18
      Terminal.Gui/Views/TextField.cs
  89. 2 2
      Terminal.Gui/Views/TextValidateField.cs
  90. 46 53
      Terminal.Gui/Views/TextView.cs
  91. 4 5
      Terminal.Gui/Views/TileView.cs
  92. 3 3
      Terminal.Gui/Views/TimeField.cs
  93. 9 244
      Terminal.Gui/Views/Toplevel.cs
  94. 0 8
      Terminal.Gui/Views/ToplevelOverlapped.cs
  95. 23 14
      Terminal.Gui/Views/TreeView/TreeView.cs
  96. 1 1
      Terminal.Gui/Views/Window.cs
  97. 16 10
      Terminal.Gui/Views/Wizard/Wizard.cs
  98. 2 2
      UICatalog/Resources/config.json
  99. 36 37
      UICatalog/Scenario.cs
  100. 2 2
      UICatalog/Scenarios/AdornmentEditor.cs

+ 1 - 1
NativeAot/Properties/launchSettings.json

@@ -3,7 +3,7 @@
     "NativeAot": {
       "commandName": "Project"
     },
-    "WSL : UICatalog": {
+    "WSL : NativeAot": {
       "commandName": "Executable",
       "executablePath": "wsl",
       "commandLineArgs": "dotnet NativeAot.dll",

+ 3 - 3
README.md

@@ -8,7 +8,7 @@
 
 * The current, stable, release of Terminal.Gui v1 is [![Version](https://img.shields.io/nuget/v/Terminal.Gui.svg)](https://www.nuget.org/packages/Terminal.Gui).
 * The current `prealpha` release of Terminal.Gui v2 can be found on [Nuget](https://www.nuget.org/packages/Terminal.Gui).
-* Developers starting new TUI projects are encouraged to target `v2`. The API is signifcantly changed, and significantly improved. There will be breaking changes in the API before Beta, but the core API is stable.
+* Developers starting new TUI projects are encouraged to target `v2`. The API is significantly changed, and significantly improved. There will be breaking changes in the API before Beta, but the core API is stable.
 * `v1` is in maintenance mode and we will only accept PRs for issues impacting existing functionality.
  
 **Terminal.Gui**: A toolkit for building rich console apps for .NET, .NET Core, and Mono that works on Windows, the Mac, and Linux/Unix.
@@ -35,9 +35,9 @@ dotnet run
 * [API Documentation](https://gui-cs.github.io/Terminal.GuiV2Docs/api/Terminal.Gui.html)
 * [Documentation Home](https://gui-cs.github.io/Terminal.GuiV2Docs)
 
-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)
+The above documentation matches the most recent Nuget release from the `v2_develop` branch. Get the [v1 documentation 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. 
 
 ## Showcase & Examples
 

+ 1 - 1
Terminal.Gui/Application/Application.Driver.cs

@@ -10,7 +10,7 @@ public static partial class Application // Driver abstractions
 
     /// <summary>
     ///     Gets or sets whether <see cref="Application.Driver"/> will be forced to output only the 16 colors defined in
-    ///     <see cref="ColorName"/>. The default is <see langword="false"/>, meaning 24-bit (TrueColor) colors will be output
+    ///     <see cref="ColorName16"/>. The default is <see langword="false"/>, meaning 24-bit (TrueColor) colors will be output
     ///     as long as the selected <see cref="ConsoleDriver"/> supports TrueColor.
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]

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

@@ -71,7 +71,7 @@ public static partial class Application // Initialization (Init/Shutdown)
         if (!calledViaRunT)
         {
             // Reset all class variables (Application is a singleton).
-            ResetState ();
+            ResetState (ignoreDisposed: true);
         }
 
         Navigation = new ();
@@ -198,10 +198,16 @@ public static partial class Application // Initialization (Init/Shutdown)
     public static void Shutdown ()
     {
         // TODO: Throw an exception if Init hasn't been called.
+
+        bool wasInitialized = IsInitialized;
         ResetState ();
         PrintJsonErrors ();
-        bool init = IsInitialized;
-        InitializedChanged?.Invoke (null, new (in init));
+
+        if (wasInitialized)
+        {
+            bool init = IsInitialized;
+            InitializedChanged?.Invoke (null, new (in init));
+        }
     }
 
     /// <summary>

+ 59 - 54
Terminal.Gui/Application/Application.Keyboard.cs

@@ -5,12 +5,10 @@ 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 _prevTabGroupKey = Key.F6.WithShift; // Resources/config.json overrrides
-
     private static Key _prevTabKey = Key.Tab.WithShift; // Resources/config.json overrrides
-
     private static Key _quitKey = Key.Esc; // Resources/config.json overrrides
+    private static Key _arrangeKey = Key.F5.WithCtrl; // Resources/config.json overrrides
 
     static Application () { AddApplicationKeyBindings (); }
 
@@ -97,7 +95,7 @@ public static partial class Application // Keyboard handling
             return true;
         }
 
-        if (Current is null)
+        if (Top is null)
         {
             foreach (Toplevel topLevel in TopLevels.ToList ())
             {
@@ -114,7 +112,7 @@ public static partial class Application // Keyboard handling
         }
         else
         {
-            if (Current.NewKeyDownEvent (keyEvent))
+            if (Top.NewKeyDownEvent (keyEvent))
             {
                 return true;
             }
@@ -155,7 +153,7 @@ public static partial class Application // Keyboard handling
     }
 
     /// <summary>
-    /// INTENRAL method to invoke one of the commands in <see cref="CommandImplementations"/>
+    ///     INTENRAL method to invoke one of the commands in <see cref="CommandImplementations"/>
     /// </summary>
     /// <param name="command"></param>
     /// <param name="keyEvent"></param>
@@ -174,6 +172,7 @@ public static partial class Application // Keyboard handling
         if (CommandImplementations.TryGetValue (command, out Func<CommandContext, bool?>? implementation))
         {
             var context = new CommandContext (command, keyEvent, appBinding); // Create the context here
+
             return implementation (context);
         }
 
@@ -262,24 +261,31 @@ public static partial class Application // Keyboard handling
         }
     }
 
+    /// <summary>Gets or sets the key to activate arranging views using the keyboard.</summary>
+    [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
+    public static Key ArrangeKey
+    {
+        get => _arrangeKey;
+        set
+        {
+            if (_arrangeKey != value)
+            {
+                ReplaceKey (_arrangeKey, value);
+                _arrangeKey = value;
+            }
+        }
+    }
+
     internal static void AddApplicationKeyBindings ()
     {
         CommandImplementations = new ();
 
         // Things this view knows how to do
         AddCommand (
-                    Command.QuitToplevel, // TODO: IRunnable: Rename to Command.Quit to make more generic.
+                    Command.Quit,
                     static () =>
                     {
-                        if (ApplicationOverlapped.OverlappedTop is { })
-                        {
-                            RequestStop (Current!);
-                        }
-                        else
-                        {
-                            RequestStop ();
-                        }
-
+                        RequestStop ();
                         return true;
                     }
                    );
@@ -295,54 +301,50 @@ public static partial class Application // Keyboard handling
                    );
 
         AddCommand (
-                    Command.NextView,
+                    Command.NextTabStop,
                     static () => Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop));
 
         AddCommand (
-                    Command.PreviousView,
+                    Command.PreviousTabStop,
                     static () => Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop));
 
         AddCommand (
-                    Command.NextViewOrTop,
-                    static () =>
-                    {
-                        // TODO: This OverlapppedTop tomfoolery goes away in addressing #2491
-                        if (ApplicationOverlapped.OverlappedTop is { })
-                        {
-                            ApplicationOverlapped.OverlappedMoveNext ();
-
-                            return true;
-                        }
+                    Command.NextTabGroup,
+                    static () => Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup));
 
-                        return Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
-                    }
-                   );
+        AddCommand (
+                    Command.PreviousTabGroup,
+                    static () => Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup));
 
         AddCommand (
-                    Command.PreviousViewOrTop,
+                    Command.Refresh,
                     static () =>
                     {
-                        // TODO: This OverlapppedTop tomfoolery goes away in addressing #2491
-                        if (ApplicationOverlapped.OverlappedTop is { })
-                        {
-                            ApplicationOverlapped.OverlappedMovePrevious ();
-
-                            return true;
-                        }
+                        Refresh ();
 
-                        return Navigation?.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
+                        return true;
                     }
                    );
 
         AddCommand (
-                    Command.Refresh,
+                    Command.Edit,
                     static () =>
                     {
-                        Refresh ();
+                        View? viewToArrange = Navigation?.GetFocused ();
 
-                        return true;
-                    }
-                   );
+                        // Go up the superview hierarchy and find the first that is not ViewArrangement.Fixed
+                        while (viewToArrange is { SuperView: { }, Arrangement: ViewArrangement.Fixed })
+                        {
+                            viewToArrange = viewToArrange.SuperView;
+                        }
+
+                        if (viewToArrange is { })
+                        {
+                            return viewToArrange.Border?.EnterArrangeMode (ViewArrangement.Fixed);
+                        }
+
+                        return false;
+                    });
 
         KeyBindings.Clear ();
 
@@ -352,18 +354,21 @@ public static partial class Application // Keyboard handling
         NextTabGroupKey = Key.F6;
         PrevTabGroupKey = Key.F6.WithShift;
         QuitKey = Key.Esc;
+        ArrangeKey = Key.F5.WithCtrl;
+
+        KeyBindings.Add (QuitKey, KeyBindingScope.Application, Command.Quit);
 
-        KeyBindings.Add (QuitKey, KeyBindingScope.Application, Command.QuitToplevel);
+        KeyBindings.Add (Key.CursorRight, KeyBindingScope.Application, Command.NextTabStop);
+        KeyBindings.Add (Key.CursorDown, KeyBindingScope.Application, Command.NextTabStop);
+        KeyBindings.Add (Key.CursorLeft, KeyBindingScope.Application, Command.PreviousTabStop);
+        KeyBindings.Add (Key.CursorUp, KeyBindingScope.Application, Command.PreviousTabStop);
+        KeyBindings.Add (NextTabKey, KeyBindingScope.Application, Command.NextTabStop);
+        KeyBindings.Add (PrevTabKey, KeyBindingScope.Application, Command.PreviousTabStop);
 
-        KeyBindings.Add (Key.CursorRight, KeyBindingScope.Application, Command.NextView);
-        KeyBindings.Add (Key.CursorDown, KeyBindingScope.Application, Command.NextView);
-        KeyBindings.Add (Key.CursorLeft, KeyBindingScope.Application, Command.PreviousView);
-        KeyBindings.Add (Key.CursorUp, KeyBindingScope.Application, Command.PreviousView);
-        KeyBindings.Add (NextTabKey, KeyBindingScope.Application, Command.NextView);
-        KeyBindings.Add (PrevTabKey, KeyBindingScope.Application, Command.PreviousView);
+        KeyBindings.Add (NextTabGroupKey, KeyBindingScope.Application, Command.NextTabGroup);
+        KeyBindings.Add (PrevTabGroupKey, KeyBindingScope.Application, Command.PreviousTabGroup);
 
-        KeyBindings.Add (NextTabGroupKey, KeyBindingScope.Application, Command.NextViewOrTop);
-        KeyBindings.Add (PrevTabGroupKey, KeyBindingScope.Application, Command.PreviousViewOrTop);
+        KeyBindings.Add (ArrangeKey, KeyBindingScope.Application, Command.Edit);
 
         // TODO: Refresh Key should be configurable
         KeyBindings.Add (Key.F5, KeyBindingScope.Application, Command.Refresh);

+ 137 - 104
Terminal.Gui/Application/Application.Mouse.cs

@@ -1,10 +1,11 @@
 #nullable enable
+using System.ComponentModel;
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 
 public static partial class Application // Mouse handling
 {
-    #region Mouse handling
-
     /// <summary>Disable or enable the mouse. The mouse is enabled by default.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
     public static bool IsMouseDisabled { get; set; }
@@ -116,9 +117,6 @@ public static partial class Application // Mouse handling
         UnGrabbedMouse?.Invoke (view, new (view));
     }
 
-    // Used by OnMouseEvent to track the last view that was clicked on.
-    internal static View? MouseEnteredView { get; set; }
-
     /// <summary>Event fired when a mouse move or click occurs. Coordinates are screen relative.</summary>
     /// <remarks>
     ///     <para>
@@ -129,7 +127,7 @@ public static partial class Application // Mouse handling
     /// </remarks>
     public static event EventHandler<MouseEvent>? MouseEvent;
 
-    /// <summary>Called when a mouse event occurs. Raises the <see cref="MouseEvent"/> event.</summary>
+    /// <summary>Called when a mouse event is raised by the driver.</summary>
     /// <remarks>This method can be used to simulate a mouse event, e.g. in unit tests.</remarks>
     /// <param name="mouseEvent">The mouse event with coordinates relative to the screen.</param>
     internal static void OnMouseEvent (MouseEvent mouseEvent)
@@ -139,17 +137,19 @@ public static partial class Application // Mouse handling
             return;
         }
 
-        var view = View.FindDeepestView (Current, mouseEvent.Position);
+        List<View?> currentViewsUnderMouse = View.GetViewsUnderMouse (mouseEvent.Position);
+
+        View? deepestViewUnderMouse = currentViewsUnderMouse.LastOrDefault ();
 
-        if (view is { })
+        if (deepestViewUnderMouse is { })
         {
 #if DEBUG_IDISPOSABLE
-            if (view.WasDisposed)
+            if (deepestViewUnderMouse.WasDisposed)
             {
-                throw new ObjectDisposedException (view.GetType ().FullName);
+                throw new ObjectDisposedException (deepestViewUnderMouse.GetType ().FullName);
             }
 #endif
-            mouseEvent.View = view;
+            mouseEvent.View = deepestViewUnderMouse;
         }
 
         MouseEvent?.Invoke (null, mouseEvent);
@@ -159,82 +159,31 @@ public static partial class Application // Mouse handling
             return;
         }
 
-        if (MouseGrabView is { })
+        if (GrabMouse (deepestViewUnderMouse, mouseEvent))
         {
-
-#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.
-            // The coordinates are relative to the Bounds of the view that grabbed the mouse.
-            Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.Position);
-
-            var viewRelativeMouseEvent = new MouseEvent
-            {
-                Position = frameLoc,
-                Flags = mouseEvent.Flags,
-                ScreenPosition = mouseEvent.Position,
-                View = view ?? MouseGrabView
-            };
-
-            if ((MouseGrabView.Viewport with { Location = Point.Empty }).Contains (viewRelativeMouseEvent.Position) is false)
-            {
-                // The mouse has moved outside the bounds of the view that grabbed the mouse
-                MouseGrabView.NewMouseLeaveEvent (mouseEvent);
-            }
-
-            //System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
-            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;
         }
 
         // We can combine this into the switch expression to reduce cognitive complexity even more and likely
         // avoid one or two of these checks in the process, as well.
-        WantContinuousButtonPressedView = view switch
-                                          {
-                                              { WantContinuousButtonPressed: true } => view,
-                                              _                                     => null
-                                          };
-
-        if (view is not Adornment
-         && (view is null || view == ApplicationOverlapped.OverlappedTop)
-         && Current is { Modal: false }
-         && ApplicationOverlapped.OverlappedTop != null
-         && mouseEvent.Flags is not MouseFlags.ReportMousePosition and not 0)
-        {
-            // This occurs when there are multiple overlapped "tops"
-            // E.g. "Mdi" - in the Background Worker Scenario
-            View? top = ApplicationOverlapped.FindDeepestTop (Top!, mouseEvent.Position);
-            view = View.FindDeepestView (top, mouseEvent.Position);
 
-            if (view is { } && view != ApplicationOverlapped.OverlappedTop && top != Current && top is { })
-            {
-                ApplicationOverlapped.MoveCurrent ((Toplevel)top);
-            }
-        }
+        WantContinuousButtonPressedView = deepestViewUnderMouse switch
+        {
+            { WantContinuousButtonPressed: true } => deepestViewUnderMouse,
+            _ => null
+        };
 
         // May be null before the prior condition or the condition may set it as null.
         // So, the checking must be outside the prior condition.
-        if (view is null)
+        if (deepestViewUnderMouse is null)
         {
             return;
         }
 
+        // TODO: Move this after call to RaiseMouseEnterLeaveEvents once MouseEnter/Leave don't use MouseEvent anymore.
         MouseEvent? me;
 
-        if (view is Adornment adornment)
+        if (deepestViewUnderMouse is Adornment adornment)
         {
             Point frameLoc = adornment.ScreenToFrame (mouseEvent.Position);
 
@@ -243,76 +192,160 @@ public static partial class Application // Mouse handling
                 Position = frameLoc,
                 Flags = mouseEvent.Flags,
                 ScreenPosition = mouseEvent.Position,
-                View = view
+                View = deepestViewUnderMouse
             };
         }
-        else if (view.ViewportToScreen (Rectangle.Empty with { Size = view.Viewport.Size }).Contains (mouseEvent.Position))
+        else if (deepestViewUnderMouse.ViewportToScreen (Rectangle.Empty with { Size = deepestViewUnderMouse.Viewport.Size }).Contains (mouseEvent.Position))
         {
-            Point viewportLocation = view.ScreenToViewport (mouseEvent.Position);
+            Point viewportLocation = deepestViewUnderMouse.ScreenToViewport (mouseEvent.Position);
 
             me = new ()
             {
                 Position = viewportLocation,
                 Flags = mouseEvent.Flags,
                 ScreenPosition = mouseEvent.Position,
-                View = view
+                View = deepestViewUnderMouse
             };
         }
         else
         {
+            Debug.Fail ("This should never happen");
             return;
         }
 
-        if (MouseEnteredView is null)
-        {
-            MouseEnteredView = view;
-            view.NewMouseEnterEvent (me);
-        }
-        else if (MouseEnteredView != view)
-        {
-            MouseEnteredView.NewMouseLeaveEvent (me);
-            view.NewMouseEnterEvent (me);
-            MouseEnteredView = view;
-        }
-
-        if (!view.WantMousePositionReports && mouseEvent.Flags == MouseFlags.ReportMousePosition)
-        {
-            return;
-        }
-
-        WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
+        RaiseMouseEnterLeaveEvents (me.ScreenPosition, currentViewsUnderMouse);
 
-        //Debug.WriteLine ($"OnMouseEvent: ({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags}");
+        WantContinuousButtonPressedView = deepestViewUnderMouse.WantContinuousButtonPressed ? deepestViewUnderMouse : null;
 
-        while (view.NewMouseEvent (me) is not true && MouseGrabView is not { })
+        while (deepestViewUnderMouse.NewMouseEvent (me) is not true && MouseGrabView is not { })
         {
-            if (view is Adornment adornmentView)
+            if (deepestViewUnderMouse is Adornment adornmentView)
             {
-                view = adornmentView.Parent!.SuperView;
+                deepestViewUnderMouse = adornmentView.Parent!.SuperView;
             }
             else
             {
-                view = view.SuperView;
+                deepestViewUnderMouse = deepestViewUnderMouse.SuperView;
             }
 
-            if (view is null)
+            if (deepestViewUnderMouse is null)
             {
                 break;
             }
 
-            Point boundsPoint = view.ScreenToViewport (mouseEvent.Position);
+            Point boundsPoint = deepestViewUnderMouse.ScreenToViewport (mouseEvent.Position);
 
             me = new ()
             {
                 Position = boundsPoint,
                 Flags = mouseEvent.Flags,
                 ScreenPosition = mouseEvent.Position,
-                View = view
+                View = deepestViewUnderMouse
             };
         }
+    }
 
-        ApplicationOverlapped.BringOverlappedTopToFront ();
+    internal static bool GrabMouse (View? deepestViewUnderMouse, MouseEvent mouseEvent)
+    {
+        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.
+            // The coordinates are relative to the Bounds of the view that grabbed the mouse.
+            Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.Position);
+
+            var viewRelativeMouseEvent = new MouseEvent
+            {
+                Position = frameLoc,
+                Flags = mouseEvent.Flags,
+                ScreenPosition = mouseEvent.Position,
+                View = deepestViewUnderMouse ?? MouseGrabView
+            };
+
+            //System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
+            if (MouseGrabView?.NewMouseEvent (viewRelativeMouseEvent) is true)
+            {
+                return true;
+            }
+
+            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+            if (MouseGrabView is null && deepestViewUnderMouse is Adornment)
+            {
+                // The view that grabbed the mouse has been disposed
+                return true;
+            }
+        }
+
+        return false;
     }
 
-    #endregion Mouse handling
+    internal static readonly List<View?> _cachedViewsUnderMouse = new ();
+
+    // TODO: Refactor MouseEnter/LeaveEvents to not take MouseEvent param.
+    /// <summary>
+    ///     INTERNAL: Raises the MouseEnter and MouseLeave events for the views that are under the mouse.
+    /// </summary>
+    /// <param name="screenPosition">The position of the mouse.</param>
+    /// <param name="currentViewsUnderMouse">The most recent result from GetViewsUnderMouse().</param>
+    internal static void RaiseMouseEnterLeaveEvents (Point screenPosition, List<View?> currentViewsUnderMouse)
+    {
+        // Tell any views that are no longer under the mouse that the mouse has left
+        List<View?> viewsToLeave = _cachedViewsUnderMouse.Where (v => v is { } && !currentViewsUnderMouse.Contains (v)).ToList ();
+        foreach (View? view in viewsToLeave)
+        {
+            if (view is null)
+            {
+                continue;
+            }
+
+            view.NewMouseLeaveEvent ();
+            _cachedViewsUnderMouse.Remove (view);
+        }
+
+        // Tell any views that are now under the mouse that the mouse has entered and add them to the list
+        foreach (View? view in currentViewsUnderMouse)
+        {
+            if (view is null)
+            {
+                continue;
+            }
+
+            if (_cachedViewsUnderMouse.Contains (view))
+            {
+                continue;
+            }
+
+            _cachedViewsUnderMouse.Add (view);
+            bool raise = false;
+            if (view is Adornment { Parent: { } } adornmentView)
+            {
+                Point superViewLoc = adornmentView.Parent.SuperView?.ScreenToViewport (screenPosition) ?? screenPosition;
+                raise = adornmentView.Contains (superViewLoc);
+            }
+            else
+            {
+                Point superViewLoc = view.SuperView?.ScreenToViewport (screenPosition) ?? screenPosition;
+                raise = view.Contains (superViewLoc);
+            }
+
+            if (!raise)
+            {
+                continue;
+            }
+
+            CancelEventArgs eventArgs = new ();
+            bool? cancelled = view.NewMouseEnterEvent (eventArgs);
+
+            if (cancelled is true || eventArgs.Cancel)
+            {
+                break;
+            }
+        }
+    }
 }

+ 51 - 300
Terminal.Gui/Application/Application.Run.cs

@@ -54,11 +54,6 @@ public static partial class Application // Run (Begin, Run, End, Stop)
         }
 #endif
 
-        if (toplevel.IsOverlappedContainer && ApplicationOverlapped.OverlappedTop != toplevel && ApplicationOverlapped.OverlappedTop is { })
-        {
-            throw new InvalidOperationException ("Only one Overlapped Container is allowed.");
-        }
-
         // Ensure the mouse is ungrabbed.
         MouseGrabView = null;
 
@@ -96,11 +91,6 @@ public static partial class Application // Run (Begin, Run, End, Stop)
                     throw new ObjectDisposedException (Top.GetType ().FullName);
                 }
             }
-            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.
-                //Top!.OnLeave (toplevel);
-            }
 
             // BUGBUG: We should not depend on `Id` internally.
             // BUGBUG: It is super unclear what this code does anyway.
@@ -135,79 +125,46 @@ public static partial class Application // Run (Begin, Run, End, Stop)
             }
         }
 
-        if (Top is null || toplevel.IsOverlappedContainer)
+        if (Top is null)
         {
             Top = toplevel;
         }
 
-        var refreshDriver = true;
-
-        if (ApplicationOverlapped.OverlappedTop is null
-            || toplevel.IsOverlappedContainer
-            || (Current?.Modal == false && toplevel.Modal)
-            || (Current?.Modal == false && !toplevel.Modal)
-            || (Current?.Modal == true && toplevel.Modal))
+        if ((Top?.Modal == false && toplevel.Modal)
+            || (Top?.Modal == false && !toplevel.Modal)
+            || (Top?.Modal == true && toplevel.Modal))
         {
             if (toplevel.Visible)
             {
-                if (Current is { HasFocus: true })
+                if (Top is { HasFocus: true })
                 {
-                    Current.HasFocus = false;
+                    Top.HasFocus = false;
                 }
 
-                Current?.OnDeactivate (toplevel);
-                Toplevel previousCurrent = Current!;
+                Top?.OnDeactivate (toplevel);
+                Toplevel previousCurrent = Top!;
 
-                Current = toplevel;
-                Current.OnActivate (previousCurrent);
-
-                ApplicationOverlapped.SetCurrentOverlappedAsTop ();
+                Top = toplevel;
+                Top.OnActivate (previousCurrent);
             }
-            else
-            {
-                refreshDriver = false;
-            }
-        }
-        else if ((toplevel != ApplicationOverlapped.OverlappedTop
-                  && Current?.Modal == true
-                  && !TopLevels.Peek ().Modal)
-                 || (toplevel != ApplicationOverlapped.OverlappedTop && Current?.Running == false))
-        {
-            refreshDriver = false;
-            ApplicationOverlapped.MoveCurrent (toplevel);
-        }
-        else
-        {
-            refreshDriver = false;
-            ApplicationOverlapped.MoveCurrent (Current!);
         }
 
         toplevel.SetRelativeLayout (Driver!.Screen.Size);
-
         toplevel.LayoutSubviews ();
-        toplevel.PositionToplevels ();
 
-        // TODO: Should this use FindDeepestFocusableView instead?
         // Try to set initial focus to any TabStop
         if (!toplevel.HasFocus)
         {
             toplevel.SetFocus ();
         }
 
-        ApplicationOverlapped.BringOverlappedTopToFront ();
+        toplevel.OnLoaded ();
 
-        if (refreshDriver)
-        {
-            ApplicationOverlapped.OverlappedTop?.OnChildLoaded (toplevel);
-            toplevel.OnLoaded ();
-            toplevel.SetNeedsDisplay ();
-            toplevel.Draw ();
-            Driver.UpdateScreen ();
+        Refresh ();
 
-            if (PositionCursor (toplevel))
-            {
-                Driver.UpdateCursor ();
-            }
+        if (PositionCursor ())
+        {
+            Driver.UpdateCursor ();
         }
 
         NotifyNewRunState?.Invoke (toplevel, new (rs));
@@ -216,35 +173,22 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     }
 
     /// <summary>
-    ///     Calls <see cref="View.PositionCursor"/> on the most focused view in the view starting with <paramref name="view"/>.
+    ///     Calls <see cref="View.PositionCursor"/> on the most focused view.
     /// </summary>
     /// <remarks>
-    ///     Does nothing if <paramref name="view"/> is <see langword="null"/> or if the most focused view is not visible or
-    ///     enabled.
+    ///     Does nothing if there is no most focused view.
     ///     <para>
     ///         If the most focused view is not visible within it's superview, the cursor will be hidden.
     ///     </para>
     /// </remarks>
     /// <returns><see langword="true"/> if a view positioned the cursor and the position is visible.</returns>
-    internal static bool PositionCursor (View view)
+    internal static bool PositionCursor ()
     {
         // Find the most focused view and position the cursor there.
-        View? mostFocused = view?.MostFocused;
-
-        if (mostFocused is null)
-        {
-            if (view is { HasFocus: true })
-            {
-                mostFocused = view;
-            }
-            else
-            {
-                return false;
-            }
-        }
+        View? mostFocused = Navigation?.GetFocused ();
 
         // If the view is not visible or enabled, don't position the cursor
-        if (!mostFocused.Visible || !mostFocused.Enabled)
+        if (mostFocused is null || !mostFocused.Visible || !mostFocused.Enabled)
         {
             Driver!.GetCursorVisibility (out CursorVisibility current);
 
@@ -510,20 +454,17 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     /// <summary>Triggers a refresh of the entire display.</summary>
     public static void Refresh ()
     {
-        // TODO: Figure out how to remove this call to ClearContents. Refresh should just repaint damaged areas, not clear
-        Driver!.ClearContents ();
-
-        foreach (Toplevel v in TopLevels.Reverse ())
+        foreach (Toplevel tl in TopLevels.Reverse ())
         {
-            if (v.Visible)
+            if (tl.LayoutNeeded)
             {
-                v.SetNeedsDisplay ();
-                v.SetSubViewNeedsDisplay ();
-                v.Draw ();
+                tl.LayoutSubviews ();
             }
+
+            tl.Draw ();
         }
 
-        Driver.Refresh ();
+        Driver!.Refresh ();
     }
 
     /// <summary>This event is raised on each iteration of the main loop.</summary>
@@ -586,80 +527,22 @@ public static partial class Application // Run (Begin, Run, End, Stop)
 
             MainLoop.RunIteration ();
             Iteration?.Invoke (null, new ());
-            EnsureModalOrVisibleAlwaysOnTop (state.Toplevel);
-
-            // TODO: Overlapped - Move elsewhere
-            if (state.Toplevel != Current)
-            {
-                ApplicationOverlapped.OverlappedTop?.OnDeactivate (state.Toplevel);
-                state.Toplevel = Current;
-                ApplicationOverlapped.OverlappedTop?.OnActivate (state.Toplevel!);
-                Top!.SetSubViewNeedsDisplay ();
-                Refresh ();
-            }
         }
 
         firstIteration = false;
 
-        if (Current == null)
+        if (Top is null)
         {
             return;
         }
 
-        if (state.Toplevel != Top && (Top!.NeedsDisplay || Top.SubViewNeedsDisplay || Top.LayoutNeeded))
-        {
-            state.Toplevel!.SetNeedsDisplay (state.Toplevel.Frame);
-            Top.Draw ();
+        Refresh ();
 
-            foreach (Toplevel top in TopLevels.Reverse ())
-            {
-                if (top != Top && top != state.Toplevel)
-                {
-                    top.SetNeedsDisplay ();
-                    top.SetSubViewNeedsDisplay ();
-                    top.Draw ();
-                }
-            }
-        }
-
-        if (TopLevels.Count == 1
-            && state.Toplevel == Top
-            && (Driver!.Cols != state.Toplevel!.Frame.Width
-                || Driver!.Rows != state.Toplevel.Frame.Height)
-            && (state.Toplevel.NeedsDisplay
-                || state.Toplevel.SubViewNeedsDisplay
-                || state.Toplevel.LayoutNeeded))
-        {
-            Driver.ClearContents ();
-        }
-
-        if (state.Toplevel!.NeedsDisplay || state.Toplevel.SubViewNeedsDisplay || state.Toplevel.LayoutNeeded || ApplicationOverlapped.OverlappedChildNeedsDisplay ())
-        {
-            state.Toplevel.SetNeedsDisplay ();
-            state.Toplevel.Draw ();
-            Driver!.UpdateScreen ();
-
-            //Driver.UpdateCursor ();
-        }
-
-        if (PositionCursor (state.Toplevel))
+        if (PositionCursor ())
         {
             Driver!.UpdateCursor ();
         }
 
-        //        else
-        {
-            //if (PositionCursor (state.Toplevel))
-            //{
-            //    Driver.Refresh ();
-            //}
-            //Driver.UpdateCursor ();
-        }
-
-        if (state.Toplevel != Top && !state.Toplevel.Modal && (Top!.NeedsDisplay || Top.SubViewNeedsDisplay || Top.LayoutNeeded))
-        {
-            Top.Draw ();
-        }
     }
 
     /// <summary>Stops the provided <see cref="Toplevel"/>, causing or the <paramref name="top"/> if provided.</summary>
@@ -673,112 +556,26 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     /// </remarks>
     public static void RequestStop (Toplevel? top = null)
     {
-        if (ApplicationOverlapped.OverlappedTop is null || top is null)
+        if (top is null)
         {
-            top = Current;
+            top = Top;
         }
 
-        if (ApplicationOverlapped.OverlappedTop != null
-            && top!.IsOverlappedContainer
-            && top?.Running == true
-            && (Current?.Modal == false || Current is { Modal: true, Running: false }))
+        if (!top!.Running)
         {
-            ApplicationOverlapped.OverlappedTop.RequestStop ();
+            return;
         }
-        else if (ApplicationOverlapped.OverlappedTop != null
-                 && top != Current
-                 && Current is { Running: true, Modal: true }
-                 && top!.Modal
-                 && top.Running)
-        {
-            var ev = new ToplevelClosingEventArgs (Current);
-            Current.OnClosing (ev);
-
-            if (ev.Cancel)
-            {
-                return;
-            }
 
-            ev = new (top);
-            top.OnClosing (ev);
+        var ev = new ToplevelClosingEventArgs (top);
+        top.OnClosing (ev);
 
-            if (ev.Cancel)
-            {
-                return;
-            }
-
-            Current.Running = false;
-            OnNotifyStopRunState (Current);
-            top.Running = false;
-            OnNotifyStopRunState (top);
-        }
-        else if ((ApplicationOverlapped.OverlappedTop != null
-                  && top != ApplicationOverlapped.OverlappedTop
-                  && top != Current
-                  && Current is { Modal: false, Running: true }
-                  && !top!.Running)
-                 || (ApplicationOverlapped.OverlappedTop != null
-                     && top != ApplicationOverlapped.OverlappedTop
-                     && top != Current
-                     && Current is { Modal: false, Running: false }
-                     && !top!.Running
-                     && TopLevels.ToArray () [1].Running))
-        {
-            ApplicationOverlapped.MoveCurrent (top);
-        }
-        else if (ApplicationOverlapped.OverlappedTop != null
-                 && Current != top
-                 && Current?.Running == true
-                 && !top!.Running
-                 && Current?.Modal == true
-                 && top.Modal)
+        if (ev.Cancel)
         {
-            // The Current and the top are both modal so needed to set the Current.Running to false too.
-            Current.Running = false;
-            OnNotifyStopRunState (Current);
-        }
-        else if (ApplicationOverlapped.OverlappedTop != null
-                 && Current == top
-                 && ApplicationOverlapped.OverlappedTop?.Running == true
-                 && Current?.Running == true
-                 && top!.Running
-                 && Current?.Modal == true
-                 && top!.Modal)
-        {
-            // The OverlappedTop was requested to stop inside a modal Toplevel which is the Current and top,
-            // both are the same, so needed to set the Current.Running to false too.
-            Current.Running = false;
-            OnNotifyStopRunState (Current);
+            return;
         }
-        else
-        {
-            Toplevel currentTop;
-
-            if (top == Current || (Current?.Modal == true && !top!.Modal))
-            {
-                currentTop = Current!;
-            }
-            else
-            {
-                currentTop = top!;
-            }
-
-            if (!currentTop.Running)
-            {
-                return;
-            }
-
-            var ev = new ToplevelClosingEventArgs (currentTop);
-            currentTop.OnClosing (ev);
 
-            if (ev.Cancel)
-            {
-                return;
-            }
-
-            currentTop.Running = false;
-            OnNotifyStopRunState (currentTop);
-        }
+        top.Running = false;
+        OnNotifyStopRunState (top);
     }
 
     private static void OnNotifyStopRunState (Toplevel top)
@@ -798,14 +595,7 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     {
         ArgumentNullException.ThrowIfNull (runState);
 
-        if (ApplicationOverlapped.OverlappedTop is { })
-        {
-            ApplicationOverlapped.OverlappedTop.OnChildUnloaded (runState.Toplevel);
-        }
-        else
-        {
-            runState.Toplevel.OnUnloaded ();
-        }
+        runState.Toplevel.OnUnloaded ();
 
         // End the RunState.Toplevel
         // First, take it off the Toplevel Stack
@@ -824,66 +614,27 @@ public static partial class Application // Run (Begin, Run, End, Stop)
         // Notify that it is closing
         runState.Toplevel?.OnClosed (runState.Toplevel);
 
-        // If there is a OverlappedTop that is not the RunState.Toplevel then RunState.Toplevel
-        // is a child of MidTop, and we should notify the OverlappedTop that it is closing
-        if (ApplicationOverlapped.OverlappedTop is { } && !runState.Toplevel!.Modal && runState.Toplevel != ApplicationOverlapped.OverlappedTop)
+        if (TopLevels.Count > 0)
         {
-            ApplicationOverlapped.OverlappedTop.OnChildClosed (runState.Toplevel);
+            Top = TopLevels.Peek ();
+            Top.SetNeedsDisplay ();
         }
 
-        // Set Current and Top to the next TopLevel on the stack
-        if (TopLevels.Count == 0)
+        if (runState.Toplevel is { HasFocus: true })
         {
-            if (Current is { HasFocus: true })
-            {
-                Current.HasFocus = false;
-            }
-            Current = null;
+            runState.Toplevel.HasFocus = false;
         }
-        else
-        {
-            if (TopLevels.Count > 1 && TopLevels.Peek () == ApplicationOverlapped.OverlappedTop && ApplicationOverlapped.OverlappedChildren?.Any (t => t.Visible) != null)
-            {
-                ApplicationOverlapped.OverlappedMoveNext ();
-            }
 
-            Current = TopLevels.Peek ();
-
-            if (TopLevels.Count == 1 && Current == ApplicationOverlapped.OverlappedTop)
-            {
-                ApplicationOverlapped.OverlappedTop.OnAllChildClosed ();
-            }
-            else
-            {
-                ApplicationOverlapped.SetCurrentOverlappedAsTop ();
-                // BUGBUG: We should not call OnEnter/OnLeave directly; they should only be called by SetHasFocus
-                if (runState.Toplevel is { HasFocus: true })
-                {
-                    runState.Toplevel.HasFocus = false;
-                }
-
-                if (Current is { HasFocus: false })
-                {
-                    Current.SetFocus ();
-                }
-            }
-
-            Refresh ();
-        }
-
-        // Don't dispose runState.Toplevel. It's up to caller dispose it
-        // If it's not the same as the current in the RunIteration,
-        // it will be fixed later in the next RunIteration.
-        if (ApplicationOverlapped.OverlappedTop is { } && !TopLevels.Contains (ApplicationOverlapped.OverlappedTop))
+        if (Top is { HasFocus: false })
         {
-            _cachedRunStateToplevel = ApplicationOverlapped.OverlappedTop;
-        }
-        else
-        {
-            _cachedRunStateToplevel = runState.Toplevel;
+            Top.SetFocus ();
         }
 
+        _cachedRunStateToplevel = runState.Toplevel;
+
         runState.Toplevel = null;
         runState.Dispose ();
+
+        Refresh ();
     }
 }

+ 0 - 6
Terminal.Gui/Application/Application.Screen.cs

@@ -37,13 +37,7 @@ public static partial class Application // Screen related stuff
         {
             t.SetRelativeLayout (args.Size.Value);
             t.LayoutSubviews ();
-            t.PositionToplevels ();
             t.OnSizeChanging (new (args.Size));
-
-            if (PositionCursor (t))
-            {
-                Driver?.UpdateCursor ();
-            }
         }
 
         Refresh ();

+ 1 - 43
Terminal.Gui/Application/Application.Toplevel.cs

@@ -8,49 +8,7 @@ public static partial class Application // Toplevel handling
     /// <summary>Holds the stack of TopLevel views.</summary>
     internal static Stack<Toplevel> TopLevels { get; } = new ();
 
-    /// <summary>The <see cref="Toplevel"/> object used for the application on startup (<seealso cref="Top"/>)</summary>
+    /// <summary>The <see cref="Toplevel"/> that is currently active.</summary>
     /// <value>The top.</value>
     public static Toplevel? Top { get; internal set; }
-
-    // TODO: Determine why this can't just return _topLevels.Peek()?
-    /// <summary>
-    ///     The current <see cref="Toplevel"/> object. This is updated in <see cref="Application.Begin"/> enters and leaves to
-    ///     point to the current
-    ///     <see cref="Toplevel"/> .
-    /// </summary>
-    /// <remarks>
-    ///     This will only be distinct from <see cref="Application.Top"/> in scenarios where <see cref="Toplevel.IsOverlappedContainer"/> is <see langword="true"/>.
-    /// </remarks>
-    /// <value>The current.</value>
-    public static Toplevel? Current { get; internal set; }
-
-    /// <summary>
-    ///     If <paramref name="topLevel"/> is not already Current and visible, finds the last Modal Toplevel in the stack and makes it Current.
-    /// </summary>
-    private static void EnsureModalOrVisibleAlwaysOnTop (Toplevel topLevel)
-    {
-        if (!topLevel.Running
-            || (topLevel == Current && topLevel.Visible)
-            || ApplicationOverlapped.OverlappedTop == null
-            || TopLevels.Peek ().Modal)
-        {
-            return;
-        }
-
-        foreach (Toplevel top in TopLevels.Reverse ())
-        {
-            if (top.Modal && top != Current)
-            {
-                ApplicationOverlapped.MoveCurrent (top);
-
-                return;
-            }
-        }
-
-        if (!topLevel.Visible && topLevel == Current)
-        {
-            ApplicationOverlapped.OverlappedMoveNext ();
-        }
-    }
-
 }

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

@@ -149,7 +149,6 @@ public static partial class Application
         }
 
         TopLevels.Clear ();
-        Current = null;
 #if DEBUG_IDISPOSABLE
 
         // Don't dispose the Top. It's up to caller dispose it
@@ -198,7 +197,7 @@ public static partial class Application
         IsInitialized = false;
 
         // Mouse
-        MouseEnteredView = null;
+        _cachedViewsUnderMouse.Clear ();
         WantContinuousButtonPressedView = null;
         MouseEvent = null;
         GrabbedMouse = null;

+ 3 - 1
Terminal.Gui/Application/ApplicationNavigation.cs

@@ -1,5 +1,7 @@
 #nullable enable
 
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 
 /// <summary>
@@ -98,6 +100,6 @@ public class ApplicationNavigation
     /// </returns>
     public bool AdvanceFocus (NavigationDirection direction, TabBehavior? behavior)
     {
-        return Application.Current is { } && Application.Current.AdvanceFocus (direction, behavior);
+        return Application.Top is { } && Application.Top.AdvanceFocus (direction, behavior);
     }
 }

+ 0 - 444
Terminal.Gui/Application/ApplicationOverlapped.cs

@@ -1,444 +0,0 @@
-#nullable enable
-using System.Diagnostics;
-using System.Reflection;
-
-namespace Terminal.Gui;
-
-/// <summary>
-/// Helper class for managing overlapped views in the application.
-/// </summary>
-public static class ApplicationOverlapped
-{
-
-    /// <summary>
-    ///     Gets or sets if <paramref name="top"/> is in overlapped mode within a Toplevel container.
-    /// </summary>
-    /// <param name="top"></param>
-    /// <returns></returns>
-    public static bool IsOverlapped (Toplevel? top)
-    {
-        return ApplicationOverlapped.OverlappedTop is { } && ApplicationOverlapped.OverlappedTop != top && !top!.Modal;
-    }
-
-    /// <summary>
-    ///     Gets the list of the Overlapped children which are not modal <see cref="Toplevel"/> from the
-    ///     <see cref="OverlappedTop"/>.
-    /// </summary>
-    public static List<Toplevel>? OverlappedChildren
-    {
-        get
-        {
-            if (OverlappedTop is { })
-            {
-                List<Toplevel> overlappedChildren = new ();
-
-                lock (Application.TopLevels)
-                {
-                    foreach (Toplevel top in Application.TopLevels)
-                    {
-                        if (top != OverlappedTop && !top.Modal)
-                        {
-                            overlappedChildren.Add (top);
-                        }
-                    }
-                }
-
-                return overlappedChildren;
-            }
-
-            return null;
-        }
-    }
-
-    /// <summary>
-    ///     The <see cref="Toplevel"/> object used for the application on startup which
-    ///     <see cref="Toplevel.IsOverlappedContainer"/> is true.
-    /// </summary>
-    public static Toplevel? OverlappedTop
-    {
-        get
-        {
-            if (Application.Top is { IsOverlappedContainer: true })
-            {
-                return Application.Top;
-            }
-
-            return null;
-        }
-    }
-
-    /// <summary>Brings the superview of the most focused overlapped view is on front.</summary>
-    public static void BringOverlappedTopToFront ()
-    {
-        if (OverlappedTop is { })
-        {
-            return;
-        }
-
-        View? top = FindTopFromView (Application.Top?.MostFocused);
-
-        if (top is Toplevel && Application.Top?.Subviews.Count > 1 && Application.Top.Subviews [^1] != top)
-        {
-            Application.Top.MoveSubviewToStart (top);
-        }
-    }
-
-    /// <summary>Gets the current visible Toplevel overlapped child that matches the arguments pattern.</summary>
-    /// <param name="type">The type.</param>
-    /// <param name="exclude">The strings to exclude.</param>
-    /// <returns>The matched view.</returns>
-    public static Toplevel? GetTopOverlappedChild (Type? type = null, string []? exclude = null)
-    {
-        if (OverlappedChildren is null || OverlappedTop is null)
-        {
-            return null;
-        }
-
-        foreach (Toplevel top in OverlappedChildren)
-        {
-            if (type is { } && top.GetType () == type && exclude?.Contains (top.Data?.ToString ()) == false)
-            {
-                return top;
-            }
-
-            if ((type is { } && top.GetType () != type) || exclude?.Contains (top.Data?.ToString ()) == true)
-            {
-                continue;
-            }
-
-            return top;
-        }
-
-        return null;
-    }
-
-
-    /// <summary>
-    /// Sets the focus to the next view in the specified direction within the provided list of views.
-    /// If the end of the list is reached, the focus wraps around to the first view in the list.
-    /// The method considers the current focused view (`Application.Current`) and attempts to move the focus
-    /// to the next view in the specified direction. If the focus cannot be set to the next view, it wraps around
-    /// to the first view in the list.
-    /// </summary>
-    /// <param name="viewsInTabIndexes"></param>
-    /// <param name="direction"></param>
-    internal static void SetFocusToNextViewWithWrap (IEnumerable<View>? viewsInTabIndexes, NavigationDirection direction)
-    {
-        if (viewsInTabIndexes is null)
-        {
-            return;
-        }
-
-        // This code-path only executes in obtuse IsOverlappedContainer scenarios.
-        Debug.Assert (Application.Current!.IsOverlappedContainer);
-
-        bool foundCurrentView = false;
-        bool focusSet = false;
-        IEnumerable<View> indexes = viewsInTabIndexes as View [] ?? viewsInTabIndexes.ToArray ();
-        int viewCount = indexes.Count ();
-        int currentIndex = 0;
-
-        foreach (View view in indexes)
-        {
-            if (view == Application.Current)
-            {
-                foundCurrentView = true;
-            }
-            else if (foundCurrentView && !focusSet)
-            {
-                // One of the views is Current, but view is not. Attempt to Advance...
-                Application.Current!.SuperView?.AdvanceFocus (direction, null);
-                // QUESTION: AdvanceFocus returns false AND sets Focused to null if no view was found to advance to. Should't we only set focusProcessed if it returned true?
-                focusSet = true;
-
-                if (Application.Current.SuperView?.Focused != Application.Current)
-                {
-                    return;
-                }
-
-                // Either AdvanceFocus didn't set focus or the view it set focus to is not current...
-                // continue...
-            }
-
-            currentIndex++;
-
-            if (foundCurrentView && !focusSet && currentIndex == viewCount)
-            {
-                // One of the views is Current AND AdvanceFocus didn't set focus AND we are at the last view in the list...
-                // This means we should wrap around to the first view in the list.
-                indexes.First ().SetFocus ();
-            }
-        }
-    }
-
-    /// <summary>
-    ///     Move to the next Overlapped child from the <see cref="OverlappedTop"/> and set it as the <see cref="Application.Top"/> if
-    ///     it is not already.
-    /// </summary>
-    /// <param name="top"></param>
-    /// <returns></returns>
-    public static bool MoveToOverlappedChild (Toplevel? top)
-    {
-        ArgumentNullException.ThrowIfNull (top);
-
-        if (top.Visible && OverlappedTop is { } && Application.Current?.Modal == false)
-        {
-            lock (Application.TopLevels)
-            {
-                Application.TopLevels.MoveTo (top, 0, new ToplevelEqualityComparer ());
-                Application.Current = top;
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    /// <summary>Move to the next Overlapped child from the <see cref="OverlappedTop"/>.</summary>
-    public static void OverlappedMoveNext ()
-    {
-        if (OverlappedTop is { } && !Application.Current!.Modal)
-        {
-            lock (Application.TopLevels)
-            {
-                Application.TopLevels.MoveNext ();
-                var isOverlapped = false;
-
-                while (Application.TopLevels.Peek () == OverlappedTop || !Application.TopLevels.Peek ().Visible)
-                {
-                    if (!isOverlapped && Application.TopLevels.Peek () == OverlappedTop)
-                    {
-                        isOverlapped = true;
-                    }
-                    else if (isOverlapped && Application.TopLevels.Peek () == OverlappedTop)
-                    {
-                        MoveCurrent (Application.Top!);
-
-                        break;
-                    }
-
-                    Application.TopLevels.MoveNext ();
-                }
-
-                Application.Current = Application.TopLevels.Peek ();
-            }
-        }
-    }
-
-    /// <summary>Move to the previous Overlapped child from the <see cref="OverlappedTop"/>.</summary>
-    public static void OverlappedMovePrevious ()
-    {
-        if (OverlappedTop is { } && !Application.Current!.Modal)
-        {
-            lock (Application.TopLevels)
-            {
-                Application.TopLevels.MovePrevious ();
-                var isOverlapped = false;
-
-                while (Application.TopLevels.Peek () == OverlappedTop || !Application.TopLevels.Peek ().Visible)
-                {
-                    if (!isOverlapped && Application.TopLevels.Peek () == OverlappedTop)
-                    {
-                        isOverlapped = true;
-                    }
-                    else if (isOverlapped && Application.TopLevels.Peek () == OverlappedTop)
-                    {
-                        MoveCurrent (Application.Top!);
-
-                        break;
-                    }
-
-                    Application.TopLevels.MovePrevious ();
-                }
-
-                 Application.Current = Application.TopLevels.Peek ();
-            }
-        }
-    }
-
-    internal static bool OverlappedChildNeedsDisplay ()
-    {
-        if (OverlappedTop is null)
-        {
-            return false;
-        }
-
-        lock (Application.TopLevels)
-        {
-            foreach (Toplevel top in Application.TopLevels)
-            {
-                if (top != Application.Current && top.Visible && (top.NeedsDisplay || top.SubViewNeedsDisplay || top.LayoutNeeded))
-                {
-                    OverlappedTop.SetSubViewNeedsDisplay ();
-
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    internal static bool SetCurrentOverlappedAsTop ()
-    {
-        if (OverlappedTop is null && Application.Current != Application.Top && Application.Current?.SuperView is null && Application.Current?.Modal == false)
-        {
-            Application.Top = Application.Current;
-
-            return true;
-        }
-
-        return false;
-    }
-
-    /// <summary>
-    ///     Finds the first Toplevel in the stack that is Visible and who's Frame contains the <paramref name="location"/>.
-    /// </summary>
-    /// <param name="start"></param>
-    /// <param name="location"></param>
-    /// <returns></returns>
-    internal static Toplevel? FindDeepestTop (Toplevel start, in Point location)
-    {
-        if (!start.Frame.Contains (location))
-        {
-            return null;
-        }
-
-        lock (Application.TopLevels)
-        {
-            if (Application.TopLevels is not { Count: > 0 })
-            {
-                return start;
-            }
-
-            int rx = location.X - start.Frame.X;
-            int ry = location.Y - start.Frame.Y;
-
-            foreach (Toplevel t in Application.TopLevels)
-            {
-                if (t == Application.Current)
-                {
-                    continue;
-                }
-
-                if (t != start && t.Visible && t.Frame.Contains (rx, ry))
-                {
-                    start = t;
-
-                    break;
-                }
-            }
-        }
-
-        return start;
-    }
-
-    /// <summary>
-    ///     Given <paramref name="view"/>, returns the first Superview up the chain that is <see cref="Application.Top"/>.
-    /// </summary>
-    internal static View? FindTopFromView (View? view)
-    {
-        if (view is null)
-        {
-            return null;
-        }
-
-        View top = view.SuperView is { } && view.SuperView != Application.Top
-                       ? view.SuperView
-                       : view;
-
-        while (top?.SuperView is { } && top?.SuperView != Application.Top)
-        {
-            top = top!.SuperView;
-        }
-
-        return top;
-    }
-
-    /// <summary>
-    ///     If the <see cref="Application.Current"/> is not the <paramref name="top"/> then <paramref name="top"/> is moved to the top of
-    ///     the Toplevel stack and made Current.
-    /// </summary>
-    /// <param name="top"></param>
-    /// <returns></returns>
-    internal static bool MoveCurrent (Toplevel top)
-    {
-        // The Current is modal and the top is not modal Toplevel then
-        // the Current must be moved above the first not modal Toplevel.
-        if (OverlappedTop is { }
-            && top != OverlappedTop
-            && top != Application.Current
-            && Application.Current?.Modal == true
-            && !Application.TopLevels.Peek ().Modal)
-        {
-            lock (Application.TopLevels)
-            {
-                Application.TopLevels.MoveTo (Application.Current, 0, new ToplevelEqualityComparer ());
-            }
-
-            var index = 0;
-            Toplevel [] savedToplevels = Application.TopLevels.ToArray ();
-
-            foreach (Toplevel t in savedToplevels)
-            {
-                if (!t!.Modal && t != Application.Current && t != top && t != savedToplevels [index])
-                {
-                    lock (Application.TopLevels)
-                    {
-                        Application.TopLevels.MoveTo (top, index, new ToplevelEqualityComparer ());
-                    }
-                }
-
-                index++;
-            }
-
-            return false;
-        }
-
-        // The Current and the top are both not running Toplevel then
-        // the top must be moved above the first not running Toplevel.
-        if (OverlappedTop is { }
-            && top != OverlappedTop
-            && top != Application.Current
-            && Application.Current?.Running == false
-            && top?.Running == false)
-        {
-            lock (Application.TopLevels)
-            {
-                Application.TopLevels.MoveTo (Application.Current, 0, new ToplevelEqualityComparer ());
-            }
-
-            var index = 0;
-
-            foreach (Toplevel t in Application.TopLevels.ToArray ())
-            {
-                if (!t.Running && t != Application.Current && index > 0)
-                {
-                    lock (Application.TopLevels)
-                    {
-                        Application.TopLevels.MoveTo (top, index - 1, new ToplevelEqualityComparer ());
-                    }
-                }
-
-                index++;
-            }
-
-            return false;
-        }
-
-        if ((OverlappedTop is { } && top?.Modal == true && Application.TopLevels.Peek () != top)
-            || (OverlappedTop is { } && Application.Current != OverlappedTop && Application.Current?.Modal == false && top == OverlappedTop)
-            || (OverlappedTop is { } && Application.Current?.Modal == false && top != Application.Current)
-            || (OverlappedTop is { } && Application.Current?.Modal == true && top == OverlappedTop))
-        {
-            lock (Application.TopLevels)
-            {
-                Application.TopLevels.MoveTo (top!, 0, new ToplevelEqualityComparer ());
-                Application.Current = top;
-            }
-        }
-
-        return true;
-    }
-}

+ 1 - 1
Terminal.Gui/Configuration/AppScope.cs

@@ -16,7 +16,7 @@ namespace Terminal.Gui;
 /// 	public static bool? MyProperty { get; set; } = true;
 ///  }
 ///  </code>
-///     <para>THe resultant Json will look like this:</para>
+///     <para>The resultant Json will look like this:</para>
 ///     <code>
 ///    "AppSettings": {
 ///      "MyAppSettings.MyProperty": true,

+ 18 - 5
Terminal.Gui/Configuration/ColorJsonConverter.cs

@@ -1,9 +1,19 @@
 using System.Text.Json;
 using System.Text.Json.Serialization;
+using ColorHelper;
 
 namespace Terminal.Gui;
 
-/// <summary>Json converter for the <see cref="Color"/> class.</summary>
+/// <summary>
+/// Json converter for the <see cref="Color"/> class.
+/// <para>
+///     Serialization outputs a string with the color name if the color matches a name in <see cref="ColorStrings"/>
+///     or the "#RRGGBB" hexadecimal representation (e.g. "#FF0000" for red).
+/// </para>
+/// <para>
+///     Deserialization formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
+///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", or any W3C color name.</para>
+/// </summary>
 internal class ColorJsonConverter : JsonConverter<Color>
 {
     private static ColorJsonConverter _instance;
@@ -15,7 +25,7 @@ internal class ColorJsonConverter : JsonConverter<Color>
         {
             if (_instance is null)
             {
-                _instance = new ColorJsonConverter ();
+                _instance = new ();
             }
 
             return _instance;
@@ -31,10 +41,10 @@ internal class ColorJsonConverter : JsonConverter<Color>
             ReadOnlySpan<char> colorString = reader.GetString ();
 
             // Check if the color string is a color name
-            if (Enum.TryParse (colorString, true, out ColorName color))
+            if (ColorStrings.TryParseW3CColorName (colorString.ToString (), out Color color1))
             {
                 // Return the parsed color
-                return new Color (in color);
+                return new (color1);
             }
 
             if (Color.TryParse (colorString, null, out Color parsedColor))
@@ -48,5 +58,8 @@ internal class ColorJsonConverter : JsonConverter<Color>
         throw new JsonException ($"Unexpected token when parsing Color: {reader.TokenType}");
     }
 
-    public override void Write (Utf8JsonWriter writer, Color value, JsonSerializerOptions options) { writer.WriteStringValue (value.ToString ()); }
+    public override void Write (Utf8JsonWriter writer, Color value, JsonSerializerOptions options)
+    {
+        writer.WriteStringValue (value.ToString ());
+    }
 }

+ 1 - 1
Terminal.Gui/Configuration/ConfigurationManager.cs

@@ -24,7 +24,7 @@ namespace Terminal.Gui;
 ///     </para>
 ///     <para>
 ///         Settings are defined in JSON format, according to this schema:
-///         https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json
+///        https://gui-cs.github.io/Terminal.GuiV2Docs/schemas/tui-config-schema.json
 ///     </para>
 ///     <para>
 ///         Settings that will apply to all applications (global settings) reside in files named <c>config.json</c>.

+ 24 - 17
Terminal.Gui/Configuration/RuneJsonConverter.cs

@@ -6,9 +6,17 @@ using System.Text.RegularExpressions;
 namespace Terminal.Gui;
 
 /// <summary>
-///     Json converter for <see cref="Rune"/>. Supports Json converter for <see cref="Rune"/>. Supports A string as
-///     one of: - unicode char (e.g. "☑") - U+hex format (e.g. "U+2611") - \u format (e.g. "\\u2611") A number - The
-///     unicode code in decimal
+///     Json converter for <see cref="Rune"/>.
+///     <para>
+///         If the Rune is printable, it will be serialized as the glyph; otherwise the \u format (e.g. "\\u2611") is used.
+///     </para>
+///     <para>
+///         Supports deserializing as one of:
+///         - unicode glyph in a string (e.g. "☑")
+///         - U+hex format in a string  (e.g. "U+2611")
+///         - \u format in a string (e.g. "\\u2611")
+///         - A decimal number (e.g. 97 for "a")
+///     </para>
 /// </summary>
 internal class RuneJsonConverter : JsonConverter<Rune>
 {
@@ -108,7 +116,7 @@ internal class RuneJsonConverter : JsonConverter<Rune>
                     throw new JsonException ($"Invalid combined Rune ({value})");
                 }
 
-                return new Rune (combined [0]);
+                return new (combined [0]);
             }
             case JsonTokenType.Number:
             {
@@ -116,7 +124,7 @@ internal class RuneJsonConverter : JsonConverter<Rune>
 
                 if (Rune.IsValid (num))
                 {
-                    return new Rune (num);
+                    return new (num);
                 }
 
                 throw new JsonException ($"Invalid Rune (not a scalar Unicode value): {num}.");
@@ -128,18 +136,17 @@ internal class RuneJsonConverter : JsonConverter<Rune>
 
     public override void Write (Utf8JsonWriter writer, Rune value, JsonSerializerOptions options)
     {
-        // HACK: Writes a JSON comment in addition to the glyph to ease debugging.
-        // Technically, JSON comments are not valid, but we use relaxed decoding
-        // (ReadCommentHandling = JsonCommentHandling.Skip)
-        //writer.WriteCommentValue ($"(U+{value.Value:X8})");
-        //var printable = value.MakePrintable ();
-        //if (printable == Rune.ReplacementChar) {
-        //	writer.WriteStringValue (value.ToString ());
-        //} else {
-        //	//writer.WriteRawValue ($"\"{value}\"");
-        //}
-
-        writer.WriteNumberValue (value.Value);
+        Rune printable = value.MakePrintable ();
+        if (printable == Rune.ReplacementChar)
+        {
+            // Write as /u string
+            writer.WriteRawValue ($"\"{value}\"");
+        }
+        else
+        {
+            // Write as the actual glyph
+            writer.WriteStringValue (value.ToString ());
+        }
     }
 }
 #pragma warning restore 1591

+ 25 - 8
Terminal.Gui/Configuration/SettingsScope.cs

@@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using System.Text.Json;
 using System.Text.Json.Serialization;
-using static Terminal.Gui.SpinnerStyle;
 
 namespace Terminal.Gui;
 
@@ -15,7 +14,7 @@ namespace Terminal.Gui;
 /// <example>
 ///     <code>
 ///  {
-///    "$schema" : "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json",
+///    "$schema" : "https://gui-cs.github.io/Terminal.GuiV2Docs/schemas/tui-config-schema.json",
 ///    "Application.UseSystemConsole" : true,
 ///    "Theme" : "Default",
 ///    "Themes": {
@@ -33,7 +32,7 @@ public class SettingsScope : Scope<SettingsScope>
     /// <summary>Points to our JSON schema.</summary>
     [JsonInclude]
     [JsonPropertyName ("$schema")]
-    public string Schema { get; set; } = "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json";
+    public string Schema { get; set; } = "https://gui-cs.github.io/Terminal.GuiV2Docs/schemas/tui-config-schema.json";
 
     /// <summary>Updates the <see cref="SettingsScope"/> with the settings in a JSON string.</summary>
     /// <param name="stream">Json document to update the settings with.</param>
@@ -87,12 +86,30 @@ public class SettingsScope : Scope<SettingsScope>
             return this;
         }
 
-        FileStream stream = File.OpenRead (realPath);
-        SettingsScope? s = Update (stream, filePath);
-        stream.Close ();
-        stream.Dispose ();
+        int retryCount = 0;
 
-        return s;
+        // Sometimes when the config file is written by an external agent, the change notification comes
+        // before the file is closed. This works around that.
+        while (retryCount < 2)
+        {
+            try
+            {
+                FileStream? stream = File.OpenRead (realPath);
+                SettingsScope? s = Update (stream, filePath);
+                stream.Close ();
+                stream.Dispose ();
+
+                return s;
+            }
+            catch (IOException ioe)
+            {
+                Debug.WriteLine($"Couldn't open {filePath}. Retrying...: {ioe}");
+                Task.Delay (100);
+                retryCount++;
+            }
+        }
+
+        return null;
     }
 
     /// <summary>Updates the <see cref="SettingsScope"/> with the settings in a JSON string.</summary>

+ 3 - 1
Terminal.Gui/Configuration/SourceGenerationContext.cs

@@ -15,9 +15,11 @@ namespace Terminal.Gui;
 [JsonSerializable (typeof (AlignmentModes))]
 [JsonSerializable (typeof (LineStyle))]
 [JsonSerializable (typeof (ShadowStyle))]
+[JsonSerializable (typeof (HighlightStyle))]
 [JsonSerializable (typeof (bool?))]
-[JsonSerializable (typeof (Dictionary<ColorName, string>))]
+[JsonSerializable (typeof (Dictionary<ColorName16, string>))]
 [JsonSerializable (typeof (Dictionary<string, ThemeScope>))]
 [JsonSerializable (typeof (Dictionary<string, ColorScheme>))]
+[JsonSerializable (typeof (Dictionary<string, Color>))]
 internal partial class SourceGenerationContext : JsonSerializerContext
 { }

+ 8 - 1
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -485,6 +485,7 @@ public abstract class ConsoleDriver
     public virtual bool SupportsTrueColor => true;
 
     // TODO: This makes ConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
+    // BUGBUG: Application.Force16Colors should be bool? so if SupportsTrueColor and Application.Force16Colors == false, this doesn't override
     /// <summary>
     ///     Gets or sets whether the <see cref="ConsoleDriver"/> should use 16 colors instead of the default TrueColors.
     ///     See <see cref="Application.Force16Colors"/> to change this setting via <see cref="ConfigurationManager"/>.
@@ -591,7 +592,13 @@ public abstract class ConsoleDriver
 
     /// <summary>Called when a mouse event occurs. Fires the <see cref="MouseEvent"/> event.</summary>
     /// <param name="a"></param>
-    public void OnMouseEvent (MouseEvent a) { MouseEvent?.Invoke (this, a); }
+    public void OnMouseEvent (MouseEvent a)
+    {
+        // Ensure ScreenPosition is set
+        a.ScreenPosition = a.Position;
+
+        MouseEvent?.Invoke (this, a);
+    }
 
     /// <summary>Simulates a key press.</summary>
     /// <param name="keyChar">The key character.</param>

+ 39 - 39
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -374,7 +374,7 @@ internal class CursesDriver : ConsoleDriver
                                                            );
         }
 
-        CurrentAttribute = new Attribute (ColorName.White, ColorName.Black);
+        CurrentAttribute = new Attribute (ColorName16.White, ColorName16.Black);
 
         if (Environment.OSVersion.Platform == PlatformID.Win32NT)
         {
@@ -859,8 +859,8 @@ internal class CursesDriver : ConsoleDriver
 
         return new Attribute (
                               Curses.ColorPair (v),
-                              CursesColorNumberToColorName (foreground),
-                              CursesColorNumberToColorName (background)
+                              CursesColorNumberToColorName16 (foreground),
+                              CursesColorNumberToColorName16 (background)
                              );
     }
 
@@ -875,8 +875,8 @@ internal class CursesDriver : ConsoleDriver
         if (!RunningUnitTests)
         {
             return MakeColor (
-                              ColorNameToCursesColorNumber (foreground.GetClosestNamedColor ()),
-                              ColorNameToCursesColorNumber (background.GetClosestNamedColor ())
+                              ColorNameToCursesColorNumber (foreground.GetClosestNamedColor16 ()),
+                              ColorNameToCursesColorNumber (background.GetClosestNamedColor16 ())
                              );
         }
 
@@ -887,83 +887,83 @@ internal class CursesDriver : ConsoleDriver
                              );
     }
 
-    private static short ColorNameToCursesColorNumber (ColorName color)
+    private static short ColorNameToCursesColorNumber (ColorName16 color)
     {
         switch (color)
         {
-            case ColorName.Black:
+            case ColorName16.Black:
                 return Curses.COLOR_BLACK;
-            case ColorName.Blue:
+            case ColorName16.Blue:
                 return Curses.COLOR_BLUE;
-            case ColorName.Green:
+            case ColorName16.Green:
                 return Curses.COLOR_GREEN;
-            case ColorName.Cyan:
+            case ColorName16.Cyan:
                 return Curses.COLOR_CYAN;
-            case ColorName.Red:
+            case ColorName16.Red:
                 return Curses.COLOR_RED;
-            case ColorName.Magenta:
+            case ColorName16.Magenta:
                 return Curses.COLOR_MAGENTA;
-            case ColorName.Yellow:
+            case ColorName16.Yellow:
                 return Curses.COLOR_YELLOW;
-            case ColorName.Gray:
+            case ColorName16.Gray:
                 return Curses.COLOR_WHITE;
-            case ColorName.DarkGray:
+            case ColorName16.DarkGray:
                 return Curses.COLOR_GRAY;
-            case ColorName.BrightBlue:
+            case ColorName16.BrightBlue:
                 return Curses.COLOR_BLUE | Curses.COLOR_GRAY;
-            case ColorName.BrightGreen:
+            case ColorName16.BrightGreen:
                 return Curses.COLOR_GREEN | Curses.COLOR_GRAY;
-            case ColorName.BrightCyan:
+            case ColorName16.BrightCyan:
                 return Curses.COLOR_CYAN | Curses.COLOR_GRAY;
-            case ColorName.BrightRed:
+            case ColorName16.BrightRed:
                 return Curses.COLOR_RED | Curses.COLOR_GRAY;
-            case ColorName.BrightMagenta:
+            case ColorName16.BrightMagenta:
                 return Curses.COLOR_MAGENTA | Curses.COLOR_GRAY;
-            case ColorName.BrightYellow:
+            case ColorName16.BrightYellow:
                 return Curses.COLOR_YELLOW | Curses.COLOR_GRAY;
-            case ColorName.White:
+            case ColorName16.White:
                 return Curses.COLOR_WHITE | Curses.COLOR_GRAY;
         }
 
         throw new ArgumentException ("Invalid color code");
     }
 
-    private static ColorName CursesColorNumberToColorName (short color)
+    private static ColorName16 CursesColorNumberToColorName16 (short color)
     {
         switch (color)
         {
             case Curses.COLOR_BLACK:
-                return ColorName.Black;
+                return ColorName16.Black;
             case Curses.COLOR_BLUE:
-                return ColorName.Blue;
+                return ColorName16.Blue;
             case Curses.COLOR_GREEN:
-                return ColorName.Green;
+                return ColorName16.Green;
             case Curses.COLOR_CYAN:
-                return ColorName.Cyan;
+                return ColorName16.Cyan;
             case Curses.COLOR_RED:
-                return ColorName.Red;
+                return ColorName16.Red;
             case Curses.COLOR_MAGENTA:
-                return ColorName.Magenta;
+                return ColorName16.Magenta;
             case Curses.COLOR_YELLOW:
-                return ColorName.Yellow;
+                return ColorName16.Yellow;
             case Curses.COLOR_WHITE:
-                return ColorName.Gray;
+                return ColorName16.Gray;
             case Curses.COLOR_GRAY:
-                return ColorName.DarkGray;
+                return ColorName16.DarkGray;
             case Curses.COLOR_BLUE | Curses.COLOR_GRAY:
-                return ColorName.BrightBlue;
+                return ColorName16.BrightBlue;
             case Curses.COLOR_GREEN | Curses.COLOR_GRAY:
-                return ColorName.BrightGreen;
+                return ColorName16.BrightGreen;
             case Curses.COLOR_CYAN | Curses.COLOR_GRAY:
-                return ColorName.BrightCyan;
+                return ColorName16.BrightCyan;
             case Curses.COLOR_RED | Curses.COLOR_GRAY:
-                return ColorName.BrightRed;
+                return ColorName16.BrightRed;
             case Curses.COLOR_MAGENTA | Curses.COLOR_GRAY:
-                return ColorName.BrightMagenta;
+                return ColorName16.BrightMagenta;
             case Curses.COLOR_YELLOW | Curses.COLOR_GRAY:
-                return ColorName.BrightYellow;
+                return ColorName16.BrightYellow;
             case Curses.COLOR_WHITE | Curses.COLOR_GRAY:
-                return ColorName.White;
+                return ColorName16.White;
         }
 
         throw new ArgumentException ("Invalid curses color code");

+ 3 - 3
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -93,7 +93,7 @@ public class FakeDriver : ConsoleDriver
         FakeConsole.Clear ();
         ResizeScreen ();
         CurrentAttribute = new Attribute (Color.White, Color.Black);
-        ClearContents ();
+        //ClearContents ();
 
         _mainLoopDriver = new FakeMainLoop (this);
         _mainLoopDriver.MockKeyPressed = MockKeyPressedHandler;
@@ -165,8 +165,8 @@ public class FakeDriver : ConsoleDriver
                     if (attr != redrawAttr)
                     {
                         redrawAttr = attr;
-                        FakeConsole.ForegroundColor = (ConsoleColor)attr.Foreground.GetClosestNamedColor ();
-                        FakeConsole.BackgroundColor = (ConsoleColor)attr.Background.GetClosestNamedColor ();
+                        FakeConsole.ForegroundColor = (ConsoleColor)attr.Foreground.GetClosestNamedColor16 ();
+                        FakeConsole.BackgroundColor = (ConsoleColor)attr.Background.GetClosestNamedColor16 ();
                     }
 
                     outputWidth++;

+ 2 - 2
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -961,10 +961,10 @@ internal class NetDriver : ConsoleDriver
                             output.Append (
                                            EscSeqUtils.CSI_SetGraphicsRendition (
                                                                                  MapColors (
-                                                                                            (ConsoleColor)attr.Background.GetClosestNamedColor (),
+                                                                                            (ConsoleColor)attr.Background.GetClosestNamedColor16 (),
                                                                                             false
                                                                                            ),
-                                                                                 MapColors ((ConsoleColor)attr.Foreground.GetClosestNamedColor ())
+                                                                                 MapColors ((ConsoleColor)attr.Foreground.GetClosestNamedColor16 ())
                                                                                 )
                                           );
                         }

+ 1 - 1
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -72,7 +72,7 @@ internal class WindowsConsole
                 {
                     Char = new CharUnion { UnicodeChar = info.Char },
                     Attributes =
-                        (ushort)((int)info.Attribute.Foreground.GetClosestNamedColor () | ((int)info.Attribute.Background.GetClosestNamedColor () << 4))
+                        (ushort)((int)info.Attribute.Foreground.GetClosestNamedColor16 () | ((int)info.Attribute.Background.GetClosestNamedColor16 () << 4))
                 };
             }
 

+ 55 - 53
Terminal.Gui/Drawing/Alignment.cs

@@ -10,72 +10,74 @@ public enum Alignment
 {
     /// <summary>
     ///     The items will be aligned to the start (left or top) of the container.
+    ///     <remarks>
+    ///         <para>
+    ///             If the container is smaller than the total size of the items, the end items will be clipped (their
+    ///             locations
+    ///             will be greater than the container size).
+    ///         </para>
+    ///         <para>
+    ///             The <see cref="AlignmentModes"/> enumeration provides additional options for aligning items in a container.
+    ///         </para>
+    ///     </remarks>
+    ///     <example>
+    ///         <c>
+    ///             |111 2222 33333    |
+    ///         </c>
+    ///     </example>
     /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         If the container is smaller than the total size of the items, the end items will be clipped (their locations
-    ///         will be greater than the container size).
-    ///     </para>
-    ///     <para>
-    ///         The <see cref="AlignmentModes"/> enumeration provides additional options for aligning items in a container.
-    ///     </para>
-    /// </remarks>
-    /// <example>
-    ///     <c>
-    ///         |111 2222 33333    |
-    ///     </c>
-    /// </example>
     Start = 0,
 
     /// <summary>
     ///     The items will be aligned to the end (right or bottom) of the container.
+    ///     <remarks>
+    ///         <para>
+    ///             If the container is smaller than the total size of the items, the start items will be clipped (their
+    ///             locations
+    ///             will be negative).
+    ///         </para>
+    ///         <para>
+    ///             The <see cref="AlignmentModes"/> enumeration provides additional options for aligning items in a container.
+    ///         </para>
+    ///     </remarks>
+    ///     <example>
+    ///         <c>
+    ///             |    111 2222 33333|
+    ///         </c>
+    ///     </example>
     /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         If the container is smaller than the total size of the items, the start items will be clipped (their locations
-    ///         will be negative).
-    ///     </para>
-    ///     <para>
-    ///         The <see cref="AlignmentModes"/> enumeration provides additional options for aligning items in a container.
-    ///     </para>
-    /// </remarks>
-    /// <example>
-    ///     <c>
-    ///         |    111 2222 33333|
-    ///     </c>
-    /// </example>
     End,
 
     /// <summary>
     ///     Center in the available space.
+    ///     <remarks>
+    ///         <para>
+    ///             If centering is not possible, the group will be left-aligned.
+    ///         </para>
+    ///         <para>
+    ///             Extra space will be distributed between the items, biased towards the left.
+    ///         </para>
+    ///     </remarks>
+    ///     <example>
+    ///         <c>
+    ///             |  111 2222 33333  |
+    ///         </c>
+    ///     </example>
     /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///     If centering is not possible, the group will be left-aligned.
-    ///     </para>
-    ///     <para>
-    ///         Extra space will be distributed between the items, biased towards the left.
-    ///     </para>
-    /// </remarks>
-    /// <example>
-    ///     <c>
-    ///         |  111 2222 33333  |
-    ///     </c>
-    /// </example>
     Center,
 
     /// <summary>
     ///     The items will fill the available space.
+    ///     <remarks>
+    ///         <para>
+    ///             Extra space will be distributed between the items, biased towards the end.
+    ///         </para>
+    ///     </remarks>
+    ///     <example>
+    ///         <c>
+    ///             |111  2222    33333|
+    ///         </c>
+    ///     </example>
     /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         Extra space will be distributed between the items, biased towards the end.
-    ///     </para>
-    /// </remarks>
-    /// <example>
-    ///     <c>
-    ///        |111  2222    33333|
-    ///     </c>
-    /// </example>
-    Fill,
-}
+    Fill
+}

+ 8 - 14
Terminal.Gui/Drawing/AlignmentModes.cs

@@ -17,30 +17,24 @@ public enum AlignmentModes
     /// <summary>
     ///     The items will be arranged from end (right/bottom) to start (left/top).
     /// </summary>
-    /// <remarks>
-    ///     Not implemented.
-    /// </remarks>
     EndToStart = 1,
 
     /// <summary>
     ///     At least one space will be added between items. Useful for justifying text where at least one space is needed.
     /// </summary>
     /// <remarks>
-    ///     <para>
-    ///         If the total size of the items is greater than the container size, the space between items will be ignored
-    ///         starting from the end.
-    ///     </para>
+    ///     If the total size of the items is greater than the container size, the space between items will be ignored
+    ///     starting from the end.
     /// </remarks>
     AddSpaceBetweenItems = 2,
 
     /// <summary>
-    ///    When aligning via <see cref="Alignment.Start"/> or <see cref="Alignment.End"/>, the item opposite to the alignment (the first or last item) will be ignored.
+    ///     When aligning via <see cref="Alignment.Start"/> or <see cref="Alignment.End"/>, the item opposite to the alignment
+    ///     (the first or last item) will be ignored.
     /// </summary>
     /// <remarks>
-    ///     <para>
-    ///         If the container is smaller than the total size of the items, the end items will be clipped (their locations
-    ///         will be greater than the container size).
-    ///     </para>
+    ///     If the container is smaller than the total size of the items, the end items will be clipped (their locations
+    ///     will be greater than the container size).
     /// </remarks>
     /// <example>
     ///     <c>
@@ -48,5 +42,5 @@ public enum AlignmentModes
     ///         End:   |111     2222 33333|
     ///     </c>
     /// </example>
-    IgnoreFirstOrLast = 4,
-}
+    IgnoreFirstOrLast = 4
+}

+ 6 - 6
Terminal.Gui/Drawing/Attribute.cs

@@ -15,7 +15,7 @@ public readonly record struct Attribute : IEqualityOperators<Attribute, Attribut
 {
     /// <summary>Default empty attribute.</summary>
     [JsonIgnore]
-    public static Attribute Default => new (Color.White, ColorName.Black);
+    public static Attribute Default => new (Color.White, Color.Black);
 
     /// <summary>The <see cref="ConsoleDriver"/>-specific color value.</summary>
     [JsonIgnore (Condition = JsonIgnoreCondition.Always)]
@@ -65,28 +65,28 @@ public readonly record struct Attribute : IEqualityOperators<Attribute, Attribut
     }
 
     /// <summary>
-    ///     Initializes a new instance with a <see cref="ColorName"/> value. Both <see cref="Foreground"/> and
+    ///     Initializes a new instance with a <see cref="ColorName16"/> value. Both <see cref="Foreground"/> and
     ///     <see cref="Background"/> will be set to the specified color.
     /// </summary>
     /// <param name="colorName">Value.</param>
-    internal Attribute (in ColorName colorName) : this (in colorName, in colorName) { }
+    internal Attribute (in ColorName16 colorName) : this (in colorName, in colorName) { }
 
     /// <summary>Initializes a new instance of the <see cref="Attribute"/> struct.</summary>
     /// <param name="foregroundName">Foreground</param>
     /// <param name="backgroundName">Background</param>
-    public Attribute (in ColorName foregroundName, in ColorName backgroundName)
+    public Attribute (in ColorName16 foregroundName, in ColorName16 backgroundName)
         : this (new Color (in foregroundName), new Color (in backgroundName))
     { }
 
     /// <summary>Initializes a new instance of the <see cref="Attribute"/> struct.</summary>
     /// <param name="foregroundName">Foreground</param>
     /// <param name="background">Background</param>
-    public Attribute (in ColorName foregroundName, in Color background) : this (new Color (in foregroundName), in background) { }
+    public Attribute (in ColorName16 foregroundName, in Color background) : this (new Color (in foregroundName), in background) { }
 
     /// <summary>Initializes a new instance of the <see cref="Attribute"/> struct.</summary>
     /// <param name="foreground">Foreground</param>
     /// <param name="backgroundName">Background</param>
-    public Attribute (in Color foreground, in ColorName backgroundName) : this (in foreground, new Color (in backgroundName)) { }
+    public Attribute (in Color foreground, in ColorName16 backgroundName) : this (in foreground, new Color (in backgroundName)) { }
 
     /// <summary>
     ///     Initializes a new instance of the <see cref="Attribute"/> struct with the same colors for the foreground and

+ 47 - 44
Terminal.Gui/Drawing/Color.ColorExtensions.cs

@@ -1,72 +1,75 @@
 using System.Collections.Frozen;
+using ColorHelper;
 
 namespace Terminal.Gui;
 
 internal static class ColorExtensions
 {
-    private static FrozenDictionary<Color, ColorName> colorToNameMap;
+    // TODO: This should be refactored to support all W3CColors (`ColorStrings` and this should be merged).
+    // TODO: ColorName and AnsiColorCode are only needed when a driver is in Force16Color mode and we
+    // TODO: should be able to remove these from any non-Driver-specific usages.
+    private static FrozenDictionary<Color, ColorName16> colorToNameMap;
 
     static ColorExtensions ()
     {
-        Dictionary<ColorName, AnsiColorCode> nameToCodeMap = new ()
+        Dictionary<ColorName16, AnsiColorCode> nameToCodeMap = new ()
         {
-            { ColorName.Black, AnsiColorCode.BLACK },
-            { ColorName.Blue, AnsiColorCode.BLUE },
-            { ColorName.Green, AnsiColorCode.GREEN },
-            { ColorName.Cyan, AnsiColorCode.CYAN },
-            { ColorName.Red, AnsiColorCode.RED },
-            { ColorName.Magenta, AnsiColorCode.MAGENTA },
-            { ColorName.Yellow, AnsiColorCode.YELLOW },
-            { ColorName.Gray, AnsiColorCode.WHITE },
-            { ColorName.DarkGray, AnsiColorCode.BRIGHT_BLACK },
-            { ColorName.BrightBlue, AnsiColorCode.BRIGHT_BLUE },
-            { ColorName.BrightGreen, AnsiColorCode.BRIGHT_GREEN },
-            { ColorName.BrightCyan, AnsiColorCode.BRIGHT_CYAN },
-            { ColorName.BrightRed, AnsiColorCode.BRIGHT_RED },
-            { ColorName.BrightMagenta, AnsiColorCode.BRIGHT_MAGENTA },
-            { ColorName.BrightYellow, AnsiColorCode.BRIGHT_YELLOW },
-            { ColorName.White, AnsiColorCode.BRIGHT_WHITE }
+            { ColorName16.Black, AnsiColorCode.BLACK },
+            { ColorName16.Blue, AnsiColorCode.BLUE },
+            { ColorName16.Green, AnsiColorCode.GREEN },
+            { ColorName16.Cyan, AnsiColorCode.CYAN },
+            { ColorName16.Red, AnsiColorCode.RED },
+            { ColorName16.Magenta, AnsiColorCode.MAGENTA },
+            { ColorName16.Yellow, AnsiColorCode.YELLOW },
+            { ColorName16.Gray, AnsiColorCode.WHITE },
+            { ColorName16.DarkGray, AnsiColorCode.BRIGHT_BLACK },
+            { ColorName16.BrightBlue, AnsiColorCode.BRIGHT_BLUE },
+            { ColorName16.BrightGreen, AnsiColorCode.BRIGHT_GREEN },
+            { ColorName16.BrightCyan, AnsiColorCode.BRIGHT_CYAN },
+            { ColorName16.BrightRed, AnsiColorCode.BRIGHT_RED },
+            { ColorName16.BrightMagenta, AnsiColorCode.BRIGHT_MAGENTA },
+            { ColorName16.BrightYellow, AnsiColorCode.BRIGHT_YELLOW },
+            { ColorName16.White, AnsiColorCode.BRIGHT_WHITE }
         };
-        ColorNameToAnsiColorMap = nameToCodeMap.ToFrozenDictionary ();
+        ColorName16ToAnsiColorMap = nameToCodeMap.ToFrozenDictionary ();
 
-        ColorToNameMap = new Dictionary<Color, ColorName>
+        ColorToName16Map = new Dictionary<Color, ColorName16>
         {
-            // using "Windows 10 Console/PowerShell 6" here: https://i.stack.imgur.com/9UVnC.png
-            // See also: https://en.wikipedia.org/wiki/ANSI_escape_code
-            { new Color (12, 12, 12), ColorName.Black },
-            { new Color (0, 55, 218), ColorName.Blue },
-            { new Color (19, 161, 14), ColorName.Green },
-            { new Color (58, 150, 221), ColorName.Cyan },
-            { new Color (197, 15, 31), ColorName.Red },
-            { new Color (136, 23, 152), ColorName.Magenta },
-            { new Color (128, 64, 32), ColorName.Yellow },
-            { new Color (204, 204, 204), ColorName.Gray },
-            { new Color (118, 118, 118), ColorName.DarkGray },
-            { new Color (59, 120, 255), ColorName.BrightBlue },
-            { new Color (22, 198, 12), ColorName.BrightGreen },
-            { new Color (97, 214, 214), ColorName.BrightCyan },
-            { new Color (231, 72, 86), ColorName.BrightRed },
-            { new Color (180, 0, 158), ColorName.BrightMagenta },
-            { new Color (249, 241, 165), ColorName.BrightYellow },
-            { new Color (242, 242, 242), ColorName.White }
+            // These match the values in Strings.resx, which are the W3C colors.
+            { new Color(0, 0, 0), ColorName16.Black },
+            { new Color(0, 0, 255), ColorName16.Blue },
+            { new Color(0, 128, 0), ColorName16.Green },
+            { new Color(0, 255, 255), ColorName16.Cyan },
+            { new Color(255, 0, 0), ColorName16.Red },
+            { new Color(255, 0, 255), ColorName16.Magenta },
+            { new Color(255, 255, 0), ColorName16.Yellow },
+            { new Color(128, 128, 128), ColorName16.Gray },
+            { new Color(118, 118, 118), ColorName16.DarkGray },
+            { new Color(59, 120, 255), ColorName16.BrightBlue },
+            { new Color(22, 198, 12), ColorName16.BrightGreen },
+            { new Color(97, 214, 214), ColorName16.BrightCyan },
+            { new Color(231, 72, 86), ColorName16.BrightRed },
+            { new Color(180, 0, 158), ColorName16.BrightMagenta },
+            { new Color(249, 241, 165), ColorName16.BrightYellow },
+            { new Color(255, 255, 255), ColorName16.White }
         }.ToFrozenDictionary ();
     }
 
     /// <summary>Defines the 16 legacy color names and their corresponding ANSI color codes.</summary>
-    internal static FrozenDictionary<ColorName, AnsiColorCode> ColorNameToAnsiColorMap { get; }
+    internal static FrozenDictionary<ColorName16, AnsiColorCode> ColorName16ToAnsiColorMap { get; }
 
-    /// <summary>Reverse mapping for <see cref="ColorToNameMap"/>.</summary>
-    internal static FrozenDictionary<ColorName, Color> ColorNameToColorMap { get; private set; }
+    /// <summary>Reverse mapping for <see cref="ColorToName16Map"/>.</summary>
+    internal static FrozenDictionary<ColorName16, Color> ColorName16ToColorMap { get; private set; }
 
     /// <summary>
     ///     Gets or sets a <see cref="FrozenDictionary{TKey,TValue}"/> that maps legacy 16-color values to the
-    ///     corresponding <see cref="ColorName"/>.
+    ///     corresponding <see cref="ColorName16"/>.
     /// </summary>
     /// <remarks>
     ///     Setter should be called as infrequently as possible, as <see cref="FrozenDictionary{TKey,TValue}"/> is
     ///     expensive to create.
     /// </remarks>
-    internal static FrozenDictionary<Color, ColorName> ColorToNameMap
+    internal static FrozenDictionary<Color, ColorName16> ColorToName16Map
     {
         get => colorToNameMap;
         set
@@ -74,7 +77,7 @@ internal static class ColorExtensions
             colorToNameMap = value;
 
             //Also be sure to set the reverse mapping
-            ColorNameToColorMap = value.ToFrozenDictionary (static kvp => kvp.Value, static kvp => kvp.Key);
+            ColorName16ToColorMap = value.ToFrozenDictionary (static kvp => kvp.Value, static kvp => kvp.Key);
         }
     }
 }

+ 2 - 2
Terminal.Gui/Drawing/Color.ColorName.cs

@@ -8,10 +8,10 @@ namespace Terminal.Gui;
 ///     <para>These colors match the 16 colors defined for ANSI escape sequences for 4-bit (16) colors.</para>
 ///     <para>
 ///         For terminals that support 24-bit color (TrueColor), the RGB values for each of these colors can be
-///         configured using the <see cref="Color.Colors"/> property.
+///         configured using the <see cref="Color.Colors16"/> property.
 ///     </para>
 /// </remarks>
-public enum ColorName
+public enum ColorName16
 {
     /// <summary>The black color. ANSI escape sequence: <c>\u001b[30m</c>.</summary>
     Black,

+ 180 - 177
Terminal.Gui/Drawing/Color.Formatting.cs

@@ -97,49 +97,49 @@ public readonly partial record struct Color
     )
     {
         return (formatString, formatProvider) switch
-               {
-                   // Null or empty string and null formatProvider - Revert to 'g' case behavior
-                   (null or { Length: 0 }, null) => ToString (),
-
-                   // Null or empty string and formatProvider is an ICustomColorFormatter - Output according to the given ICustomColorFormatted, with R, G, B, and A as typed arguments
-                   (null or { Length: 0 }, ICustomColorFormatter ccf) => ccf.Format (null, R, G, B, A),
-
-                   // Null or empty string and formatProvider is otherwise non-null but not the invariant culture - Output according to string.Format with the given IFormatProvider and R, G, B, and A as boxed arguments, with string.Empty as the format string
-                   (null or { Length: 0 }, { }) when !Equals (formatProvider, CultureInfo.InvariantCulture) =>
-                       string.Format (formatProvider, formatString ?? string.Empty, R, G, B, A),
-
-                   // Null or empty string and formatProvider is the invariant culture - Output according to string.Format with the given IFormatProvider and R, G, B, and A as boxed arguments, with string.Empty as the format string
-                   (null or { Length: 0 }, { }) when Equals (formatProvider, CultureInfo.InvariantCulture) =>
-                       $"#{R:X2}{G:X2}{B:X2}",
-
-                   // Non-null string and non-null formatProvider - let formatProvider handle it and give it R, G, B, and A
-                   ({ }, { }) => string.Format (formatProvider, CompositeFormat.Parse (formatString), R, G, B, A),
-
-                   // g format string and null formatProvider - Output as 24-bit hex according to invariant culture rules from R, G, and B
-                   (['g'], null) => ToString (),
-
-                   // G format string and null formatProvider - Output as 32-bit hex according to invariant culture rules from Argb
-                   (['G'], null) => $"#{A:X2}{R:X2}{G:X2}{B:X2}",
-
-                   // d format string and null formatProvider - Output as 24-bit decimal rgb(r,g,b) according to invariant culture rules from R, G, and B
-                   (['d'], null) => $"rgb({R:D},{G:D},{B:D})",
-
-                   // D format string and null formatProvider - Output as 32-bit decimal rgba(r,g,b,a) according to invariant culture rules from R, G, B, and A. Non-standard: a is a decimal byte value.
-                   (['D'], null) => $"rgba({R:D},{G:D},{B:D},{A:D})",
-
-                   // All other cases (formatString is not null here) - Delegate to formatProvider, first, and otherwise to invariant culture, and try to format the provided string from the channels
-                   ({ }, _) => string.Format (
-                                              formatProvider ?? CultureInfo.InvariantCulture,
-                                              CompositeFormat.Parse (formatString),
-                                              R,
-                                              G,
-                                              B,
-                                              A
-                                             ),
-                   _ => throw new InvalidOperationException (
-                                                             $"Unable to create string from Color with value {Argb}, using format string {formatString}"
-                                                            )
-               }
+        {
+            // Null or empty string and null formatProvider - Revert to 'g' case behavior
+            (null or { Length: 0 }, null) => ToString (),
+
+            // Null or empty string and formatProvider is an ICustomColorFormatter - Output according to the given ICustomColorFormatted, with R, G, B, and A as typed arguments
+            (null or { Length: 0 }, ICustomColorFormatter ccf) => ccf.Format (null, R, G, B, A),
+
+            // Null or empty string and formatProvider is otherwise non-null but not the invariant culture - Output according to string.Format with the given IFormatProvider and R, G, B, and A as boxed arguments, with string.Empty as the format string
+            (null or { Length: 0 }, { }) when !Equals (formatProvider, CultureInfo.InvariantCulture) =>
+                string.Format (formatProvider, formatString ?? string.Empty, R, G, B, A),
+
+            // Null or empty string and formatProvider is the invariant culture - Output according to string.Format with the given IFormatProvider and R, G, B, and A as boxed arguments, with string.Empty as the format string
+            (null or { Length: 0 }, { }) when Equals (formatProvider, CultureInfo.InvariantCulture) =>
+                $"#{R:X2}{G:X2}{B:X2}",
+
+            // Non-null string and non-null formatProvider - let formatProvider handle it and give it R, G, B, and A
+            ({ }, { }) => string.Format (formatProvider, CompositeFormat.Parse (formatString), R, G, B, A),
+
+            // g format string and null formatProvider - Output as 24-bit hex according to invariant culture rules from R, G, and B
+            ( ['g'], null) => ToString (),
+
+            // G format string and null formatProvider - Output as 32-bit hex according to invariant culture rules from Argb
+            ( ['G'], null) => $"#{A:X2}{R:X2}{G:X2}{B:X2}",
+
+            // d format string and null formatProvider - Output as 24-bit decimal rgb(r,g,b) according to invariant culture rules from R, G, and B
+            ( ['d'], null) => $"rgb({R:D},{G:D},{B:D})",
+
+            // D format string and null formatProvider - Output as 32-bit decimal rgba(r,g,b,a) according to invariant culture rules from R, G, B, and A. Non-standard: a is a decimal byte value.
+            ( ['D'], null) => $"rgba({R:D},{G:D},{B:D},{A:D})",
+
+            // All other cases (formatString is not null here) - Delegate to formatProvider, first, and otherwise to invariant culture, and try to format the provided string from the channels
+            ({ }, _) => string.Format (
+                                       formatProvider ?? CultureInfo.InvariantCulture,
+                                       CompositeFormat.Parse (formatString),
+                                       R,
+                                       G,
+                                       B,
+                                       A
+                                      ),
+            _ => throw new InvalidOperationException (
+                                                      $"Unable to create string from Color with value {Argb}, using format string {formatString}"
+                                                     )
+        }
                ?? throw new InvalidOperationException (
                                                        $"Unable to create string from Color with value {Argb}, using format string {formatString}"
                                                       );
@@ -205,7 +205,7 @@ public readonly partial record struct Color
     /// <summary>Converts the provided <see langword="string"/> to a new <see cref="Color"/> value.</summary>
     /// <param name="text">
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
-    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="Gui.ColorName"/> string values.
+    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="ColorName16"/> string values.
     /// </param>
     /// <param name="formatProvider">
     ///     If specified and not <see langword="null"/>, will be passed to
@@ -246,7 +246,7 @@ public readonly partial record struct Color
     /// </summary>
     /// <param name="text">
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#RGBA", "#AARRGGBB", "rgb(r,g,b)",
-    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="Gui.ColorName"/> string values.
+    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="ColorName16"/> string values.
     /// </param>
     /// <param name="formatProvider">
     ///     Optional <see cref="IFormatProvider"/> to provide parsing services for the input text.
@@ -265,95 +265,95 @@ public readonly partial record struct Color
     public static Color Parse (ReadOnlySpan<char> text, IFormatProvider? formatProvider = null)
     {
         return text switch
-               {
-                   // Null string or empty span provided
-                   { IsEmpty: true } when formatProvider is null => throw new ColorParseException (
-                                                                                                   in text,
-                                                                                                   "The text provided was null or empty.",
-                                                                                                   in text
-                                                                                                  ),
-
-                   // A valid ICustomColorFormatter was specified and the text wasn't null or empty
-                   { IsEmpty: false } when formatProvider is ICustomColorFormatter f => f.Parse (text),
-
-                   // Input string is only whitespace
-                   { Length: > 0 } when text.IsWhiteSpace () => throw new ColorParseException (
-                                                                                               in text,
-                                                                                               "The text provided consisted of only whitespace characters.",
-                                                                                               in text
-                                                                                              ),
-
-                   // Any string too short to possibly be any supported format.
-                   { Length: > 0 and < 3 } => throw new ColorParseException (
-                                                                             in text,
-                                                                             "Text was too short to be any possible supported format.",
-                                                                             in text
-                                                                            ),
-
-                   // The various hexadecimal cases
-                   ['#', ..] hexString => hexString switch
-                                          {
-                                              // #RGB
-                                              ['#', var rChar, var gChar, var bChar] chars when chars [1..]
-                                                      .IsAllAsciiHexDigits () =>
-                                                  new Color (
-                                                             byte.Parse ([rChar, rChar], NumberStyles.HexNumber),
-                                                             byte.Parse ([gChar, gChar], NumberStyles.HexNumber),
-                                                             byte.Parse ([bChar, bChar], NumberStyles.HexNumber)
-                                                            ),
-
-                                              // #ARGB
-                                              ['#', var aChar, var rChar, var gChar, var bChar] chars when chars [1..]
-                                                      .IsAllAsciiHexDigits () =>
-                                                  new Color (
-                                                             byte.Parse ([rChar, rChar], NumberStyles.HexNumber),
-                                                             byte.Parse ([gChar, gChar], NumberStyles.HexNumber),
-                                                             byte.Parse ([bChar, bChar], NumberStyles.HexNumber),
-                                                             byte.Parse ([aChar, aChar], NumberStyles.HexNumber)
-                                                            ),
-
-                                              // #RRGGBB
-                                              [
-                                                      '#', var r1Char, var r2Char, var g1Char, var g2Char, var b1Char,
-                                                      var b2Char
-                                                  ] chars when chars [1..].IsAllAsciiHexDigits () =>
-                                                  new Color (
-                                                             byte.Parse ([r1Char, r2Char], NumberStyles.HexNumber),
-                                                             byte.Parse ([g1Char, g2Char], NumberStyles.HexNumber),
-                                                             byte.Parse ([b1Char, b2Char], NumberStyles.HexNumber)
-                                                            ),
-
-                                              // #AARRGGBB
-                                              [
-                                                      '#', var a1Char, var a2Char, var r1Char, var r2Char, var g1Char,
-                                                      var g2Char, var b1Char, var b2Char
-                                                  ] chars when chars [1..].IsAllAsciiHexDigits () =>
-                                                  new Color (
-                                                             byte.Parse ([r1Char, r2Char], NumberStyles.HexNumber),
-                                                             byte.Parse ([g1Char, g2Char], NumberStyles.HexNumber),
-                                                             byte.Parse ([b1Char, b2Char], NumberStyles.HexNumber),
-                                                             byte.Parse ([a1Char, a2Char], NumberStyles.HexNumber)
-                                                            ),
-                                              _ => throw new ColorParseException (
-                                                                                  in hexString,
-                                                                                  $"Color hex string {hexString} was not in a supported format",
-                                                                                  in hexString
-                                                                                 )
-                                          },
-
-                   // rgb(r,g,b) or rgb(r,g,b,a)
-                   ['r', 'g', 'b', '(', .., ')'] => ParseRgbaFormat (in text, 4),
-
-                   // rgba(r,g,b,a) or rgba(r,g,b)
-                   ['r', 'g', 'b', 'a', '(', .., ')'] => ParseRgbaFormat (in text, 5),
-
-                   // Attempt to parse as a named color from the ColorName enum
-                   { } when char.IsLetter (text [0]) && Enum.TryParse (text, true, out ColorName colorName) =>
-                       new Color (colorName),
-
-                   // Any other input
-                   _ => throw new ColorParseException (in text, "Text did not match any expected format.", in text, [])
-               };
+        {
+            // Null string or empty span provided
+            { IsEmpty: true } when formatProvider is null => throw new ColorParseException (
+                                                                                            in text,
+                                                                                            "The text provided was null or empty.",
+                                                                                            in text
+                                                                                           ),
+
+            // A valid ICustomColorFormatter was specified and the text wasn't null or empty
+            { IsEmpty: false } when formatProvider is ICustomColorFormatter f => f.Parse (text),
+
+            // Input string is only whitespace
+            { Length: > 0 } when text.IsWhiteSpace () => throw new ColorParseException (
+                                                                                        in text,
+                                                                                        "The text provided consisted of only whitespace characters.",
+                                                                                        in text
+                                                                                       ),
+
+            // Any string too short to possibly be any supported format.
+            { Length: > 0 and < 3 } => throw new ColorParseException (
+                                                                      in text,
+                                                                      "Text was too short to be any possible supported format.",
+                                                                      in text
+                                                                     ),
+
+                                                                     // The various hexadecimal cases
+                                                                     ['#', ..] hexString => hexString switch
+                                                                     {
+                                                                     // #RGB
+                                                                     ['#', var rChar, var gChar, var bChar] chars when chars [1..]
+                                                                                    .IsAllAsciiHexDigits () =>
+                                                                                new Color (
+                                                                                           byte.Parse ([rChar, rChar], NumberStyles.HexNumber),
+                                                                                           byte.Parse ([gChar, gChar], NumberStyles.HexNumber),
+                                                                                           byte.Parse ([bChar, bChar], NumberStyles.HexNumber)
+                                                                                          ),
+
+                                                                                          // #ARGB
+                                                                                          ['#', var aChar, var rChar, var gChar, var bChar] chars when chars [1..]
+                                                                                                         .IsAllAsciiHexDigits () =>
+                                                                                                     new Color (
+                                                                                                                byte.Parse ([rChar, rChar], NumberStyles.HexNumber),
+                                                                                                                byte.Parse ([gChar, gChar], NumberStyles.HexNumber),
+                                                                                                                byte.Parse ([bChar, bChar], NumberStyles.HexNumber),
+                                                                                                                byte.Parse ([aChar, aChar], NumberStyles.HexNumber)
+                                                                                                               ),
+
+                                                                                                               // #RRGGBB
+                                                                                                               [
+                                                                                         '#', var r1Char, var r2Char, var g1Char, var g2Char, var b1Char,
+                                                                                         var b2Char
+                                                                                     ] chars when chars [1..].IsAllAsciiHexDigits () =>
+                                                                                     new Color (
+                                                                                                byte.Parse ([r1Char, r2Char], NumberStyles.HexNumber),
+                                                                                                byte.Parse ([g1Char, g2Char], NumberStyles.HexNumber),
+                                                                                                byte.Parse ([b1Char, b2Char], NumberStyles.HexNumber)
+                                                                                               ),
+
+                                                                                               // #AARRGGBB
+                                                                                               [
+                                                                                         '#', var a1Char, var a2Char, var r1Char, var r2Char, var g1Char,
+                                                                                         var g2Char, var b1Char, var b2Char
+                                                                                     ] chars when chars [1..].IsAllAsciiHexDigits () =>
+                                                                                     new Color (
+                                                                                                byte.Parse ([r1Char, r2Char], NumberStyles.HexNumber),
+                                                                                                byte.Parse ([g1Char, g2Char], NumberStyles.HexNumber),
+                                                                                                byte.Parse ([b1Char, b2Char], NumberStyles.HexNumber),
+                                                                                                byte.Parse ([a1Char, a2Char], NumberStyles.HexNumber)
+                                                                                               ),
+                                                                         _ => throw new ColorParseException (
+                                                                                                                    in hexString,
+                                                                                                                    $"Color hex string {hexString} was not in a supported format",
+                                                                                                                    in hexString
+                                                                                                                   )
+                                                                     },
+
+                                                                     // rgb(r,g,b) or rgb(r,g,b,a)
+                                                                     ['r', 'g', 'b', '(', .., ')'] => ParseRgbaFormat (in text, 4),
+
+                                                                     // rgba(r,g,b,a) or rgba(r,g,b)
+                                                                     ['r', 'g', 'b', 'a', '(', .., ')'] => ParseRgbaFormat (in text, 5),
+
+            // Attempt to parse as a named color from the ColorStrings resources
+            { } when char.IsLetter (text [0]) && ColorStrings.TryParseW3CColorName (text.ToString (), out Color color) =>
+                new Color (color),
+
+            // Any other input
+            _ => throw new ColorParseException (in text, "Text did not match any expected format.", in text, [])
+        };
 
         [Pure]
         [SkipLocalsInit]
@@ -372,44 +372,44 @@ public readonly partial record struct Color
             switch (rangeCount)
             {
                 case 3:
-                {
-                    // rgba(r,g,b)
-                    ParseRgbValues (
-                                    in valuesSubstring,
-                                    in valueRanges,
-                                    in originalString,
-                                    out ReadOnlySpan<char> rSpan,
-                                    out ReadOnlySpan<char> gSpan,
-                                    out ReadOnlySpan<char> bSpan
-                                   );
-
-                    return new Color (int.Parse (rSpan), int.Parse (gSpan), int.Parse (bSpan));
-                }
+                    {
+                        // rgba(r,g,b)
+                        ParseRgbValues (
+                                        in valuesSubstring,
+                                        in valueRanges,
+                                        in originalString,
+                                        out ReadOnlySpan<char> rSpan,
+                                        out ReadOnlySpan<char> gSpan,
+                                        out ReadOnlySpan<char> bSpan
+                                       );
+
+                        return new Color (int.Parse (rSpan), int.Parse (gSpan), int.Parse (bSpan));
+                    }
                 case 4:
-                {
-                    // rgba(r,g,b,a)
-                    ParseRgbValues (
-                                    in valuesSubstring,
-                                    in valueRanges,
-                                    in originalString,
-                                    out ReadOnlySpan<char> rSpan,
-                                    out ReadOnlySpan<char> gSpan,
-                                    out ReadOnlySpan<char> bSpan
-                                   );
-                    ReadOnlySpan<char> aSpan = valuesSubstring [valueRanges [3]];
-
-                    if (!aSpan.IsAllAsciiDigits ())
                     {
-                        throw new ColorParseException (
-                                                       in originalString,
-                                                       "Value was not composed entirely of decimal digits.",
-                                                       in aSpan,
-                                                       nameof (A)
-                                                      );
+                        // rgba(r,g,b,a)
+                        ParseRgbValues (
+                                        in valuesSubstring,
+                                        in valueRanges,
+                                        in originalString,
+                                        out ReadOnlySpan<char> rSpan,
+                                        out ReadOnlySpan<char> gSpan,
+                                        out ReadOnlySpan<char> bSpan
+                                       );
+                        ReadOnlySpan<char> aSpan = valuesSubstring [valueRanges [3]];
+
+                        if (!aSpan.IsAllAsciiDigits ())
+                        {
+                            throw new ColorParseException (
+                                                           in originalString,
+                                                           "Value was not composed entirely of decimal digits.",
+                                                           in aSpan,
+                                                           nameof (A)
+                                                          );
+                        }
+
+                        return new Color (int.Parse (rSpan), int.Parse (gSpan), int.Parse (bSpan), int.Parse (aSpan));
                     }
-
-                    return new Color (int.Parse (rSpan), int.Parse (gSpan), int.Parse (bSpan), int.Parse (aSpan));
-                }
                 default:
                     throw new ColorParseException (
                                                    in originalString,
@@ -471,7 +471,7 @@ public readonly partial record struct Color
     /// <summary>Converts the provided <see langword="string"/> to a new <see cref="Color"/> value.</summary>
     /// <param name="text">
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
-    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="GetClosestNamedColor (Color)"/> string
+    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> string
     ///     values.
     /// </param>
     /// <param name="formatProvider">
@@ -501,7 +501,7 @@ public readonly partial record struct Color
     /// </summary>
     /// <param name="text">
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
-    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="GetClosestNamedColor (Color)"/> string
+    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any W3C color name."/> string
     ///     values.
     /// </param>
     /// <param name="formatProvider">
@@ -585,17 +585,20 @@ public readonly partial record struct Color
     [SkipLocalsInit]
     public override string ToString ()
     {
-        // If Values has an exact match with a named color (in _colorNames), use that.
-        return ColorExtensions.ColorToNameMap.TryGetValue (this, out ColorName colorName)
-                   ? Enum.GetName (typeof (ColorName), colorName) ?? $"#{R:X2}{G:X2}{B:X2}"
-                   : // Otherwise return as an RGB hex value.
-                   $"#{R:X2}{G:X2}{B:X2}";
+        string? name = ColorStrings.GetW3CColorName (this);
+
+        if (name is { })
+        {
+            return name;
+        }
+
+        return $"#{R:X2}{G:X2}{B:X2}";
     }
 
     /// <summary>Converts the provided string to a new <see cref="Color"/> instance.</summary>
     /// <param name="text">
     ///     The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
-    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="Gui.ColorName"/> string values.
+    ///     "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="ColorName16"/> string values.
     /// </param>
     /// <param name="color">The parsed value.</param>
     /// <returns>A boolean value indicating whether parsing was successful.</returns>

+ 3 - 3
Terminal.Gui/Drawing/Color.Operators.cs

@@ -53,11 +53,11 @@ public readonly partial record struct Color
     public static implicit operator Color (uint u) { return new Color (u); }
 
     /// <summary>
-    ///     Implicit conversion from <see cref="GetClosestNamedColor (Color)"/> to <see cref="Color"/> via lookup from
-    ///     <see cref="ColorExtensions.ColorNameToColorMap"/>.
+    ///     Implicit conversion from <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> to <see cref="Color"/> via lookup from
+    ///     <see cref="ColorExtensions.ColorName16ToColorMap"/>.
     /// </summary>
     [Pure]
-    public static implicit operator Color (ColorName colorName) { return ColorExtensions.ColorNameToColorMap [colorName]; }
+    public static implicit operator Color (ColorName16 colorName) { return ColorExtensions.ColorName16ToColorMap [colorName]; }
 
     /// <summary>
     ///     Implicit conversion from <see cref="Vector4"/> to <see cref="Color"/>, where (<see cref="Vector4.X"/>,

+ 40 - 40
Terminal.Gui/Drawing/Color.cs

@@ -17,7 +17,7 @@ namespace Terminal.Gui;
 /// </summary>
 /// <seealso cref="Attribute"/>
 /// <seealso cref="ColorExtensions"/>
-/// <seealso cref="ColorName"/>
+/// <seealso cref="ColorName16"/>
 [JsonConverter (typeof (ColorJsonConverter))]
 [StructLayout (LayoutKind.Explicit)]
 public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanParsable<Color>, ISpanFormattable,
@@ -109,7 +109,7 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
 
     /// <summary>Initializes a new instance of the <see cref="Color"/> color from a legacy 16-color named value.</summary>
     /// <param name="colorName">The 16-color value.</param>
-    public Color (in ColorName colorName) { this = ColorExtensions.ColorNameToColorMap [colorName]; }
+    public Color (in ColorName16 colorName) { this = ColorExtensions.ColorName16ToColorMap [colorName]; }
 
     /// <summary>
     ///     Initializes a new instance of the <see cref="Color"/> color from string. See
@@ -131,26 +131,28 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     /// <summary>Initializes a new instance of the <see cref="Color"/> with all channels set to 0.</summary>
     public Color () { Argb = 0u; }
 
+    // TODO: ColorName and AnsiColorCode are only needed when a driver is in Force16Color mode and we
+    // TODO: should be able to remove these from any non-Driver-specific usages.
     /// <summary>Gets or sets the 3-byte/6-character hexadecimal value for each of the legacy 16-color values.</summary>
     [SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
-    public static Dictionary<ColorName, string> Colors
+    public static Dictionary<ColorName16, string> Colors16
     {
         get =>
 
             // Transform _colorToNameMap into a Dictionary<ColorNames,string>
-            ColorExtensions.ColorToNameMap.ToDictionary (static kvp => kvp.Value, static kvp => kvp.Key.ToString ("g"));
+            ColorExtensions.ColorToName16Map.ToDictionary (static kvp => kvp.Value, static kvp => kvp.Key.ToString ("g"));
         set
         {
             // Transform Dictionary<ColorNames,string> into _colorToNameMap
-            ColorExtensions.ColorToNameMap = value.ToFrozenDictionary (GetColorToNameMapKey, GetColorToNameMapValue);
+            ColorExtensions.ColorToName16Map = value.ToFrozenDictionary (GetColorToNameMapKey, GetColorToNameMapValue);
 
             return;
 
-            static Color GetColorToNameMapKey (KeyValuePair<ColorName, string> kvp) { return new Color (kvp.Value); }
+            static Color GetColorToNameMapKey (KeyValuePair<ColorName16, string> kvp) { return new Color (kvp.Value); }
 
-            static ColorName GetColorToNameMapValue (KeyValuePair<ColorName, string> kvp)
+            static ColorName16 GetColorToNameMapValue (KeyValuePair<ColorName16, string> kvp)
             {
-                return Enum.TryParse (kvp.Key.ToString (), true, out ColorName colorName)
+                return Enum.TryParse (kvp.Key.ToString (), true, out ColorName16 colorName)
                            ? colorName
                            : throw new ArgumentException ($"Invalid color name: {kvp.Key}");
             }
@@ -158,31 +160,31 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     }
 
     /// <summary>
-    ///     Gets the <see cref="Color"/> using a legacy 16-color <see cref="ColorName"/> value. <see langword="get"/> will
+    ///     Gets the <see cref="Color"/> using a legacy 16-color <see cref="ColorName16"/> value. <see langword="get"/> will
     ///     return the closest 16 color match to the true color when no exact value is found.
     /// </summary>
     /// <remarks>
-    ///     Get returns the <see cref="GetClosestNamedColor (Color)"/> of the closest 24-bit color value. Set sets the RGB
+    ///     Get returns the <see cref="GetClosestNamedColor16(Color)"/> of the closest 24-bit color value. Set sets the RGB
     ///     value using a hard-coded map.
     /// </remarks>
-    public AnsiColorCode GetAnsiColorCode () { return ColorExtensions.ColorNameToAnsiColorMap [GetClosestNamedColor ()]; }
+    public AnsiColorCode GetAnsiColorCode () { return ColorExtensions.ColorName16ToAnsiColorMap [GetClosestNamedColor16 ()]; }
 
     /// <summary>
-    ///     Gets the <see cref="Color"/> using a legacy 16-color <see cref="Gui.ColorName"/> value. <see langword="get"/>
+    ///     Gets the <see cref="Color"/> using a legacy 16-color <see cref="ColorName16"/> value. <see langword="get"/>
     ///     will return the closest 16 color match to the true color when no exact value is found.
     /// </summary>
     /// <remarks>
-    ///     Get returns the <see cref="GetClosestNamedColor (Color)"/> of the closest 24-bit color value. Set sets the RGB
+    ///     Get returns the <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> of the closest 24-bit color value. Set sets the RGB
     ///     value using a hard-coded map.
     /// </remarks>
-    public ColorName GetClosestNamedColor () { return GetClosestNamedColor (this); }
+    public ColorName16 GetClosestNamedColor16 () { return GetClosestNamedColor16 (this); }
 
     /// <summary>
     ///     Determines if the closest named <see cref="Color"/> to <see langword="this"/> is the provided
     ///     <paramref name="namedColor"/>.
     /// </summary>
     /// <param name="namedColor">
-    ///     The <see cref="GetClosestNamedColor (Color)"/> to check if this <see cref="Color"/> is closer
+    ///     The <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> to check if this <see cref="Color"/> is closer
     ///     to than any other configured named color.
     /// </param>
     /// <returns>
@@ -195,18 +197,18 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     /// </remarks>
     [Pure]
     [MethodImpl (MethodImplOptions.AggressiveInlining)]
-    public bool IsClosestToNamedColor (in ColorName namedColor) { return GetClosestNamedColor () == namedColor; }
+    public bool IsClosestToNamedColor16 (in ColorName16 namedColor) { return GetClosestNamedColor16 () == namedColor; }
 
     /// <summary>
     ///     Determines if the closest named <see cref="Color"/> to <paramref name="color"/>/> is the provided
     ///     <paramref name="namedColor"/>.
     /// </summary>
     /// <param name="color">
-    ///     The color to test against the <see cref="GetClosestNamedColor (Color)"/> value in
+    ///     The color to test against the <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> value in
     ///     <paramref name="namedColor"/>.
     /// </param>
     /// <param name="namedColor">
-    ///     The <see cref="GetClosestNamedColor (Color)"/> to check if this <see cref="Color"/> is closer
+    ///     The <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> to check if this <see cref="Color"/> is closer
     ///     to than any other configured named color.
     /// </param>
     /// <returns>
@@ -220,20 +222,18 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     /// </remarks>
     [Pure]
     [MethodImpl (MethodImplOptions.AggressiveInlining)]
-    public static bool IsColorClosestToNamedColor (in Color color, in ColorName namedColor) { return color.IsClosestToNamedColor (in namedColor); }
+    public static bool IsColorClosestToNamedColor16 (in Color color, in ColorName16 namedColor) { return color.IsClosestToNamedColor16 (in namedColor); }
 
     /// <summary>Gets the "closest" named color to this <see cref="Color"/> value.</summary>
     /// <param name="inputColor"></param>
     /// <remarks>
     ///     Distance is defined here as the Euclidean distance between each color interpreted as a <see cref="Vector3"/>.
-    ///     <para/>
-    ///     The order of the values in the passed Vector3 must be
     /// </remarks>
     /// <returns></returns>
     [SkipLocalsInit]
-    internal static ColorName GetClosestNamedColor (Color inputColor)
+    internal static ColorName16 GetClosestNamedColor16 (Color inputColor)
     {
-        return ColorExtensions.ColorToNameMap.MinBy (pair => CalculateColorDistance (inputColor, pair.Key)).Value;
+        return ColorExtensions.ColorToName16Map.MinBy (pair => CalculateColorDistance (inputColor, pair.Key)).Value;
     }
 
     [SkipLocalsInit]
@@ -246,7 +246,7 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     public Color GetHighlightColor ()
     {
         // TODO: This is a temporary implementation; just enough to show how it could work. 
-        var hsl = ColorHelper.ColorConverter.RgbToHsl(new RGB (R, G, B));
+        var hsl = ColorHelper.ColorConverter.RgbToHsl (new RGB (R, G, B));
 
         var amount = .7;
         if (hsl.L <= 5)
@@ -284,52 +284,52 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     #region Legacy Color Names
 
     /// <summary>The black color.</summary>
-    public const ColorName Black = ColorName.Black;
+    public const ColorName16 Black = ColorName16.Black;
 
     /// <summary>The blue color.</summary>
-    public const ColorName Blue = ColorName.Blue;
+    public const ColorName16 Blue = ColorName16.Blue;
 
     /// <summary>The green color.</summary>
-    public const ColorName Green = ColorName.Green;
+    public const ColorName16 Green = ColorName16.Green;
 
     /// <summary>The cyan color.</summary>
-    public const ColorName Cyan = ColorName.Cyan;
+    public const ColorName16 Cyan = ColorName16.Cyan;
 
     /// <summary>The red color.</summary>
-    public const ColorName Red = ColorName.Red;
+    public const ColorName16 Red = ColorName16.Red;
 
     /// <summary>The magenta color.</summary>
-    public const ColorName Magenta = ColorName.Magenta;
+    public const ColorName16 Magenta = ColorName16.Magenta;
 
     /// <summary>The yellow color.</summary>
-    public const ColorName Yellow = ColorName.Yellow;
+    public const ColorName16 Yellow = ColorName16.Yellow;
 
     /// <summary>The gray color.</summary>
-    public const ColorName Gray = ColorName.Gray;
+    public const ColorName16 Gray = ColorName16.Gray;
 
     /// <summary>The dark gray color.</summary>
-    public const ColorName DarkGray = ColorName.DarkGray;
+    public const ColorName16 DarkGray = ColorName16.DarkGray;
 
     /// <summary>The bright bBlue color.</summary>
-    public const ColorName BrightBlue = ColorName.BrightBlue;
+    public const ColorName16 BrightBlue = ColorName16.BrightBlue;
 
     /// <summary>The bright green color.</summary>
-    public const ColorName BrightGreen = ColorName.BrightGreen;
+    public const ColorName16 BrightGreen = ColorName16.BrightGreen;
 
     /// <summary>The bright cyan color.</summary>
-    public const ColorName BrightCyan = ColorName.BrightCyan;
+    public const ColorName16 BrightCyan = ColorName16.BrightCyan;
 
     /// <summary>The bright red color.</summary>
-    public const ColorName BrightRed = ColorName.BrightRed;
+    public const ColorName16 BrightRed = ColorName16.BrightRed;
 
     /// <summary>The bright magenta color.</summary>
-    public const ColorName BrightMagenta = ColorName.BrightMagenta;
+    public const ColorName16 BrightMagenta = ColorName16.BrightMagenta;
 
     /// <summary>The bright yellow color.</summary>
-    public const ColorName BrightYellow = ColorName.BrightYellow;
+    public const ColorName16 BrightYellow = ColorName16.BrightYellow;
 
     /// <summary>The White color.</summary>
-    public const ColorName White = ColorName.White;
+    public const ColorName16 White = ColorName16.White;
 
     #endregion
 }

+ 43 - 55
Terminal.Gui/Drawing/ColorScheme.cs

@@ -15,12 +15,6 @@ namespace Terminal.Gui;
 [JsonConverter (typeof (ColorSchemeJsonConverter))]
 public record ColorScheme : IEqualityOperators<ColorScheme, ColorScheme, bool>
 {
-    private readonly Attribute _disabled;
-    private readonly Attribute _focus;
-    private readonly Attribute _hotFocus;
-    private readonly Attribute _hotNormal;
-    private readonly Attribute _normal;
-
     /// <summary>Creates a new instance set to the default colors (see <see cref="Attribute.Default"/>).</summary>
     public ColorScheme () : this (Attribute.Default) { }
 
@@ -30,22 +24,22 @@ public record ColorScheme : IEqualityOperators<ColorScheme, ColorScheme, bool>
     {
         ArgumentNullException.ThrowIfNull (scheme);
 
-        _normal = scheme.Normal;
-        _focus = scheme.Focus;
-        _hotNormal = scheme.HotNormal;
-        _disabled = scheme.Disabled;
-        _hotFocus = scheme.HotFocus;
+        Normal = scheme.Normal;
+        Focus = scheme.Focus;
+        HotNormal = scheme.HotNormal;
+        Disabled = scheme.Disabled;
+        HotFocus = scheme.HotFocus;
     }
 
     /// <summary>Creates a new instance, initialized with the values from <paramref name="attribute"/>.</summary>
     /// <param name="attribute">The attribute to initialize the new instance with.</param>
     public ColorScheme (Attribute attribute)
     {
-        _normal = attribute;
-        _focus = attribute;
-        _hotNormal = attribute;
-        _disabled = attribute;
-        _hotFocus = attribute;
+        Normal = attribute;
+        Focus = attribute;
+        HotNormal = attribute;
+        Disabled = attribute;
+        HotFocus = attribute;
     }
 
     /// <summary>Creates a new instance, initialized with the values provided.</summary>
@@ -54,48 +48,45 @@ public record ColorScheme : IEqualityOperators<ColorScheme, ColorScheme, bool>
         Attribute focus,
         Attribute hotNormal,
         Attribute disabled,
-        Attribute hotFocus)
+        Attribute hotFocus
+    )
     {
-        _normal = normal;
-        _focus = focus;
-        _hotNormal = hotNormal;
-        _disabled = disabled;
-        _hotFocus = hotFocus;
+        Normal = normal;
+        Focus = focus;
+        HotNormal = hotNormal;
+        Disabled = disabled;
+        HotFocus = hotFocus;
     }
 
     /// <summary>The default foreground and background color for text when the view is disabled.</summary>
-    public Attribute Disabled
-    {
-        get => _disabled;
-        init => _disabled = value;
-    }
+    public Attribute Disabled { get; init; }
 
     /// <summary>The foreground and background color for text when the view has the focus.</summary>
-    public Attribute Focus
-    {
-        get => _focus;
-        init => _focus = value;
-    }
+    public Attribute Focus { get; init; }
 
     /// <summary>The foreground and background color for text in a focused view that indicates a <see cref="View.HotKey"/>.</summary>
-    public Attribute HotFocus
-    {
-        get => _hotFocus;
-        init => _hotFocus = value;
-    }
+    public Attribute HotFocus { get; init; }
 
     /// <summary>The foreground and background color for text in a non-focused view that indicates a <see cref="View.HotKey"/>.</summary>
-    public Attribute HotNormal
-    {
-        get => _hotNormal;
-        init => _hotNormal = value;
-    }
+    public Attribute HotNormal { get; init; }
 
     /// <summary>The foreground and background color for text when the view is not focused, hot, or disabled.</summary>
-    public Attribute Normal
+    public Attribute Normal { get; init; }
+
+    /// <summary>
+    ///     Gets a new <see cref="ColorScheme"/> with the same values as this instance, but with the foreground and background
+    ///     colors adjusted to be more visible.
+    /// </summary>
+    /// <returns></returns>
+    public ColorScheme GetHighlightColorScheme ()
     {
-        get => _normal;
-        init => _normal = value;
+        return this with
+        {
+            Normal = new (Normal.Foreground.GetHighlightColor (), Normal.Background),
+            HotNormal = new (HotNormal.Foreground.GetHighlightColor (), HotNormal.Background),
+            Focus = new (Focus.Foreground.GetHighlightColor (), Focus.Background),
+            HotFocus = new (HotFocus.Foreground.GetHighlightColor (), HotFocus.Background)
+        };
     }
 
     /// <summary>Compares two <see cref="ColorScheme"/> objects for equality.</summary>
@@ -104,20 +95,17 @@ public record ColorScheme : IEqualityOperators<ColorScheme, ColorScheme, bool>
     public virtual bool Equals (ColorScheme? other)
     {
         return other is { }
-               && EqualityComparer<Attribute>.Default.Equals (_normal, other._normal)
-               && EqualityComparer<Attribute>.Default.Equals (_focus, other._focus)
-               && EqualityComparer<Attribute>.Default.Equals (_hotNormal, other._hotNormal)
-               && EqualityComparer<Attribute>.Default.Equals (_hotFocus, other._hotFocus)
-               && EqualityComparer<Attribute>.Default.Equals (_disabled, other._disabled);
+               && EqualityComparer<Attribute>.Default.Equals (Normal, other.Normal)
+               && EqualityComparer<Attribute>.Default.Equals (Focus, other.Focus)
+               && EqualityComparer<Attribute>.Default.Equals (HotNormal, other.HotNormal)
+               && EqualityComparer<Attribute>.Default.Equals (HotFocus, other.HotFocus)
+               && EqualityComparer<Attribute>.Default.Equals (Disabled, other.Disabled);
     }
 
     /// <summary>Returns a hashcode for this instance.</summary>
     /// <returns>hashcode for this instance</returns>
-    public override int GetHashCode ()
-    {
-        return HashCode.Combine (_normal, _focus, _hotNormal, _hotFocus, _disabled);
-    }
+    public override int GetHashCode () { return HashCode.Combine (Normal, Focus, HotNormal, HotFocus, Disabled); }
 
     /// <inheritdoc/>
     public override string ToString () { return $"Normal: {Normal}; Focus: {Focus}; HotNormal: {HotNormal}; HotFocus: {HotFocus}; Disabled: {Disabled}"; }
-}
+}

+ 30 - 27
Terminal.Gui/Drawing/ColorStrings.cs

@@ -1,6 +1,7 @@
 #nullable enable
 using System.Collections;
 using System.Globalization;
+using System.Resources;
 using Terminal.Gui.Resources;
 
 namespace Terminal.Gui;
@@ -10,6 +11,8 @@ namespace Terminal.Gui;
 /// </summary>
 public static class ColorStrings
 {
+    // PERFORMANCE: See https://stackoverflow.com/a/15521524/297526 for why GlobalResources.GetString is fast.
+
     /// <summary>
     ///     Gets the W3C standard string for <paramref name="color"/>.
     /// </summary>
@@ -17,7 +20,6 @@ public static class ColorStrings
     /// <returns><see langword="null"/> if there is no standard color name for the specified color.</returns>
     public static string? GetW3CColorName (Color color)
     {
-        // Fetch the color name from the resource file
         return GlobalResources.GetString ($"#{color.R:X2}{color.G:X2}{color.B:X2}", CultureInfo.CurrentUICulture);
     }
 
@@ -27,18 +29,18 @@ public static class ColorStrings
     /// <returns></returns>
     public static IEnumerable<string> GetW3CColorNames ()
     {
-        foreach (DictionaryEntry entry in GlobalResources.GetResourceSet (
-                                                                          CultureInfo.CurrentUICulture,
-                                                                          true,
-                                                                          true,
-                                                                          e =>
-                                                                          {
-                                                                              string keyName = e.Key.ToString () ?? string.Empty;
+        ResourceSet? resourceSet = GlobalResources.GetResourceSet (CultureInfo.CurrentUICulture, true, true);
+        if (resourceSet == null)
+        {
+            yield break;
+        }
 
-                                                                              return e.Value is string && keyName.StartsWith ('#');
-                                                                          })!)
+        foreach (DictionaryEntry entry in resourceSet)
         {
-            yield return (entry.Value as string)!;
+            if (entry is { Value: string colorName, Key: string key } && key.StartsWith ('#'))
+            {
+                yield return colorName;
+            }
         }
     }
 
@@ -50,30 +52,31 @@ public static class ColorStrings
     /// <returns><see langword="true"/> if <paramref name="name"/> was parsed successfully.</returns>
     public static bool TryParseW3CColorName (string name, out Color color)
     {
-        // Iterate through all resource entries to find the matching color name
         foreach (DictionaryEntry entry in GlobalResources.GetResourceSet (CultureInfo.CurrentUICulture, true, true)!)
         {
             if (entry.Value is string colorName && colorName.Equals (name, StringComparison.OrdinalIgnoreCase))
             {
-                // Parse the key to extract the color components
-                string key = entry.Key.ToString () ?? string.Empty;
+                return TryParseColorKey (entry.Key.ToString (), out color);
+            }
+        }
 
-                if (key.StartsWith ("#") && key.Length == 7)
-                {
-                    if (int.TryParse (key.Substring (1, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int r)
-                        && int.TryParse (key.Substring (3, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int g)
-                        && int.TryParse (key.Substring (5, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int b))
-                    {
-                        color = new (r, g, b);
+        return TryParseColorKey (name, out color);
 
-                        return true;
-                    }
+        bool TryParseColorKey (string? key, out Color color)
+        {
+            if (key != null && key.StartsWith ('#') && key.Length == 7)
+            {
+                if (int.TryParse (key.AsSpan (1, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int r) &&
+                    int.TryParse (key.AsSpan (3, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int g) &&
+                    int.TryParse (key.AsSpan (5, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int b))
+                {
+                    color = new Color (r, g, b);
+                    return true;
                 }
             }
-        }
-
-        color = default (Color);
 
-        return false;
+            color = default (Color);
+            return false;
+        }
     }
 }

+ 28 - 5
Terminal.Gui/Drawing/Glyphs.cs

@@ -8,11 +8,14 @@
 ///     </para>
 ///     <para>
 ///         The default glyphs can be changed via the <see cref="ConfigurationManager"/>. Within a <c>config.json</c>
-///         file The Json property name is the property name prefixed with "Glyphs.".
+///         file the Json property name is the property name prefixed with "Glyphs.".
 ///     </para>
 ///     <para>
-///         The JSon property can be either a decimal number or a string. The string may be one of: - A unicode char
-///         (e.g. "☑") - A hex value in U+ format (e.g. "U+2611") - A hex value in UTF-16 format (e.g. "\\u2611")
+///         The Json property can be one of:
+///         - unicode glyph in a string (e.g. "☑")
+///         - U+hex format in a string  (e.g. "U+2611")
+///         - \u format in a string (e.g. "\\u2611")
+///         - A decimal number (e.g. 97 for "a")
 ///     </para>
 /// </remarks>
 public class GlyphDefinitions
@@ -106,6 +109,27 @@ public class GlyphDefinitions
     /// <summary>Identical To (U+226)</summary>
     public Rune IdenticalTo { get; set; } = (Rune)'≡';
 
+    /// <summary>Move indicator. Default is Lozenge (U+25CA) - ◊.</summary>
+    public Rune Move { get; set; } = (Rune)'◊';
+
+    /// <summary>Size Horizontally indicator. Default is ┥Left Right Arrow - ↔ U+02194</summary>
+    public Rune SizeHorizontal { get; set; } = (Rune)'↔';
+
+    /// <summary>Size Vertical indicator. Default Up Down Arrow - ↕ U+02195</summary>
+    public Rune SizeVertical { get; set; } = (Rune)'↕';
+
+    /// <summary>Size Top Left indicator. North West Arrow - ↖ U+02196</summary>
+    public Rune SizeTopLeft { get; set; } = (Rune)'↖';
+
+    /// <summary>Size Top Right indicator. North East Arrow - ↗ U+02197</summary>
+    public Rune SizeTopRight { get; set; } = (Rune)'↗';
+
+    /// <summary>Size Bottom Right indicator. South East Arrow - ↘ U+02198</summary>
+    public Rune SizeBottomRight { get; set; } = (Rune)'↘';
+
+    /// <summary>Size Bottom Left indicator. South West Arrow - ↙ U+02199</summary>
+    public Rune SizeBottomLeft { get; set; } = (Rune)'↙';
+
     /// <summary>Apple (non-BMP). Because snek. And because it's an example of a non-BMP surrogate pair. See Issue #2610.</summary>
     public Rune Apple { get; set; } = "🍎".ToRunes () [0]; // nonBMP
 
@@ -440,9 +464,8 @@ public class GlyphDefinitions
 
     #region ----------------- ShadowStyle -----------------
 
-
     /// <summary>Shadow - Vertical Start - Left Half Block - ▌ U+0258c</summary>
-    public Rune ShadowVerticalStart { get; set; } =  (Rune)'▖'; // Half: '\u2596'  ▖;
+    public Rune ShadowVerticalStart { get; set; } = (Rune)'▖'; // Half: '\u2596'  ▖;
 
     /// <summary>Shadow - Vertical - Left Half Block - ▌ U+0258c</summary>
     public Rune ShadowVertical { get; set; } = (Rune)'▌';

+ 164 - 142
Terminal.Gui/Input/Command.cs

@@ -6,157 +6,146 @@ namespace Terminal.Gui;
 /// <summary>Actions which can be performed by the application or bound to keys in a <see cref="View"/> control.</summary>
 public enum Command
 {
+    #region Default View Commands
+
     /// <summary>Invoked when the HotKey for the View has been pressed.</summary>
     HotKey,
 
-    /// <summary>Accepts the current state (e.g. list selection, button press, toggle, etc).</summary>
+    /// <summary>Accepts the current state (e.g. list selection, button press, toggle, etc.).</summary>
     Accept,
 
     /// <summary>Selects an item (e.g. a list item or menu item) without necessarily accepting it.</summary>
     Select,
 
-    /// <summary>Moves down one item (cell, line, etc...).</summary>
-    LineDown,
-
-    /// <summary>Extends the selection down one (cell, line, etc...).</summary>
-    LineDownExtend,
-
-    /// <summary>Moves down to the last child node of the branch that holds the current selection.</summary>
-    LineDownToLastBranch,
-
-    /// <summary>Scrolls down one (cell, line, etc...) (without changing the selection).</summary>
-    ScrollDown,
+    #endregion
 
-    // --------------------------------------------------------------------
+    #region Movement Commands
 
     /// <summary>Moves up one (cell, line, etc...).</summary>
-    LineUp,
+    Up,
 
-    /// <summary>Extends the selection up one item (cell, line, etc...).</summary>
-    LineUpExtend,
-
-    /// <summary>Moves up to the first child node of the branch that holds the current selection.</summary>
-    LineUpToFirstBranch,
-
-    /// <summary>Scrolls up one item (cell, line, etc...) (without changing the selection).</summary>
-    ScrollUp,
+    /// <summary>Moves down one item (cell, line, etc...).</summary>
+    Down,
 
     /// <summary>
-    ///     Moves the selection left one by the minimum increment supported by the <see cref="View"/> e.g. single
-    ///     character, cell, item etc.
+    ///     Moves left one (cell, line, etc...).
     /// </summary>
     Left,
 
-    /// <summary>Scrolls one item (cell, character, etc...) to the left</summary>
-    ScrollLeft,
-
     /// <summary>
-    ///     Extends the selection left one by the minimum increment supported by the view e.g. single character, cell,
-    ///     item etc.
-    /// </summary>
-    LeftExtend,
-
-    /// <summary>
-    ///     Moves the selection right one by the minimum increment supported by the view e.g. single character, cell, item
-    ///     etc.
+    ///     Moves right one (cell, line, etc...).
     /// </summary>
     Right,
 
-    /// <summary>Scrolls one item (cell, character, etc...) to the right.</summary>
-    ScrollRight,
+    /// <summary>Move one page up.</summary>
+    PageUp,
 
-    /// <summary>
-    ///     Extends the selection right one by the minimum increment supported by the view e.g. single character, cell,
-    ///     item etc.
-    /// </summary>
-    RightExtend,
+    /// <summary>Move one page down.</summary>
+    PageDown,
 
-    /// <summary>Moves the caret to the start of the previous word.</summary>
-    WordLeft,
+    /// <summary>Moves to the left page.</summary>
+    PageLeft,
 
-    /// <summary>Extends the selection to the start of the previous word.</summary>
-    WordLeftExtend,
+    /// <summary>Moves to the right page.</summary>
+    PageRight,
 
-    /// <summary>Moves the caret to the start of the next word.</summary>
-    WordRight,
+    /// <summary>Moves to the top of page.</summary>
+    StartOfPage,
 
-    /// <summary>Extends the selection to the start of the next word.</summary>
-    WordRightExtend,
+    /// <summary>Moves to the bottom of page.</summary>
+    EndOfPage,
 
-    /// <summary>Cuts to the clipboard the characters from the current position to the end of the line.</summary>
-    CutToEndLine,
+    /// <summary>Moves to the start (e.g. the top or home).</summary>
+    Start,
 
-    /// <summary>Cuts to the clipboard the characters from the current position to the start of the line.</summary>
-    CutToStartLine,
+    /// <summary>Moves to the end (e.g. the bottom).</summary>
+    End,
 
-    /// <summary>Deletes the characters forwards.</summary>
-    KillWordForwards,
+    /// <summary>Moves left to the start on the current row/line.</summary>
+    LeftStart,
 
-    /// <summary>Deletes the characters backwards.</summary>
-    KillWordBackwards,
+    /// <summary>Moves right to the end on the current row/line.</summary>
+    RightEnd,
+
+    /// <summary>Moves to the start of the previous word.</summary>
+    WordLeft,
+
+    /// <summary>Moves the start of the next word.</summary>
+    WordRight,
+
+    #endregion
+
+    #region Movement With Extension Commands
+
+    /// <summary>Extends the selection up one item (cell, line, etc...).</summary>
+    UpExtend,
+
+    /// <summary>Extends the selection down one (cell, line, etc...).</summary>
+    DownExtend,
 
     /// <summary>
-    ///     Toggles overwrite mode such that newly typed text overwrites the text that is already there (typically
-    ///     associated with the Insert key).
+    ///     Extends the selection left one item (cell, line, etc...)
     /// </summary>
-    ToggleOverwrite,
+    LeftExtend,
 
     /// <summary>
-    ///     Enables overwrite mode such that newly typed text overwrites the text that is already there (typically
-    ///     associated with the Insert key).
+    ///     Extends the selection right one item (cell, line, etc...)
     /// </summary>
-    EnableOverwrite,
+    RightExtend,
 
-    /// <summary>Disables overwrite mode (<see cref="EnableOverwrite"/>)</summary>
-    DisableOverwrite,
+    /// <summary>Extends the selection to the start of the previous word.</summary>
+    WordLeftExtend,
 
-    /// <summary>Move one page down.</summary>
-    PageDown,
+    /// <summary>Extends the selection to the start of the next word.</summary>
+    WordRightExtend,
 
     /// <summary>Move one page down extending the selection to cover revealed objects/characters.</summary>
     PageDownExtend,
 
-    /// <summary>Move one page up.</summary>
-    PageUp,
-
     /// <summary>Move one page up extending the selection to cover revealed objects/characters.</summary>
     PageUpExtend,
 
-    /// <summary>Moves to the top/home.</summary>
-    TopHome,
+    /// <summary>Extends the selection to start (e.g. home or top).</summary>
+    StartExtend,
 
-    /// <summary>Extends the selection to the top/home.</summary>
-    TopHomeExtend,
+    /// <summary>Extends the selection to end (e.g. bottom).</summary>
+    EndExtend,
 
-    /// <summary>Moves to the bottom/end.</summary>
-    BottomEnd,
+    /// <summary>Extends the selection to the start on the current row/line.</summary>
+    LeftStartExtend,
 
-    /// <summary>Extends the selection to the bottom/end.</summary>
-    BottomEndExtend,
+    /// <summary>Extends the selection to the right on the current row/line.</summary>
+    RightEndExtend,
 
-    /// <summary>Open the selected item.</summary>
-    OpenSelectedItem,
+    /// <summary>Toggles the selection.</summary>
+    ToggleExtend,
 
-    /// <summary>Toggles the Expanded or collapsed state of a list or item (with subitems).</summary>
-    ToggleExpandCollapse,
+    #endregion
 
-    /// <summary>Expands a list or item (with subitems).</summary>
-    Expand,
+    #region Editing Commands
 
-    /// <summary>Recursively Expands all child items and their child items (if any).</summary>
-    ExpandAll,
+    /// <summary>Deletes the characters forwards.</summary>
+    KillWordForwards,
 
-    /// <summary>Collapses a list or item (with subitems).</summary>
-    Collapse,
+    /// <summary>Deletes the characters backwards.</summary>
+    KillWordBackwards,
 
-    /// <summary>Recursively collapses a list items of their children (if any).</summary>
-    CollapseAll,
+    /// <summary>
+    ///     Toggles overwrite mode such that newly typed text overwrites the text that is already there (typically
+    ///     associated with the Insert key).
+    /// </summary>
+    ToggleOverwrite,
 
-    /// <summary>Cancels an action or any temporary states on the control e.g. expanding a combo list.</summary>
-    Cancel,
+    // QUESTION: What is the difference between EnableOverwrite and ToggleOverwrite?
 
-    /// <summary>Unix emulation.</summary>
-    UnixEmulation,
+    /// <summary>
+    ///     Enables overwrite mode such that newly typed text overwrites the text that is already there (typically
+    ///     associated with the Insert key).
+    /// </summary>
+    EnableOverwrite,
+
+    /// <summary>Disables overwrite mode (<see cref="EnableOverwrite"/>)</summary>
+    DisableOverwrite,
 
     /// <summary>Deletes the character on the right.</summary>
     DeleteCharRight,
@@ -170,41 +159,41 @@ public enum Command
     /// <summary>Deletes all objects.</summary>
     DeleteAll,
 
-    /// <summary>Moves the cursor to the start of line.</summary>
-    StartOfLine,
+    /// <summary>Inserts a new item.</summary>
+    NewLine,
 
-    /// <summary>Extends the selection to the start of line.</summary>
-    StartOfLineExtend,
+    /// <summary>Unix emulation.</summary>
+    UnixEmulation,
 
-    /// <summary>Moves the cursor to the end of line.</summary>
-    EndOfLine,
+    #endregion
 
-    /// <summary>Extends the selection to the end of line.</summary>
-    EndOfLineExtend,
+    #region Tree Commands
 
-    /// <summary>Moves the cursor to the top of page.</summary>
-    StartOfPage,
+    /// <summary>Moves down to the last child node of the branch that holds the current selection.</summary>
+    LineDownToLastBranch,
 
-    /// <summary>Moves the cursor to the bottom of page.</summary>
-    EndOfPage,
+    /// <summary>Moves up to the first child node of the branch that holds the current selection.</summary>
+    LineUpToFirstBranch,
 
-    /// <summary>Moves to the left page.</summary>
-    PageLeft,
+    #endregion
 
-    /// <summary>Moves to the right page.</summary>
-    PageRight,
+    #region Scroll Commands
 
-    /// <summary>Moves to the left begin.</summary>
-    LeftHome,
+    /// <summary>Scrolls down one (cell, line, etc...).</summary>
+    ScrollDown,
 
-    /// <summary>Extends the selection to the left begin.</summary>
-    LeftHomeExtend,
+    /// <summary>Scrolls up one item (cell, line, etc...).</summary>
+    ScrollUp,
 
-    /// <summary>Moves to the right end.</summary>
-    RightEnd,
+    /// <summary>Scrolls one item (cell, character, etc...) to the left.</summary>
+    ScrollLeft,
 
-    /// <summary>Extends the selection to the right end.</summary>
-    RightEndExtend,
+    /// <summary>Scrolls one item (cell, character, etc...) to the right.</summary>
+    ScrollRight,
+
+    #endregion
+
+    #region Clipboard Commands
 
     /// <summary>Undo changes.</summary>
     Undo,
@@ -221,35 +210,27 @@ public enum Command
     /// <summary>Pastes the current selection.</summary>
     Paste,
 
-    /// TODO: IRunnable: Rename to Command.Quit to make more generic.
-    /// <summary>Quit a <see cref="Toplevel"/>.</summary>
-    QuitToplevel,
-
-    /// TODO: Overlapped: Add Command.ShowHide
+    /// <summary>Cuts to the clipboard the characters from the current position to the end of the line.</summary>
+    CutToEndLine,
 
-    /// <summary>Suspend an application (Only implemented in <see cref="CursesDriver"/>).</summary>
-    Suspend,
+    /// <summary>Cuts to the clipboard the characters from the current position to the start of the line.</summary>
+    CutToStartLine,
 
-    /// <summary>Moves focus to the next view.</summary>
-    NextView,
+    #endregion
 
-    /// <summary>Moves focus to the previous view.</summary>
-    PreviousView,
+    #region Navigation Commands
 
-    /// <summary>Moves focus to the next view or Toplevel (case of Overlapped).</summary>
-    NextViewOrTop,
+    /// <summary>Moves focus to the next <see cref="TabBehavior.TabStop"/>.</summary>
+    NextTabStop,
 
-    /// <summary>Moves focus to the next previous or Toplevel (case of Overlapped).</summary>
-    PreviousViewOrTop,
+    /// <summary>Moves focus to the previous <see cref="TabBehavior.TabStop"/>.</summary>
+    PreviousTabStop,
 
-    /// <summary>Refresh.</summary>
-    Refresh,
+    /// <summary>Moves focus to the next <see cref="TabBehavior.TabGroup"/>.</summary>
+    NextTabGroup,
 
-    /// <summary>Toggles the selection.</summary>
-    ToggleExtend,
-
-    /// <summary>Inserts a new item.</summary>
-    NewLine,
+    /// <summary>Moves focus to the next<see cref="TabBehavior.TabGroup"/>.</summary>
+    PreviousTabGroup,
 
     /// <summary>Tabs to the next item.</summary>
     Tab,
@@ -257,6 +238,40 @@ public enum Command
     /// <summary>Tabs back to the previous item.</summary>
     BackTab,
 
+    #endregion
+
+    #region Action Commands
+
+    /// <summary>Toggles something (e.g. the expanded or collapsed state of a list).</summary>
+    Toggle,
+
+    /// <summary>Expands a list or item (with subitems).</summary>
+    Expand,
+
+    /// <summary>Recursively Expands all child items and their child items (if any).</summary>
+    ExpandAll,
+
+    /// <summary>Collapses a list or item (with subitems).</summary>
+    Collapse,
+
+    /// <summary>Recursively collapses a list items of their children (if any).</summary>
+    CollapseAll,
+
+    /// <summary>Cancels an action or any temporary states on the control e.g. expanding a combo list.</summary>
+    Cancel,
+
+    /// <summary>Quit.</summary>
+    Quit,
+
+    /// <summary>Refresh.</summary>
+    Refresh,
+
+    /// <summary>Suspend an application (Only implemented in <see cref="CursesDriver"/>).</summary>
+    Suspend,
+
+    /// <summary>Open the selected item or invoke a UI for opening something.</summary>
+    Open,
+
     /// <summary>Saves the current document.</summary>
     Save,
 
@@ -267,5 +282,12 @@ public enum Command
     New,
 
     /// <summary>Shows context about the item (e.g. a context menu).</summary>
-    ShowContextMenu
+    Context,
+
+    /// <summary>
+    ///     Invokes a user interface for editing or configuring something.
+    /// </summary>
+    Edit,
+
+    #endregion
 }

+ 1 - 1
Terminal.Gui/Input/GrabMouseEventArgs.cs

@@ -1,6 +1,6 @@
 namespace Terminal.Gui;
 
-/// <summary>Args <see cref="Application.GrabMouse"/> related events.</summary>
+/// <summary>Args GrabMouse related events.</summary>
 public class GrabMouseEventArgs : EventArgs
 {
     /// <summary>Creates a new instance of the <see cref="GrabMouseEventArgs"/> class.</summary>

+ 93 - 47
Terminal.Gui/Input/Key.cs

@@ -1,6 +1,5 @@
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
-using System.Text.Json.Serialization;
 
 namespace Terminal.Gui;
 
@@ -80,7 +79,7 @@ public class Key : EventArgs, IEquatable<Key>
     public Key (KeyCode k) { KeyCode = k; }
 
     /// <summary>
-    /// Copy constructor.
+    ///     Copy constructor.
     /// </summary>
     /// <param name="key">The Key to copy</param>
     public Key (Key key)
@@ -155,18 +154,13 @@ public class Key : EventArgs, IEquatable<Key>
     /// </remarks>
     public Rune AsRune => ToRune (KeyCode);
 
-    private bool _handled = false;
-
     /// <summary>
     ///     Indicates if the current Key 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
+    ///     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 => _handled;
-        set => _handled = value;
-    }
+    public bool Handled { get; set; }
 
     /// <summary>Gets a value indicating whether the Alt key was pressed (real or synthesized)</summary>
     /// <value><see langword="true"/> if is alternate; otherwise, <see langword="false"/>.</value>
@@ -339,11 +333,11 @@ public class Key : EventArgs, IEquatable<Key>
         switch (baseKey)
         {
             case >= KeyCode.A and <= KeyCode.Z when !key.HasFlag (KeyCode.ShiftMask):
-                return new Rune ((uint)(baseKey + 32));
+                return new ((uint)(baseKey + 32));
             case >= KeyCode.A and <= KeyCode.Z when key.HasFlag (KeyCode.ShiftMask):
-                return new Rune ((uint)baseKey);
+                return new ((uint)baseKey);
             case > KeyCode.Null and < KeyCode.A:
-                return new Rune ((uint)baseKey);
+                return new ((uint)baseKey);
         }
 
         if (Enum.IsDefined (typeof (KeyCode), baseKey))
@@ -351,7 +345,7 @@ public class Key : EventArgs, IEquatable<Key>
             return default (Rune);
         }
 
-        return new Rune ((uint)baseKey);
+        return new ((uint)baseKey);
     }
 
     #region Operators
@@ -381,17 +375,17 @@ public class Key : EventArgs, IEquatable<Key>
 
     /// <summary>Cast <see cref="KeyCode"/> to a <see cref="Key"/>.</summary>
     /// <param name="keyCode"></param>
-    public static implicit operator Key (KeyCode keyCode) { return new Key (keyCode); }
+    public static implicit operator Key (KeyCode keyCode) { return new (keyCode); }
 
     /// <summary>Cast <see langword="char"/> to a <see cref="Key"/>.</summary>
     /// <remarks>See <see cref="Key(char)"/> for more information.</remarks>
     /// <param name="ch"></param>
-    public static implicit operator Key (char ch) { return new Key (ch); }
+    public static implicit operator Key (char ch) { return new (ch); }
 
     /// <summary>Cast <see langword="string"/> to a <see cref="Key"/>.</summary>
     /// <remarks>See <see cref="Key(string)"/> for more information.</remarks>
     /// <param name="str"></param>
-    public static implicit operator Key (string str) { return new Key (str); }
+    public static implicit operator Key (string str) { return new (str); }
 
     /// <summary>Cast a <see cref="Key"/> to a <see langword="string"/>.</summary>
     /// <remarks>See <see cref="Key(string)"/> for more information.</remarks>
@@ -399,10 +393,7 @@ public class Key : EventArgs, IEquatable<Key>
     public static implicit operator string (Key key) { return key.ToString (); }
 
     /// <inheritdoc/>
-    public override bool Equals (object obj)
-    {
-        return obj is Key k && k.KeyCode == KeyCode && k.Handled == Handled;
-    }
+    public override bool Equals (object obj) { return obj is Key k && k.KeyCode == KeyCode && k.Handled == Handled; }
 
     bool IEquatable<Key>.Equals (Key other) { return Equals (other); }
 
@@ -568,7 +559,10 @@ public class Key : EventArgs, IEquatable<Key>
     /// <summary>Converts the provided string to a new <see cref="Key"/> instance.</summary>
     /// <param name="text">
     ///     The text to analyze. Formats supported are "Ctrl+X", "Alt+X", "Shift+X", "Ctrl+Alt+X",
-    ///     "Ctrl+Shift+X", "Alt+Shift+X", "Ctrl+Alt+Shift+X", and "X".
+    ///     "Ctrl+Shift+X", "Alt+Shift+X", "Ctrl+Alt+Shift+X", "X", and "120" (Unicode codepoint).
+    ///     <para>
+    ///         The separator can be any character, not just <see cref="Key.Separator"/> (e.g. "Ctrl@Alt@X").
+    ///     </para>
     /// </param>
     /// <param name="key">The parsed value.</param>
     /// <returns>A boolean value indicating whether parsing was successful.</returns>
@@ -577,38 +571,88 @@ public class Key : EventArgs, IEquatable<Key>
     {
         if (string.IsNullOrEmpty (text))
         {
-            key = Key.Empty;
+            key = Empty;
 
             return true;
         }
 
+        switch (text)
+        {
+            case "Ctrl":
+                key = KeyCode.CtrlMask;
+
+                return true;
+            case "Alt":
+                key = KeyCode.AltMask;
+
+                return true;
+            case "Shift":
+                key = KeyCode.ShiftMask;
+
+                return true;
+        }
+
         key = null;
 
-        // Split the string into parts
-        string [] parts = text.Split ('+', '-', (char)Separator.Value);
+        Rune separator = Separator;
 
-        if (parts.Length is 0 or > 4 || parts.Any (string.IsNullOrEmpty))
+        // Perhaps the separator was written using a different Key.Separator? Does the string
+        // start with "Ctrl", "Alt" or "Shift"? If so, get the char after the modifier string and use that as the separator.
+        if (text.StartsWith ("Ctrl", StringComparison.InvariantCultureIgnoreCase))
+        {
+            separator = (Rune)text [4];
+        }
+        else if (text.StartsWith ("Alt", StringComparison.InvariantCultureIgnoreCase))
+        {
+            separator = (Rune)text [3];
+        }
+        else if (text.StartsWith ("Shift", StringComparison.InvariantCultureIgnoreCase))
+        {
+            separator = (Rune)text [5];
+        }
+        else if (text.EndsWith ("Ctrl", StringComparison.InvariantCultureIgnoreCase))
         {
+            separator = (Rune)text [^5];
+        }
+        else if (text.EndsWith ("Alt", StringComparison.InvariantCultureIgnoreCase))
+        {
+            separator = (Rune)text [^4];
+        }
+        else if (text.EndsWith ("Shift", StringComparison.InvariantCultureIgnoreCase))
+        {
+            separator = (Rune)text [^6];
+        }
+
+        // Split the string into parts using the set Separator
+        string [] parts = text.Split ((char)separator.Value);
+
+        if (parts.Length is > 4)
+        {
+            // Invalid
             return false;
         }
 
-        // if it's just a shift key
-        if (parts.Length == 1)
+        // e.g. "Ctrl++"
+        if ((Rune)text [^1] != separator && parts.Any (string.IsNullOrEmpty))
         {
-            switch (parts [0])
-            {
-                case "Ctrl":
-                    key = KeyCode.CtrlMask;
+            // Invalid
+            return false;
+        }
 
-                    return true;
-                case "Alt":
-                    key = KeyCode.AltMask;
+        if ((Rune)text [^1] == separator)
+        {
+            parts [^1] = separator.Value.ToString ();
+            key = (char)separator.Value;
+        }
 
-                    return true;
-                case "Shift":
-                    key = KeyCode.ShiftMask;
+        if (separator != Separator && (parts.Length is 1 || (key is { } && parts.Length is 2)))
+        {
+            parts = text.Split ((char)separator.Value);
 
-                    return true;
+            if (parts.Length is 0 or > 4 || parts.Any (string.IsNullOrEmpty))
+            {
+                // Invalid
+                return false;
             }
         }
 
@@ -649,12 +693,12 @@ public class Key : EventArgs, IEquatable<Key>
                 {
                     if (parsedKeyCode is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
                     {
-                        key = new Key (parsedKeyCode | KeyCode.ShiftMask);
+                        key = new (parsedKeyCode | KeyCode.ShiftMask);
 
                         return true;
                     }
 
-                    key = new Key (parsedKeyCode | modifiers);
+                    key = new (parsedKeyCode | modifiers);
 
                     return true;
                 }
@@ -664,7 +708,8 @@ public class Key : EventArgs, IEquatable<Key>
             {
                 keyCode = keyCode & ~KeyCode.Space;
             }
-            key = new Key (keyCode | modifiers);
+
+            key = new (keyCode | modifiers);
 
             return true;
         }
@@ -675,7 +720,7 @@ public class Key : EventArgs, IEquatable<Key>
             {
                 if (parsedKeyCode is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
                 {
-                    key = new Key (parsedKeyCode | KeyCode.ShiftMask);
+                    key = new (parsedKeyCode | KeyCode.ShiftMask);
 
                     return true;
                 }
@@ -684,7 +729,8 @@ public class Key : EventArgs, IEquatable<Key>
                 {
                     parsedKeyCode = parsedKeyCode & ~KeyCode.Space;
                 }
-                key = new Key (parsedKeyCode | modifiers);
+
+                key = new (parsedKeyCode | modifiers);
 
                 return true;
             }
@@ -705,12 +751,12 @@ public class Key : EventArgs, IEquatable<Key>
 
             if ((KeyCode)parsedInt is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
             {
-                key = new Key ((KeyCode)parsedInt | KeyCode.ShiftMask);
+                key = new ((KeyCode)parsedInt | KeyCode.ShiftMask);
 
                 return true;
             }
 
-            key = new Key ((KeyCode)parsedInt);
+            key = new ((KeyCode)parsedInt);
 
             return true;
         }
@@ -722,7 +768,7 @@ public class Key : EventArgs, IEquatable<Key>
 
         if (GetIsKeyCodeAtoZ (parsedKeyCode))
         {
-            key = new Key (parsedKeyCode | (modifiers & ~KeyCode.Space));
+            key = new (parsedKeyCode | (modifiers & ~KeyCode.Space));
 
             return true;
         }

+ 10 - 17
Terminal.Gui/Input/KeyBindingScope.cs

@@ -1,6 +1,4 @@
-
-
-namespace Terminal.Gui;
+namespace Terminal.Gui;
 
 /// <summary>
 ///     Defines the scope of a <see cref="Command"/> that has been bound to a key with
@@ -10,7 +8,6 @@ namespace Terminal.Gui;
 ///     <para>Key bindings are scoped to the most-focused view (<see cref="Focused"/>) by default.</para>
 /// </remarks>
 [Flags]
-
 public enum KeyBindingScope
 {
     /// <summary>The key binding is disabled.</summary>
@@ -23,27 +20,23 @@ public enum KeyBindingScope
     ///     The key binding is scoped to the View's Superview hierarchy and will be triggered even when the View does not have
     ///     focus, as
     ///     long as the SuperView does have focus. This is typically used for <see cref="View.HotKey"/>s.
-    ///     <remarks>
-    ///         <para>
-    ///             The View must be visible.
-    ///         </para>
-    ///         <para>
-    ///             HotKey-scoped key bindings are only invoked if the key down event was not handled by the focused view or
-    ///             any of its subviews.
-    ///         </para>
-    ///     </remarks>
+    ///     <para>
+    ///         The View must be visible.
+    ///     </para>
+    ///     <para>
+    ///         HotKey-scoped key bindings are only invoked if the key down event was not handled by the focused view or
+    ///         any of its subviews.
+    ///     </para>
     /// </summary>
     HotKey = 2,
 
     /// <summary>
     ///     The key binding will be triggered regardless of which view has focus. This is typically used for global
     ///     commands, which are called Shortcuts.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         Application-scoped key bindings are only invoked if the key down event was not handled by the focused view or
     ///         any of its subviews, and if the key was not bound to a <see cref="View.HotKey"/>.
     ///     </para>
-    /// </remarks>
-    Application = 4,
+    /// </summary>
+    Application = 4
 }

+ 63 - 0
Terminal.Gui/Resources/Strings.Designer.cs

@@ -195,6 +195,15 @@ namespace Terminal.Gui.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to BrightGreen.
+        /// </summary>
+        internal static string _16C60C {
+            get {
+                return ResourceManager.GetString("#16C60C", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to MidnightBlue.
         /// </summary>
@@ -258,6 +267,15 @@ namespace Terminal.Gui.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to BrightBlue.
+        /// </summary>
+        internal static string _3B78FF {
+            get {
+                return ResourceManager.GetString("#3B78FF", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to MediumSeaGreen.
         /// </summary>
@@ -339,6 +357,15 @@ namespace Terminal.Gui.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to BrightCyan.
+        /// </summary>
+        internal static string _61D6D6 {
+            get {
+                return ResourceManager.GetString("#61D6D6", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to CornflowerBlue.
         /// </summary>
@@ -402,6 +429,15 @@ namespace Terminal.Gui.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to DarkGray.
+        /// </summary>
+        internal static string _767676 {
+            get {
+                return ResourceManager.GetString("#767676", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to LightSlateGrey.
         /// </summary>
@@ -681,6 +717,15 @@ namespace Terminal.Gui.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to BrightMagenta.
+        /// </summary>
+        internal static string _B4009E {
+            get {
+                return ResourceManager.GetString("#B4009E", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to DarkGoldenRod.
         /// </summary>
@@ -870,6 +915,15 @@ namespace Terminal.Gui.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to BrightRed.
+        /// </summary>
+        internal static string _E74856 {
+            get {
+                return ResourceManager.GetString("#E74856", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to DarkSalmon.
         /// </summary>
@@ -996,6 +1050,15 @@ namespace Terminal.Gui.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to BrightYellow.
+        /// </summary>
+        internal static string _F9F1A5 {
+            get {
+                return ResourceManager.GetString("#F9F1A5", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Salmon.
         /// </summary>

+ 21 - 0
Terminal.Gui/Resources/Strings.resx

@@ -697,4 +697,25 @@
     <data name="#9ACD32" xml:space="preserve">
     <value>YellowGreen</value>
   </data>
+    <data name="#3B78FF" xml:space="preserve">
+    <value>BrightBlue</value>
+  </data>
+    <data name="#61D6D6" xml:space="preserve">
+    <value>BrightCyan</value>
+  </data>
+    <data name="#E74856" xml:space="preserve">
+    <value>BrightRed</value>
+</data>
+    <data name="#16C60C" xml:space="preserve">
+    <value>BrightGreen</value>
+</data>
+    <data name="#B4009E" xml:space="preserve">
+    <value>BrightMagenta</value>
+</data>
+    <data name="#F9F1A5" xml:space="preserve">
+    <value>BrightYellow</value>
+</data>
+    <data name="#767676" xml:space="preserve">
+    <value>DarkGray</value>
+</data>
 </root>

+ 324 - 38
Terminal.Gui/Resources/config.json

@@ -10,8 +10,7 @@
   // note that not all values here will be recreated (e.g. the Light and Dark themes and any property initialized
   // null).
   //
-  // TODO: V2 - Reference via http
-  "$schema": "../../docfx/schemas/tui-config-schema.json",
+  "$schema": "https://gui-cs.github.io/Terminal.GuiV2Docs/schemas/tui-config-schema.json",
 
   // Set this to true in a .config file to be loaded to cause JSON parsing errors
   // to throw exceptions. 
@@ -22,6 +21,7 @@
   "Application.NextTabGroupKey": "F6",
   "Application.PrevTabGroupKey": "Shift+F6",
   "Application.QuitKey": "Esc",
+  "Application.ArrangeKey": "Ctrl+F5",
   "Key.Separator": "+",
 
   "Theme": "Default",
@@ -30,34 +30,35 @@
       "Default": {
         "Dialog.DefaultButtonAlignment": "End",
         "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
+        "Dialog.DefaultBorderStyle": "Heavy",
+        "Dialog.DefaultShadow": "Transparent",
         "FrameView.DefaultBorderStyle": "Single",
         "Window.DefaultBorderStyle": "Single",
-        "Dialog.DefaultBorderStyle": "Heavy",
         "MessageBox.DefaultButtonAlignment": "Center",
         "MessageBox.DefaultBorderStyle": "Heavy",
-        "Button.DefaultShadow": "None",
+        "Button.DefaultShadow": "Opaque",
         "ColorSchemes": [
           {
             "TopLevel": {
               "Normal": {
                 "Foreground": "BrightGreen",
-                "Background": "Black"
+                "Background": "#505050" // DarkerGray
               },
               "Focus": {
                 "Foreground": "White",
-                "Background": "Cyan"
+                "Background": "#696969" // DimGray
               },
               "HotNormal": {
                 "Foreground": "Yellow",
-                "Background": "Black"
+                "Background": "#505050" // DarkerGray
               },
               "HotFocus": {
-                "Foreground": "Blue",
-                "Background": "Cyan"
+                "Foreground": "Yellow",
+                "Background": "#696969" // DimGray
               },
               "Disabled": {
                 "Foreground": "DarkGray",
-                "Background": "Black"
+                "Background": "#505050" // DarkerGray
               }
             }
           },
@@ -68,8 +69,8 @@
                 "Background": "Blue"
               },
               "Focus": {
-                "Foreground": "Black",
-                "Background": "Gray"
+                "Foreground": "DarkBlue",
+                "Background": "LightGray"
               },
               "HotNormal": {
                 "Foreground": "BrightCyan",
@@ -77,7 +78,7 @@
               },
               "HotFocus": {
                 "Foreground": "BrightBlue",
-                "Background": "Gray"
+                "Background": "LightGray"
               },
               "Disabled": {
                 "Foreground": "DarkGray",
@@ -89,19 +90,19 @@
             "Dialog": {
               "Normal": {
                 "Foreground": "Black",
-                "Background": "Gray"
+                "Background": "LightGray"
               },
               "Focus": {
-                "Foreground": "White",
-                "Background": "DarkGray"
+                "Foreground": "DarkGray",
+                "Background": "LightGray"
               },
               "HotNormal": {
                 "Foreground": "Blue",
-                "Background": "Gray"
+                "Background": "LightGray"
               },
               "HotFocus": {
-                "Foreground": "BrightYellow",
-                "Background": "DarkGray"
+                "Foreground": "BrightBlue",
+                "Background": "LightGray"
               },
               "Disabled": {
                 "Foreground": "Gray",
@@ -113,19 +114,19 @@
             "Menu": {
               "Normal": {
                 "Foreground": "White",
-                "Background": "DarkGray"
+                "Background": "DarkBlue"
               },
               "Focus": {
                 "Foreground": "White",
-                "Background": "Black"
+                "Background": "Blue"
               },
               "HotNormal": {
-                "Foreground": "BrightYellow",
-                "Background": "DarkGray"
+                "Foreground": "Yellow",
+                "Background": "DarkBlue"
               },
               "HotFocus": {
-                "Foreground": "BrightYellow",
-                "Background": "Black"
+                "Foreground": "Yellow",
+                "Background": "Blue"
               },
               "Disabled": {
                 "Foreground": "Gray",
@@ -137,18 +138,18 @@
             "Error": {
               "Normal": {
                 "Foreground": "Red",
-                "Background": "White"
+                "Background": "Pink"
               },
               "Focus": {
-                "Foreground": "Black",
+                "Foreground": "White",
                 "Background": "BrightRed"
               },
               "HotNormal": {
                 "Foreground": "Black",
-                "Background": "White"
+                "Background": "Pink"
               },
               "HotFocus": {
-                "Foreground": "White",
+                "Foreground": "Pink",
                 "Background": "BrightRed"
               },
               "Disabled": {
@@ -162,6 +163,15 @@
     },
     {
       "Dark": {
+        "Dialog.DefaultButtonAlignment": "End",
+        "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
+        "Dialog.DefaultBorderStyle": "Heavy",
+        "Dialog.DefaultShadow": "Transparent",
+        "FrameView.DefaultBorderStyle": "Single",
+        "Window.DefaultBorderStyle": "Single",
+        "MessageBox.DefaultButtonAlignment": "Center",
+        "MessageBox.DefaultBorderStyle": "Heavy",
+        "Button.DefaultShadow": "Opaque",
         "ColorSchemes": [
           {
             "TopLevel": {
@@ -238,16 +248,16 @@
           {
             "Menu": {
               "Normal": {
-                "Foreground": "White",
-                "Background": "DarkGray"
+                "Foreground": "LightGray",
+                "Background": "#505050" // DarkerGray
               },
               "Focus": {
                 "Foreground": "White",
                 "Background": "Black"
               },
               "HotNormal": {
-                "Foreground": "Gray",
-                "Background": "DarkGray"
+                "Foreground": "White",
+                "Background": "#505050" // DarkerGray
               },
               "HotFocus": {
                 "Foreground": "White",
@@ -288,6 +298,15 @@
     },
     {
       "Light": {
+        "Dialog.DefaultButtonAlignment": "End",
+        "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
+        "Dialog.DefaultBorderStyle": "Heavy",
+        "Dialog.DefaultShadow": "Transparent",
+        "FrameView.DefaultBorderStyle": "Single",
+        "Window.DefaultBorderStyle": "Single",
+        "MessageBox.DefaultButtonAlignment": "Center",
+        "MessageBox.DefaultBorderStyle": "Heavy",
+        "Button.DefaultShadow": "Opaque",
         "ColorSchemes": [
           {
             "TopLevel": {
@@ -316,7 +335,7 @@
           {
             "Base": {
               "Normal": {
-                "Foreground": "DarkGray",
+                "Foreground": "#505050", // DarkerGray
                 "Background": "White"
               },
               "Focus": {
@@ -365,19 +384,19 @@
             "Menu": {
               "Normal": {
                 "Foreground": "DarkGray",
-                "Background": "White"
+                "Background": "LightGray"
               },
               "Focus": {
                 "Foreground": "DarkGray",
-                "Background": "Gray"
+                "Background": "White"
               },
               "HotNormal": {
                 "Foreground": "BrightRed",
-                "Background": "White"
+                "Background": "LightGray"
               },
               "HotFocus": {
                 "Foreground": "BrightRed",
-                "Background": "Gray"
+                "Background": "White"
               },
               "Disabled": {
                 "Foreground": "Gray",
@@ -411,6 +430,273 @@
           }
         ]
       }
+    },
+    {
+      "Black & White": {
+        "Dialog.DefaultShadow": "None",
+        "FrameView.DefaultBorderStyle": "Single",
+        "Window.DefaultBorderStyle": "Single",
+        "MessageBox.DefaultButtonAlignment": "Center",
+        "MessageBox.DefaultBorderStyle": "Heavy",
+        "Button.DefaultShadow": "None",
+        "ColorSchemes": [
+          {
+            "TopLevel": {
+              "Normal": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Focus": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "HotNormal": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Disabled": {
+                "Foreground": "Black",
+                "Background": "Black"
+              }
+            }
+          },
+          {
+            "Base": {
+              "Normal": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Focus": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "HotNormal": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Disabled": {
+                "Foreground": "Black",
+                "Background": "Black"
+              }
+            }
+          },
+          {
+            "Dialog": {
+              "Normal": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotNormal": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotFocus": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "Disabled": {
+                "Foreground": "White",
+                "Background": "White"
+              }
+            }
+          },
+          {
+            "Menu": {
+              "Normal": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotNormal": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotFocus": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "Disabled": {
+                "Foreground": "White",
+                "Background": "White"
+              }
+            }
+          },
+          {
+            "Error": {
+              "Normal": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Focus": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "HotNormal": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Disabled": {
+                "Foreground": "Black",
+                "Background": "Black"
+              }
+            }
+          }
+        ]
+      }
+    },
+    {
+      "Gray Scale": {
+        "Dialog.DefaultButtonAlignment": "End",
+        "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
+        "Dialog.DefaultBorderStyle": "Heavy",
+        "Dialog.DefaultShadow": "Transparent",
+        "FrameView.DefaultBorderStyle": "Single",
+        "Window.DefaultBorderStyle": "Single",
+        "MessageBox.DefaultButtonAlignment": "Center",
+        "MessageBox.DefaultBorderStyle": "Heavy",
+        "Button.DefaultShadow": "Opaque",
+        "ColorSchemes": [
+          {
+            "TopLevel": {
+              "Normal": {
+                "Foreground": "#A9A9A9", // DarkGray
+                "Background": "#505050" // DarkerGray
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "#696969" // DimGray
+              },
+              "HotNormal": {
+                "Foreground": "#808080", // Gray
+                "Background": "#505050" // DarkerGray
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "#808080" // Gray
+              },
+              "Disabled": {
+                "Foreground": "#505050", // DarkerGray
+                "Background": "Black"
+              }
+            }
+          },
+          {
+            "Base": {
+              "Normal": {
+                "Foreground": "#A9A9A9", // DarkGray
+                "Background": "Black"
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "#505050" // DarkerGray
+              },
+              "HotNormal": {
+                "Foreground": "#808080", // Gray
+                "Background": "Black"
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "#505050" // DarkerGray
+              },
+              "Disabled": {
+                "Foreground": "#696969", // DimGray
+                "Background": "Black"
+              }
+            }
+          },
+          {
+            "Dialog": {
+              "Normal": {
+                "Foreground": "#505050", // DarkerGray
+                "Background": "White"
+              },
+              "Focus": {
+                "Foreground": "Black",
+                "Background": "#D3D3D3" // LightGray
+              },
+              "HotNormal": {
+                "Foreground": "#808080", // Gray
+                "Background": "White"
+              },
+              "HotFocus": {
+                "Foreground": "Black",
+                "Background": "#D3D3D3" // LightGray
+              },
+              "Disabled": {
+                "Foreground": "#696969", // DimGray
+                "Background": "White"
+              }
+            }
+          },
+          {
+            "Menu": {
+              "Normal": {
+                "Foreground": "#D3D3D3", // LightGray
+                "Background": "#505050" // DarkerGray
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "#808080" // Gray
+              },
+              "HotNormal": {
+                "Foreground": "#808080", // Gray
+                "Background": "#505050" // DarkerGray
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "#808080" // Gray
+              },
+              "Disabled": {
+                "Foreground": "#505050", // DarkerGray
+                "Background": "#505050" // DarkerGray
+              }
+            }
+          },
+          {
+            "Error": {
+              "Normal": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotNormal": {
+                "Foreground": "Black",
+                "Background": "#D3D3D3" // LightGray
+              },
+              "HotFocus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "Disabled": {
+                "Foreground": "#696969", // DimGray
+                "Background": "White"
+              }
+            }
+          }
+        ]
+      }
     }
   ]
 }

+ 1 - 0
Terminal.Gui/Terminal.Gui.csproj

@@ -141,4 +141,5 @@
     <EnableSourceLink>true</EnableSourceLink>
     <Authors>Miguel de Icaza, Tig Kindel (@tig), @BDisp</Authors>
   </PropertyGroup>
+  <ProjectExtensions><VisualStudio><UserProperties resources_4config_1json__JsonSchema="../../docfx/schemas/tui-config-schema.json" /></VisualStudio></ProjectExtensions>
 </Project>

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

@@ -1,4 +1,5 @@
 #nullable enable
+using System.ComponentModel;
 using Terminal.Gui;
 using Attribute = Terminal.Gui.Attribute;
 
@@ -26,7 +27,9 @@ public class Adornment : View
     /// <param name="parent"></param>
     public Adornment (View parent)
     {
-        CanFocus = true;
+        // By default Adornments can't get focus; has to be enabled specifically.
+        CanFocus = false;
+        TabStop = TabBehavior.NoStop;
         Parent = parent;
     }
 
@@ -226,37 +229,34 @@ public class Adornment : View
         return Thickness.Contains (frame, location);
     }
 
-    /// <inheritdoc/>
-    protected internal override bool? OnMouseEnter (MouseEvent mouseEvent)
-    {
-        // Invert Normal
-        if (Diagnostics.HasFlag (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null)
-        {
-            var cs = new ColorScheme (ColorScheme)
-            {
-                Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground)
-            };
-            ColorScheme = cs;
-        }
-
-        return base.OnMouseEnter (mouseEvent);
-    }
-
-    /// <inheritdoc/>   
-    protected internal override bool OnMouseLeave (MouseEvent mouseEvent)
-    {
-        // Invert Normal
-        if (Diagnostics.FastHasFlags (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null)
-        {
-            var cs = new ColorScheme (ColorScheme)
-            {
-                Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground)
-            };
-            ColorScheme = cs;
-        }
-
-        return base.OnMouseLeave (mouseEvent);
-    }
+    ///// <inheritdoc/>
+    //protected override bool OnMouseEnter (CancelEventArgs mouseEvent)
+    //{
+    //    // Invert Normal
+    //    if (Diagnostics.HasFlag (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null)
+    //    {
+    //        var cs = new ColorScheme (ColorScheme)
+    //        {
+    //            Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground)
+    //        };
+    //        ColorScheme = cs;
+    //    }
+
+    //    return false;
+    //}
 
+    ///// <inheritdoc/>   
+    //protected override void OnMouseLeave ()
+    //{
+    //    // Invert Normal
+    //    if (Diagnostics.FastHasFlags (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null)
+    //    {
+    //        var cs = new ColorScheme (ColorScheme)
+    //        {
+    //            Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground)
+    //        };
+    //        ColorScheme = cs;
+    //    }
+    //}
     #endregion Mouse Support
 }

+ 910 - 172
Terminal.Gui/View/Adornment/Border.cs

@@ -1,6 +1,9 @@
+#nullable enable
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 
-/// <summary>The Border for a <see cref="View"/>.</summary>
+/// <summary>The Border for a <see cref="View"/>. Accessed via <see cref="View.Border"/></summary>
 /// <remarks>
 ///     <para>
 ///         Renders a border around the view with the <see cref="View.Title"/>. A border using <see cref="LineStyle"/>
@@ -52,8 +55,10 @@ public class Border : Adornment
     /// <inheritdoc/>
     public Border (View parent) : base (parent)
     {
-        /* Do nothing; View.CreateAdornment requires a constructor that takes a parent */
         Parent = parent;
+        CanFocus = false;
+        TabStop = TabBehavior.TabGroup;
+
         Application.GrabbingMouse += Application_GrabbingMouse;
         Application.UnGrabbingMouse += Application_UnGrabbingMouse;
 
@@ -73,14 +78,6 @@ public class Border : Adornment
     /// <inheritdoc/>
     public override void BeginInit ()
     {
-#if HOVER
-        // TOOD: Hack - make Arrangement overridable
-        if ((Parent?.Arrangement & ViewArrangement.Movable) != 0)
-        {
-            HighlightStyle |= HighlightStyle.Hover;
-        }
-#endif
-
         base.BeginInit ();
 
 #if SUBVIEW_BASED_BORDER
@@ -131,7 +128,7 @@ public class Border : Adornment
     ///     The color scheme for the Border. If set to <see langword="null"/>, gets the <see cref="Adornment.Parent"/>
     ///     scheme. color scheme.
     /// </summary>
-    public override ColorScheme ColorScheme
+    public override ColorScheme? ColorScheme
     {
         get
         {
@@ -152,6 +149,7 @@ public class Border : Adornment
     internal Rectangle GetBorderRectangle ()
     {
         Rectangle screenRect = ViewportToScreen (Viewport);
+
         return new (
                     screenRect.X + Math.Max (0, Thickness.Left - 1),
                     screenRect.Y + Math.Max (0, Thickness.Top - 1),
@@ -193,7 +191,7 @@ public class Border : Adornment
             // TODO: Make Border.LineStyle inherit from the SuperView hierarchy
             // TODO: Right now, Window and FrameView use CM to set BorderStyle, which negates
             // TODO: all this.
-            return Parent.SuperView?.BorderStyle ?? LineStyle.None;
+            return Parent!.SuperView?.BorderStyle ?? LineStyle.None;
         }
         set => _lineStyle = value;
     }
@@ -223,9 +221,9 @@ public class Border : Adornment
 
     private Color? _savedForeColor;
 
-    private void Border_Highlight (object sender, CancelEventArgs<HighlightStyle> e)
+    private void Border_Highlight (object? sender, CancelEventArgs<HighlightStyle> e)
     {
-        if (!Parent.Arrangement.HasFlag (ViewArrangement.Movable))
+        if (!Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
         {
             e.Cancel = true;
 
@@ -236,31 +234,22 @@ public class Border : Adornment
         {
             if (!_savedForeColor.HasValue)
             {
-                _savedForeColor = ColorScheme.Normal.Foreground;
+                _savedForeColor = ColorScheme!.Normal.Foreground;
             }
 
             var cs = new ColorScheme (ColorScheme)
             {
-                Normal = new (ColorScheme.Normal.Foreground.GetHighlightColor (), ColorScheme.Normal.Background)
+                Normal = new (ColorScheme!.Normal.Foreground.GetHighlightColor (), ColorScheme.Normal.Background)
             };
             ColorScheme = cs;
         }
-#if HOVER
-        else if (e.HighlightStyle.HasFlag (HighlightStyle.Hover))
-        {
-            if (!_savedHighlightLineStyle.HasValue)
-            {
-                _savedHighlightLineStyle = Parent?.BorderStyle ?? LineStyle;
-            }
-            LineStyle = LineStyle.Double;
-        }
-#endif
+
 
         if (e.NewValue == HighlightStyle.None && _savedForeColor.HasValue)
         {
             var cs = new ColorScheme (ColorScheme)
             {
-                Normal = new (_savedForeColor.Value, ColorScheme.Normal.Background)
+                Normal = new (_savedForeColor.Value, ColorScheme!.Normal.Background)
             };
             ColorScheme = cs;
         }
@@ -280,46 +269,177 @@ public class Border : Adornment
             return true;
         }
 
-        // BUGBUG: Shouldn't non-focusable views be draggable??
-        //if (!Parent.CanFocus)
-        //{
-        //    return false;
-        //}
-
-        if (!Parent.Arrangement.HasFlag (ViewArrangement.Movable))
-        {
-            return false;
-        }
-
         // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/3312
         if (!_dragPosition.HasValue && mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
         {
-            Parent.SetFocus ();
-            ApplicationOverlapped.BringOverlappedTopToFront ();
+            Parent!.SetFocus ();
+
+            if (!Parent!.Arrangement.HasFlag (ViewArrangement.Movable)
+                && !Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable)
+                && !Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable)
+                && !Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable)
+                && !Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable)
+               )
+            {
+                return false;
+            }
 
             // Only start grabbing if the user clicks in the Thickness area
             // Adornment.Contains takes Parent SuperView=relative coords.
             if (Contains (new (mouseEvent.Position.X + Parent.Frame.X + Frame.X, mouseEvent.Position.Y + Parent.Frame.Y + Frame.Y)))
             {
+                if (_arranging != ViewArrangement.Fixed)
+                {
+                    EndArrangeMode ();
+                }
+
                 // Set the start grab point to the Frame coords
                 _startGrabPoint = new (mouseEvent.Position.X + Frame.X, mouseEvent.Position.Y + Frame.Y);
                 _dragPosition = mouseEvent.Position;
                 Application.GrabMouse (this);
 
-                SetHighlight (HighlightStyle);
+                SetPressedHighlight (HighlightStyle);
+
+                // Arrange Mode -
+                // TODO: This code can be refactored to be more readable and maintainable.
+
+                // If not resizable, but movable: Drag anywhere is move
+                // If resizable and movable: Drag on top is move, other 3 sides are size
+                // If not movable, but resizable: Drag on any side sizes.
+
+                // Get rectangle representing Thickness.Top
+                // If mouse is in that rectangle, set _arranging to ViewArrangement.Movable
+                Rectangle sideRect;
+
+                // If mouse is in any other rectangle, set _arranging to ViewArrangement.<side>
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable))
+                {
+                    sideRect = new (Frame.X, Frame.Y + Thickness.Top, Thickness.Left, Frame.Height - Thickness.Top - Thickness.Bottom);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.LeftResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable))
+                {
+                    sideRect = new (
+                                    Frame.X + Frame.Width - Thickness.Right,
+                                    Frame.Y + Thickness.Top,
+                                    Thickness.Right,
+                                    Frame.Height - Thickness.Top - Thickness.Bottom);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.RightResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable) && !Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
+                {
+                    sideRect = new (Frame.X + Thickness.Left, Frame.Y, Frame.Width - Thickness.Left - Thickness.Right, Thickness.Top);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.TopResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable))
+                {
+                    sideRect = new (
+                                    Frame.X + Thickness.Left,
+                                    Frame.Y + Frame.Height - Thickness.Bottom,
+                                    Frame.Width - Thickness.Left - Thickness.Right,
+                                    Thickness.Bottom);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.BottomResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable) && Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable))
+                {
+                    sideRect = new (Frame.X, Frame.Height - Thickness.Top, Thickness.Left, Thickness.Bottom);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.BottomResizable | ViewArrangement.LeftResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable) && Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable))
+                {
+                    sideRect = new (Frame.X + Frame.Width - Thickness.Right, Frame.Height - Thickness.Top, Thickness.Right, Thickness.Bottom);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.BottomResizable | ViewArrangement.RightResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable) && Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable))
+                {
+                    sideRect = new (Frame.X + Frame.Width - Thickness.Right, Frame.Y, Thickness.Right, Thickness.Top);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.TopResizable | ViewArrangement.RightResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable) && Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable))
+                {
+                    sideRect = new (Frame.X, Frame.Y, Thickness.Left, Thickness.Top);
+
+                    if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.TopResizable | ViewArrangement.LeftResizable);
+
+                        return true;
+                    }
+                }
+
+                if (Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
+                {
+                    //sideRect = new (Frame.X + Thickness.Left, Frame.Y, Frame.Width - Thickness.Left - Thickness.Right, Thickness.Top);
+
+                    //if (sideRect.Contains (_startGrabPoint))
+                    {
+                        EnterArrangeMode (ViewArrangement.Movable);
+
+                        return true;
+                    }
+                }
             }
 
             return true;
         }
 
-        if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))
+        if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && Application.MouseGrabView == this)
         {
-            if (Application.MouseGrabView == this && _dragPosition.HasValue)
+            if (_dragPosition.HasValue)
             {
-                if (Parent.SuperView is null)
+                if (Parent!.SuperView is null)
                 {
                     // Redraw the entire app window.
-                    Application.Top.SetNeedsDisplay ();
+                    Application.Top!.SetNeedsDisplay ();
                 }
                 else
                 {
@@ -331,17 +451,122 @@ public class Border : Adornment
                 Point parentLoc = Parent.SuperView?.ScreenToViewport (new (mouseEvent.ScreenPosition.X, mouseEvent.ScreenPosition.Y))
                                   ?? mouseEvent.ScreenPosition;
 
-                GetLocationEnsuringFullVisibility (
-                                                   Parent,
-                                                   parentLoc.X - _startGrabPoint.X,
-                                                   parentLoc.Y - _startGrabPoint.Y,
-                                                   out int nx,
-                                                   out int ny,
-                                                   out _
-                                                  );
+                int minHeight = Thickness.Vertical + Parent!.Margin.Thickness.Bottom;
+                int minWidth = Thickness.Horizontal + Parent!.Margin.Thickness.Right;
+
+                // TODO: This code can be refactored to be more readable and maintainable.
+                switch (_arranging)
+                {
+                    case ViewArrangement.Movable:
+
+                        GetLocationEnsuringFullVisibility (
+                                                           Parent,
+                                                           parentLoc.X - _startGrabPoint.X,
+                                                           parentLoc.Y - _startGrabPoint.Y,
+                                                           out int nx,
+                                                           out int ny,
+                                                           out _
+                                                          );
+
+                        Parent.X = parentLoc.X - _startGrabPoint.X;
+                        Parent.Y = parentLoc.Y - _startGrabPoint.Y;
+
+                        break;
+
+                    case ViewArrangement.TopResizable:
+                        // Get how much the mouse has moved since the start of the drag
+                        // and adjust the height of the parent by that amount
+                        int deltaY = parentLoc.Y - Parent.Frame.Y;
+                        int newHeight = Math.Max (minHeight, Parent.Frame.Height - deltaY);
+
+                        if (newHeight != Parent.Frame.Height)
+                        {
+                            Parent.Height = newHeight;
+                            Parent.Y = parentLoc.Y - _startGrabPoint.Y;
+                        }
+
+                        break;
+
+                    case ViewArrangement.BottomResizable:
+                        Parent.Height = Math.Max (minHeight, parentLoc.Y - Parent.Frame.Y + Parent!.Margin.Thickness.Bottom + 1);
+
+                        break;
+
+                    case ViewArrangement.LeftResizable:
+                        // Get how much the mouse has moved since the start of the drag
+                        // and adjust the height of the parent by that amount
+                        int deltaX = parentLoc.X - Parent.Frame.X;
+                        int newWidth = Math.Max (minWidth, Parent.Frame.Width - deltaX);
+
+                        if (newWidth != Parent.Frame.Width)
+                        {
+                            Parent.Width = newWidth;
+                            Parent.X = parentLoc.X - _startGrabPoint.X;
+                        }
 
-                Parent.X = nx;
-                Parent.Y = ny;
+                        break;
+
+                    case ViewArrangement.RightResizable:
+                        Parent.Width = Math.Max (minWidth, parentLoc.X - Parent.Frame.X + Parent!.Margin.Thickness.Right + 1);
+
+                        break;
+
+                    case ViewArrangement.BottomResizable | ViewArrangement.RightResizable:
+                        Parent.Width = Math.Max (minWidth, parentLoc.X - Parent.Frame.X + Parent!.Margin.Thickness.Right + 1);
+                        Parent.Height = Math.Max (minHeight, parentLoc.Y - Parent.Frame.Y + Parent!.Margin.Thickness.Bottom + 1);
+
+                        break;
+
+                    case ViewArrangement.BottomResizable | ViewArrangement.LeftResizable:
+                        int dX = parentLoc.X - Parent.Frame.X;
+                        int newW = Math.Max (minWidth, Parent.Frame.Width - dX);
+
+                        if (newW != Parent.Frame.Width)
+                        {
+                            Parent.Width = newW;
+                            Parent.X = parentLoc.X - _startGrabPoint.X;
+                        }
+
+                        Parent.Height = Math.Max (minHeight, parentLoc.Y - Parent.Frame.Y + Parent!.Margin.Thickness.Bottom + 1);
+
+                        break;
+
+                    case ViewArrangement.TopResizable | ViewArrangement.RightResizable:
+                        int dY = parentLoc.Y - Parent.Frame.Y;
+                        int newH = Math.Max (minHeight, Parent.Frame.Height - dY);
+
+                        if (newH != Parent.Frame.Height)
+                        {
+                            Parent.Height = newH;
+                            Parent.Y = parentLoc.Y - _startGrabPoint.Y;
+                        }
+
+                        Parent.Width = Math.Max (minWidth, parentLoc.X - Parent.Frame.X + Parent!.Margin.Thickness.Right + 1);
+
+                        break;
+
+                    case ViewArrangement.TopResizable | ViewArrangement.LeftResizable:
+                        int dY2 = parentLoc.Y - Parent.Frame.Y;
+                        int newH2 = Math.Max (minHeight, Parent.Frame.Height - dY2);
+
+                        if (newH2 != Parent.Frame.Height)
+                        {
+                            Parent.Height = newH2;
+                            Parent.Y = parentLoc.Y - _startGrabPoint.Y;
+                        }
+
+                        int dX2 = parentLoc.X - Parent.Frame.X;
+                        int newW2 = Math.Max (minWidth, Parent.Frame.Width - dX2);
+
+                        if (newW2 != Parent.Frame.Width)
+                        {
+                            Parent.Width = newW2;
+                            Parent.X = parentLoc.X - _startGrabPoint.X;
+                        }
+
+                        break;
+                }
+                Application.Refresh ();
 
                 return true;
             }
@@ -351,7 +576,9 @@ public class Border : Adornment
         {
             _dragPosition = null;
             Application.UngrabMouse ();
-            SetHighlight (HighlightStyle.None);
+            SetPressedHighlight (HighlightStyle.None);
+
+            EndArrangeMode ();
 
             return true;
         }
@@ -359,17 +586,7 @@ public class Border : Adornment
         return false;
     }
 
-    /// <inheritdoc/>
-    protected override void Dispose (bool disposing)
-    {
-        Application.GrabbingMouse -= Application_GrabbingMouse;
-        Application.UnGrabbingMouse -= Application_UnGrabbingMouse;
-
-        _dragPosition = null;
-        base.Dispose (disposing);
-    }
-
-    private void Application_GrabbingMouse (object sender, GrabMouseEventArgs e)
+    private void Application_GrabbingMouse (object? sender, GrabMouseEventArgs e)
     {
         if (Application.MouseGrabView == this && _dragPosition.HasValue)
         {
@@ -377,7 +594,7 @@ public class Border : Adornment
         }
     }
 
-    private void Application_UnGrabbingMouse (object sender, GrabMouseEventArgs e)
+    private void Application_UnGrabbingMouse (object? sender, GrabMouseEventArgs e)
     {
         if (Application.MouseGrabView == this && _dragPosition.HasValue)
         {
@@ -417,7 +634,7 @@ public class Border : Adornment
         int maxTitleWidth = Math.Max (
                                       0,
                                       Math.Min (
-                                                Parent.TitleTextFormatter.FormatAndGetSize ().Width,
+                                                Parent!.TitleTextFormatter.FormatAndGetSize ().Width,
                                                 Math.Min (screenBounds.Width - 4, borderBounds.Width - 4)
                                                )
                                      );
@@ -427,6 +644,8 @@ public class Border : Adornment
         int sideLineLength = borderBounds.Height;
         bool canDrawBorder = borderBounds is { Width: > 0, Height: > 0 };
 
+        LineStyle lineStyle = LineStyle;
+
         if (Settings.FastHasFlags (BorderSettings.Title))
         {
             if (Thickness.Top == 2)
@@ -477,7 +696,7 @@ public class Border : Adornment
 
         if (canDrawBorder && LineStyle != LineStyle.None)
         {
-            LineCanvas lc = Parent?.LineCanvas;
+            LineCanvas? lc = Parent?.LineCanvas;
 
             bool drawTop = Thickness.Top > 0 && Frame.Width > 1 && Frame.Height >= 1;
             bool drawLeft = Thickness.Left > 0 && (Frame.Height > 1 || Thickness.Top == 0);
@@ -492,7 +711,7 @@ public class Border : Adornment
             }
             else
             {
-                Driver.SetAttribute (Parent.GetNormalColor ());
+                Driver.SetAttribute (Parent!.GetNormalColor ());
             }
 
             if (drawTop)
@@ -502,13 +721,13 @@ public class Border : Adornment
                 if (borderBounds.Width < 4 || !Settings.FastHasFlags (BorderSettings.Title) || string.IsNullOrEmpty (Parent?.Title))
                 {
                     // ╔╡╞╗ should be ╔══╗
-                    lc.AddLine (
-                                new (borderBounds.Location.X, titleY),
-                                borderBounds.Width,
-                                Orientation.Horizontal,
-                                LineStyle,
-                                Driver.GetAttribute ()
-                               );
+                    lc?.AddLine (
+                                 new (borderBounds.Location.X, titleY),
+                                 borderBounds.Width,
+                                 Orientation.Horizontal,
+                                 lineStyle,
+                                 Driver.GetAttribute ()
+                                );
                 }
                 else
                 {
@@ -517,13 +736,13 @@ public class Border : Adornment
                     //│
                     if (Thickness.Top == 2)
                     {
-                        lc.AddLine (
-                                    new (borderBounds.X + 1, topTitleLineY),
-                                    Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
-                                    Orientation.Horizontal,
-                                    LineStyle,
-                                    Driver.GetAttribute ()
-                                   );
+                        lc?.AddLine (
+                                     new (borderBounds.X + 1, topTitleLineY),
+                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
+                                     Orientation.Horizontal,
+                                     lineStyle,
+                                     Driver.GetAttribute ()
+                                    );
                     }
 
                     // ┌────┐
@@ -531,71 +750,71 @@ public class Border : Adornment
                     //│
                     if (borderBounds.Width >= 4 && Thickness.Top > 2)
                     {
-                        lc.AddLine (
-                                    new (borderBounds.X + 1, topTitleLineY),
-                                    Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
-                                    Orientation.Horizontal,
-                                    LineStyle,
-                                    Driver.GetAttribute ()
-                                   );
-
-                        lc.AddLine (
-                                    new (borderBounds.X + 1, topTitleLineY + 2),
-                                    Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
-                                    Orientation.Horizontal,
-                                    LineStyle,
-                                    Driver.GetAttribute ()
-                                   );
+                        lc?.AddLine (
+                                     new (borderBounds.X + 1, topTitleLineY),
+                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
+                                     Orientation.Horizontal,
+                                     lineStyle,
+                                     Driver.GetAttribute ()
+                                    );
+
+                        lc?.AddLine (
+                                     new (borderBounds.X + 1, topTitleLineY + 2),
+                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
+                                     Orientation.Horizontal,
+                                     lineStyle,
+                                     Driver.GetAttribute ()
+                                    );
                     }
 
                     // ╔╡Title╞═════╗
                     // Add a short horiz line for ╔╡
-                    lc.AddLine (
-                                new (borderBounds.Location.X, titleY),
-                                2,
-                                Orientation.Horizontal,
-                                LineStyle,
-                                Driver.GetAttribute ()
-                               );
+                    lc?.AddLine (
+                                 new (borderBounds.Location.X, titleY),
+                                 2,
+                                 Orientation.Horizontal,
+                                 lineStyle,
+                                 Driver.GetAttribute ()
+                                );
 
                     // Add a vert line for ╔╡
-                    lc.AddLine (
-                                new (borderBounds.X + 1, topTitleLineY),
-                                titleBarsLength,
-                                Orientation.Vertical,
-                                LineStyle.Single,
-                                Driver.GetAttribute ()
-                               );
+                    lc?.AddLine (
+                                 new (borderBounds.X + 1, topTitleLineY),
+                                 titleBarsLength,
+                                 Orientation.Vertical,
+                                 LineStyle.Single,
+                                 Driver.GetAttribute ()
+                                );
 
                     // Add a vert line for ╞
-                    lc.AddLine (
-                                new (
-                                     borderBounds.X
-                                     + 1
-                                     + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2)
-                                     - 1,
-                                     topTitleLineY
-                                    ),
-                                titleBarsLength,
-                                Orientation.Vertical,
-                                LineStyle.Single,
-                                Driver.GetAttribute ()
-                               );
+                    lc?.AddLine (
+                                 new (
+                                      borderBounds.X
+                                      + 1
+                                      + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2)
+                                      - 1,
+                                      topTitleLineY
+                                     ),
+                                 titleBarsLength,
+                                 Orientation.Vertical,
+                                 LineStyle.Single,
+                                 Driver.GetAttribute ()
+                                );
 
                     // Add the right hand line for ╞═════╗
-                    lc.AddLine (
-                                new (
-                                     borderBounds.X
-                                     + 1
-                                     + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2)
-                                     - 1,
-                                     titleY
-                                    ),
-                                borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
-                                Orientation.Horizontal,
-                                LineStyle,
-                                Driver.GetAttribute ()
-                               );
+                    lc?.AddLine (
+                                 new (
+                                      borderBounds.X
+                                      + 1
+                                      + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2)
+                                      - 1,
+                                      titleY
+                                     ),
+                                 borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
+                                 Orientation.Horizontal,
+                                 lineStyle,
+                                 Driver.GetAttribute ()
+                                );
                 }
             }
 
@@ -603,36 +822,36 @@ public class Border : Adornment
 
             if (drawLeft)
             {
-                lc.AddLine (
-                            new (borderBounds.Location.X, titleY),
-                            sideLineLength,
-                            Orientation.Vertical,
-                            LineStyle,
-                            Driver.GetAttribute ()
-                           );
+                lc?.AddLine (
+                             new (borderBounds.Location.X, titleY),
+                             sideLineLength,
+                             Orientation.Vertical,
+                             lineStyle,
+                             Driver.GetAttribute ()
+                            );
             }
 #endif
 
             if (drawBottom)
             {
-                lc.AddLine (
-                            new (borderBounds.X, borderBounds.Y + borderBounds.Height - 1),
-                            borderBounds.Width,
-                            Orientation.Horizontal,
-                            LineStyle,
-                            Driver.GetAttribute ()
-                           );
+                lc?.AddLine (
+                             new (borderBounds.X, borderBounds.Y + borderBounds.Height - 1),
+                             borderBounds.Width,
+                             Orientation.Horizontal,
+                             lineStyle,
+                             Driver.GetAttribute ()
+                            );
             }
 
             if (drawRight)
             {
-                lc.AddLine (
-                            new (borderBounds.X + borderBounds.Width - 1, titleY),
-                            sideLineLength,
-                            Orientation.Vertical,
-                            LineStyle,
-                            Driver.GetAttribute ()
-                           );
+                lc?.AddLine (
+                             new (borderBounds.X + borderBounds.Width - 1, titleY),
+                             sideLineLength,
+                             Orientation.Vertical,
+                             lineStyle,
+                             Driver.GetAttribute ()
+                            );
             }
 
             Driver.SetAttribute (prevAttr);
@@ -651,10 +870,10 @@ public class Border : Adornment
                 // Redraw title 
                 if (drawTop && maxTitleWidth > 0 && Settings.FastHasFlags (BorderSettings.Title))
                 {
-                    Parent.TitleTextFormatter.Draw (
-                                                    new (borderBounds.X + 2, titleY, maxTitleWidth, 1),
-                                                    Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor (),
-                                                    Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor ());
+                    Parent!.TitleTextFormatter.Draw (
+                                                     new (borderBounds.X + 2, titleY, maxTitleWidth, 1),
+                                                     Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor (),
+                                                     Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor ());
                 }
 
                 //Left
@@ -681,11 +900,11 @@ public class Border : Adornment
             // TODO: This should not be done on each draw?
             if (Settings.FastHasFlags (BorderSettings.Gradient))
             {
-                SetupGradientLineCanvas (lc, screenBounds);
+                SetupGradientLineCanvas (lc!, screenBounds);
             }
             else
             {
-                lc.Fill = null;
+                lc!.Fill = null;
             }
         }
     }
@@ -705,17 +924,536 @@ public class Border : Adornment
     private static void GetAppealingGradientColors (out List<Color> stops, out List<int> steps)
     {
         // Define the colors of the gradient stops with more appealing colors
-        stops = new()
-        {
+        stops =
+        [
             new (0, 128, 255), // Bright Blue
             new (0, 255, 128), // Bright Green
             new (255, 255), // Bright Yellow
             new (255, 128), // Bright Orange
-            new (255, 0, 128) // Bright Pink
-        };
+            new (255, 0, 128)
+        ];
 
         // Define the number of steps between each color for smoother transitions
         // If we pass only a single value then it will assume equal steps between all pairs
-        steps = new() { 15 };
+        steps = [15];
+    }
+
+    private ViewArrangement _arranging;
+
+    private Button? _moveButton; // always top-left
+    private Button? _allSizeButton;
+    private Button? _leftSizeButton;
+    private Button? _rightSizeButton;
+    private Button? _topSizeButton;
+    private Button? _bottomSizeButton;
+
+    /// <summary>
+    ///     Starts "Arrange Mode" where <see cref="Adornment.Parent"/> can be moved and/or resized using the mouse
+    ///     or keyboard. If <paramref name="arrangement"/> is <see cref="ViewArrangement.Fixed"/> keyboard mode is enabled.
+    /// </summary>
+    /// <remarks>
+    ///     Arrange Mode is exited by the user pressing <see cref="Application.ArrangeKey"/>, <see cref="Key.Esc"/>, or by
+    ///     clicking
+    ///     the mouse out of the <see cref="Adornment.Parent"/>'s Frame.
+    /// </remarks>
+    /// <returns></returns>
+    public bool? EnterArrangeMode (ViewArrangement arrangement)
+    {
+        Debug.Assert (_arranging == ViewArrangement.Fixed);
+
+        if (!Parent!.Arrangement.HasFlag (ViewArrangement.Movable)
+            && !Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable)
+            && !Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable)
+            && !Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable)
+            && !Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable)
+           )
+        {
+            return false;
+        }
+
+        // Add Commands and Keybindigs - Note it's ok these get added each time. KeyBindings are cleared in EndArrange()
+        AddArrangeModeKeyBindings ();
+
+        Application.MouseEvent += ApplicationOnMouseEvent;
+
+        // TODO: This code can be refactored to be more readable and maintainable.
+
+        // Create buttons for resizing and moving
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
+        {
+            Debug.Assert (_moveButton is null);
+
+            _moveButton = new ()
+            {
+                Id = "moveButton",
+                CanFocus = true,
+                Width = 1,
+                Height = 1,
+                NoDecorations = true,
+                NoPadding = true,
+                ShadowStyle = ShadowStyle.None,
+                Text = $"{Glyphs.Move}",
+                Visible = false,
+                Data = ViewArrangement.Movable
+            };
+            Add (_moveButton);
+        }
+
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.Resizable))
+        {
+            Debug.Assert (_allSizeButton is null);
+
+            _allSizeButton = new ()
+            {
+                Id = "allSizeButton",
+                CanFocus = true,
+                Width = 1,
+                Height = 1,
+                NoDecorations = true,
+                NoPadding = true,
+                ShadowStyle = ShadowStyle.None,
+                Text = $"{Glyphs.SizeBottomRight}",
+                X = Pos.AnchorEnd (),
+                Y = Pos.AnchorEnd (),
+                Visible = false,
+                Data = ViewArrangement.Resizable
+            };
+            Add (_allSizeButton);
+        }
+
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.TopResizable))
+        {
+            Debug.Assert (_topSizeButton is null);
+
+            _topSizeButton = new ()
+            {
+                Id = "topSizeButton",
+                CanFocus = true,
+                Width = 1,
+                Height = 1,
+                NoDecorations = true,
+                NoPadding = true,
+                ShadowStyle = ShadowStyle.None,
+                Text = $"{Glyphs.SizeVertical}",
+                X = Pos.Center () + Parent!.Margin.Thickness.Horizontal,
+                Y = 0,
+                Visible = false,
+                Data = ViewArrangement.TopResizable
+            };
+            Add (_topSizeButton);
+        }
+
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.RightResizable))
+        {
+            Debug.Assert (_rightSizeButton is null);
+
+            _rightSizeButton = new ()
+            {
+                Id = "rightSizeButton",
+                CanFocus = true,
+                Width = 1,
+                Height = 1,
+                NoDecorations = true,
+                NoPadding = true,
+                ShadowStyle = ShadowStyle.None,
+                Text = $"{Glyphs.SizeHorizontal}",
+                X = Pos.AnchorEnd (),
+                Y = Pos.Center () + Parent!.Margin.Thickness.Vertical / 2,
+                Visible = false,
+                Data = ViewArrangement.RightResizable
+            };
+            Add (_rightSizeButton);
+        }
+
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.LeftResizable))
+        {
+            Debug.Assert (_leftSizeButton is null);
+
+            _leftSizeButton = new ()
+            {
+                Id = "leftSizeButton",
+                CanFocus = true,
+                Width = 1,
+                Height = 1,
+                NoDecorations = true,
+                NoPadding = true,
+                ShadowStyle = ShadowStyle.None,
+                Text = $"{Glyphs.SizeHorizontal}",
+                X = 0,
+                Y = Pos.Center () + Parent!.Margin.Thickness.Vertical / 2,
+                Visible = false,
+                Data = ViewArrangement.LeftResizable
+            };
+            Add (_leftSizeButton);
+        }
+
+        if (Parent!.Arrangement.HasFlag (ViewArrangement.BottomResizable))
+        {
+            Debug.Assert (_bottomSizeButton is null);
+
+            _bottomSizeButton = new ()
+            {
+                Id = "bottomSizeButton",
+                CanFocus = true,
+                Width = 1,
+                Height = 1,
+                NoDecorations = true,
+                NoPadding = true,
+                ShadowStyle = ShadowStyle.None,
+                Text = $"{Glyphs.SizeVertical}",
+                X = Pos.Center () + Parent!.Margin.Thickness.Horizontal / 2,
+                Y = Pos.AnchorEnd (),
+                Visible = false,
+                Data = ViewArrangement.BottomResizable
+            };
+            Add (_bottomSizeButton);
+        }
+
+        if (arrangement == ViewArrangement.Fixed)
+        {
+            // Keyboard mode
+            if (Parent!.Arrangement.HasFlag (ViewArrangement.Movable))
+            {
+                _moveButton!.Visible = true;
+            }
+
+            if (Parent!.Arrangement.HasFlag (ViewArrangement.Resizable))
+            {
+                _allSizeButton!.Visible = true;
+            }
+
+            _arranging = ViewArrangement.Movable;
+            CanFocus = true;
+            SetFocus ();
+        }
+        else
+        {
+            // Mouse mode
+            _arranging = arrangement;
+
+            switch (_arranging)
+            {
+                case ViewArrangement.Movable:
+                    _moveButton!.Visible = true;
+
+                    break;
+
+                case ViewArrangement.RightResizable | ViewArrangement.BottomResizable:
+                case ViewArrangement.Resizable:
+                    _rightSizeButton!.Visible = true;
+                    _bottomSizeButton!.Visible = true;
+
+                    if (_allSizeButton is { })
+                    {
+                        _allSizeButton!.X = Pos.AnchorEnd ();
+                        _allSizeButton!.Y = Pos.AnchorEnd ();
+                        _allSizeButton!.Visible = true;
+                    }
+
+                    break;
+
+                case ViewArrangement.LeftResizable:
+                    _leftSizeButton!.Visible = true;
+
+                    break;
+
+                case ViewArrangement.RightResizable:
+                    _rightSizeButton!.Visible = true;
+
+                    break;
+
+                case ViewArrangement.TopResizable:
+                    _topSizeButton!.Visible = true;
+
+                    break;
+
+                case ViewArrangement.BottomResizable:
+                    _bottomSizeButton!.Visible = true;
+
+                    break;
+
+                case ViewArrangement.LeftResizable | ViewArrangement.BottomResizable:
+                    _rightSizeButton!.Visible = true;
+                    _bottomSizeButton!.Visible = true;
+
+                    if (_allSizeButton is { })
+                    {
+                        _allSizeButton.X = 0;
+                        _allSizeButton.Y = Pos.AnchorEnd ();
+                        _allSizeButton.Visible = true;
+                    }
+
+                    break;
+
+                case ViewArrangement.LeftResizable | ViewArrangement.TopResizable:
+                    _leftSizeButton!.Visible = true;
+                    _topSizeButton!.Visible = true;
+
+                    break;
+
+                case ViewArrangement.RightResizable | ViewArrangement.TopResizable:
+                    _rightSizeButton!.Visible = true;
+                    _topSizeButton!.Visible = true;
+
+                    if (_allSizeButton is { })
+                    {
+                        _allSizeButton.X = Pos.AnchorEnd ();
+                        _allSizeButton.Y = 0;
+                        _allSizeButton.Visible = true;
+                    }
+
+                    break;
+            }
+        }
+
+        if (_arranging != ViewArrangement.Fixed)
+        {
+            if (arrangement == ViewArrangement.Fixed)
+            {
+                // Keyboard mode - enable nav
+                // TODO: Keyboard mode only supports sizing from bottom/right.
+                _arranging = (ViewArrangement)(Focused?.Data ?? ViewArrangement.Fixed);
+            }
+
+            return true;
+        }
+
+        // Hack for now
+        EndArrangeMode ();
+
+        return false;
+    }
+
+    private void AddArrangeModeKeyBindings ()
+    {
+        AddCommand (Command.Quit, EndArrangeMode);
+
+        AddCommand (
+                    Command.Up,
+                    () =>
+                    {
+                        if (Parent is null)
+                        {
+                            return false;
+                        }
+
+                        if (_arranging == ViewArrangement.Movable)
+                        {
+                            Parent!.Y = Parent.Y - 1;
+                        }
+
+                        if (_arranging == ViewArrangement.Resizable)
+                        {
+                            if (Parent!.Viewport.Height > 0)
+                            {
+                                Parent!.Height = Parent.Height! - 1;
+                            }
+                        }
+
+                        Application.Refresh ();
+
+                        return true;
+                    });
+
+        AddCommand (
+                    Command.Down,
+                    () =>
+                    {
+                        if (Parent is null)
+                        {
+                            return false;
+                        }
+
+                        if (_arranging == ViewArrangement.Movable)
+                        {
+                            Parent!.Y = Parent.Y + 1;
+                        }
+
+                        if (_arranging == ViewArrangement.Resizable)
+                        {
+                            Parent!.Height = Parent.Height! + 1;
+                        }
+
+                        Application.Refresh ();
+
+                        return true;
+                    });
+
+        AddCommand (
+                    Command.Left,
+                    () =>
+                    {
+                        if (Parent is null)
+                        {
+                            return false;
+                        }
+
+                        if (_arranging == ViewArrangement.Movable)
+                        {
+                            Parent!.X = Parent.X - 1;
+                        }
+
+                        if (_arranging == ViewArrangement.Resizable)
+                        {
+                            if (Parent!.Viewport.Width > 0)
+                            {
+                                Parent!.Width = Parent.Width! - 1;
+                            }
+                        }
+
+                        Application.Refresh ();
+
+                        return true;
+                    });
+
+        AddCommand (
+                    Command.Right,
+                    () =>
+                    {
+                        if (Parent is null)
+                        {
+                            return false;
+                        }
+
+                        if (_arranging == ViewArrangement.Movable)
+                        {
+                            Parent!.X = Parent.X + 1;
+                        }
+
+                        if (_arranging == ViewArrangement.Resizable)
+                        {
+                            Parent!.Width = Parent.Width! + 1;
+                        }
+
+                        Application.Refresh ();
+
+                        return true;
+                    });
+
+        AddCommand (
+                    Command.Tab,
+                    () =>
+                    {
+                        // BUGBUG: If an arrangable view has only arrangable subviews, it's not possible to activate
+                        // BUGBUG: ArrangeMode with keyboard for the superview.
+                        // BUGBUG: AdvanceFocus should be wise to this and when in ArrangeMode, should move across
+                        // BUGBUG: the view hierachy.
+
+                        AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
+                        _arranging = (ViewArrangement)(Focused?.Data ?? ViewArrangement.Fixed);
+
+                        return true; // Always eat
+                    });
+
+        AddCommand (
+                    Command.BackTab,
+                    () =>
+                    {
+                        AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop);
+                        _arranging = (ViewArrangement)(Focused?.Data ?? ViewArrangement.Fixed);
+
+                        return true; // Always eat
+                    });
+
+        KeyBindings.Add (Key.Esc, KeyBindingScope.HotKey, Command.Quit);
+        KeyBindings.Add (Application.ArrangeKey, KeyBindingScope.HotKey, Command.Quit);
+        KeyBindings.Add (Key.CursorUp, KeyBindingScope.HotKey, Command.Up);
+        KeyBindings.Add (Key.CursorDown, KeyBindingScope.HotKey, Command.Down);
+        KeyBindings.Add (Key.CursorLeft, KeyBindingScope.HotKey, Command.Left);
+        KeyBindings.Add (Key.CursorRight, KeyBindingScope.HotKey, Command.Right);
+
+        KeyBindings.Add (Key.Tab, KeyBindingScope.HotKey, Command.Tab);
+        KeyBindings.Add (Key.Tab.WithShift, KeyBindingScope.HotKey, Command.BackTab);
+    }
+
+    private void ApplicationOnMouseEvent (object? sender, MouseEvent e)
+    {
+        if (e.Flags != MouseFlags.Button1Clicked)
+        {
+            return;
+        }
+
+        // If mouse click is outside of Border.Thickness then exit Arrange Mode
+        // e.Position is screen relative
+        Point framePos = ScreenToFrame (e.ScreenPosition);
+
+        if (!Thickness.Contains (Frame, framePos))
+        {
+            EndArrangeMode ();
+        }
+    }
+
+    private bool? EndArrangeMode ()
+    {
+        // Debug.Assert (_arranging != ViewArrangement.Fixed);
+        _arranging = ViewArrangement.Fixed;
+
+        Application.MouseEvent -= ApplicationOnMouseEvent;
+
+        if (Application.MouseGrabView == this && _dragPosition.HasValue)
+        {
+            Application.UngrabMouse ();
+        }
+
+        if (_moveButton is { })
+        {
+            Remove (_moveButton);
+            _moveButton.Dispose ();
+            _moveButton = null;
+        }
+
+        if (_allSizeButton is { })
+        {
+            Remove (_allSizeButton);
+            _allSizeButton.Dispose ();
+            _allSizeButton = null;
+        }
+
+        if (_leftSizeButton is { })
+        {
+            Remove (_leftSizeButton);
+            _leftSizeButton.Dispose ();
+            _leftSizeButton = null;
+        }
+
+        if (_rightSizeButton is { })
+        {
+            Remove (_rightSizeButton);
+            _rightSizeButton.Dispose ();
+            _rightSizeButton = null;
+        }
+
+        if (_topSizeButton is { })
+        {
+            Remove (_topSizeButton);
+            _topSizeButton.Dispose ();
+            _topSizeButton = null;
+        }
+
+        if (_bottomSizeButton is { })
+        {
+            Remove (_bottomSizeButton);
+            _bottomSizeButton.Dispose ();
+            _bottomSizeButton = null;
+        }
+
+        KeyBindings.Clear ();
+
+        if (CanFocus)
+        {
+            CanFocus = false;
+        }
+
+        return true;
+    }
+
+    /// <inheritdoc/>
+    protected override void Dispose (bool disposing)
+    {
+        Application.GrabbingMouse -= Application_GrabbingMouse;
+        Application.UnGrabbingMouse -= Application_UnGrabbingMouse;
+
+        _dragPosition = null;
+        base.Dispose (disposing);
     }
 }

+ 7 - 4
Terminal.Gui/View/Adornment/Margin.cs

@@ -2,7 +2,7 @@
 
 namespace Terminal.Gui;
 
-/// <summary>The Margin for a <see cref="View"/>.</summary>
+/// <summary>The Margin for a <see cref="View"/>. Accessed via <see cref="View.Margin"/></summary>
 /// <remarks>
 ///     <para>See the <see cref="Adornment"/> class.</para>
 /// </remarks>
@@ -69,7 +69,7 @@ public class Margin : Adornment
     ///     The color scheme for the Margin. If set to <see langword="null"/>, gets the <see cref="Adornment.Parent"/>'s
     ///     <see cref="View.SuperView"/> scheme. color scheme.
     /// </summary>
-    public override ColorScheme ColorScheme
+    public override ColorScheme? ColorScheme
     {
         get
         {
@@ -170,6 +170,9 @@ public class Margin : Adornment
         set => base.ShadowStyle = SetShadow (value);
     }
 
+    private const int PRESS_MOVE_HORIZONTAL = 1;
+    private const int PRESS_MOVE_VERTICAL = 0;
+
     private void Margin_Highlight (object? sender, CancelEventArgs<HighlightStyle> e)
     {
         if (ShadowStyle != ShadowStyle.None)
@@ -179,7 +182,7 @@ public class Margin : Adornment
                 // If the view is pressed and the highlight is being removed, move the shadow back.
                 // Note, for visual effects reasons, we only move horizontally.
                 // TODO: Add a setting or flag that lets the view move vertically as well.
-                Thickness = new (Thickness.Left - 1, Thickness.Top, Thickness.Right + 1, Thickness.Bottom);
+                Thickness = new (Thickness.Left - PRESS_MOVE_HORIZONTAL, Thickness.Top - PRESS_MOVE_VERTICAL, Thickness.Right + PRESS_MOVE_HORIZONTAL, Thickness.Bottom + PRESS_MOVE_VERTICAL);
 
                 if (_rightShadow is { })
                 {
@@ -201,7 +204,7 @@ public class Margin : Adornment
                 // If the view is not pressed and we want highlight move the shadow
                 // Note, for visual effects reasons, we only move horizontally.
                 // TODO: Add a setting or flag that lets the view move vertically as well.
-                Thickness = new (Thickness.Left + 1, Thickness.Top, Thickness.Right - 1, Thickness.Bottom);
+                Thickness = new (Thickness.Left + PRESS_MOVE_HORIZONTAL, Thickness.Top+ PRESS_MOVE_VERTICAL, Thickness.Right - PRESS_MOVE_HORIZONTAL, Thickness.Bottom - PRESS_MOVE_VERTICAL);
                 _pressed = true;
 
                 if (_rightShadow is { })

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

@@ -1,6 +1,6 @@
 namespace Terminal.Gui;
 
-/// <summary>The Padding for a <see cref="View"/>.</summary>
+/// <summary>The Padding for a <see cref="View"/>. Accessed via <see cref="View.Padding"/></summary>
 /// <remarks>
 ///     <para>See the <see cref="Adornment"/> class.</para>
 /// </remarks>

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

@@ -137,7 +137,7 @@ internal class ShadowView : View
         Rectangle screen = ViewportToScreen (viewport);
 
         // Fill the rest of the rectangle
-        for (int i = screen.Y; i < screen.Y + viewport.Height; i++)
+        for (int i = Math.Max (0, screen.Y); i < screen.Y + viewport.Height; i++)
         {
             Driver.Move (screen.X, i);
 

+ 9 - 8
Terminal.Gui/View/HighlightStyle.cs

@@ -1,30 +1,31 @@
-namespace Terminal.Gui;
+using System.Text.Json.Serialization;
+
+namespace Terminal.Gui;
 
 /// <summary>
-/// Describes the highlight style of a view.
+///     Describes the highlight style of a view when the mouse is over it.
 /// </summary>
+[JsonConverter (typeof (JsonStringEnumConverter<HighlightStyle>))]
 [Flags]
 public enum HighlightStyle
 {
     /// <summary>
-    /// No highlight.
+    ///     No highlight.
     /// </summary>
     None = 0,
 
-#if HOVER
     /// <summary>
-    /// The mouse is hovering over the view.
+    ///     The mouse is hovering over the view (but not pressed). See <see cref="View.MouseEnter"/>.
     /// </summary>
     Hover = 1,
-#endif
 
     /// <summary>
-    /// The mouse is pressed within the <see cref="View.Viewport"/>.
+    ///     The mouse is pressed within the <see cref="View.Viewport"/>.
     /// </summary>
     Pressed = 2,
 
     /// <summary>
-    /// The mouse is pressed but moved outside the <see cref="View.Viewport"/>.
+    ///     The mouse is pressed but moved outside the <see cref="View.Viewport"/>.
     /// </summary>
     PressedOutside = 4
 }

+ 1 - 1
Terminal.Gui/View/Layout/DimAutoStyle.cs

@@ -35,7 +35,7 @@ public enum DimAutoStyle
     ///     </para>
     ///     <para>
     ///         If <see cref="DimAuto.MaximumContentDim"/> is set, the dimension will be the maximum of the formatted text and the
-    ///         demension provided by <see cref="DimAuto.MaximumContentDim"/>. Otherwise, the dimension will be that of the formatted text.
+    ///         dimension provided by <see cref="DimAuto.MaximumContentDim"/>. Otherwise, the dimension will be that of the formatted text.
     ///     </para>
     /// </summary>
     Text = 2,

+ 5 - 7
Terminal.Gui/View/Navigation/TabBehavior.cs

@@ -6,25 +6,23 @@
 public enum TabBehavior
 {
     /// <summary>
-    ///     The View will not be a stop-poknt for keyboard-based navigation.
-    /// </summary>
-    /// <remarks>
+    ///     The View will not be a stop-point for keyboard-based navigation.
     ///     <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>
+    /// </summary>
     NoStop = 0,
 
     /// <summary>
-    ///     The View will be a stop-point for keybaord-based navigation across Views (e.g. if the user presses `Tab`).
+    ///     The View will be a stop-point for keyboard-based navigation across Views (e.g. if the user presses `Tab`).
     /// </summary>
     TabStop = 1,
 
     /// <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>
     TabGroup = 2
 }

+ 29 - 18
Terminal.Gui/View/View.Adornments.cs

@@ -1,12 +1,9 @@
-using System.ComponentModel;
-using System.Text.Json.Serialization;
-
-namespace Terminal.Gui;
+namespace Terminal.Gui;
 
 public partial class View // Adornments
 {
     /// <summary>
-    ///    Initializes the Adornments of the View. Called by the constructor.
+    ///     Initializes the Adornments of the View. Called by the constructor.
     /// </summary>
     private void SetupAdornments ()
     {
@@ -49,6 +46,9 @@ public partial class View // Adornments
     /// </summary>
     /// <remarks>
     ///     <para>
+    ///         Enabling <see cref="ShadowStyle"/> will change the Thickness of the Margin to include the shadow.
+    ///     </para>
+    ///     <para>
     ///         The adornments (<see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/>) are not part of the
     ///         View's content and are not clipped by the View's Clip Area.
     ///     </para>
@@ -61,8 +61,10 @@ public partial class View // Adornments
     public Margin Margin { get; private set; }
 
     private ShadowStyle _shadowStyle;
+
     /// <summary>
-    ///     Gets or sets whether the View is shown with a shadow effect. The shadow is drawn on the right and bottom sides of the
+    ///     Gets or sets whether the View is shown with a shadow effect. The shadow is drawn on the right and bottom sides of
+    ///     the
     ///     Margin.
     /// </summary>
     /// <remarks>
@@ -78,7 +80,9 @@ public partial class View // Adornments
             {
                 return;
             }
+
             _shadowStyle = value;
+
             if (Margin is { })
             {
                 Margin.ShadowStyle = value;
@@ -88,9 +92,15 @@ public partial class View // Adornments
 
     /// <summary>
     ///     The <see cref="Adornment"/> that offsets the <see cref="Viewport"/> from the <see cref="Margin"/>.
-    ///     The Border provides the space for a visual border (drawn using
-    ///     line-drawing glyphs) and the Title. The Border expands inward; in other words if `Border.Thickness.Top == 2` the
-    ///     border and title will take up the first row and the second row will be filled with spaces.
+    ///     <para>
+    ///         The Border provides the space for a visual border (drawn using
+    ///         line-drawing glyphs) and the Title. The Border expands inward; in other words if `Border.Thickness.Top == 2`
+    ///         the
+    ///         border and title will take up the first row and the second row will be filled with spaces.
+    ///     </para>
+    ///     <para>
+    ///         The Border provides the UI for mouse and keyboard arrangement of the View. See <see cref="Arrangement"/>.
+    ///     </para>
     /// </summary>
     /// <remarks>
     ///     <para><see cref="BorderStyle"/> provides a simple helper for turning a simple border frame on or off.</para>
@@ -124,15 +134,15 @@ public partial class View // Adornments
         get => Border?.LineStyle ?? LineStyle.Single;
         set
         {
-            var old = Border?.LineStyle ?? LineStyle.None;
+            LineStyle old = Border?.LineStyle ?? LineStyle.None;
             CancelEventArgs<LineStyle> e = new (ref old, ref value);
             OnBorderStyleChanging (e);
-
         }
     }
 
     /// <summary>
-    /// Called when the <see cref="BorderStyle"/> is changing. Invokes <see cref="BorderStyleChanging"/>, which allows the event to be cancelled.
+    ///     Called when the <see cref="BorderStyle"/> is changing. Invokes <see cref="BorderStyleChanging"/>, which allows the
+    ///     event to be cancelled.
     /// </summary>
     /// <remarks>
     ///     Override <see cref="SetBorderStyle"/> to prevent the <see cref="BorderStyle"/> from changing.
@@ -146,6 +156,7 @@ public partial class View // Adornments
         }
 
         BorderStyleChanging?.Invoke (this, e);
+
         if (e.Cancel)
         {
             return;
@@ -154,8 +165,6 @@ public partial class View // Adornments
         SetBorderStyle (e.NewValue);
         LayoutAdornments ();
         SetNeedsLayout ();
-
-        return;
     }
 
     /// <summary>
@@ -163,7 +172,8 @@ public partial class View // Adornments
     /// </summary>
     /// <remarks>
     ///     <para>
-    ///          <see cref="BorderStyle"/> is a helper for manipulating the view's <see cref="Border"/>. Setting this property to any value other
+    ///         <see cref="BorderStyle"/> is a helper for manipulating the view's <see cref="Border"/>. Setting this property
+    ///         to any value other
     ///         than <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
     ///         <see cref="Adornment.Thickness"/> to `1` and <see cref="BorderStyle"/> to the value.
     ///     </para>
@@ -218,9 +228,9 @@ public partial class View // Adornments
     ///     <para>Gets the thickness describing the sum of the Adornments' thicknesses.</para>
     /// </summary>
     /// <remarks>
-    /// <para>
-    ///     The <see cref="Viewport"/> is offset from the <see cref="Frame"/> by the thickness returned by this method.
-    /// </para>
+    ///     <para>
+    ///         The <see cref="Viewport"/> is offset from the <see cref="Frame"/> by the thickness returned by this method.
+    ///     </para>
     /// </remarks>
     /// <returns>A thickness that describes the sum of the Adornments' thicknesses.</returns>
     public Thickness GetAdornmentsThickness ()
@@ -229,6 +239,7 @@ public partial class View // Adornments
         {
             return Thickness.Empty;
         }
+
         return Margin.Thickness + Border.Thickness + Padding.Thickness;
     }
 

+ 2 - 3
Terminal.Gui/View/View.Arrangement.cs

@@ -3,12 +3,11 @@
 public partial class View
 {
     /// <summary>
-    ///    Gets or sets the user actions that are enabled for the view within it's <see cref="SuperView"/>.
+    ///    Gets or sets the user actions that are enabled for the arranging this view within it's <see cref="SuperView"/>.
     /// </summary>
     /// <remarks>
     /// <para>
-    ///     Sizing or moving a view is only possible if the <see cref="View"/> is part of a <see cref="SuperView"/> and
-    ///     the relevant position and dimensions of the <see cref="View"/> are independent of other SubViews
+    ///     See the View Arrangement Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/arrangement.html"/>
     /// </para>
     /// </remarks>
     public ViewArrangement Arrangement { get; set; }

+ 12 - 0
Terminal.Gui/View/View.Content.cs

@@ -12,6 +12,9 @@ public partial class View
     /// </summary>
     /// <remarks>
     ///     <para>
+    ///         See the View Layout Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+    ///     </para>
+    ///     <para>
     ///         Negative sizes are not supported.
     ///     </para>
     ///     <para>
@@ -55,6 +58,9 @@ public partial class View
     /// </summary>
     /// <remarks>a>
     ///     <para>
+    ///         See the View Layout Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+    ///     </para>
+    ///     <para>
     ///         If the content size was not explicitly set by <see cref="SetContentSize"/>, and the View has no visible subviews, <see cref="GetContentSize ()"/> will return the
     ///         size of
     ///         <see cref="Viewport"/>.
@@ -85,6 +91,9 @@ public partial class View
     ///     size or not.
     /// </summary>
     /// <remarks>
+    ///     <para>
+    ///         See the View Layout Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+    ///     </para>
     ///     <list type="bullet">
     ///         <listheader>
     ///             <term>Value</term> <description>Result</description>
@@ -236,6 +245,9 @@ public partial class View
     /// </value>
     /// <remarks>
     ///     <para>
+    ///         See the View Layout Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+    ///     </para>
+    ///     <para>
     ///         Positive values for the location indicate the visible area is offset into (down-and-right) the View's virtual
     ///         <see cref="GetContentSize ()"/>. This enables scrolling down and to the right (e.g. in a <see cref="ListView"/>
     ///         .

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

@@ -20,10 +20,9 @@ public enum ViewDiagnosticFlags : uint
     Padding = 0b_0000_0010,
 
     /// <summary>
-    ///     When enabled, <see cref="Adornment.OnMouseEnter(Gui.MouseEvent)"/> and <see cref="Adornment.OnMouseLeave(Gui.MouseEvent)"/>
-    ///     will invert the foreground and background colors.
+    ///     When enabled the View's colors will be darker when the mouse is hovering over the View (See <see cref="View.MouseEnter"/> and <see cref="View.MouseLeave"/>.
     /// </summary>
-    MouseEnter = 0b_0000_00100
+    Hover = 0b_0000_00100
 }
 
 public partial class View

+ 117 - 63
Terminal.Gui/View/View.Drawing.cs

@@ -1,13 +1,12 @@
-using System.Drawing;
-
+#nullable enable
 namespace Terminal.Gui;
 
 public partial class View // Drawing APIs
 {
-    private ColorScheme _colorScheme;
+    private ColorScheme? _colorScheme;
 
     /// <summary>The color scheme for this view, if it is not defined, it returns the <see cref="SuperView"/>'s color scheme.</summary>
-    public virtual ColorScheme ColorScheme
+    public virtual ColorScheme? ColorScheme
     {
         get
         {
@@ -23,6 +22,11 @@ public partial class View // Drawing APIs
             if (_colorScheme != value)
             {
                 _colorScheme = value;
+
+                if (Border is { } && Border.LineStyle != LineStyle.None && Border.ColorScheme is { })
+                {
+                    Border.ColorScheme = _colorScheme;
+                }
                 SetNeedsDisplay ();
             }
         }
@@ -56,7 +60,7 @@ public partial class View // Drawing APIs
     public bool SubViewNeedsDisplay { get; private set; }
 
     /// <summary>
-    ///     Gets or sets whether this View will use it's SuperView's <see cref="LineCanvas"/> for rendering any 
+    ///     Gets or sets whether this View will use it's SuperView's <see cref="LineCanvas"/> for rendering any
     ///     lines. If <see langword="true"/> the rendering of any borders drawn by this Frame will be done by its parent's
     ///     SuperView. If <see langword="false"/> (the default) this View's <see cref="OnDrawAdornments"/> method will be
     ///     called to render the borders.
@@ -86,7 +90,8 @@ public partial class View // Drawing APIs
     ///     <para>
     ///         If <see cref="ViewportSettings"/> has <see cref="Gui.ViewportSettings.ClearContentOnly"/> only
     ///         the portion of the content
-    ///         area that is visible within the <see cref="View.Viewport"/> will be cleared. This is useful for views that have a
+    ///         area that is visible within the <see cref="View.Viewport"/> will be cleared. This is useful for views that have
+    ///         a
     ///         content area larger than the Viewport (e.g. when <see cref="ViewportSettings.AllowNegativeLocation"/> is
     ///         enabled) and want
     ///         the area outside the content to be visually distinct.
@@ -143,15 +148,15 @@ public partial class View // Drawing APIs
 
     /// <summary>Sets the <see cref="ConsoleDriver"/>'s clip region to <see cref="Viewport"/>.</summary>
     /// <remarks>
-    /// <para>
-    ///     By default, the clip rectangle is set to the intersection of the current clip region and the
-    ///     <see cref="Viewport"/>. This ensures that drawing is constrained to the viewport, but allows
-    ///     content to be drawn beyond the viewport.
-    /// </para>
-    /// <para>
-    ///     If <see cref="ViewportSettings"/> has <see cref="Gui.ViewportSettings.ClipContentOnly"/> set, clipping will be
-    ///     applied to just the visible content area.
-    /// </para>
+    ///     <para>
+    ///         By default, the clip rectangle is set to the intersection of the current clip region and the
+    ///         <see cref="Viewport"/>. This ensures that drawing is constrained to the viewport, but allows
+    ///         content to be drawn beyond the viewport.
+    ///     </para>
+    ///     <para>
+    ///         If <see cref="ViewportSettings"/> has <see cref="Gui.ViewportSettings.ClipContentOnly"/> set, clipping will be
+    ///         applied to just the visible content area.
+    ///     </para>
     /// </remarks>
     /// <returns>
     ///     The current screen-relative clip region, which can be then re-applied by setting
@@ -182,11 +187,15 @@ public partial class View // Drawing APIs
     }
 
     /// <summary>
-    ///     Draws the view. Causes the following virtual methods to be called (along with their related events):
+    ///     Draws the view if it needs to be drawn. Causes the following virtual methods to be called (along with their related events):
     ///     <see cref="OnDrawContent"/>, <see cref="OnDrawContentComplete"/>.
     /// </summary>
     /// <remarks>
     ///     <para>
+    ///         The view will only be drawn if it is visible, and has any of <see cref="NeedsDisplay"/>, <see cref="SubViewNeedsDisplay"/>,
+    ///         or <see cref="LayoutNeeded"/> set.
+    ///     </para>
+    ///     <para>
     ///         Always use <see cref="Viewport"/> (view-relative) when calling <see cref="OnDrawContent(Rectangle)"/>, NOT
     ///         <see cref="Frame"/> (superview-relative).
     ///     </para>
@@ -202,6 +211,23 @@ public partial class View // Drawing APIs
     /// </remarks>
     public void Draw ()
     {
+        if (!CanBeVisible (this))
+        {
+            return;
+        }
+
+        // TODO: This ensures overlapped views are drawn correctly. However, this is inefficient.
+        // TODO: The correct fix is to implement non-rectangular clip regions: https://github.com/gui-cs/Terminal.Gui/issues/3413
+        if (Arrangement.HasFlag (ViewArrangement.Overlapped))
+        {
+            SetNeedsDisplay ();
+        }
+
+        if (!NeedsDisplay && !SubViewNeedsDisplay && !LayoutNeeded)
+        {
+            return;
+        }
+
         OnDrawAdornments ();
 
         if (ColorScheme is { })
@@ -257,8 +283,7 @@ public partial class View // Drawing APIs
     ///         <see cref="View"/> .
     ///     </para>
     /// </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>
     /// <remarks>
@@ -268,8 +293,7 @@ public partial class View // Drawing APIs
     ///         <see cref="View"/> .
     ///     </para>
     /// </remarks>
-    [CanBeNull]
-    public event EventHandler<DrawEventArgs> DrawContentComplete;
+    public event EventHandler<DrawEventArgs>? DrawContentComplete;
 
     /// <summary>Utility function to draw strings that contain a hotkey.</summary>
     /// <param name="text">String to display, the hotkey specifier before a letter flags the next letter as the hotkey.</param>
@@ -310,19 +334,18 @@ public partial class View // Drawing APIs
     ///     If set to <see langword="true"/> this uses the focused colors from the color scheme, otherwise
     ///     the regular ones.
     /// </param>
-    /// <param name="scheme">The color scheme to use.</param>
-    public void DrawHotString (string text, bool focused, ColorScheme scheme)
+    public void DrawHotString (string text, bool focused)
     {
         if (focused)
         {
-            DrawHotString (text, scheme.HotFocus, scheme.Focus);
+            DrawHotString (text, GetHotFocusColor (), GetFocusColor ());
         }
         else
         {
             DrawHotString (
                            text,
-                           Enabled ? scheme.HotNormal : scheme.Disabled,
-                           Enabled ? scheme.Normal : scheme.Disabled
+                           Enabled ? GetHotNormalColor () : ColorScheme.Disabled,
+                           Enabled ? GetNormalColor () : ColorScheme.Disabled
                           );
         }
     }
@@ -334,14 +357,34 @@ public partial class View // Drawing APIs
     ///     overridden can return other values.
     /// </returns>
     public virtual Attribute GetFocusColor ()
+    {
+        ColorScheme? cs = ColorScheme;
+
+        if (cs is null)
+        {
+            cs = new ();
+        }
+
+        return Enabled ? GetColor (cs.Focus) : cs.Disabled;
+    }
+
+    /// <summary>Determines the current <see cref="ColorScheme"/> based on the <see cref="Enabled"/> value.</summary>
+    /// <returns>
+    ///     <see cref="ColorScheme.Focus"/> if <see cref="Enabled"/> is <see langword="true"/> or
+    ///     <see cref="ColorScheme.Disabled"/> if <see cref="Enabled"/> is <see langword="false"/>. If it's
+    ///     overridden can return other values.
+    /// </returns>
+    public virtual Attribute GetHotFocusColor ()
     {
         ColorScheme cs = ColorScheme;
+
+
         if (cs is null)
         {
             cs = new ();
         }
 
-        return Enabled ? cs.Focus : cs.Disabled;
+        return Enabled ? GetColor (cs.HotFocus) : cs.Disabled;
     }
 
     /// <summary>Determines the current <see cref="ColorScheme"/> based on the <see cref="Enabled"/> value.</summary>
@@ -352,14 +395,14 @@ public partial class View // Drawing APIs
     /// </returns>
     public virtual Attribute GetHotNormalColor ()
     {
-        ColorScheme cs = ColorScheme;
+        ColorScheme? cs = ColorScheme;
 
         if (cs is null)
         {
             cs = new ();
         }
 
-        return Enabled ? cs.HotNormal : cs.Disabled;
+        return Enabled ? GetColor (cs.HotNormal) : cs.Disabled;
     }
 
     /// <summary>Determines the current <see cref="ColorScheme"/> based on the <see cref="Enabled"/> value.</summary>
@@ -370,14 +413,30 @@ public partial class View // Drawing APIs
     /// </returns>
     public virtual Attribute GetNormalColor ()
     {
-        ColorScheme cs = ColorScheme;
+        ColorScheme? cs = ColorScheme;
 
         if (cs is null)
         {
             cs = new ();
         }
 
-        return Enabled ? cs.Normal : cs.Disabled;
+        Attribute disabled = new (cs.Disabled.Foreground, cs.Disabled.Background);
+        if (Diagnostics.HasFlag (ViewDiagnosticFlags.Hover) && _hovering)
+        {
+            disabled = new (disabled.Foreground.GetDarkerColor (), disabled.Background.GetDarkerColor ());
+        }
+        return Enabled ? GetColor (cs.Normal) : disabled;
+    }
+
+    private Attribute GetColor (Attribute inputAttribute)
+    {
+        Attribute attr = inputAttribute;
+        if (Diagnostics.HasFlag (ViewDiagnosticFlags.Hover) && _hovering)
+        {
+            attr = new (attr.Foreground.GetDarkerColor (), attr.Background.GetDarkerColor ());
+        }
+
+        return attr;
     }
 
     /// <summary>Moves the drawing cursor to the specified <see cref="Viewport"/>-relative location in the view.</summary>
@@ -403,7 +462,7 @@ public partial class View // Drawing APIs
             return false;
         }
 
-        var screen = ViewportToScreen (new Point (col, row));
+        Point screen = ViewportToScreen (new Point (col, row));
         Driver?.Move (screen.X, screen.Y);
 
         return true;
@@ -442,12 +501,14 @@ public partial class View // Drawing APIs
     ///     </para>
     ///     <para>
     ///         The <see cref="Viewport"/> Location and Size indicate what part of the View's content, defined
-    ///         by <see cref="GetContentSize ()"/>, is visible and should be drawn. The coordinates taken by <see cref="Move"/> and
+    ///         by <see cref="GetContentSize ()"/>, is visible and should be drawn. The coordinates taken by <see cref="Move"/>
+    ///         and
     ///         <see cref="AddRune"/> are relative to <see cref="Viewport"/>, thus if <c>ViewPort.Location.Y</c> is <c>5</c>
     ///         the 6th row of the content should be drawn using <c>MoveTo (x, 5)</c>.
     ///     </para>
     ///     <para>
-    ///         If <see cref="GetContentSize ()"/> is larger than <c>ViewPort.Size</c> drawing code should use <see cref="Viewport"/>
+    ///         If <see cref="GetContentSize ()"/> is larger than <c>ViewPort.Size</c> drawing code should use
+    ///         <see cref="Viewport"/>
     ///         to constrain drawing for better performance.
     ///     </para>
     ///     <para>
@@ -467,14 +528,15 @@ public partial class View // Drawing APIs
     {
         if (NeedsDisplay)
         {
-            if (SuperView is { })
+            if (!CanBeVisible (this))
             {
-                Clear ();
+                return;
             }
 
-            if (!CanBeVisible (this))
+            // BUGBUG: this clears way too frequently. Need to optimize this.
+            if (SuperView is { } || Arrangement.HasFlag (ViewArrangement.Overlapped))
             {
-                return;
+                Clear ();
             }
 
             if (!string.IsNullOrEmpty (TextFormatter.Text))
@@ -492,7 +554,7 @@ public partial class View // Drawing APIs
             TextFormatter?.Draw (
                                  drawRect,
                                  HasFocus ? GetFocusColor () : GetNormalColor (),
-                                 HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
+                                 HasFocus ? GetHotFocusColor () : GetHotNormalColor (),
                                  Rectangle.Empty
                                 );
             SetSubViewNeedsDisplay ();
@@ -503,24 +565,14 @@ public partial class View // Drawing APIs
         // TODO: Implement OnDrawSubviews (cancelable);
         if (_subviews is { } && SubViewNeedsDisplay)
         {
-            IEnumerable<View> subviewsNeedingDraw;
-            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
-                subviewsNeedingDraw = _subviews.Where (
-                                                       view => view.Visible
-                                                               && (view.NeedsDisplay || view.SubViewNeedsDisplay || view.LayoutNeeded)
-                                                      ).Reverse ();
-
-            }
-            else
-            {
-                subviewsNeedingDraw = _subviews.Where (
-                                                                         view => view.Visible
-                                                                                 && (view.NeedsDisplay || view.SubViewNeedsDisplay || view.LayoutNeeded)
-                                                                        );
+            IEnumerable<View> subviewsNeedingDraw = _subviews.Where (
+                                                                     view => view.Visible
+                                                                             && (view.NeedsDisplay
+                                                                                 || view.SubViewNeedsDisplay
+                                                                                 || view.LayoutNeeded
+                                                                                 || view.Arrangement.HasFlag (ViewArrangement.Overlapped)
+                                                                    ));
 
-            }
             foreach (View view in subviewsNeedingDraw)
             {
                 if (view.LayoutNeeded)
@@ -528,6 +580,13 @@ public partial class View // Drawing APIs
                     view.LayoutSubviews ();
                 }
 
+                // TODO: This ensures overlapped views are drawn correctly. However, this is inefficient.
+                // TODO: The correct fix is to implement non-rectangular clip regions: https://github.com/gui-cs/Terminal.Gui/issues/3413
+                if (view.Arrangement.HasFlag (ViewArrangement.Overlapped))
+                {
+                    view.SetNeedsDisplay ();
+                }
+
                 view.Draw ();
             }
         }
@@ -564,7 +623,7 @@ public partial class View // Drawing APIs
                 // Get the entire map
                 if (p.Value is { })
                 {
-                    Driver.SetAttribute (p.Value.Value.Attribute ?? ColorScheme.Normal);
+                    Driver.SetAttribute (p.Value.Value.Attribute ?? ColorScheme!.Normal);
                     Driver.Move (p.Key.X, p.Key.Y);
 
                     // TODO: #2616 - Support combining sequences that don't normalize
@@ -589,7 +648,7 @@ public partial class View // Drawing APIs
                 // Get the entire map
                 if (p.Value is { })
                 {
-                    Driver.SetAttribute (p.Value.Value.Attribute ?? ColorScheme.Normal);
+                    Driver.SetAttribute (p.Value.Value.Attribute ?? ColorScheme!.Normal);
                     Driver.Move (p.Key.X, p.Key.Y);
 
                     // TODO: #2616 - Support combining sequences that don't normalize
@@ -608,10 +667,7 @@ public partial class View // Drawing APIs
     ///     If the view has not been initialized (<see cref="IsInitialized"/> is <see langword="false"/>), this method
     ///     does nothing.
     /// </remarks>
-    public void SetNeedsDisplay ()
-    {
-        SetNeedsDisplay (Viewport);
-    }
+    public void SetNeedsDisplay () { SetNeedsDisplay (Viewport); }
 
     /// <summary>Expands the area of this view needing to be redrawn to include <paramref name="region"/>.</summary>
     /// <remarks>
@@ -671,8 +727,6 @@ public partial class View // Drawing APIs
         if (SuperView is { SubViewNeedsDisplay: false })
         {
             SuperView.SetSubViewNeedsDisplay ();
-
-            return;
         }
     }
 

+ 11 - 6
Terminal.Gui/View/View.Hierarchy.cs

@@ -159,14 +159,17 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
 
         Rectangle touched = view.Frame;
 
-        // If a view being removed is focused, it should lose focus.
-        if (view.HasFocus)
+        bool hadFocus = view.HasFocus;
+        bool couldFocus = view.CanFocus;
+
+        if (hadFocus)
         {
-            view.HasFocus = false;
+            view.CanFocus = false; // If view had focus, this will ensure it doesn't and it stays that way
         }
+        Debug.Assert (!view.HasFocus);
 
         _subviews.Remove (view);
-        view._superView = null; // Null this AFTER removing focus
+        view._superView = null;
 
         SetNeedsLayout ();
         SetNeedsDisplay ();
@@ -179,9 +182,11 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
             }
         }
 
-        if (HasFocus)
+        view.CanFocus = couldFocus; // Restore to previous value
+
+        if (_previouslyFocused == view)
         {
-            FocusDeepest (NavigationDirection.Forward, TabStop);
+            _previouslyFocused = null;
         }
 
         OnRemoved (new (this, view));

+ 7 - 0
Terminal.Gui/View/View.Keyboard.cs

@@ -579,6 +579,13 @@ public partial class View // Keyboard APIs
 
     private bool ProcessAdornmentKeyBindings (Adornment adornment, Key keyEvent, KeyBindingScope scope, ref bool? handled)
     {
+        bool? adornmentHandled = adornment.OnInvokingKeyBindings (keyEvent, scope);
+
+        if (adornmentHandled is true)
+        {
+            return true;
+        }
+
         if (adornment?.Subviews is null)
         {
             return false;

+ 41 - 78
Terminal.Gui/View/View.Layout.cs

@@ -1,5 +1,6 @@
 #nullable enable
 using System.Diagnostics;
+using Microsoft.CodeAnalysis;
 
 namespace Terminal.Gui;
 
@@ -12,83 +13,7 @@ public partial class View // Layout APIs
     /// <returns><see langword="true"/> if the specified SuperView-relative coordinates are within the View.</returns>
     public virtual bool Contains (in Point location) { return Frame.Contains (location); }
 
-    /// <summary>Finds the first Subview of <paramref name="start"/> that is visible at the provided location.</summary>
-    /// <remarks>
-    ///     <para>
-    ///         Used to determine what view the mouse is over.
-    ///     </para>
-    /// </remarks>
-    /// <param name="start">The view to scope the search by.</param>
-    /// <param name="location"><paramref name="start"/>.SuperView-relative coordinate.</param>
-    /// <returns>
-    ///     The view that was found at the <paramref name="location"/> coordinate.
-    ///     <see langword="null"/> if no view was found.
-    /// </returns>
-
-    // CONCURRENCY: This method is not thread-safe. Undefined behavior and likely program crashes are exposed by unsynchronized access to InternalSubviews.
-    internal static View? FindDeepestView (View? start, in Point location)
-    {
-        Point currentLocation = location;
-
-        while (start is { Visible: true } && start.Contains (currentLocation))
-        {
-            Adornment? found = null;
-
-            if (start.Margin.Contains (currentLocation))
-            {
-                found = start.Margin;
-            }
-            else if (start.Border.Contains (currentLocation))
-            {
-                found = start.Border;
-            }
-            else if (start.Padding.Contains (currentLocation))
-            {
-                found = start.Padding;
-            }
-
-            Point viewportOffset = start.GetViewportOffsetFromFrame ();
-
-            if (found is { })
-            {
-                start = found;
-                viewportOffset = found.Parent?.Frame.Location ?? Point.Empty;
-            }
-
-            int startOffsetX = currentLocation.X - (start.Frame.X + viewportOffset.X);
-            int startOffsetY = currentLocation.Y - (start.Frame.Y + viewportOffset.Y);
-
-            View? subview = null;
-
-            for (int i = start.InternalSubviews.Count - 1; i >= 0; i--)
-            {
-                if (start.InternalSubviews [i].Visible
-                    && start.InternalSubviews [i].Contains (new (startOffsetX + start.Viewport.X, startOffsetY + start.Viewport.Y)))
-                {
-                    subview = start.InternalSubviews [i];
-                    currentLocation.X = startOffsetX + start.Viewport.X;
-                    currentLocation.Y = startOffsetY + start.Viewport.Y;
-
-                    // start is the deepest subview under the mouse; stop searching the subviews
-                    break;
-                }
-            }
-
-            if (subview is null)
-            {
-                // No subview was found that's under the mouse, so we're done
-                return start;
-            }
-
-            // We found a subview of start that's under the mouse, continue...
-            start = subview;
-        }
-
-        return null;
-    }
-
     // BUGBUG: This method interferes with Dialog/MessageBox default min/max size.
-
     /// <summary>
     ///     Gets a new location of the <see cref="View"/> that is within the Viewport of the <paramref name="viewToMove"/>'s
     ///     <see cref="View.SuperView"/> (e.g. for dragging a Window). The `out` parameters are the new X and Y coordinates.
@@ -252,6 +177,10 @@ public partial class View // Layout APIs
     /// </value>
     /// <remarks>
     ///     <para>
+    ///         See the View Layout Deep Dive for more information:
+    ///         <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+    ///     </para>
+    ///     <para>
     ///         Frame is relative to the <see cref="SuperView"/>'s Content, which is bound by <see cref="GetContentSize ()"/>
     ///         .
     ///     </para>
@@ -288,6 +217,8 @@ public partial class View // Layout APIs
             {
                 OnResizeNeeded ();
             }
+
+            SetNeedsDisplay ();
         }
     }
 
@@ -369,6 +300,10 @@ public partial class View // Layout APIs
     /// <value>The <see cref="Pos"/> object representing the X position.</value>
     /// <remarks>
     ///     <para>
+    ///         See the View Layout Deep Dive for more information:
+    ///         <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+    ///     </para>
+    ///     <para>
     ///         The position is relative to the <see cref="SuperView"/>'s Content, which is bound by
     ///         <see cref="GetContentSize ()"/>.
     ///     </para>
@@ -408,6 +343,10 @@ public partial class View // Layout APIs
     /// <value>The <see cref="Pos"/> object representing the Y position.</value>
     /// <remarks>
     ///     <para>
+    ///         See the View Layout Deep Dive for more information:
+    ///         <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+    ///     </para>
+    ///     <para>
     ///         The position is relative to the <see cref="SuperView"/>'s Content, which is bound by
     ///         <see cref="GetContentSize ()"/>.
     ///     </para>
@@ -446,6 +385,10 @@ public partial class View // Layout APIs
     /// <value>The <see cref="Dim"/> object representing the height of the view (the number of rows).</value>
     /// <remarks>
     ///     <para>
+    ///         See the View Layout Deep Dive for more information:
+    ///         <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+    ///     </para>
+    ///     <para>
     ///         The dimension is relative to the <see cref="SuperView"/>'s Content, which is bound by
     ///         <see cref="GetContentSize ()"/>
     ///         .
@@ -495,6 +438,10 @@ public partial class View // Layout APIs
     /// <value>The <see cref="Dim"/> object representing the width of the view (the number of columns).</value>
     /// <remarks>
     ///     <para>
+    ///         See the View Layout Deep Dive for more information:
+    ///         <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+    ///     </para>
+    ///     <para>
     ///         The dimension is relative to the <see cref="SuperView"/>'s Content, which is bound by
     ///         <see cref="GetContentSize ()"/>
     ///         .
@@ -666,6 +613,10 @@ public partial class View // Layout APIs
     /// </summary>
     /// <remarks>
     ///     <para>
+    ///         See the View Layout Deep Dive for more information:
+    ///         <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+    ///     </para>
+    ///     <para>
     ///         The position and dimensions of the view are indeterminate until the view has been initialized. Therefore, the
     ///         behavior of this method is indeterminate if <see cref="IsInitialized"/> is <see langword="false"/>.
     ///     </para>
@@ -768,14 +719,26 @@ public partial class View // Layout APIs
             LayoutAdornments ();
         }
 
-        SetNeedsDisplay ();
         SetNeedsLayout ();
+
+        // TODO: This ensures overlapped views are drawn correctly. However, this is inefficient.
+        // TODO: The correct fix is to implement non-rectangular clip regions: https://github.com/gui-cs/Terminal.Gui/issues/3413
+        if (Arrangement.HasFlag (ViewArrangement.Overlapped))
+        {
+            foreach (Toplevel v in Application.TopLevels)
+            {
+                if (v.Visible && v != this)
+                {
+                    v.SetNeedsDisplay ();
+                }
+            }
+        }
     }
 
     internal bool LayoutNeeded { get; private set; } = true;
 
     /// <summary>
-    ///     Sets the internal <see cref="LayoutNeeded"/> flag for this View and all of it's subviews and it's SuperView.
+    ///     Sets <see cref="LayoutNeeded"/> for this View and all of it's subviews and it's SuperView.
     ///     The main loop will call SetRelativeLayout and LayoutSubviews for any view with <see cref="LayoutNeeded"/> set.
     /// </summary>
     internal void SetNeedsLayout ()

+ 362 - 225
Terminal.Gui/View/View.Mouse.cs

@@ -5,34 +5,196 @@ namespace Terminal.Gui;
 
 public partial class View // Mouse APIs
 {
-    private ColorScheme? _savedHighlightColorScheme;
+    #region MouseEnterLeave
+
+    private bool _hovering;
+    private ColorScheme? _savedNonHoverColorScheme;
 
     /// <summary>
-    ///     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.
+    ///     INTERNAL Called by <see cref="Application.OnMouseEvent"/> when the mouse moves over the View's <see cref="Frame"/>.
+    ///     <see cref="MouseLeave"/> will
+    ///     be raised when the mouse is no longer over the <see cref="Frame"/>. If another View occludes this View, the
+    ///     that View will also receive MouseEnter/Leave events.
     /// </summary>
-    public event EventHandler<CancelEventArgs<HighlightStyle>>? Highlight;
+    /// <param name="eventArgs"></param>
+    /// <returns>
+    ///     <see langword="true"/> if the event was canceled, <see langword="false"/> if not, <see langword="null"/> if the
+    ///     view is not visible. Cancelling the event
+    ///     prevents Views higher in the visible hierarchy from receiving Enter/Leave events.
+    /// </returns>
+    internal bool? NewMouseEnterEvent (CancelEventArgs eventArgs)
+    {
+        // Pre-conditions
+        if (!CanBeVisible (this))
+        {
+            return null;
+        }
+
+        // Cancellable event
+        if (OnMouseEnter (eventArgs))
+        {
+            return true;
+        }
+
+        MouseEnter?.Invoke (this, eventArgs);
+
+        _hovering = !eventArgs.Cancel;
+
+        if (eventArgs.Cancel)
+        {
+            return true;
+        }
+
+        // Post-conditions
+        if (HighlightStyle.HasFlag (HighlightStyle.Hover) || Diagnostics.HasFlag (ViewDiagnosticFlags.Hover))
+        {
+            HighlightStyle copy = HighlightStyle;
+            var hover = HighlightStyle.Hover;
+            CancelEventArgs<HighlightStyle> args = new (ref copy, ref hover);
+
+            if (RaiseHighlight (args) || args.Cancel)
+            {
+                return args.Cancel;
+            }
+
+            ColorScheme cs = ColorScheme;
+
+            if (cs is null)
+            {
+                cs = new ();
+            }
+
+            _savedNonHoverColorScheme = cs;
+
+            ColorScheme = ColorScheme.GetHighlightColorScheme ();
+        }
+
+        return false;
+    }
 
     /// <summary>
-    ///     Gets or sets whether the <see cref="View"/> will be highlighted visually while the mouse button is
-    ///     pressed.
+    ///     Called when the mouse moves over the View's <see cref="Frame"/> and no other non-Subview occludes it.
+    ///     <see cref="MouseLeave"/> will
+    ///     be raised when the mouse is no longer over the <see cref="Frame"/>.
     /// </summary>
-    public HighlightStyle HighlightStyle { get; set; }
+    /// <remarks>
+    ///     <para>
+    ///         A view must be visible to receive Enter events (Leave events are always received).
+    ///     </para>
+    ///     <para>
+    ///         If the event is cancelled, the mouse event will not be propagated to other views and <see cref="MouseEnter"/>
+    ///         will not be raised.
+    ///     </para>
+    ///     <para>
+    ///         Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
+    ///     </para>
+    ///     <para>
+    ///         See <see cref="SetPressedHighlight"/> for more information.
+    ///     </para>
+    /// </remarks>
+    /// <param name="eventArgs"></param>
+    /// <returns>
+    ///     <see langword="true"/> if the event was canceled, <see langword="false"/> if not. Cancelling the event
+    ///     prevents Views higher in the visible hierarchy from receiving Enter/Leave events.
+    /// </returns>
+    protected virtual bool OnMouseEnter (CancelEventArgs eventArgs) { return false; }
 
-    /// <summary>Event fired when a mouse click occurs.</summary>
+    /// <summary>
+    ///     Raised when the mouse moves over the View's <see cref="Frame"/>. <see cref="MouseLeave"/> will
+    ///     be raised when the mouse is no longer over the <see cref="Frame"/>. If another View occludes this View, the
+    ///     that View will also receive MouseEnter/Leave events.
+    /// </summary>
     /// <remarks>
     ///     <para>
-    ///         Fired when the mouse is either clicked or double-clicked. Check
-    ///         <see cref="MouseEvent.Flags"/> to see which button was clicked.
+    ///         A view must be visible to receive Enter events (Leave events are always received).
     ///     </para>
     ///     <para>
-    ///         The coordinates are relative to <see cref="View.Viewport"/>.
+    ///         If the event is cancelled, the mouse event will not be propagated to other views.
+    ///     </para>
+    ///     <para>
+    ///         Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
+    ///     </para>
+    ///     <para>
+    ///         Set <see cref="CancelEventArgs.Cancel"/> to <see langword="true"/> if the event was canceled,
+    ///         <see langword="false"/> if not. Cancelling the event
+    ///         prevents Views higher in the visible hierarchy from receiving Enter/Leave events.
+    ///     </para>
+    ///     <para>
+    ///         See <see cref="SetPressedHighlight"/> for more information.
     ///     </para>
     /// </remarks>
-    public event EventHandler<MouseEventEventArgs>? MouseClick;
+    public event EventHandler<CancelEventArgs>? MouseEnter;
+
+    /// <summary>
+    ///     INTERNAL Called by <see cref="Application.OnMouseEvent"/> when the mouse leaves <see cref="Frame"/>, or is occluded
+    ///     by another non-SubView.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This method calls <see cref="OnMouseLeave"/> and raises the <see cref="MouseLeave"/> event.
+    ///     </para>
+    ///     <para>
+    ///         Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
+    ///     </para>
+    ///     <para>
+    ///         See <see cref="SetPressedHighlight"/> for more information.
+    ///     </para>
+    /// </remarks>
+    internal void NewMouseLeaveEvent ()
+    {
+        // Pre-conditions
+
+        // Non-cancellable event
+        OnMouseLeave ();
+
+        MouseLeave?.Invoke (this, EventArgs.Empty);
 
-    /// <summary>Event fired when the mouse moves into the View's <see cref="Viewport"/>.</summary>
-    public event EventHandler<MouseEventEventArgs>? MouseEnter;
+        // Post-conditions
+        _hovering = false;
+
+        if (HighlightStyle.HasFlag (HighlightStyle.Hover) || Diagnostics.HasFlag (ViewDiagnosticFlags.Hover))
+        {
+            HighlightStyle copy = HighlightStyle;
+            var hover = HighlightStyle.None;
+            RaiseHighlight (new (ref copy, ref hover));
+
+            if (_savedNonHoverColorScheme is { })
+            {
+                ColorScheme = _savedNonHoverColorScheme;
+                _savedNonHoverColorScheme = null;
+            }
+        }
+    }
+
+    /// <summary>
+    ///     Called when the mouse moves outside View's <see cref="Frame"/>, or is occluded by another non-SubView.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
+    ///     </para>
+    ///     <para>
+    ///         See <see cref="SetPressedHighlight"/> for more information.
+    ///     </para>
+    /// </remarks>
+    protected virtual void OnMouseLeave () { }
+
+    /// <summary>
+    ///     Raised when the mouse moves outside View's <see cref="Frame"/>, or is occluded by another non-SubView.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
+    ///     </para>
+    ///     <para>
+    ///         See <see cref="SetPressedHighlight"/> for more information.
+    ///     </para>
+    /// </remarks>
+    public event EventHandler? MouseLeave;
+
+    #endregion MouseEnterLeave
+
+    #region Low Level Mouse Events
 
     /// <summary>Event fired when a mouse event occurs.</summary>
     /// <remarks>
@@ -42,9 +204,6 @@ public partial class View // Mouse APIs
     /// </remarks>
     public event EventHandler<MouseEventEventArgs>? MouseEvent;
 
-    /// <summary>Event fired when the mouse leaves the View's <see cref="Viewport"/>.</summary>
-    public event EventHandler<MouseEventEventArgs>? MouseLeave;
-
     /// <summary>
     ///     Processes a <see cref="MouseEvent"/>. This method is called by <see cref="Application.OnMouseEvent"/> when a mouse
     ///     event occurs.
@@ -58,7 +217,7 @@ public partial class View // Mouse APIs
     ///         mouse buttons was clicked, it calls <see cref="OnMouseClick"/> to process the click.
     ///     </para>
     ///     <para>
-    ///         See <see cref="SetHighlight"/> for more information.
+    ///         See <see cref="SetPressedHighlight"/> for more information.
     ///     </para>
     ///     <para>
     ///         If <see cref="WantContinuousButtonPressed"/> is <see langword="true"/>, the <see cref="OnMouseClick"/> event
@@ -69,6 +228,7 @@ public partial class View // Mouse APIs
     /// <returns><see langword="true"/> if the event was handled, <see langword="false"/> otherwise.</returns>
     public bool? NewMouseEvent (MouseEvent mouseEvent)
     {
+        // Pre-conditions
         if (!Enabled)
         {
             // A disabled view should not eat mouse events
@@ -80,6 +240,12 @@ public partial class View // Mouse APIs
             return false;
         }
 
+        if (!WantMousePositionReports && mouseEvent.Flags == MouseFlags.ReportMousePosition)
+        {
+            return false;
+        }
+
+        // Cancellable event
         if (OnMouseEvent (mouseEvent))
         {
             // Technically mouseEvent.Handled should already be true if implementers of OnMouseEvent
@@ -87,19 +253,22 @@ public partial class View // Mouse APIs
             return mouseEvent.Handled = true;
         }
 
+        // BUGBUG: MouseEvent should be fired from here. Fix this in https://github.com/gui-cs/Terminal.Gui/issues/3029
+
+        // Post-Conditions
         if (HighlightStyle != HighlightStyle.None || (WantContinuousButtonPressed && WantMousePositionReports))
         {
-            if (HandlePressed (mouseEvent))
+            if (WhenGrabbedHandlePressed (mouseEvent))
             {
                 return mouseEvent.Handled;
             }
 
-            if (HandleReleased (mouseEvent))
+            if (WhenGrabbedHandleReleased (mouseEvent))
             {
                 return mouseEvent.Handled;
             }
 
-            if (HandleClicked (mouseEvent))
+            if (WhenGrabbedHandleClicked (mouseEvent))
             {
                 return mouseEvent.Handled;
             }
@@ -119,7 +288,7 @@ public partial class View // Mouse APIs
             || mouseEvent.Flags.HasFlag (MouseFlags.Button4TripleClicked)
            )
         {
-            // If it's a click, and we didn't handle it, then we'll call OnMouseClick
+            // If it's a click, and we didn't handle it, then we need to generate a click event
             // We get here if the view did not handle the mouse event via OnMouseEvent/MouseEvent and
             // it did not handle the press/release/clicked events via HandlePress/HandleRelease/HandleClicked
             return OnMouseClick (new (mouseEvent));
@@ -135,29 +304,6 @@ public partial class View // Mouse APIs
     /// <value><see langword="true"/> if mouse position reports are wanted; otherwise, <see langword="false"/>.</value>
     public virtual bool WantMousePositionReports { get; set; }
 
-    /// <summary>
-    ///     Called by <see cref="NewMouseEvent"/> when the mouse enters <see cref="Viewport"/>. The view will
-    ///     then receive mouse events until <see cref="OnMouseLeave"/> is called indicating the mouse has left
-    ///     the view.
-    /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         Override this method or subscribe to <see cref="MouseEnter"/> to change the default enter behavior.
-    ///     </para>
-    ///     <para>
-    ///         The coordinates are relative to <see cref="View.Viewport"/>.
-    ///     </para>
-    /// </remarks>
-    /// <param name="mouseEvent"></param>
-    /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
-    protected internal virtual bool? OnMouseEnter (MouseEvent mouseEvent)
-    {
-        var args = new MouseEventEventArgs (mouseEvent);
-        MouseEnter?.Invoke (this, args);
-
-        return args.Handled;
-    }
-
     /// <summary>Called when a mouse event occurs within the view's <see cref="Viewport"/>.</summary>
     /// <remarks>
     ///     <para>
@@ -175,61 +321,22 @@ public partial class View // Mouse APIs
         return args.Handled;
     }
 
-    /// <summary>
-    ///     Called by <see cref="NewMouseEvent"/> when a mouse leaves <see cref="Viewport"/>. The view will
-    ///     no longer receive mouse events.
-    /// </summary>
+    #endregion Low Level Mouse Events
+
+    #region Mouse Click Events
+
+    /// <summary>Event fired when a mouse click occurs.</summary>
+    /// 
     /// <remarks>
     ///     <para>
-    ///         Override this method or subscribe to <see cref="MouseEnter"/> to change the default leave behavior.
+    ///         Fired when the mouse is either clicked or double-clicked. Check
+    ///         <see cref="MouseEvent.Flags"/> to see which button was clicked.
     ///     </para>
     ///     <para>
     ///         The coordinates are relative to <see cref="View.Viewport"/>.
     ///     </para>
     /// </remarks>
-    /// <param name="mouseEvent"></param>
-    /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
-    protected internal virtual bool OnMouseLeave (MouseEvent mouseEvent)
-    {
-        if (!Enabled)
-        {
-            return true;
-        }
-
-        if (!CanBeVisible (this))
-        {
-            return false;
-        }
-
-        var args = new MouseEventEventArgs (mouseEvent);
-        MouseLeave?.Invoke (this, args);
-
-        return args.Handled;
-    }
-
-    /// <summary>
-    ///     Called when the view is to be highlighted.
-    /// </summary>
-    /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
-    protected virtual bool? OnHighlight (CancelEventArgs<HighlightStyle> args)
-    {
-        Highlight?.Invoke (this, args);
-
-        if (args.Cancel)
-        {
-            return true;
-        }
-
-        Margin?.Highlight?.Invoke (this, args);
-
-        //args = new (highlight);
-        //Border?.Highlight?.Invoke (this, args);
-
-        //args = new (highlight);
-        //Padding?.Highlight?.Invoke (this, args);
-
-        return args.Cancel;
-    }
+    public event EventHandler<MouseEventEventArgs>? MouseClick;
 
     /// <summary>Invokes the MouseClick event.</summary>
     /// <remarks>
@@ -241,12 +348,19 @@ public partial class View // Mouse APIs
     /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
     protected bool OnMouseClick (MouseEventEventArgs args)
     {
+        // BUGBUG: This should be named NewMouseClickEvent. Fix this in https://github.com/gui-cs/Terminal.Gui/issues/3029
+
+        // Pre-conditions
         if (!Enabled)
         {
             // QUESTION: Is this right? Should a disabled view eat mouse clicks?
             return args.Handled = false;
         }
 
+        // Cancellable event
+
+        // BUGBUG: There should be a call to a protected virtual OnMouseClick here. Fix this in https://github.com/gui-cs/Terminal.Gui/issues/3029
+
         MouseClick?.Invoke (this, args);
 
         if (args.Handled)
@@ -254,6 +368,7 @@ public partial class View // Mouse APIs
             return true;
         }
 
+        // Post-conditions
         if (!HasFocus && CanFocus)
         {
             args.Handled = true;
@@ -264,7 +379,7 @@ public partial class View // Mouse APIs
     }
 
     /// <summary>
-    ///     For cases where the view is grabbed and the mouse is clicked, this method handles the click event (typically
+    ///     INTERNAL For cases where the view is grabbed and the mouse is clicked, this method handles the click event (typically
     ///     when <see cref="WantContinuousButtonPressed"/> or <see cref="HighlightStyle"/> are set).
     /// </summary>
     /// <remarks>
@@ -272,7 +387,7 @@ public partial class View // Mouse APIs
     /// </remarks>
     /// <param name="mouseEvent"></param>
     /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
-    internal bool HandleClicked (MouseEvent mouseEvent)
+    internal bool WhenGrabbedHandleClicked (MouseEvent mouseEvent)
     {
         if (Application.MouseGrabView == this
             && (mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked)
@@ -283,12 +398,12 @@ public partial class View // Mouse APIs
             // We're grabbed. Clicked event comes after the last Release. This is our signal to ungrab
             Application.UngrabMouse ();
 
-            if (SetHighlight (HighlightStyle.None))
+            if (SetPressedHighlight (HighlightStyle.None))
             {
                 return true;
             }
 
-            // If mouse is still in bounds, click
+            // If mouse is still in bounds, generate a click
             if (!WantContinuousButtonPressed && Viewport.Contains (mouseEvent.Position))
             {
                 return OnMouseClick (new (mouseEvent));
@@ -301,7 +416,7 @@ public partial class View // Mouse APIs
     }
 
     /// <summary>
-    ///     For cases where the view is grabbed and the mouse is clicked, this method handles the released event (typically
+    ///     INTERNAL For cases where the view is grabbed and the mouse is clicked, this method handles the released event (typically
     ///     when <see cref="WantContinuousButtonPressed"/> or <see cref="HighlightStyle"/> are set).
     /// </summary>
     /// <remarks>
@@ -309,7 +424,7 @@ public partial class View // Mouse APIs
     /// </remarks>
     /// <param name="mouseEvent"></param>
     /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
-    internal bool HandleReleased (MouseEvent mouseEvent)
+    internal bool WhenGrabbedHandleReleased (MouseEvent mouseEvent)
     {
         if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released)
             || mouseEvent.Flags.HasFlag (MouseFlags.Button2Released)
@@ -318,7 +433,7 @@ public partial class View // Mouse APIs
         {
             if (Application.MouseGrabView == this)
             {
-                SetHighlight (HighlightStyle.None);
+                SetPressedHighlight (HighlightStyle.None);
             }
 
             return mouseEvent.Handled = true;
@@ -328,111 +443,134 @@ public partial class View // Mouse APIs
     }
 
     /// <summary>
-    ///     Called by <see cref="Application.OnMouseEvent"/> when the mouse enters <see cref="Viewport"/>. The view will
-    ///     then receive mouse events until <see cref="NewMouseLeaveEvent"/> is called indicating the mouse has left
-    ///     the view.
+    ///     INTERNAL For cases where the view is grabbed and the mouse is clicked, this method handles the released event (typically
+    ///     when <see cref="WantContinuousButtonPressed"/> or <see cref="HighlightStyle"/> are set).
     /// </summary>
     /// <remarks>
     ///     <para>
-    ///         A view must be both enabled and visible to receive mouse events.
-    ///     </para>
-    ///     <para>
-    ///         This method calls <see cref="OnMouseEnter"/> to fire the event.
-    ///     </para>
-    ///     <para>
-    ///         See <see cref="SetHighlight"/> for more information.
+    ///         Marked internal just to support unit tests
     ///     </para>
     /// </remarks>
     /// <param name="mouseEvent"></param>
-    /// <returns><see langword="true"/> if the event was handled, <see langword="false"/> otherwise.</returns>
-    internal bool? NewMouseEnterEvent (MouseEvent mouseEvent)
+    /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
+    private bool WhenGrabbedHandlePressed (MouseEvent mouseEvent)
     {
-        if (!Enabled)
+        if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button2Pressed)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button3Pressed)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button4Pressed))
         {
-            return true;
-        }
+            // The first time we get pressed event, grab the mouse and set focus
+            if (Application.MouseGrabView != this)
+            {
+                Application.GrabMouse (this);
 
-        if (!CanBeVisible (this))
-        {
-            return false;
-        }
+                if (!HasFocus && CanFocus)
+                {
+                    // Set the focus, but don't invoke Accept
+                    SetFocus ();
+                }
 
-        if (OnMouseEnter (mouseEvent) == true)
-        {
-            return true;
-        }
+                mouseEvent.Handled = true;
+            }
 
-#if HOVER
-        if (HighlightStyle.HasFlag(HighlightStyle.Hover))
-        {
-            if (SetHighlight (HighlightStyle.Hover))
+            if (Viewport.Contains (mouseEvent.Position))
             {
-                return true;
+                if (this is not Adornment
+                    && SetPressedHighlight (HighlightStyle.HasFlag (HighlightStyle.Pressed) ? HighlightStyle.Pressed : HighlightStyle.None))
+                {
+                    return true;
+                }
+            }
+            else
+            {
+                if (this is not Adornment
+                    && SetPressedHighlight (HighlightStyle.HasFlag (HighlightStyle.PressedOutside) ? HighlightStyle.PressedOutside : HighlightStyle.None))
+
+                {
+                    return true;
+                }
+            }
+
+            if (WantContinuousButtonPressed && Application.MouseGrabView == this)
+            {
+                // If this is not the first pressed event, generate a click
+                return OnMouseClick (new (mouseEvent));
             }
+
+            return mouseEvent.Handled = true;
         }
-#endif
+
         return false;
     }
 
+    #endregion Mouse Click Events
+
+    #region Highlight Handling
+
+    // Used for Pressed highlighting
+    private ColorScheme? _savedHighlightColorScheme;
+
     /// <summary>
-    ///     Called by <see cref="Application.OnMouseEvent"/> when the mouse leaves <see cref="Viewport"/>. The view will
-    ///     then no longer receive mouse events.
+    ///     Gets or sets whether the <see cref="View"/> will be highlighted visually by mouse interaction.
     /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         A view must be both enabled and visible to receive mouse events.
-    ///     </para>
-    ///     <para>
-    ///         This method calls <see cref="OnMouseLeave"/> to fire the event.
-    ///     </para>
-    ///     <para>
-    ///         See <see cref="SetHighlight"/> for more information.
-    ///     </para>
-    /// </remarks>
-    /// <param name="mouseEvent"></param>
-    /// <returns><see langword="true"/> if the event was handled, <see langword="false"/> otherwise.</returns>
-    internal bool? NewMouseLeaveEvent (MouseEvent mouseEvent)
+    public HighlightStyle HighlightStyle { get; set; }
+
+    /// <summary>
+    ///     INTERNAL Raises the <see cref="Highlight"/> event. Returns <see langword="true"/> if the event was handled,
+    ///     <see langword="false"/> otherwise.
+    /// </summary>
+    /// <param name="args"></param>
+    /// <returns></returns>
+    private bool RaiseHighlight (CancelEventArgs<HighlightStyle> args)
     {
-        if (!Enabled)
+        if (OnHighlight (args))
         {
             return true;
         }
 
-        if (!CanBeVisible (this))
-        {
-            return false;
-        }
-
-        if (OnMouseLeave (mouseEvent))
-        {
-            return true;
-        }
-#if HOVER
-        if (HighlightStyle.HasFlag (HighlightStyle.Hover))
-        {
-            SetHighlight (HighlightStyle.None);
-        }
-#endif
+        Highlight?.Invoke (this, args);
 
-        return false;
+        return args.Cancel;
     }
 
     /// <summary>
-    ///     Enables the highlight for the view when the mouse is pressed. Called from OnMouseEvent.
+    ///     Called when the view is to be highlighted. The <see cref="HighlightStyle"/> passed in the event indicates the
+    ///     highlight style that will be applied. The view can modify the highlight style by setting the
+    ///     <see cref="CancelEventArgs{T}.NewValue"/> property.
+    /// </summary>
+    /// <param name="args">
+    ///     Set the <see cref="CancelEventArgs{T}.NewValue"/> property to <see langword="true"/>, to cancel, indicating custom
+    ///     highlighting.
+    /// </param>
+    /// <returns><see langword="true"/>, to cancel, indicating custom highlighting.</returns>
+    protected virtual bool OnHighlight (CancelEventArgs<HighlightStyle> args) { return false; }
+
+    /// <summary>
+    ///     Raised when the view is to be highlighted. The <see cref="HighlightStyle"/> passed in the event indicates the
+    ///     highlight style that will be applied. The view can modify the highlight style by setting the
+    ///     <see cref="CancelEventArgs{T}.NewValue"/> property.
+    ///     Set to <see langword="true"/>, to cancel, indicating custom highlighting.
+    /// </summary>
+    public event EventHandler<CancelEventArgs<HighlightStyle>>? Highlight;
+
+    /// <summary>
+    ///     INTERNAL Enables the highlight for the view when the mouse is pressed. Called from OnMouseEvent.
     /// </summary>
     /// <remarks>
     ///     <para>
-    ///         Set <see cref="HighlightStyle"/> to have the view highlighted based on the mouse.
+    ///         Set <see cref="HighlightStyle"/> to <see cref="HighlightStyle.Pressed"/> and/or
+    ///         <see cref="HighlightStyle.PressedOutside"/> to enable.
     ///     </para>
     ///     <para>
-    ///         Calls <see cref="OnHighlight"/> which fires the <see cref="Highlight"/> event.
+    ///         Calls <see cref="OnHighlight"/> and raises the <see cref="Highlight"/> event.
     ///     </para>
     ///     <para>
     ///         Marked internal just to support unit tests
     ///     </para>
     /// </remarks>
     /// <returns><see langword="true"/>, if the Highlight event was handled, <see langword="false"/> otherwise.</returns>
-    internal bool SetHighlight (HighlightStyle newHighlightStyle)
+    internal bool SetPressedHighlight (HighlightStyle newHighlightStyle)
     {
         // TODO: Make the highlight colors configurable
         if (!CanFocus)
@@ -440,32 +578,18 @@ public partial class View // Mouse APIs
             return false;
         }
 
-        // Enable override via virtual method and/or event
         HighlightStyle copy = HighlightStyle;
-        var args = new CancelEventArgs<HighlightStyle> (ref copy, ref newHighlightStyle);
+        CancelEventArgs<HighlightStyle> args = new (ref copy, ref newHighlightStyle);
 
-        if (OnHighlight (args) == true)
+        if (RaiseHighlight (args) || args.Cancel)
         {
             return true;
         }
-#if HOVER
-        if (style.HasFlag (HighlightStyle.Hover))
-        {
-            if (_savedHighlightColorScheme is null && ColorScheme is { })
-            {
-                _savedHighlightColorScheme ??= ColorScheme;
 
-                var cs = new ColorScheme (ColorScheme)
-                {
-                    Normal = GetFocusColor (),
-                    HotNormal = ColorScheme.HotFocus
-                };
-                ColorScheme = cs;
-            }
+        // For 3D Pressed Style - Note we don't care about canceling the event here
+        Margin?.RaiseHighlight (args);
+        args.Cancel = false; // Just in case
 
-            return true;
-        }
-#endif
         if (args.NewValue.HasFlag (HighlightStyle.Pressed) || args.NewValue.HasFlag (HighlightStyle.PressedOutside))
         {
             if (_savedHighlightColorScheme is null && ColorScheme is { })
@@ -509,65 +633,78 @@ public partial class View // Mouse APIs
         return false;
     }
 
+    #endregion Highlight Handling
+
     /// <summary>
-    ///     For cases where the view is grabbed and the mouse is clicked, this method handles the released event (typically
-    ///     when <see cref="WantContinuousButtonPressed"/> or <see cref="HighlightStyle"/> are set).
+    ///     INTERNAL: Gets the Views that are under the mouse at <paramref name="location"/>, including Adornments.
     /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         Marked internal just to support unit tests
-    ///     </para>
-    /// </remarks>
-    /// <param name="mouseEvent"></param>
-    /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
-    private bool HandlePressed (MouseEvent mouseEvent)
+    /// <param name="location"></param>
+    /// <returns></returns>
+    internal static List<View?> GetViewsUnderMouse (in Point location)
     {
-        if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button2Pressed)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button3Pressed)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button4Pressed))
+        List<View?> viewsUnderMouse = new ();
+
+        View? start = Application.Top;
+
+        Point currentLocation = location;
+
+        while (start is { Visible: true } && start.Contains (currentLocation))
         {
-            // The first time we get pressed event, grab the mouse and set focus
-            if (Application.MouseGrabView != this)
-            {
-                Application.GrabMouse (this);
+            viewsUnderMouse.Add (start);
 
-                if (!HasFocus && CanFocus)
-                {
-                    // Set the focus, but don't invoke Accept
-                    SetFocus ();
-                }
+            Adornment? found = null;
 
-                mouseEvent.Handled = true;
+            if (start.Margin.Contains (currentLocation))
+            {
+                found = start.Margin;
             }
-
-            if (Viewport.Contains (mouseEvent.Position))
+            else if (start.Border.Contains (currentLocation))
             {
-                if (this is not Adornment
-                    && SetHighlight (HighlightStyle.HasFlag (HighlightStyle.Pressed) ? HighlightStyle.Pressed : HighlightStyle.None))
-                {
-                    return true;
-                }
+                found = start.Border;
             }
-            else
+            else if (start.Padding.Contains (currentLocation))
             {
-                if (this is not Adornment
-                    && SetHighlight (HighlightStyle.HasFlag (HighlightStyle.PressedOutside) ? HighlightStyle.PressedOutside : HighlightStyle.None))
+                found = start.Padding;
+            }
+
+            Point viewportOffset = start.GetViewportOffsetFromFrame ();
+
+            if (found is { })
+            {
+                start = found;
+                viewsUnderMouse.Add (start);
+                viewportOffset = found.Parent?.Frame.Location ?? Point.Empty;
+            }
 
+            int startOffsetX = currentLocation.X - (start.Frame.X + viewportOffset.X);
+            int startOffsetY = currentLocation.Y - (start.Frame.Y + viewportOffset.Y);
+
+            View? subview = null;
+
+            for (int i = start.InternalSubviews.Count - 1; i >= 0; i--)
+            {
+                if (start.InternalSubviews [i].Visible
+                    && start.InternalSubviews [i].Contains (new (startOffsetX + start.Viewport.X, startOffsetY + start.Viewport.Y)))
                 {
-                    return true;
+                    subview = start.InternalSubviews [i];
+                    currentLocation.X = startOffsetX + start.Viewport.X;
+                    currentLocation.Y = startOffsetY + start.Viewport.Y;
+
+                    // start is the deepest subview under the mouse; stop searching the subviews
+                    break;
                 }
             }
 
-            if (WantContinuousButtonPressed && Application.MouseGrabView == this)
+            if (subview is null)
             {
-                // If this is not the first pressed event, click
-                return OnMouseClick (new (mouseEvent));
+                // No subview was found that's under the mouse, so we're done
+                return viewsUnderMouse;
             }
 
-            return mouseEvent.Handled = true;
+            // We found a subview of start that's under the mouse, continue...
+            start = subview;
         }
 
-        return false;
+        return viewsUnderMouse;
     }
 }

+ 150 - 56
Terminal.Gui/View/View.Navigation.cs

@@ -1,5 +1,6 @@
 #nullable enable
 using System.Diagnostics;
+using System.Reflection.PortableExecutable;
 
 namespace Terminal.Gui;
 
@@ -16,6 +17,9 @@ public partial class View // Focus and cross-view navigation management (TabStop
     ///     <para>
     ///         If there is no next/previous view to advance to, the focus is set to the view itself.
     ///     </para>
+    ///     <para>
+    ///         See the View Navigation Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/navigation.html"/>
+    ///     </para>
     /// </remarks>
     /// <param name="direction"></param>
     /// <param name="behavior"></param>
@@ -39,7 +43,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
         // AdvanceFocus did not advance - do we wrap, or move up to the superview?
 
-        View [] focusChain = GetSubviewFocusChain (direction, behavior);
+        View [] focusChain = GetFocusChain (direction, behavior);
 
         if (focusChain.Length == 0)
         {
@@ -52,11 +56,11 @@ public partial class View // Focus and cross-view navigation management (TabStop
             if (direction == NavigationDirection.Forward && focused == focusChain [^1] && SuperView is null)
             {
                 // We're at the top of the focus chain. Go back down the focus chain and focus the first TabGroup
-                View [] views = GetSubviewFocusChain (NavigationDirection.Forward, TabBehavior.TabGroup);
+                View [] views = GetFocusChain (NavigationDirection.Forward, TabBehavior.TabGroup);
 
                 if (views.Length > 0)
                 {
-                    View [] subViews = views [0].GetSubviewFocusChain (NavigationDirection.Forward, TabBehavior.TabStop);
+                    View [] subViews = views [0].GetFocusChain (NavigationDirection.Forward, TabBehavior.TabStop);
 
                     if (subViews.Length > 0)
                     {
@@ -71,11 +75,11 @@ public partial class View // Focus and cross-view navigation management (TabStop
             if (direction == NavigationDirection.Backward && focused == focusChain [0])
             {
                 // We're at the bottom of the focus chain
-                View [] views = GetSubviewFocusChain (NavigationDirection.Forward, TabBehavior.TabGroup);
+                View [] views = GetFocusChain (NavigationDirection.Forward, TabBehavior.TabGroup);
 
                 if (views.Length > 0)
                 {
-                    View [] subViews = views [^1].GetSubviewFocusChain (NavigationDirection.Forward, TabBehavior.TabStop);
+                    View [] subViews = views [^1].GetFocusChain (NavigationDirection.Forward, TabBehavior.TabStop);
 
                     if (subViews.Length > 0)
                     {
@@ -102,7 +106,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
             if (SuperView is { })
             {
                 // If we are TabStop, and we have at least one other focusable peer, move to the SuperView's chain
-                if (TabStop == TabBehavior.TabStop && SuperView is { } && SuperView.GetSubviewFocusChain (direction, behavior).Length > 1)
+                if (TabStop == TabBehavior.TabStop && SuperView is { } && SuperView.GetFocusChain (direction, behavior).Length > 1)
                 {
                     return false;
                 }
@@ -110,7 +114,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
                 // TabGroup is special-cased. 
                 if (focused?.TabStop == TabBehavior.TabGroup)
                 {
-                    if (SuperView?.GetSubviewFocusChain (direction, TabBehavior.TabGroup)?.Length > 0)
+                    if (SuperView?.GetFocusChain (direction, TabBehavior.TabGroup)?.Length > 0)
                     {
                         // Our superview has a TabGroup subview; signal we couldn't move so we nav out to it
                         return false;
@@ -136,6 +140,9 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// <summary>Gets or sets a value indicating whether this <see cref="View"/> can be focused.</summary>
     /// <remarks>
     ///     <para>
+    ///         See the View Navigation Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/navigation.html"/>
+    ///     </para>
+    ///     <para>
     ///         <see cref="SuperView"/> must also have <see cref="CanFocus"/> set to <see langword="true"/>.
     ///     </para>
     ///     <para>
@@ -176,7 +183,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
                 HasFocus = false;
             }
 
-            if (_canFocus && !HasFocus && Visible && SuperView is { } && SuperView.Focused is null)
+            if (_canFocus && !HasFocus && Visible && SuperView is { Focused: null })
             {
                 // If CanFocus is set to true and this view does not have focus, make it enter focus
                 SetFocus ();
@@ -211,14 +218,40 @@ public partial class View // Focus and cross-view navigation management (TabStop
         return SetFocus ();
     }
 
-    /// <summary>Gets the currently focused Subview of this view, or <see langword="null"/> if nothing is focused.</summary>
+    /// <summary>Gets the currently focused Subview or Adornment of this view, or <see langword="null"/> if nothing is focused.</summary>
     public View? Focused
     {
-        get { return Subviews.FirstOrDefault (v => v.HasFocus); }
+        get
+        {
+            View? focused = Subviews.FirstOrDefault (v => v.HasFocus);
+
+            if (focused is { })
+            {
+                return focused;
+            }
+
+            // How about in Adornments?
+            if (Margin is { HasFocus: true })
+            {
+                return Margin;
+            }
+
+            if (Border is { HasFocus: true })
+            {
+                return Border;
+            }
+
+            if (Padding is { HasFocus: true })
+            {
+                return Padding;
+            }
+
+            return null;
+        }
     }
 
     /// <summary>Returns a value indicating if this View is currently on Top (Active)</summary>
-    public bool IsCurrentTop => Application.Current == this;
+    public bool IsCurrentTop => Application.Top == this;
 
     /// <summary>
     ///     Returns the most focused Subview down the subview-hierarchy.
@@ -259,14 +292,11 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// </returns>
     internal bool RestoreFocus ()
     {
-        if (Focused is null && _subviews?.Count > 0)
-        {
-            if (_previouslyMostFocused is { })
-            {
-                return _previouslyMostFocused.SetFocus ();
-            }
+        View [] indicies = GetFocusChain (NavigationDirection.Forward, TabStop);
 
-            return false;
+        if (Focused is null && _previouslyFocused is { } && indicies.Contains (_previouslyFocused))
+        {
+            return _previouslyFocused.SetFocus ();
         }
 
         return false;
@@ -274,7 +304,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
     private View? FindDeepestFocusableView (NavigationDirection direction, TabBehavior? behavior)
     {
-        View [] indicies = GetSubviewFocusChain (direction, behavior);
+        View [] indicies = GetFocusChain (direction, behavior);
 
         foreach (View v in indicies)
         {
@@ -294,6 +324,9 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// </summary>
     /// <remarks>
     ///     <para>
+    ///         See the View Navigation Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/navigation.html"/>
+    ///     </para>
+    ///     <para>
     ///         Only Views that are visible, enabled, and have <see cref="CanFocus"/> set to <see langword="true"/> are
     ///         focusable. If
     ///         these conditions are not met when this property is set to <see langword="true"/> <see cref="HasFocus"/> will
@@ -341,6 +374,12 @@ public partial class View // Focus and cross-view navigation management (TabStop
             else
             {
                 SetHasFocusFalse (null);
+
+                if (_hasFocus)
+                {
+                    // force it.
+                    _hasFocus = false;
+                }
             }
         }
         get => _hasFocus;
@@ -350,6 +389,11 @@ public partial class View // Focus and cross-view navigation management (TabStop
     ///     Causes this view to be focused. Calling this method has the same effect as setting <see cref="HasFocus"/> to
     ///     <see langword="true"/> but with the added benefit of returning a value indicating whether the focus was set.
     /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         See the View Navigation Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/navigation.html"/>
+    ///     </para>
+    /// </remarks>
     public bool SetFocus ()
     {
         (bool focusSet, bool _) = SetHasFocusTrue (Application.Navigation?.GetFocused ());
@@ -358,9 +402,9 @@ public partial class View // Focus and cross-view navigation management (TabStop
     }
 
     /// <summary>
-    ///     Caches the most focused subview when this view is losing focus. This is used by <see cref="RestoreFocus"/>.
+    ///     A cache of the subview that was focused when this view last lost focus. This is used by <see cref="RestoreFocus"/>.
     /// </summary>
-    private View? _previouslyMostFocused;
+    private View? _previouslyFocused;
 
     /// <summary>
     ///     INTERNAL: Called when focus is going to change to this view. This method is called by <see cref="SetFocus"/> and
@@ -384,7 +428,10 @@ public partial class View // Focus and cross-view navigation management (TabStop
             return (false, false);
         }
 
-        if (CanFocus && SuperView is { CanFocus: false })
+        var thisAsAdornment = this as Adornment;
+        View? superViewOrParent = thisAsAdornment?.Parent ?? SuperView;
+
+        if (CanFocus && superViewOrParent is { CanFocus: false })
         {
             Debug.WriteLine ($@"WARNING: Attempt to FocusChanging where SuperView.CanFocus == false. {this}");
 
@@ -412,7 +459,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
         // Make sure superviews up the superview hierarchy have focus.
         // Any of them may cancel gaining focus. In which case we need to back out.
-        if (SuperView is { HasFocus: false } sv)
+        if (superViewOrParent is { HasFocus: false } sv)
         {
             (bool focusSet, bool svCancelled) = sv.SetHasFocusTrue (previousFocusedView, true);
 
@@ -431,7 +478,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
         // By setting _hasFocus to true we definitively change HasFocus for this view.
 
         // Get whatever peer has focus, if any
-        View? focusedPeer = SuperView?.Focused;
+        View? focusedPeer = superViewOrParent?.Focused;
 
         _hasFocus = true;
 
@@ -440,31 +487,34 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
         if (!traversingUp)
         {
-            // Restore focus to the previously most focused subview in the subview-hierarchy
+            // Restore focus to the previously focused subview 
             if (!RestoreFocus ())
             {
                 // Couldn't restore focus, so use Advance to navigate to the next focusable subview
                 if (!AdvanceFocus (NavigationDirection.Forward, null))
                 {
                     // Couldn't advance, so we're the most focused view in the application
-                    _previouslyMostFocused = null;
                     Application.Navigation?.SetFocused (this);
                 }
             }
         }
 
-        if (previousFocusedView is { HasFocus: true } && Subviews.Contains (previousFocusedView))
+        if (previousFocusedView is { HasFocus: true } && GetFocusChain (NavigationDirection.Forward, TabStop).Contains (previousFocusedView))
         {
             previousFocusedView.SetHasFocusFalse (this);
         }
 
+        _previouslyFocused = null;
+
         if (Arrangement.HasFlag (ViewArrangement.Overlapped))
         {
-            SuperView?.MoveSubviewToStart (this);
+            SuperView?.MoveSubviewToEnd (this);
         }
 
         NotifyFocusChanged (HasFocus, previousFocusedView, this);
 
+        SetNeedsDisplay ();
+
         // Post-conditions - prove correctness
         if (HasFocus == previousValue)
         {
@@ -547,29 +597,40 @@ public partial class View // Focus and cross-view navigation management (TabStop
             throw new InvalidOperationException ("SetHasFocusFalse should not be called if the view does not have focus.");
         }
 
+        var thisAsAdornment = this as Adornment;
+        View? superViewOrParent = thisAsAdornment?.Parent ?? SuperView;
+
         // If newFocusedVew is null, we need to find the view that should get focus, and SetFocus on it.
         if (!traversingDown && newFocusedView is null)
         {
-            if (SuperView?._previouslyMostFocused is { } && SuperView?._previouslyMostFocused != this)
+            if (superViewOrParent?._previouslyFocused is { })
             {
-                SuperView?._previouslyMostFocused?.SetFocus ();
+                if (superViewOrParent._previouslyFocused != this)
+                {
+                    superViewOrParent?._previouslyFocused?.SetFocus ();
 
-                // The above will cause SetHasFocusFalse, so we can return
-                return;
+                    // The above will cause SetHasFocusFalse, so we can return
+                    return;
+                }
             }
 
-            if (SuperView is { } && SuperView.AdvanceFocus (NavigationDirection.Forward, TabStop))
+            if (superViewOrParent is { })
             {
-                // The above will cause SetHasFocusFalse, so we can return
-                return;
+                if (superViewOrParent.AdvanceFocus (NavigationDirection.Forward, TabStop))
+                {
+                    // The above will cause SetHasFocusFalse, so we can return
+                    return;
+                }
+
+                newFocusedView = superViewOrParent;
             }
 
-            if (Application.Navigation is { } && Application.Current is { })
+            if (Application.Navigation is { } && Application.Top is { })
             {
                 // Temporarily ensure this view can't get focus
                 bool prevCanFocus = _canFocus;
                 _canFocus = false;
-                bool restoredFocus = Application.Current!.RestoreFocus ();
+                bool restoredFocus = Application.Top!.RestoreFocus ();
                 _canFocus = prevCanFocus;
 
                 if (restoredFocus)
@@ -582,6 +643,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
             // No other focusable view to be found. Just "leave" us...
         }
 
+
         // Before we can leave focus, we need to make sure that all views down the subview-hierarchy have left focus.
         View? mostFocused = MostFocused;
 
@@ -595,12 +657,27 @@ public partial class View // Focus and cross-view navigation management (TabStop
                 if (bottom.HasFocus)
                 {
                     bottom.SetHasFocusFalse (newFocusedView, true);
+
+                    Debug.Assert (_hasFocus);
                 }
 
                 bottom = bottom.SuperView;
             }
 
-            _previouslyMostFocused = mostFocused;
+            if (bottom == this && bottom.SuperView is Adornment a)
+            {
+                //a.SetHasFocusFalse (newFocusedView, true);
+
+                Debug.Assert (_hasFocus);
+            }
+
+            Debug.Assert (_hasFocus);
+
+        }
+
+        if (superViewOrParent is { })
+        {
+            superViewOrParent._previouslyFocused = this;
         }
 
         bool previousValue = HasFocus;
@@ -608,8 +685,10 @@ public partial class View // Focus and cross-view navigation management (TabStop
         // Note, can't be cancelled.
         NotifyFocusChanging (HasFocus, !HasFocus, newFocusedView, this);
 
-        // Get whatever peer has focus, if any
-        View? focusedPeer = SuperView?.Focused;
+        // Get whatever peer has focus, if any so we can update our superview's _previouslyMostFocused
+        View? focusedPeer = superViewOrParent?.Focused;
+
+        // Set HasFocus false
         _hasFocus = false;
 
         if (Application.Navigation is { })
@@ -618,7 +697,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
             if (appFocused is { } || appFocused == this)
             {
-                Application.Navigation.SetFocused (newFocusedView ?? SuperView);
+                Application.Navigation.SetFocused (newFocusedView ?? superViewOrParent);
             }
         }
 
@@ -630,11 +709,6 @@ public partial class View // Focus and cross-view navigation management (TabStop
             return;
         }
 
-        if (SuperView is { })
-        {
-            //SuperView._previouslyMostFocused = focusedPeer;
-        }
-
         // Post-conditions - prove correctness
         if (HasFocus == previousValue)
         {
@@ -681,32 +755,48 @@ public partial class View // Focus and cross-view navigation management (TabStop
     #region Tab/Focus Handling
 
     /// <summary>
-    ///     Gets TabIndexes that are scoped to the specified behavior and direction. If behavior is null, all TabIndexes are
-    ///     returned.
+    ///     Gets the subviews and Adornments of this view that are scoped to the specified behavior and direction. If behavior is null, all focusable subviews and
+    ///     Adornments are returned.
     /// </summary>
     /// <param name="direction"></param>
     /// <param name="behavior"></param>
     /// <returns></returns>
-    /// GetScopedTabIndexes
-    internal View [] GetSubviewFocusChain (NavigationDirection direction, TabBehavior? behavior)
+    internal View [] GetFocusChain (NavigationDirection direction, TabBehavior? behavior)
     {
-        IEnumerable<View>? fitleredSubviews;
+        IEnumerable<View>? filteredSubviews;
 
         if (behavior.HasValue)
         {
-            fitleredSubviews = _subviews?.Where (v => v.TabStop == behavior && v is { CanFocus: true, Visible: true, Enabled: true });
+            filteredSubviews = _subviews?.Where (v => v.TabStop == behavior && v is { CanFocus: true, Visible: true, Enabled: true });
         }
         else
         {
-            fitleredSubviews = _subviews?.Where (v => v is { CanFocus: true, Visible: true, Enabled: true });
+            filteredSubviews = _subviews?.Where (v => v is { CanFocus: true, Visible: true, Enabled: true });
+        }
+
+
+        // How about in Adornments? 
+        if (Padding is { CanFocus: true, Visible: true, Enabled: true } && Padding.TabStop == behavior)
+        {
+            filteredSubviews = filteredSubviews?.Append (Padding);
+        }
+
+        if (Border is { CanFocus: true, Visible: true, Enabled: true } && Border.TabStop == behavior)
+        {
+            filteredSubviews = filteredSubviews?.Append (Border);
+        }
+
+        if (Margin is { CanFocus: true, Visible: true, Enabled: true } && Margin.TabStop == behavior)
+        {
+            filteredSubviews = filteredSubviews?.Append (Margin);
         }
 
         if (direction == NavigationDirection.Backward)
         {
-            fitleredSubviews = fitleredSubviews?.Reverse ();
+            filteredSubviews = filteredSubviews?.Reverse ();
         }
 
-        return fitleredSubviews?.ToArray () ?? Array.Empty<View> ();
+        return filteredSubviews?.ToArray () ?? Array.Empty<View> ();
     }
 
     private TabBehavior? _tabStop;
@@ -715,7 +805,11 @@ public partial class View // Focus and cross-view navigation management (TabStop
     ///     Gets or sets the behavior of <see cref="AdvanceFocus"/> for keyboard navigation.
     /// </summary>
     /// <remarks>
+    /// <remarks>
     ///     <para>
+    ///         See the View Navigation Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/navigation.html"/>
+    ///     </para>
+    /// </remarks>    ///     <para>
     ///         If <see langword="null"/> the tab stop has not been set and setting <see cref="CanFocus"/> to true will set it
     ///         to
     ///         <see cref="TabBehavior.TabStop"/>.
@@ -723,7 +817,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     ///     <para>
     ///         TabStop is independent of <see cref="CanFocus"/>. If <see cref="CanFocus"/> is <see langword="false"/>, the
     ///         view will not gain
-    ///         focus even if this property is set and vice-versa.
+    ///         focus even if this property is set and vice versa.
     ///     </para>
     ///     <para>
     ///         The default <see cref="TabBehavior.TabStop"/> keys are <see cref="Application.NextTabKey"/> (<c>Key.Tab</c>)

+ 15 - 12
Terminal.Gui/View/ViewArrangement.cs

@@ -7,6 +7,10 @@
 /// </summary>
 /// <remarks>
 ///     <para>
+///         See the View Arrangement Deep Dive for more information:
+///         <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/arrangement.html"/>
+///     </para>
+///     <para>
 ///         Sizing or moving a view is only possible if the <see cref="View"/> is part of a <see cref="View.SuperView"/>
 ///         and
 ///         the relevant position and dimensions of the <see cref="View"/> are independent of other SubViews
@@ -37,11 +41,11 @@ public enum ViewArrangement
 
     /// <summary>
     ///     The top edge of the view can be resized.
+    ///     <para>
+    ///         This flag is mutually exclusive with <see cref="Movable"/>. If both are set, <see cref="Movable"/> takes
+    ///         precedence.
+    ///     </para>
     /// </summary>
-    /// <remarks>
-    ///     This flag is mutually exclusive with <see cref="Movable"/>. If both are set, <see cref="Movable"/> takes
-    ///     precedence.
-    /// </remarks>
     TopResizable = 8,
 
     /// <summary>
@@ -51,21 +55,20 @@ public enum ViewArrangement
 
     /// <summary>
     ///     The view can be resized in any direction.
+    ///     <para>
+    ///         If <see cref="Movable"/> is also set, the top will not be resizable.
+    ///     </para>
     /// </summary>
-    /// <remarks>
-    ///     If <see cref="Movable"/> is also set, the top will not be resizable.
-    /// </remarks>
     Resizable = LeftResizable | RightResizable | TopResizable | BottomResizable,
 
     /// <summary>
-    ///     The view overlap other views.
-    /// </summary>
-    /// <remarks>
+    ///     The view overlaps other views (the order of <see cref="View.Subviews"/> dicates the Z-order). If this flag is not
+    ///     set the view will operate in tiled mode.
     ///     <para>
     ///         When set, Tab and Shift-Tab will be constrained to the subviews of the view (normally, they will navigate to
     ///         the next/prev view in the next/prev Tabindex).
     ///         Use Ctrl-Tab (Ctrl-PageDown) / Ctrl-Shift-Tab (Ctrl-PageUp) to move between overlapped views.
     ///     </para>
-    /// </remarks>
-    Overlapped = 32,
+    /// </summary>
+    Overlapped = 32
 }

+ 14 - 23
Terminal.Gui/View/ViewportSettings.cs

@@ -3,6 +3,9 @@
 /// <summary>
 ///     Settings for how the <see cref="View.Viewport"/> behaves relative to the View's Content area.
 /// </summary>
+/// <remarks>
+///     See the Layout Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+/// </remarks>
 [Flags]
 public enum ViewportSettings
 {
@@ -15,43 +18,35 @@ public enum ViewportSettings
     ///     If set, <see cref="View.Viewport"/><c>.X</c> can be set to negative values enabling scrolling beyond the left of
     ///     the
     ///     content area.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         When not set, <see cref="View.Viewport"/><c>.X</c> is constrained to positive values.
     ///     </para>
-    /// </remarks>
+    /// </summary>
     AllowNegativeX = 1,
 
     /// <summary>
     ///     If set, <see cref="View.Viewport"/><c>.Y</c> can be set to negative values enabling scrolling beyond the top of the
     ///     content area.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         When not set, <see cref="View.Viewport"/><c>.Y</c> is constrained to positive values.
     ///     </para>
-    /// </remarks>
+    /// </summary>
     AllowNegativeY = 2,
 
     /// <summary>
     ///     If set, <see cref="View.Viewport"/><c>.Size</c> can be set to negative coordinates enabling scrolling beyond the
     ///     top-left of the
     ///     content area.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         When not set, <see cref="View.Viewport"/><c>.Size</c> is constrained to positive coordinates.
     ///     </para>
-    /// </remarks>
+    /// </summary>
     AllowNegativeLocation = AllowNegativeX | AllowNegativeY,
 
     /// <summary>
     ///     If set, <see cref="View.Viewport"/><c>.X</c> can be set values greater than <see cref="View.GetContentSize ()"/>
     ///     <c>.Width</c> enabling scrolling beyond the right
     ///     of the content area.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         When not set, <see cref="View.Viewport"/><c>.X</c> is constrained to <see cref="View.GetContentSize ()"/>
     ///         <c>.Width - 1</c>.
@@ -61,15 +56,13 @@ public enum ViewportSettings
     ///     <para>
     ///         The practical effect of this is that the last column of the content will always be visible.
     ///     </para>
-    /// </remarks>
+    /// </summary>
     AllowXGreaterThanContentWidth = 4,
 
     /// <summary>
     ///     If set, <see cref="View.Viewport"/><c>.Y</c> can be set values greater than <see cref="View.GetContentSize ()"/>
     ///     <c>.Height</c> enabling scrolling beyond the right
     ///     of the content area.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         When not set, <see cref="View.Viewport"/><c>.Y</c> is constrained to <see cref="View.GetContentSize ()"/>
     ///         <c>.Height - 1</c>.
@@ -79,21 +72,19 @@ public enum ViewportSettings
     ///     <para>
     ///         The practical effect of this is that the last row of the content will always be visible.
     ///     </para>
-    /// </remarks>
+    /// </summary>
     AllowYGreaterThanContentHeight = 8,
 
     /// <summary>
     ///     If set, <see cref="View.Viewport"/><c>.Size</c> can be set values greater than <see cref="View.GetContentSize ()"/>
     ///     enabling scrolling beyond the bottom-right
     ///     of the content area.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         When not set, <see cref="View.Viewport"/> is constrained to <see cref="View.GetContentSize ()"/><c> -1</c>.
     ///         This means the last column and row of the content will remain visible even if there is an attempt to
     ///         scroll the Viewport past the last column or row.
     ///     </para>
-    /// </remarks>
+    /// </summary>
     AllowLocationGreaterThanContentSize = AllowXGreaterThanContentWidth | AllowYGreaterThanContentHeight,
 
     /// <summary>
@@ -106,10 +97,10 @@ public enum ViewportSettings
     ///     If set <see cref="View.Clear()"/> will clear only the portion of the content
     ///     area that is visible within the <see cref="View.Viewport"/>. This is useful for views that have a
     ///     content area larger than the Viewport and want the area outside the content to be visually distinct.
+    ///     <para>
+    ///         <see cref="ClipContentOnly"/> must be set for this setting to work (clipping beyond the visible area must be
+    ///         disabled).
+    ///     </para>
     /// </summary>
-    /// <remarks>
-    ///     <see cref="ClipContentOnly"/> must be set for this setting to work (clipping beyond the visible area must be
-    ///     disabled).
-    /// </remarks>
     ClearContentOnly = 32
-}
+}

+ 8 - 5
Terminal.Gui/Views/Button.cs

@@ -39,6 +39,12 @@ public class Button : View, IDesignable
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None;
 
+    /// <summary>
+    /// Gets or sets the default Highlight Style.
+    /// </summary>
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    public static HighlightStyle DefaultHighlightStyle { get; set; } = HighlightStyle.Pressed | HighlightStyle.Hover;
+
     /// <summary>Initializes a new instance of <see cref="Button"/>.</summary>
     public Button ()
     {
@@ -54,10 +60,6 @@ public class Button : View, IDesignable
         Width = Dim.Auto (DimAutoStyle.Text);
 
         CanFocus = true;
-        HighlightStyle |= HighlightStyle.Pressed;
-#if HOVER
-        HighlightStyle |= HighlightStyle.Hover;
-#endif
 
         // Override default behavior of View
         AddCommand (Command.HotKey, () =>
@@ -73,6 +75,7 @@ public class Button : View, IDesignable
         MouseClick += Button_MouseClick;
 
         ShadowStyle = DefaultShadow;
+        HighlightStyle = DefaultHighlightStyle;
     }
 
     private bool _wantContinuousButtonPressed;
@@ -166,7 +169,7 @@ public class Button : View, IDesignable
     /// <inheritdoc/>
     protected override void UpdateTextFormatterText ()
     {
-        base.UpdateTextFormatterText();
+        base.UpdateTextFormatterText ();
         if (NoDecorations)
         {
             TextFormatter.Text = Text;

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

@@ -4,6 +4,12 @@ namespace Terminal.Gui;
 /// <summary>Shows a check box that can be cycled between three states.</summary>
 public class CheckBox : View
 {
+    /// <summary>
+    /// Gets or sets the default Highlight Style.
+    /// </summary>
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    public static HighlightStyle DefaultHighlightStyle { get; set; } = HighlightStyle.PressedOutside | HighlightStyle.Pressed | HighlightStyle.Hover;
+
     /// <summary>
     ///     Initializes a new instance of <see cref="CheckBox"/>.
     /// </summary>
@@ -23,7 +29,7 @@ public class CheckBox : View
 
         TitleChanged += Checkbox_TitleChanged;
 
-        HighlightStyle = Gui.HighlightStyle.PressedOutside | Gui.HighlightStyle.Pressed;
+        HighlightStyle = DefaultHighlightStyle;
         MouseClick += CheckBox_MouseClick;
     }
 

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

@@ -23,14 +23,14 @@ internal abstract class ColorBar : View, IColorBar
         AddCommand (Command.LeftExtend, _ => Adjust (-MaxValue / 20));
         AddCommand (Command.RightExtend, _ => Adjust (MaxValue / 20));
 
-        AddCommand (Command.LeftHome, _ => SetZero ());
+        AddCommand (Command.LeftStart, _ => SetZero ());
         AddCommand (Command.RightEnd, _ => SetMax ());
 
         KeyBindings.Add (Key.CursorLeft, Command.Left);
         KeyBindings.Add (Key.CursorRight, Command.Right);
         KeyBindings.Add (Key.CursorLeft.WithShift, Command.LeftExtend);
         KeyBindings.Add (Key.CursorRight.WithShift, Command.RightExtend);
-        KeyBindings.Add (Key.Home, Command.LeftHome);
+        KeyBindings.Add (Key.Home, Command.LeftStart);
         KeyBindings.Add (Key.End, Command.RightEnd);
     }
 

+ 9 - 9
Terminal.Gui/Views/ColorPicker16.cs

@@ -61,7 +61,7 @@ public class ColorPicker16 : View
         set
         {
             int colorIndex = value.Y * _cols + value.X;
-            SelectedColor = (ColorName)colorIndex;
+            SelectedColor = (ColorName16)colorIndex;
         }
     }
 
@@ -132,7 +132,7 @@ public class ColorPicker16 : View
                     continue;
                 }
 
-                Driver.SetAttribute (new ((ColorName)foregroundColorIndex, (ColorName)colorIndex));
+                Driver.SetAttribute (new ((ColorName16)foregroundColorIndex, (ColorName16)colorIndex));
                 bool selected = x == Cursor.X && y == Cursor.Y;
                 DrawColorBox (x, y, selected);
                 colorIndex++;
@@ -141,12 +141,12 @@ public class ColorPicker16 : View
     }
 
     /// <summary>Selected color.</summary>
-    public ColorName SelectedColor
+    public ColorName16 SelectedColor
     {
-        get => (ColorName)_selectColorIndex;
+        get => (ColorName16)_selectColorIndex;
         set
         {
-            if (value == (ColorName)_selectColorIndex)
+            if (value == (ColorName16)_selectColorIndex)
             {
                 return;
             }
@@ -166,8 +166,8 @@ public class ColorPicker16 : View
     {
         AddCommand (Command.Left, () => MoveLeft ());
         AddCommand (Command.Right, () => MoveRight ());
-        AddCommand (Command.LineUp, () => MoveUp ());
-        AddCommand (Command.LineDown, () => MoveDown ());
+        AddCommand (Command.Up, () => MoveUp ());
+        AddCommand (Command.Down, () => MoveDown ());
     }
 
     /// <summary>Add the KeyBindinds.</summary>
@@ -175,8 +175,8 @@ public class ColorPicker16 : View
     {
         KeyBindings.Add (Key.CursorLeft, Command.Left);
         KeyBindings.Add (Key.CursorRight, Command.Right);
-        KeyBindings.Add (Key.CursorUp, Command.LineUp);
-        KeyBindings.Add (Key.CursorDown, Command.LineDown);
+        KeyBindings.Add (Key.CursorUp, Command.Up);
+        KeyBindings.Add (Key.CursorDown, Command.Down);
     }
 
     // TODO: Decouple Cursor from SelectedColor so that mouse press-and-hold can show the color under the cursor.

+ 11 - 11
Terminal.Gui/Views/ComboBox.cs

@@ -77,27 +77,27 @@ public class ComboBox : View, IDesignable
 
         // Things this view knows how to do
         AddCommand (Command.Accept, () => ActivateSelected ());
-        AddCommand (Command.ToggleExpandCollapse, () => ExpandCollapse ());
+        AddCommand (Command.Toggle, () => ExpandCollapse ());
         AddCommand (Command.Expand, () => Expand ());
         AddCommand (Command.Collapse, () => Collapse ());
-        AddCommand (Command.LineDown, () => MoveDown ());
-        AddCommand (Command.LineUp, () => MoveUp ());
+        AddCommand (Command.Down, () => MoveDown ());
+        AddCommand (Command.Up, () => MoveUp ());
         AddCommand (Command.PageDown, () => PageDown ());
         AddCommand (Command.PageUp, () => PageUp ());
-        AddCommand (Command.TopHome, () => MoveHome ());
-        AddCommand (Command.BottomEnd, () => MoveEnd ());
+        AddCommand (Command.Start, () => MoveHome ());
+        AddCommand (Command.End, () => MoveEnd ());
         AddCommand (Command.Cancel, () => CancelSelected ());
         AddCommand (Command.UnixEmulation, () => UnixEmulation ());
 
         // Default keybindings for this view
         KeyBindings.Add (Key.Enter, Command.Accept);
-        KeyBindings.Add (Key.F4, Command.ToggleExpandCollapse);
-        KeyBindings.Add (Key.CursorDown, Command.LineDown);
-        KeyBindings.Add (Key.CursorUp, Command.LineUp);
+        KeyBindings.Add (Key.F4, Command.Toggle);
+        KeyBindings.Add (Key.CursorDown, Command.Down);
+        KeyBindings.Add (Key.CursorUp, Command.Up);
         KeyBindings.Add (Key.PageDown, Command.PageDown);
         KeyBindings.Add (Key.PageUp, Command.PageUp);
-        KeyBindings.Add (Key.Home, Command.TopHome);
-        KeyBindings.Add (Key.End, Command.BottomEnd);
+        KeyBindings.Add (Key.Home, Command.Start);
+        KeyBindings.Add (Key.End, Command.End);
         KeyBindings.Add (Key.Esc, Command.Cancel);
         KeyBindings.Add (Key.U.WithCtrl, Command.UnixEmulation);
     }
@@ -992,7 +992,7 @@ public class ComboBox : View, IDesignable
                                                              "ComboBox container cannot be null."
                                                             );
             HideDropdownListOnClick = hideDropdownListOnClick;
-            AddCommand (Command.LineUp, () => _container.MoveUpList ());
+            AddCommand (Command.Up, () => _container.MoveUpList ());
         }
     }
 

+ 3 - 3
Terminal.Gui/Views/DateField.cs

@@ -395,7 +395,7 @@ public class DateField : TextField
                         return true;
                     }
                    );
-        AddCommand (Command.LeftHome, () => MoveHome ());
+        AddCommand (Command.LeftStart, () => MoveHome ());
         AddCommand (Command.Left, () => MoveLeft ());
         AddCommand (Command.RightEnd, () => MoveEnd ());
         AddCommand (Command.Right, () => MoveRight ());
@@ -406,8 +406,8 @@ public class DateField : TextField
 
         KeyBindings.ReplaceCommands (Key.Backspace, Command.DeleteCharLeft);
 
-        KeyBindings.ReplaceCommands (Key.Home, Command.LeftHome);
-        KeyBindings.ReplaceCommands (Key.A.WithCtrl, Command.LeftHome);
+        KeyBindings.ReplaceCommands (Key.Home, Command.LeftStart);
+        KeyBindings.ReplaceCommands (Key.A.WithCtrl, Command.LeftStart);
 
         KeyBindings.ReplaceCommands (Key.CursorLeft, Command.Left);
         KeyBindings.ReplaceCommands (Key.B.WithCtrl, Command.Left);

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

@@ -184,7 +184,6 @@ public class DatePicker : View
     private void SetInitialProperties (DateTime date)
     {
         _date = date;
-        Title = "Date Picker";
         BorderStyle = LineStyle.Single;
         Date = date;
         _dateLabel = new Label { X = 0, Y = 0, Text = "Date: " };

+ 1 - 12
Terminal.Gui/Views/Dialog.cs

@@ -66,7 +66,7 @@ public class Dialog : Window
     /// </remarks>
     public Dialog ()
     {
-        Arrangement = ViewArrangement.Movable;
+        Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped;
         ShadowStyle = DefaultShadow;
         BorderStyle = DefaultBorderStyle;
 
@@ -80,17 +80,6 @@ public class Dialog : Window
         Modal = true;
         ButtonAlignment = DefaultButtonAlignment;
         ButtonAlignmentModes = DefaultButtonAlignmentModes;
-
-        AddCommand (
-                    Command.QuitToplevel,
-                    () =>
-                    {
-                        Canceled = true;
-                        RequestStop ();
-
-                        return true;
-                    });
-        KeyBindings.Add (Key.Esc, Command.QuitToplevel);
     }
 
     // BUGBUG: We override GetNormal/FocusColor because "Dialog" ColorScheme is goofy.

+ 4 - 4
Terminal.Gui/Views/FileDialog.cs

@@ -236,10 +236,10 @@ public class FileDialog : Dialog
         _tableView.KeyUp += (s, k) => k.Handled = TableView_KeyUp (k);
         _tableView.SelectedCellChanged += TableView_SelectedCellChanged;
 
-        _tableView.KeyBindings.ReplaceCommands (Key.Home, Command.TopHome);
-        _tableView.KeyBindings.ReplaceCommands (Key.End, Command.BottomEnd);
-        _tableView.KeyBindings.ReplaceCommands (Key.Home.WithShift, Command.TopHomeExtend);
-        _tableView.KeyBindings.ReplaceCommands (Key.End.WithShift, Command.BottomEndExtend);
+        _tableView.KeyBindings.ReplaceCommands (Key.Home, Command.Start);
+        _tableView.KeyBindings.ReplaceCommands (Key.End, Command.End);
+        _tableView.KeyBindings.ReplaceCommands (Key.Home.WithShift, Command.StartExtend);
+        _tableView.KeyBindings.ReplaceCommands (Key.End.WithShift, Command.EndExtend);
         
         AllowsMultipleSelection = false;
 

+ 13 - 13
Terminal.Gui/Views/HexView.cs

@@ -59,15 +59,15 @@ public class HexView : View
         // Things this view knows how to do
         AddCommand (Command.Left, () => MoveLeft ());
         AddCommand (Command.Right, () => MoveRight ());
-        AddCommand (Command.LineDown, () => MoveDown (bytesPerLine));
-        AddCommand (Command.LineUp, () => MoveUp (bytesPerLine));
+        AddCommand (Command.Down, () => MoveDown (bytesPerLine));
+        AddCommand (Command.Up, () => MoveUp (bytesPerLine));
         AddCommand (Command.Accept, () => ToggleSide ());
         AddCommand (Command.PageUp, () => MoveUp (bytesPerLine * Frame.Height));
         AddCommand (Command.PageDown, () => MoveDown (bytesPerLine * Frame.Height));
-        AddCommand (Command.TopHome, () => MoveHome ());
-        AddCommand (Command.BottomEnd, () => MoveEnd ());
-        AddCommand (Command.StartOfLine, () => MoveStartOfLine ());
-        AddCommand (Command.EndOfLine, () => MoveEndOfLine ());
+        AddCommand (Command.Start, () => MoveHome ());
+        AddCommand (Command.End, () => MoveEnd ());
+        AddCommand (Command.LeftStart, () => MoveLeftStart ());
+        AddCommand (Command.RightEnd, () => MoveEndOfLine ());
         AddCommand (Command.StartOfPage, () => MoveUp (bytesPerLine * ((int)(position - displayStart) / bytesPerLine)));
 
         AddCommand (
@@ -78,8 +78,8 @@ public class HexView : View
         // Default keybindings for this view
         KeyBindings.Add (Key.CursorLeft, Command.Left);
         KeyBindings.Add (Key.CursorRight, Command.Right);
-        KeyBindings.Add (Key.CursorDown, Command.LineDown);
-        KeyBindings.Add (Key.CursorUp, Command.LineUp);
+        KeyBindings.Add (Key.CursorDown, Command.Down);
+        KeyBindings.Add (Key.CursorUp, Command.Up);
         KeyBindings.Add (Key.Enter, Command.Accept);
 
         KeyBindings.Add (Key.V.WithAlt, Command.PageUp);
@@ -88,10 +88,10 @@ public class HexView : View
         KeyBindings.Add (Key.V.WithCtrl, Command.PageDown);
         KeyBindings.Add (Key.PageDown, Command.PageDown);
 
-        KeyBindings.Add (Key.Home, Command.TopHome);
-        KeyBindings.Add (Key.End, Command.BottomEnd);
-        KeyBindings.Add (Key.CursorLeft.WithCtrl, Command.StartOfLine);
-        KeyBindings.Add (Key.CursorRight.WithCtrl, Command.EndOfLine);
+        KeyBindings.Add (Key.Home, Command.Start);
+        KeyBindings.Add (Key.End, Command.End);
+        KeyBindings.Add (Key.CursorLeft.WithCtrl, Command.LeftStart);
+        KeyBindings.Add (Key.CursorRight.WithCtrl, Command.RightEnd);
         KeyBindings.Add (Key.CursorUp.WithCtrl, Command.StartOfPage);
         KeyBindings.Add (Key.CursorDown.WithCtrl, Command.EndOfPage);
 
@@ -728,7 +728,7 @@ public class HexView : View
         return true;
     }
 
-    private bool MoveStartOfLine ()
+    private bool MoveLeftStart ()
     {
         position = position / bytesPerLine * bytesPerLine;
         SetNeedsDisplay ();

+ 12 - 12
Terminal.Gui/Views/ListView.cs

@@ -124,39 +124,39 @@ public class ListView : View, IDesignable
         // Things this view knows how to do
         // 
         // BUGBUG: SHould return false if selectokn doesn't change (to support nav to next view)
-        AddCommand (Command.LineUp, () => MoveUp ());
+        AddCommand (Command.Up, () => MoveUp ());
         // BUGBUG: SHould return false if selectokn doesn't change (to support nav to next view)
-        AddCommand (Command.LineDown, () => MoveDown ());
+        AddCommand (Command.Down, () => MoveDown ());
         AddCommand (Command.ScrollUp, () => ScrollVertical (-1));
         AddCommand (Command.ScrollDown, () => ScrollVertical (1));
         AddCommand (Command.PageUp, () => MovePageUp ());
         AddCommand (Command.PageDown, () => MovePageDown ());
-        AddCommand (Command.TopHome, () => MoveHome ());
-        AddCommand (Command.BottomEnd, () => MoveEnd ());
+        AddCommand (Command.Start, () => MoveHome ());
+        AddCommand (Command.End, () => MoveEnd ());
         AddCommand (Command.Accept, () => OnOpenSelectedItem ());
-        AddCommand (Command.OpenSelectedItem, () => OnOpenSelectedItem ());
+        AddCommand (Command.Open, () => OnOpenSelectedItem ());
         AddCommand (Command.Select, () => MarkUnmarkRow ());
 
         AddCommand (Command.ScrollLeft, () => ScrollHorizontal (-1));
         AddCommand (Command.ScrollRight, () => ScrollHorizontal (1));
 
         // Default keybindings for all ListViews
-        KeyBindings.Add (Key.CursorUp, Command.LineUp);
-        KeyBindings.Add (Key.P.WithCtrl, Command.LineUp);
+        KeyBindings.Add (Key.CursorUp, Command.Up);
+        KeyBindings.Add (Key.P.WithCtrl, Command.Up);
 
-        KeyBindings.Add (Key.CursorDown, Command.LineDown);
-        KeyBindings.Add (Key.N.WithCtrl, Command.LineDown);
+        KeyBindings.Add (Key.CursorDown, Command.Down);
+        KeyBindings.Add (Key.N.WithCtrl, Command.Down);
 
         KeyBindings.Add (Key.PageUp, Command.PageUp);
 
         KeyBindings.Add (Key.PageDown, Command.PageDown);
         KeyBindings.Add (Key.V.WithCtrl, Command.PageDown);
 
-        KeyBindings.Add (Key.Home, Command.TopHome);
+        KeyBindings.Add (Key.Home, Command.Start);
 
-        KeyBindings.Add (Key.End, Command.BottomEnd);
+        KeyBindings.Add (Key.End, Command.End);
 
-        KeyBindings.Add (Key.Enter, Command.OpenSelectedItem);
+        KeyBindings.Add (Key.Enter, Command.Open);
     }
 
     /// <summary>Gets or sets whether this <see cref="ListView"/> allows items to be marked.</summary>

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

@@ -177,7 +177,7 @@ public sealed class ContextMenu : IDisposable
         }
 
         MenuItems = menuItems;
-        _container = Application.Current;
+        _container = Application.Top;
         _container!.Closing += Container_Closing;
         _container.Deactivate += Container_Deactivate;
         _container.Disposing += Container_Disposing;

+ 14 - 13
Terminal.Gui/Views/Menu/Menu.cs

@@ -101,7 +101,7 @@ internal sealed class Menu : View
 
             for (var i = 0; i < _barItems.Children?.Length; i++)
             {
-                if (_barItems.Children [i]!.IsEnabled ())
+                if (_barItems.Children [i]?.IsEnabled () == true)
                 {
                     _currentChild = i;
 
@@ -144,17 +144,17 @@ internal sealed class Menu : View
 
     public Menu ()
     {
-        if (Application.Current is { })
+        if (Application.Top is { })
         {
-            Application.Current.DrawContentComplete += Current_DrawContentComplete;
-            Application.Current.SizeChanging += Current_TerminalResized;
+            Application.Top.DrawContentComplete += Current_DrawContentComplete;
+            Application.Top.SizeChanging += Current_TerminalResized;
         }
 
         Application.MouseEvent += Application_RootMouseEvent;
 
         // Things this view knows how to do
-        AddCommand (Command.LineUp, () => MoveUp ());
-        AddCommand (Command.LineDown, () => MoveDown ());
+        AddCommand (Command.Up, () => MoveUp ());
+        AddCommand (Command.Down, () => MoveDown ());
 
         AddCommand (
                     Command.Left,
@@ -186,12 +186,12 @@ internal sealed class Menu : View
                     }
                    );
         AddCommand (Command.Select, ctx => _host?.SelectItem ((ctx.KeyBinding?.Context as MenuItem)!));
-        AddCommand (Command.ToggleExpandCollapse, ctx => ExpandCollapse ((ctx.KeyBinding?.Context as MenuItem)!));
+        AddCommand (Command.Toggle, ctx => ExpandCollapse ((ctx.KeyBinding?.Context as MenuItem)!));
         AddCommand (Command.HotKey, ctx => _host?.SelectItem ((ctx.KeyBinding?.Context as MenuItem)!));
 
         // Default key bindings for this view
-        KeyBindings.Add (Key.CursorUp, Command.LineUp);
-        KeyBindings.Add (Key.CursorDown, Command.LineDown);
+        KeyBindings.Add (Key.CursorUp, Command.Up);
+        KeyBindings.Add (Key.CursorDown, Command.Down);
         KeyBindings.Add (Key.CursorLeft, Command.Left);
         KeyBindings.Add (Key.CursorRight, Command.Right);
         KeyBindings.Add (Key.Esc, Command.Cancel);
@@ -209,7 +209,7 @@ internal sealed class Menu : View
 
         foreach (MenuItem menuItem in menuItems)
         {
-            KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, menuItem);
+            KeyBinding keyBinding = new ([Command.Toggle], KeyBindingScope.HotKey, menuItem);
 
             if (menuItem.HotKey != Key.Empty)
             {
@@ -607,6 +607,7 @@ internal sealed class Menu : View
 
         Application.UngrabMouse ();
         _host.CloseAllMenus ();
+        Application.Driver!.ClearContents ();
         Application.Refresh ();
 
         _host.Run (action);
@@ -952,10 +953,10 @@ internal sealed class Menu : View
     {
         RemoveKeyBindingsHotKey (_barItems);
 
-        if (Application.Current is { })
+        if (Application.Top is { })
         {
-            Application.Current.DrawContentComplete -= Current_DrawContentComplete;
-            Application.Current.SizeChanging -= Current_TerminalResized;
+            Application.Top.DrawContentComplete -= Current_DrawContentComplete;
+            Application.Top.SizeChanging -= Current_TerminalResized;
         }
 
         Application.MouseEvent -= Application_RootMouseEvent;

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

@@ -126,7 +126,7 @@ public class MenuBar : View, IDesignable
                         return true;
                     }
                    );
-        AddCommand (Command.ToggleExpandCollapse, ctx =>
+        AddCommand (Command.Toggle, ctx =>
                                                   {
                                                       CloseOtherOpenedMenuBar ();
 
@@ -147,7 +147,7 @@ public class MenuBar : View, IDesignable
         KeyBindings.Add (Key.CursorDown, Command.Accept);
         KeyBindings.Add (Key.Enter, Command.Accept);
 
-        KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, -1); // -1 indicates Key was used
+        KeyBinding keyBinding = new ([Command.Toggle], KeyBindingScope.HotKey, -1); // -1 indicates Key was used
         KeyBindings.Add (Key, keyBinding);
 
         // TODO: Why do we have two keybindings for opening the menu? Ctrl-Space and Key?
@@ -190,10 +190,10 @@ public class MenuBar : View, IDesignable
                 if (menuBarItem.HotKey != Key.Empty)
                 {
                     KeyBindings.Remove (menuBarItem.HotKey!);
-                    KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.Focused, menuBarItem);
+                    KeyBinding keyBinding = new ([Command.Toggle], KeyBindingScope.Focused, menuBarItem);
                     KeyBindings.Add (menuBarItem.HotKey!, keyBinding);
                     KeyBindings.Remove (menuBarItem.HotKey!.WithAlt);
-                    keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, menuBarItem);
+                    keyBinding = new ([Command.Toggle], KeyBindingScope.HotKey, menuBarItem);
                     KeyBindings.Add (menuBarItem.HotKey.WithAlt, keyBinding);
                 }
 
@@ -306,7 +306,7 @@ public class MenuBar : View, IDesignable
 
             if (i == _selected && IsMenuOpen)
             {
-                hotColor = i == _selected ? ColorScheme.HotFocus : GetHotNormalColor ();
+                hotColor = i == _selected ? ColorScheme!.HotFocus : GetHotNormalColor ();
                 normalColor = i == _selected ? GetFocusColor () : GetNormalColor ();
             }
             else
@@ -406,7 +406,7 @@ public class MenuBar : View, IDesignable
         _selected = 0;
         SetNeedsDisplay ();
 
-        _previousFocused = (SuperView is null ? Application.Current?.Focused : SuperView.Focused)!;
+        _previousFocused = (SuperView is null ? Application.Top?.Focused : SuperView.Focused)!;
         OpenMenu (_selected);
 
         if (!SelectEnabledItem (
@@ -467,7 +467,7 @@ public class MenuBar : View, IDesignable
 
         if (_openMenu is null)
         {
-            _previousFocused = (SuperView is null ? Application.Current?.Focused ?? null : SuperView.Focused)!;
+            _previousFocused = (SuperView is null ? Application.Top?.Focused ?? null : SuperView.Focused)!;
         }
 
         OpenMenu (idx, sIdx, subMenu);
@@ -544,10 +544,10 @@ public class MenuBar : View, IDesignable
 
     private void CloseOtherOpenedMenuBar ()
     {
-        if (Application.Current is { })
+        if (Application.Top is { })
         {
             // Close others menu bar opened
-            Menu? menu = Application.Current.Subviews.FirstOrDefault (v => v is Menu m && m.Host != this && m.Host.IsMenuOpen) as Menu;
+            Menu? menu = Application.Top.Subviews.FirstOrDefault (v => v is Menu m && m.Host != this && m.Host.IsMenuOpen) as Menu;
             menu?.Host.CleanUp ();
         }
     }
@@ -583,7 +583,7 @@ public class MenuBar : View, IDesignable
             case false:
                 if (_openMenu is { })
                 {
-                    Application.Current?.Remove (_openMenu);
+                    Application.Top?.Remove (_openMenu);
                 }
 
                 SetNeedsDisplay ();
@@ -618,7 +618,7 @@ public class MenuBar : View, IDesignable
 
                     if (OpenCurrentMenu is { })
                     {
-                        Application.Current?.Remove (OpenCurrentMenu);
+                        Application.Top?.Remove (OpenCurrentMenu);
                         OpenCurrentMenu.Dispose ();
                         OpenCurrentMenu = null;
                     }
@@ -666,7 +666,7 @@ public class MenuBar : View, IDesignable
         }
 
         Rectangle superViewFrame = SuperView?.Frame ?? Application.Screen;
-        View? sv = SuperView ?? Application.Current;
+        View? sv = SuperView ?? Application.Top;
 
         if (sv is null)
         {
@@ -793,7 +793,7 @@ public class MenuBar : View, IDesignable
         {
             case null:
                 // Open a submenu below a MenuBar
-                _lastFocused ??= SuperView is null ? Application.Current?.MostFocused : SuperView.MostFocused;
+                _lastFocused ??= SuperView is null ? Application.Top?.MostFocused : SuperView.MostFocused;
 
                 if (_openSubMenu is { } && !CloseMenu (false, true))
                 {
@@ -802,7 +802,7 @@ public class MenuBar : View, IDesignable
 
                 if (_openMenu is { })
                 {
-                    Application.Current?.Remove (_openMenu);
+                    Application.Top?.Remove (_openMenu);
                     _openMenu.Dispose ();
                     _openMenu = null;
                 }
@@ -822,7 +822,7 @@ public class MenuBar : View, IDesignable
                     locationOffset = GetScreenOffset ();
                 }
 
-                if (SuperView is { } && SuperView != Application.Current)
+                if (SuperView is { } && SuperView != Application.Top)
                 {
                     locationOffset.X += SuperView.Border.Thickness.Left;
                     locationOffset.Y += SuperView.Border.Thickness.Top;
@@ -839,9 +839,9 @@ public class MenuBar : View, IDesignable
                 OpenCurrentMenu = _openMenu;
                 OpenCurrentMenu._previousSubFocused = _openMenu;
 
-                if (Application.Current is { })
+                if (Application.Top is { })
                 {
-                    Application.Current.Add (_openMenu);
+                    Application.Top.Add (_openMenu);
                 }
                 else
                 {
@@ -906,7 +906,7 @@ public class MenuBar : View, IDesignable
 
                     OpenCurrentMenu._previousSubFocused = last._previousSubFocused;
                     _openSubMenu.Add (OpenCurrentMenu);
-                    Application.Current?.Add (OpenCurrentMenu);
+                    Application.Top?.Add (OpenCurrentMenu);
 
                     if (!OpenCurrentMenu.IsInitialized)
                     {
@@ -989,7 +989,7 @@ public class MenuBar : View, IDesignable
         {
             foreach (Menu item in _openSubMenu)
             {
-                Application.Current!.Remove (item);
+                Application.Top!.Remove (item);
                 item.Dispose ();
             }
         }
@@ -1228,7 +1228,7 @@ public class MenuBar : View, IDesignable
             if (_openSubMenu is { })
             {
                 menu = _openSubMenu [i];
-                Application.Current!.Remove (menu);
+                Application.Top!.Remove (menu);
                 _openSubMenu.Remove (menu);
 
                 if (Application.MouseGrabView == menu)
@@ -1276,7 +1276,7 @@ public class MenuBar : View, IDesignable
             }
 
             KeyBindings.Remove (_key);
-            KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, -1); // -1 indicates Key was used
+            KeyBinding keyBinding = new ([Command.Toggle], KeyBindingScope.HotKey, -1); // -1 indicates Key was used
             KeyBindings.Add (value, keyBinding);
             _key = value;
         }

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

@@ -325,7 +325,7 @@ public class MenuItem
             if (index > -1)
             {
                 _menuBar.KeyBindings.Remove (HotKey!.WithAlt);
-                KeyBinding keyBinding = new ([Command.ToggleExpandCollapse], KeyBindingScope.HotKey, this);
+                KeyBinding keyBinding = new ([Command.Toggle], KeyBindingScope.HotKey, this);
                 _menuBar.KeyBindings.Add (HotKey.WithAlt, keyBinding);
             }
         }

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

@@ -1,4 +1,5 @@
 using System;
+using System.ComponentModel;
 using System.Reflection;
 
 namespace Terminal.Gui;
@@ -53,10 +54,31 @@ public class Menuv2 : Bar
             shortcut.CanFocus = true;
             shortcut.KeyBindingScope = KeyBindingScope.Application;
             shortcut.Orientation = Orientation.Vertical;
+            shortcut.HighlightStyle |= HighlightStyle.Hover;
 
             // TODO: not happy about using AlignmentModes for this. Too implied.
             // TODO: instead, add a property (a style enum?) to Shortcut to control this
             //shortcut.AlignmentModes = AlignmentModes.EndToStart;
+
+            shortcut.Accept += ShortcutOnAccept;
+
+            void ShortcutOnAccept (object sender, HandledEventArgs e)
+            {
+                if (Arrangement.HasFlag(ViewArrangement.Overlapped) && Visible)
+                {
+                    Visible = false;
+                    e.Handled = true;
+
+                    return;
+
+                    //Enabled = Visible;
+                }
+
+                if (!e.Handled)
+                {
+                    OnAccept ();
+                }
+            }
         }
 
         return view;

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

@@ -56,7 +56,7 @@ public class NumericUpDown<T> : View where T : notnull
             Title = $"{Glyphs.DownArrow}",
             WantContinuousButtonPressed = true,
             CanFocus = false,
-            ShadowStyle = ShadowStyle.None
+            ShadowStyle = ShadowStyle.None,
         };
 
         _number = new ()
@@ -81,7 +81,7 @@ public class NumericUpDown<T> : View where T : notnull
             Title = $"{Glyphs.UpArrow}",
             WantContinuousButtonPressed = true,
             CanFocus = false,
-            ShadowStyle = ShadowStyle.None
+            ShadowStyle = ShadowStyle.None,
         };
 
         CanFocus = true;

+ 6 - 4
Terminal.Gui/Views/ProgressBar.cs

@@ -71,6 +71,7 @@ public class ProgressBar : View, IDesignable
         {
             _fraction = Math.Min (value, 1);
             _isActivity = false;
+            SetNeedsDisplay ();
         }
     }
 
@@ -108,6 +109,7 @@ public class ProgressBar : View, IDesignable
 
                     break;
             }
+            SetNeedsDisplay ();
         }
     }
 
@@ -263,10 +265,10 @@ public class ProgressBar : View, IDesignable
 
     private void ProgressBar_Initialized (object sender, EventArgs e)
     {
-        ColorScheme = new ColorScheme (ColorScheme ?? SuperView?.ColorScheme ?? Colors.ColorSchemes ["Base"])
-        {
-            HotNormal = new Attribute (Color.BrightGreen, Color.Gray)
-        };
+        //ColorScheme = new ColorScheme (ColorScheme ?? SuperView?.ColorScheme ?? Colors.ColorSchemes ["Base"])
+        //{
+        //    HotNormal = new Attribute (Color.BrightGreen, Color.Gray)
+        //};
     }
 
     private void SetInitialProperties ()

+ 12 - 12
Terminal.Gui/Views/RadioGroup.cs

@@ -22,7 +22,7 @@ public class RadioGroup : View, IDesignable, IOrientation
 
         // Things this view knows how to do
         AddCommand (
-                    Command.LineUp,
+                    Command.Up,
                     () =>
                     {
                         if (!HasFocus)
@@ -35,7 +35,7 @@ public class RadioGroup : View, IDesignable, IOrientation
                    );
 
         AddCommand (
-                    Command.LineDown,
+                    Command.Down,
                     () =>
                     {
                         if (!HasFocus)
@@ -47,7 +47,7 @@ public class RadioGroup : View, IDesignable, IOrientation
                    );
 
         AddCommand (
-                    Command.TopHome,
+                    Command.Start,
                     () =>
                     {
                         if (!HasFocus)
@@ -62,7 +62,7 @@ public class RadioGroup : View, IDesignable, IOrientation
                    );
 
         AddCommand (
-                    Command.BottomEnd,
+                    Command.End,
                     () =>
                     {
                         if (!HasFocus)
@@ -125,17 +125,17 @@ public class RadioGroup : View, IDesignable, IOrientation
         // Default keybindings for this view
         if (Orientation == Orientation.Vertical)
         {
-            KeyBindings.Add (Key.CursorUp, Command.LineUp);
-            KeyBindings.Add (Key.CursorDown, Command.LineDown);
+            KeyBindings.Add (Key.CursorUp, Command.Up);
+            KeyBindings.Add (Key.CursorDown, Command.Down);
         }
         else
         {
-            KeyBindings.Add (Key.CursorLeft, Command.LineUp);
-            KeyBindings.Add (Key.CursorRight, Command.LineDown);
+            KeyBindings.Add (Key.CursorLeft, Command.Up);
+            KeyBindings.Add (Key.CursorRight, Command.Down);
         }
 
-        KeyBindings.Add (Key.Home, Command.TopHome);
-        KeyBindings.Add (Key.End, Command.BottomEnd);
+        KeyBindings.Add (Key.Home, Command.Start);
+        KeyBindings.Add (Key.End, Command.End);
         KeyBindings.Add (Key.Space, Command.Accept);
     }
 
@@ -284,7 +284,7 @@ public class RadioGroup : View, IDesignable, IOrientation
                     }
                     else if (HasFocus && i == _cursor)
                     {
-                        Application.Driver?.SetAttribute (ColorScheme.Focus);
+                        Application.Driver?.SetAttribute (GetFocusColor ());
                     }
 
                     if (rune == HotKeySpecifier && j + 1 < rlRunes.Length)
@@ -312,7 +312,7 @@ public class RadioGroup : View, IDesignable, IOrientation
             }
             else
             {
-                DrawHotString (rl, HasFocus && i == _cursor, ColorScheme);
+                DrawHotString (rl, HasFocus && i == _cursor);
             }
         }
     }

+ 10 - 8
Terminal.Gui/Views/ScrollView.cs

@@ -11,6 +11,8 @@
 // - Raise events
 // - Perhaps allow an option to not display the scrollbar arrow indicators?
 
+using System.ComponentModel;
+
 namespace Terminal.Gui;
 
 /// <summary>
@@ -88,9 +90,9 @@ public class ScrollView : View
         AddCommand (Command.PageDown, () => ScrollDown (Viewport.Height));
         AddCommand (Command.PageLeft, () => ScrollLeft (Viewport.Width));
         AddCommand (Command.PageRight, () => ScrollRight (Viewport.Width));
-        AddCommand (Command.TopHome, () => ScrollUp (GetContentSize ().Height));
-        AddCommand (Command.BottomEnd, () => ScrollDown (GetContentSize ().Height));
-        AddCommand (Command.LeftHome, () => ScrollLeft (GetContentSize ().Width));
+        AddCommand (Command.Start, () => ScrollUp (GetContentSize ().Height));
+        AddCommand (Command.End, () => ScrollDown (GetContentSize ().Height));
+        AddCommand (Command.LeftStart, () => ScrollLeft (GetContentSize ().Width));
         AddCommand (Command.RightEnd, () => ScrollRight (GetContentSize ().Width));
 
         // Default keybindings for this view
@@ -107,9 +109,9 @@ public class ScrollView : View
 
         KeyBindings.Add (Key.PageUp.WithCtrl, Command.PageLeft);
         KeyBindings.Add (Key.PageDown.WithCtrl, Command.PageRight);
-        KeyBindings.Add (Key.Home, Command.TopHome);
-        KeyBindings.Add (Key.End, Command.BottomEnd);
-        KeyBindings.Add (Key.Home.WithCtrl, Command.LeftHome);
+        KeyBindings.Add (Key.Home, Command.Start);
+        KeyBindings.Add (Key.End, Command.End);
+        KeyBindings.Add (Key.Home.WithCtrl, Command.LeftStart);
         KeyBindings.Add (Key.End.WithCtrl, Command.RightEnd);
 
         Initialized += (s, e) =>
@@ -743,9 +745,9 @@ public class ScrollView : View
         }
     }
 
-    private void View_MouseEnter (object sender, MouseEventEventArgs e) { Application.GrabMouse (this); }
+    private void View_MouseEnter (object sender, CancelEventArgs e) { Application.GrabMouse (this); }
 
-    private void View_MouseLeave (object sender, MouseEventEventArgs e)
+    private void View_MouseLeave (object sender, EventArgs e)
     {
         if (Application.MouseGrabView is { } && Application.MouseGrabView != this && Application.MouseGrabView != _vertical && Application.MouseGrabView != _horizontal)
         {

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

@@ -54,7 +54,6 @@ public class Shortcut : View, IOrientation, IDesignable
     {
         Id = "_shortcut";
         HighlightStyle = HighlightStyle.Pressed;
-        Highlight += Shortcut_Highlight;
         CanFocus = true;
         Width = GetWidthDimAuto ();
         Height = Dim.Auto (DimAutoStyle.Content, 1);
@@ -147,7 +146,16 @@ public class Shortcut : View, IOrientation, IDesignable
     // This is used to calculate the minimum width of the Shortcut when the width is NOT Dim.Auto
     private int? _minimumDimAutoWidth;
 
-    private Color? _savedForeColor;
+    /// <inheritdoc />
+    protected override bool OnHighlight (CancelEventArgs<HighlightStyle> args)
+    {
+        if (args.NewValue.HasFlag (HighlightStyle.Hover))
+        {
+            HasFocus = true;
+        }
+
+        return true;
+    }
 
     /// <inheritdoc/>
     public bool EnableForDesign ()
@@ -324,35 +332,6 @@ public class Shortcut : View, IOrientation, IDesignable
         return false;
     }
 
-    private void Shortcut_Highlight (object sender, CancelEventArgs<HighlightStyle> e)
-    {
-        if (e.CurrentValue.HasFlag (HighlightStyle.Pressed))
-        {
-            if (!_savedForeColor.HasValue)
-            {
-                _savedForeColor = base.ColorScheme.Normal.Foreground;
-            }
-
-            var cs = new ColorScheme (base.ColorScheme)
-            {
-                Normal = new (ColorScheme.Normal.Foreground.GetHighlightColor (), base.ColorScheme.Normal.Background)
-            };
-            base.ColorScheme = cs;
-        }
-
-        if (e.CurrentValue == HighlightStyle.None && _savedForeColor.HasValue)
-        {
-            var cs = new ColorScheme (base.ColorScheme)
-            {
-                Normal = new (_savedForeColor.Value, base.ColorScheme.Normal.Background)
-            };
-            base.ColorScheme = cs;
-        }
-
-        SuperView?.SetNeedsDisplay ();
-        e.Cancel = true;
-    }
-
     private void Shortcut_MouseClick (object sender, MouseEventEventArgs e)
     {
         // When the Shortcut is clicked, we want to invoke the Command and Set focus
@@ -507,6 +486,7 @@ public class Shortcut : View, IOrientation, IDesignable
         CommandView.Margin.Thickness = GetMarginThickness ();
         CommandView.X = Pos.Align (Alignment.End, AlignmentModes);
         CommandView.Y = 0; //Pos.Center ();
+        HelpView.HighlightStyle = HighlightStyle.None;
     }
 
     private void Shortcut_TitleChanged (object sender, EventArgs<string> e)
@@ -536,6 +516,7 @@ public class Shortcut : View, IOrientation, IDesignable
 
         HelpView.Visible = true;
         HelpView.VerticalTextAlignment = Alignment.Center;
+        HelpView.HighlightStyle = HighlightStyle.None;
     }
 
     /// <summary>
@@ -677,6 +658,7 @@ public class Shortcut : View, IOrientation, IDesignable
         KeyView.TextAlignment = Alignment.End;
         KeyView.VerticalTextAlignment = Alignment.Center;
         KeyView.KeyBindings.Clear ();
+        HelpView.HighlightStyle = HighlightStyle.None;
     }
 
     private void UpdateKeyBinding (Key oldKey)
@@ -803,12 +785,12 @@ public class Shortcut : View, IOrientation, IDesignable
 
     /// <summary>
     /// </summary>
-    internal void SetColors ()
+    internal void SetColors (bool highlight = false)
     {
         // Border should match superview.
         Border.ColorScheme = SuperView?.ColorScheme;
 
-        if (HasFocus)
+        if (HasFocus || highlight)
         {
             base.ColorScheme ??= new (Attribute.Default);
 

+ 37 - 7
Terminal.Gui/Views/Slider.cs

@@ -848,7 +848,7 @@ public class Slider<T> : View, IOrientation
 
         if (IsInitialized)
         {
-            normalAttr = ColorScheme?.Normal ?? Application.Current.ColorScheme.Normal;
+            normalAttr = ColorScheme?.Normal ?? Application.Top.ColorScheme.Normal;
             setAttr = Style.SetChar.Attribute ?? ColorScheme!.HotNormal;
         }
 
@@ -1380,6 +1380,8 @@ public class Slider<T> : View, IOrientation
             SetNeedsDisplay ();
 
             mouseEvent.Handled = true;
+
+            // BUGBUG: OnMouseClick is/should be internal. 
             return OnMouseClick (new (mouseEvent));
         }
 
@@ -1413,10 +1415,10 @@ public class Slider<T> : View, IOrientation
     private void SetCommands ()
     {
         AddCommand (Command.Right, () => MovePlus ());
-        AddCommand (Command.LineDown, () => MovePlus ());
+        AddCommand (Command.Down, () => MovePlus ());
         AddCommand (Command.Left, () => MoveMinus ());
-        AddCommand (Command.LineUp, () => MoveMinus ());
-        AddCommand (Command.LeftHome, () => MoveStart ());
+        AddCommand (Command.Up, () => MoveMinus ());
+        AddCommand (Command.LeftStart, () => MoveStart ());
         AddCommand (Command.RightEnd, () => MoveEnd ());
         AddCommand (Command.RightExtend, () => ExtendPlus ());
         AddCommand (Command.LeftExtend, () => ExtendMinus ());
@@ -1444,9 +1446,9 @@ public class Slider<T> : View, IOrientation
         else
         {
             KeyBindings.Remove (Key.CursorRight);
-            KeyBindings.Add (Key.CursorDown, Command.LineDown);
+            KeyBindings.Add (Key.CursorDown, Command.Down);
             KeyBindings.Remove (Key.CursorLeft);
-            KeyBindings.Add (Key.CursorUp, Command.LineUp);
+            KeyBindings.Add (Key.CursorUp, Command.Up);
 
             KeyBindings.Remove (Key.CursorRight.WithCtrl);
             KeyBindings.Add (Key.CursorDown.WithCtrl, Command.RightExtend);
@@ -1455,7 +1457,7 @@ public class Slider<T> : View, IOrientation
         }
 
         KeyBindings.Remove (Key.Home);
-        KeyBindings.Add (Key.Home, Command.LeftHome);
+        KeyBindings.Add (Key.Home, Command.LeftStart);
         KeyBindings.Remove (Key.End);
         KeyBindings.Add (Key.End, Command.RightEnd);
         KeyBindings.Remove (Key.Enter);
@@ -1466,6 +1468,34 @@ public class Slider<T> : View, IOrientation
 
     private Dictionary<int, SliderOption<T>> GetSetOptionDictionary () { return _setOptions.ToDictionary (e => e, e => _options [e]); }
 
+    /// <summary>
+    /// Sets or unsets <paramref name="optionIndex"/> based on <paramref name="set"/>.
+    /// </summary>
+    /// <param name="optionIndex">The option to change.</param>
+    /// <param name="set">If <see langword="true"/>, sets the option. Unsets it otherwise.</param>
+    public void ChangeOption (int optionIndex, bool set)
+    {
+        if (set)
+        {
+            if (!_setOptions.Contains (optionIndex))
+            {
+                _setOptions.Add (optionIndex);
+                _options [optionIndex].OnSet ();
+            }
+        }
+        else
+        {
+            if (_setOptions.Contains (optionIndex))
+            {
+                _setOptions.Remove (optionIndex);
+                _options [optionIndex].OnUnSet ();
+            }
+        }
+
+        // Raise slider changed event.
+        OnOptionsChanged ();
+    }
+
     private void SetFocusedOption ()
     {
         switch (_config._type)

+ 5 - 1
Terminal.Gui/Views/Tab.cs

@@ -18,7 +18,11 @@ public class Tab : View
     public string DisplayText
     {
         get => _displayText ?? "Unnamed";
-        set => _displayText = value;
+        set
+        {
+            _displayText = value;
+            SetNeedsDisplay ();
+        }
     }
 
     /// <summary>The control to display when the tab is selected.</summary>

+ 2 - 2
Terminal.Gui/Views/TabView.cs

@@ -44,7 +44,7 @@ public class TabView : View
         AddCommand (Command.Right, () => SwitchTabBy (1));
 
         AddCommand (
-                    Command.LeftHome,
+                    Command.LeftStart,
                     () =>
                     {
                         TabScrollOffset = 0;
@@ -90,7 +90,7 @@ public class TabView : View
         // Default keybindings for this view
         KeyBindings.Add (Key.CursorLeft, Command.Left);
         KeyBindings.Add (Key.CursorRight, Command.Right);
-        KeyBindings.Add (Key.Home, Command.LeftHome);
+        KeyBindings.Add (Key.Home, Command.LeftStart);
         KeyBindings.Add (Key.End, Command.RightEnd);
         KeyBindings.Add (Key.PageDown, Command.PageDown);
         KeyBindings.Add (Key.PageUp, Command.PageUp);

+ 25 - 21
Terminal.Gui/Views/TableView/TableView.cs

@@ -61,11 +61,11 @@ public class TableView : View
                     () => ChangeSelectionByOffsetWithReturn (-1, 0));
 
         AddCommand (
-                    Command.LineUp,
+                    Command.Up,
                     () => ChangeSelectionByOffsetWithReturn (0, -1));
 
         AddCommand (
-                    Command.LineDown,
+                    Command.Down,
                     () => ChangeSelectionByOffsetWithReturn (0, 1));
 
         AddCommand (
@@ -89,7 +89,7 @@ public class TableView : View
                    );
 
         AddCommand (
-                    Command.LeftHome,
+                    Command.LeftStart,
                     () =>
                     {
                         ChangeSelectionToStartOfRow (false);
@@ -109,7 +109,7 @@ public class TableView : View
                    );
 
         AddCommand (
-                    Command.TopHome,
+                    Command.Start,
                     () =>
                     {
                         ChangeSelectionToStartOfTable (false);
@@ -119,7 +119,7 @@ public class TableView : View
                    );
 
         AddCommand (
-                    Command.BottomEnd,
+                    Command.End,
                     () =>
                     {
                         ChangeSelectionToEndOfTable (false);
@@ -149,7 +149,7 @@ public class TableView : View
                    );
 
         AddCommand (
-                    Command.LineUpExtend,
+                    Command.UpExtend,
                     () =>
                     {
                         ChangeSelectionByOffset (0, -1, true);
@@ -159,7 +159,7 @@ public class TableView : View
                    );
 
         AddCommand (
-                    Command.LineDownExtend,
+                    Command.DownExtend,
                     () =>
                     {
                         ChangeSelectionByOffset (0, 1, true);
@@ -189,7 +189,7 @@ public class TableView : View
                    );
 
         AddCommand (
-                    Command.LeftHomeExtend,
+                    Command.LeftStartExtend,
                     () =>
                     {
                         ChangeSelectionToStartOfRow (true);
@@ -209,7 +209,7 @@ public class TableView : View
                    );
 
         AddCommand (
-                    Command.TopHomeExtend,
+                    Command.StartExtend,
                     () =>
                     {
                         ChangeSelectionToStartOfTable (true);
@@ -219,7 +219,7 @@ public class TableView : View
                    );
 
         AddCommand (
-                    Command.BottomEndExtend,
+                    Command.EndExtend,
                     () =>
                     {
                         ChangeSelectionToEndOfTable (true);
@@ -262,25 +262,25 @@ public class TableView : View
         // Default keybindings for this view
         KeyBindings.Add (Key.CursorLeft, Command.Left);
         KeyBindings.Add (Key.CursorRight, Command.Right);
-        KeyBindings.Add (Key.CursorUp, Command.LineUp);
-        KeyBindings.Add (Key.CursorDown, Command.LineDown);
+        KeyBindings.Add (Key.CursorUp, Command.Up);
+        KeyBindings.Add (Key.CursorDown, Command.Down);
         KeyBindings.Add (Key.PageUp, Command.PageUp);
         KeyBindings.Add (Key.PageDown, Command.PageDown);
-        KeyBindings.Add (Key.Home, Command.LeftHome);
+        KeyBindings.Add (Key.Home, Command.LeftStart);
         KeyBindings.Add (Key.End, Command.RightEnd);
-        KeyBindings.Add (Key.Home.WithCtrl, Command.TopHome);
-        KeyBindings.Add (Key.End.WithCtrl, Command.BottomEnd);
+        KeyBindings.Add (Key.Home.WithCtrl, Command.Start);
+        KeyBindings.Add (Key.End.WithCtrl, Command.End);
 
         KeyBindings.Add (Key.CursorLeft.WithShift, Command.LeftExtend);
         KeyBindings.Add (Key.CursorRight.WithShift, Command.RightExtend);
-        KeyBindings.Add (Key.CursorUp.WithShift, Command.LineUpExtend);
-        KeyBindings.Add (Key.CursorDown.WithShift, Command.LineDownExtend);
+        KeyBindings.Add (Key.CursorUp.WithShift, Command.UpExtend);
+        KeyBindings.Add (Key.CursorDown.WithShift, Command.DownExtend);
         KeyBindings.Add (Key.PageUp.WithShift, Command.PageUpExtend);
         KeyBindings.Add (Key.PageDown.WithShift, Command.PageDownExtend);
-        KeyBindings.Add (Key.Home.WithShift, Command.LeftHomeExtend);
+        KeyBindings.Add (Key.Home.WithShift, Command.LeftStartExtend);
         KeyBindings.Add (Key.End.WithShift, Command.RightEndExtend);
-        KeyBindings.Add (Key.Home.WithCtrl.WithShift, Command.TopHomeExtend);
-        KeyBindings.Add (Key.End.WithCtrl.WithShift, Command.BottomEndExtend);
+        KeyBindings.Add (Key.Home.WithCtrl.WithShift, Command.StartExtend);
+        KeyBindings.Add (Key.End.WithCtrl.WithShift, Command.EndExtend);
 
         KeyBindings.Add (Key.A.WithCtrl, Command.SelectAll);
         KeyBindings.Add (CellActivationKey, Command.Accept);
@@ -323,7 +323,11 @@ public class TableView : View
         get => columnOffset;
 
         //try to prevent this being set to an out of bounds column
-        set => columnOffset = TableIsNullOrInvisible () ? 0 : Math.Max (0, Math.Min (Table.Columns - 1, value));
+        set
+        {
+            columnOffset = TableIsNullOrInvisible () ? 0 : Math.Max (0, Math.Min (Table.Columns - 1, value));
+            SetNeedsDisplay ();
+        }
     }
 
     /// <summary>True to select the entire row at once.  False to select individual cells.  Defaults to false</summary>

+ 11 - 18
Terminal.Gui/Views/TextField.cs

@@ -72,7 +72,7 @@ public class TextField : View
                    );
 
         AddCommand (
-                    Command.LeftHomeExtend,
+                    Command.LeftStartExtend,
                     () =>
                     {
                         MoveHomeExtend ();
@@ -92,7 +92,7 @@ public class TextField : View
                    );
 
         AddCommand (
-                    Command.LeftHome,
+                    Command.LeftStart,
                     () =>
                     {
                         MoveHome ();
@@ -316,7 +316,7 @@ public class TextField : View
                    );
 
         AddCommand (
-                    Command.ShowContextMenu,
+                    Command.Context,
                     () =>
                     {
                         ShowContextMenu ();
@@ -336,17 +336,17 @@ public class TextField : View
 
         KeyBindings.Add (Key.Backspace, Command.DeleteCharLeft);
 
-        KeyBindings.Add (Key.Home.WithShift, Command.LeftHomeExtend);
-        KeyBindings.Add (Key.Home.WithShift.WithCtrl, Command.LeftHomeExtend);
-        KeyBindings.Add (Key.A.WithShift.WithCtrl, Command.LeftHomeExtend);
+        KeyBindings.Add (Key.Home.WithShift, Command.LeftStartExtend);
+        KeyBindings.Add (Key.Home.WithShift.WithCtrl, Command.LeftStartExtend);
+        KeyBindings.Add (Key.A.WithShift.WithCtrl, Command.LeftStartExtend);
 
         KeyBindings.Add (Key.End.WithShift, Command.RightEndExtend);
         KeyBindings.Add (Key.End.WithShift.WithCtrl, Command.RightEndExtend);
         KeyBindings.Add (Key.E.WithShift.WithCtrl, Command.RightEndExtend);
 
-        KeyBindings.Add (Key.Home, Command.LeftHome);
-        KeyBindings.Add (Key.Home.WithCtrl, Command.LeftHome);
-        KeyBindings.Add (Key.A.WithCtrl, Command.LeftHome);
+        KeyBindings.Add (Key.Home, Command.LeftStart);
+        KeyBindings.Add (Key.Home.WithCtrl, Command.LeftStart);
+        KeyBindings.Add (Key.A.WithCtrl, Command.LeftStart);
 
         KeyBindings.Add (Key.CursorLeft.WithShift, Command.LeftExtend);
         KeyBindings.Add (Key.CursorUp.WithShift, Command.LeftExtend);
@@ -408,7 +408,7 @@ public class TextField : View
         ContextMenu = new ContextMenu { Host = this };
         ContextMenu.KeyChanged += ContextMenu_KeyChanged;
 
-        KeyBindings.Add (ContextMenu.Key, KeyBindingScope.HotKey, Command.ShowContextMenu);
+        KeyBindings.Add (ContextMenu.Key, KeyBindingScope.HotKey, Command.Context);
         KeyBindings.Add (Key.Enter, Command.Accept);
     }
 
@@ -730,14 +730,7 @@ public class TextField : View
     /// <inheritdoc/>
     public override Attribute GetNormalColor ()
     {
-        ColorScheme cs = ColorScheme;
-
-        if (ColorScheme is null)
-        {
-            cs = new ColorScheme ();
-        }
-
-        return Enabled ? cs.Focus : cs.Disabled;
+        return GetFocusColor ();
     }
 
     /// <summary>

+ 2 - 2
Terminal.Gui/Views/TextValidateField.cs

@@ -401,7 +401,7 @@ namespace Terminal.Gui
 
             // Things this view knows how to do
             AddCommand (
-                        Command.LeftHome,
+                        Command.LeftStart,
                         () =>
                         {
                             HomeKeyHandler ();
@@ -461,7 +461,7 @@ namespace Terminal.Gui
                        );
 
             // Default keybindings for this view
-            KeyBindings.Add (Key.Home, Command.LeftHome);
+            KeyBindings.Add (Key.Home, Command.LeftStart);
             KeyBindings.Add (Key.End, Command.RightEnd);
 
             KeyBindings.Add (Key.Delete, Command.DeleteCharRight);

+ 46 - 53
Terminal.Gui/Views/TextView.cs

@@ -2050,10 +2050,10 @@ public class TextView : View
                     }
                    );
 
-        AddCommand (Command.LineDown, () => ProcessMoveDown ());
+        AddCommand (Command.Down, () => ProcessMoveDown ());
 
         AddCommand (
-                    Command.LineDownExtend,
+                    Command.DownExtend,
                     () =>
                     {
                         ProcessMoveDownExtend ();
@@ -2062,10 +2062,10 @@ public class TextView : View
                     }
                    );
 
-        AddCommand (Command.LineUp, () => ProcessMoveUp ());
+        AddCommand (Command.Up, () => ProcessMoveUp ());
 
         AddCommand (
-                    Command.LineUpExtend,
+                    Command.UpExtend,
                     () =>
                     {
                         ProcessMoveUpExtend ();
@@ -2107,20 +2107,20 @@ public class TextView : View
                    );
 
         AddCommand (
-                    Command.StartOfLine,
+                    Command.LeftStart,
                     () =>
                     {
-                        ProcessMoveStartOfLine ();
+                        ProcessMoveLeftStart ();
 
                         return true;
                     }
                    );
 
         AddCommand (
-                    Command.StartOfLineExtend,
+                    Command.LeftStartExtend,
                     () =>
                     {
-                        ProcessMoveStartOfLineExtend ();
+                        ProcessMoveLeftStartExtend ();
 
                         return true;
                     }
@@ -2137,7 +2137,7 @@ public class TextView : View
                    );
 
         AddCommand (
-                    Command.EndOfLine,
+                    Command.RightEnd,
                     () =>
                     {
                         ProcessMoveEndOfLine ();
@@ -2147,10 +2147,10 @@ public class TextView : View
                    );
 
         AddCommand (
-                    Command.EndOfLineExtend,
+                    Command.RightEndExtend,
                     () =>
                     {
-                        ProcessMoveEndOfLineExtend ();
+                        ProcessMoveRightEndExtend ();
 
                         return true;
                     }
@@ -2170,7 +2170,7 @@ public class TextView : View
                     Command.CutToStartLine,
                     () =>
                     {
-                        KillToStartOfLine ();
+                        KillToLeftStart ();
 
                         return true;
                     }
@@ -2278,7 +2278,7 @@ public class TextView : View
         AddCommand (Command.NewLine, () => ProcessReturn ());
 
         AddCommand (
-                    Command.BottomEnd,
+                    Command.End,
                     () =>
                     {
                         MoveBottomEnd ();
@@ -2288,7 +2288,7 @@ public class TextView : View
                    );
 
         AddCommand (
-                    Command.BottomEndExtend,
+                    Command.EndExtend,
                     () =>
                     {
                         MoveBottomEndExtend ();
@@ -2298,7 +2298,7 @@ public class TextView : View
                    );
 
         AddCommand (
-                    Command.TopHome,
+                    Command.Start,
                     () =>
                     {
                         MoveTopHome ();
@@ -2308,7 +2308,7 @@ public class TextView : View
                    );
 
         AddCommand (
-                    Command.TopHomeExtend,
+                    Command.StartExtend,
                     () =>
                     {
                         MoveTopHomeExtend ();
@@ -2390,7 +2390,7 @@ public class TextView : View
                    );
 
         AddCommand (
-                    Command.ShowContextMenu,
+                    Command.Context,
                     () =>
                     {
                         ContextMenu!.Position = new (
@@ -2414,15 +2414,15 @@ public class TextView : View
 
         KeyBindings.Add (Key.PageUp.WithShift, Command.PageUpExtend);
 
-        KeyBindings.Add (Key.N.WithCtrl, Command.LineDown);
-        KeyBindings.Add (Key.CursorDown, Command.LineDown);
+        KeyBindings.Add (Key.N.WithCtrl, Command.Down);
+        KeyBindings.Add (Key.CursorDown, Command.Down);
 
-        KeyBindings.Add (Key.CursorDown.WithShift, Command.LineDownExtend);
+        KeyBindings.Add (Key.CursorDown.WithShift, Command.DownExtend);
 
-        KeyBindings.Add (Key.P.WithCtrl, Command.LineUp);
-        KeyBindings.Add (Key.CursorUp, Command.LineUp);
+        KeyBindings.Add (Key.P.WithCtrl, Command.Up);
+        KeyBindings.Add (Key.CursorUp, Command.Up);
 
-        KeyBindings.Add (Key.CursorUp.WithShift, Command.LineUpExtend);
+        KeyBindings.Add (Key.CursorUp.WithShift, Command.UpExtend);
 
         KeyBindings.Add (Key.F.WithCtrl, Command.Right);
         KeyBindings.Add (Key.CursorRight, Command.Right);
@@ -2436,18 +2436,18 @@ public class TextView : View
 
         KeyBindings.Add (Key.Backspace, Command.DeleteCharLeft);
 
-        KeyBindings.Add (Key.Home, Command.StartOfLine);
-        KeyBindings.Add (Key.A.WithCtrl, Command.StartOfLine);
+        KeyBindings.Add (Key.Home, Command.LeftStart);
+        KeyBindings.Add (Key.A.WithCtrl, Command.LeftStart);
 
-        KeyBindings.Add (Key.Home.WithShift, Command.StartOfLineExtend);
+        KeyBindings.Add (Key.Home.WithShift, Command.LeftStartExtend);
 
         KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
         KeyBindings.Add (Key.D.WithCtrl, Command.DeleteCharRight);
 
-        KeyBindings.Add (Key.End, Command.EndOfLine);
-        KeyBindings.Add (Key.E.WithCtrl, Command.EndOfLine);
+        KeyBindings.Add (Key.End, Command.RightEnd);
+        KeyBindings.Add (Key.E.WithCtrl, Command.RightEnd);
 
-        KeyBindings.Add (Key.End.WithShift, Command.EndOfLineExtend);
+        KeyBindings.Add (Key.End.WithShift, Command.RightEndExtend);
 
         KeyBindings.Add (Key.K.WithCtrl, Command.CutToEndLine); // kill-to-end
 
@@ -2481,10 +2481,10 @@ public class TextView : View
 
         // BUGBUG: If AllowsReturn is false, Key.Enter should not be bound (so that Toplevel can cause Command.Accept).
         KeyBindings.Add (Key.Enter, Command.NewLine);
-        KeyBindings.Add (Key.End.WithCtrl, Command.BottomEnd);
-        KeyBindings.Add (Key.End.WithCtrl.WithShift, Command.BottomEndExtend);
-        KeyBindings.Add (Key.Home.WithCtrl, Command.TopHome);
-        KeyBindings.Add (Key.Home.WithCtrl.WithShift, Command.TopHomeExtend);
+        KeyBindings.Add (Key.End.WithCtrl, Command.End);
+        KeyBindings.Add (Key.End.WithCtrl.WithShift, Command.EndExtend);
+        KeyBindings.Add (Key.Home.WithCtrl, Command.Start);
+        KeyBindings.Add (Key.Home.WithCtrl.WithShift, Command.StartExtend);
         KeyBindings.Add (Key.T.WithCtrl, Command.SelectAll);
         KeyBindings.Add (Key.InsertChar, Command.ToggleOverwrite);
         KeyBindings.Add (Key.Tab, Command.Tab);
@@ -2501,7 +2501,7 @@ public class TextView : View
         ContextMenu = new ();
         ContextMenu.KeyChanged += ContextMenu_KeyChanged!;
 
-        KeyBindings.Add ((KeyCode)ContextMenu.Key, KeyBindingScope.HotKey, Command.ShowContextMenu);
+        KeyBindings.Add ((KeyCode)ContextMenu.Key, KeyBindingScope.HotKey, Command.Context);
     }
 
     private void TextView_Added1 (object? sender, SuperViewChangedEventArgs e)
@@ -3142,14 +3142,7 @@ public class TextView : View
     /// <inheritdoc/>
     public override Attribute GetNormalColor ()
     {
-        ColorScheme? cs = ColorScheme;
-
-        if (ColorScheme is null)
-        {
-            cs = new ();
-        }
-
-        return Enabled ? cs.Focus : cs.Disabled;
+        return GetFocusColor ();
     }
 
     /// <summary>
@@ -4019,8 +4012,8 @@ public class TextView : View
         {
             Driver.SetAttribute (
                                  new (
-                                      ColorScheme.Focus.Background,
-                                      ColorScheme.Focus.Foreground
+                                      ColorScheme!.Focus.Background,
+                                      ColorScheme!.Focus.Foreground
                                      )
                                 );
         }
@@ -5019,7 +5012,7 @@ public class TextView : View
         DoNeededAction ();
     }
 
-    private void KillToStartOfLine ()
+    private void KillToLeftStart ()
     {
         if (_isReadOnly)
         {
@@ -5456,7 +5449,7 @@ public class TextView : View
         return true;
     }
 
-    private void MoveStartOfLine ()
+    private void MoveLeftStart ()
     {
         if (_leftColumn > 0)
         {
@@ -5847,7 +5840,7 @@ public class TextView : View
         MoveEndOfLine ();
     }
 
-    private void ProcessMoveEndOfLineExtend ()
+    private void ProcessMoveRightEndExtend ()
     {
         ResetAllTrack ();
         StartSelecting ();
@@ -5914,7 +5907,7 @@ public class TextView : View
         MoveRight ();
     }
 
-    private void ProcessMoveStartOfLine ()
+    private void ProcessMoveLeftStart ()
     {
         ResetAllTrack ();
 
@@ -5923,14 +5916,14 @@ public class TextView : View
             StopSelecting ();
         }
 
-        MoveStartOfLine ();
+        MoveLeftStart ();
     }
 
-    private void ProcessMoveStartOfLineExtend ()
+    private void ProcessMoveLeftStartExtend ()
     {
         ResetAllTrack ();
         StartSelecting ();
-        MoveStartOfLine ();
+        MoveLeftStart ();
     }
 
     private bool ProcessMoveUp ()
@@ -6258,11 +6251,11 @@ public class TextView : View
         DoNeededAction ();
     }
 
-    private static void SetValidUsedColor (ColorScheme colorScheme)
+    private static void SetValidUsedColor (ColorScheme? colorScheme)
     {
         // BUGBUG: (v2 truecolor) This code depends on 8-bit color names; disabling for now
         //if ((colorScheme!.HotNormal.Foreground & colorScheme.Focus.Background) == colorScheme.Focus.Foreground) {
-        Driver.SetAttribute (new (colorScheme.Focus.Background, colorScheme.Focus.Foreground));
+        Driver.SetAttribute (new (colorScheme!.Focus.Background, colorScheme!.Focus.Foreground));
     }
 
     /// <summary>Restore from original model.</summary>

+ 4 - 5
Terminal.Gui/Views/TileView.cs

@@ -887,14 +887,14 @@ public class TileView : View
 
             AddCommand (Command.Left, () => { return MoveSplitter (-1, 0); });
 
-            AddCommand (Command.LineUp, () => { return MoveSplitter (0, -1); });
+            AddCommand (Command.Up, () => { return MoveSplitter (0, -1); });
 
-            AddCommand (Command.LineDown, () => { return MoveSplitter (0, 1); });
+            AddCommand (Command.Down, () => { return MoveSplitter (0, 1); });
 
             KeyBindings.Add (Key.CursorRight, Command.Right);
             KeyBindings.Add (Key.CursorLeft, Command.Left);
-            KeyBindings.Add (Key.CursorUp, Command.LineUp);
-            KeyBindings.Add (Key.CursorDown, Command.LineDown);
+            KeyBindings.Add (Key.CursorUp, Command.Up);
+            KeyBindings.Add (Key.CursorDown, Command.Down);
         }
 
         public int Idx { get; }
@@ -916,7 +916,6 @@ public class TileView : View
             {
                 // Start a Drag
                 SetFocus ();
-                ApplicationOverlapped.BringOverlappedTopToFront ();
 
                 if (mouseEvent.Flags == MouseFlags.Button1Pressed)
                 {

+ 3 - 3
Terminal.Gui/Views/TimeField.cs

@@ -53,7 +53,7 @@ public class TimeField : TextField
                         return true;
                     }
                    );
-        AddCommand (Command.LeftHome, () => MoveHome ());
+        AddCommand (Command.LeftStart, () => MoveHome ());
         AddCommand (Command.Left, () => MoveLeft ());
         AddCommand (Command.RightEnd, () => MoveEnd ());
         AddCommand (Command.Right, () => MoveRight ());
@@ -64,8 +64,8 @@ public class TimeField : TextField
 
         KeyBindings.ReplaceCommands (Key.Backspace, Command.DeleteCharLeft);
 
-        KeyBindings.ReplaceCommands (Key.Home, Command.LeftHome);
-        KeyBindings.ReplaceCommands (Key.A.WithCtrl, Command.LeftHome);
+        KeyBindings.ReplaceCommands (Key.Home, Command.LeftStart);
+        KeyBindings.ReplaceCommands (Key.A.WithCtrl, Command.LeftStart);
 
         KeyBindings.ReplaceCommands (Key.CursorLeft, Command.Left);
         KeyBindings.ReplaceCommands (Key.B.WithCtrl, Command.Left);

+ 9 - 244
Terminal.Gui/Views/Toplevel.cs

@@ -30,7 +30,7 @@ public partial class Toplevel : View
     {
         CanFocus = true;
         TabStop = TabBehavior.TabGroup;
-        Arrangement = ViewArrangement.Fixed;
+        Arrangement = ViewArrangement.Overlapped;
         Width = Dim.Fill ();
         Height = Dim.Fill ();
         ColorScheme = Colors.ColorSchemes ["TopLevel"];
@@ -76,29 +76,6 @@ public partial class Toplevel : View
     /// <summary>Gets the latest <see cref="StatusBar"/> added into this Toplevel.</summary>
     public StatusBar? StatusBar => (StatusBar?)Subviews?.LastOrDefault (s => s is StatusBar);
 
-
-    // TODO: Overlapped - Rename to AllSubviewsClosed - Move to View?
-    /// <summary>
-    ///     Invoked when the last child of the Toplevel <see cref="RunState"/> is closed from by
-    ///     <see cref="Application.End(RunState)"/>.
-    /// </summary>
-    public event EventHandler? AllChildClosed;
-
-    // TODO: Overlapped - Rename to *Subviews* - Move to View?
-    /// <summary>
-    ///     Invoked when a child of the Toplevel <see cref="RunState"/> is closed by
-    ///     <see cref="Application.End(RunState)"/>.
-    /// </summary>
-    public event EventHandler<ToplevelEventArgs>? ChildClosed;
-
-    // TODO: Overlapped - Rename to *Subviews* - Move to View?
-    /// <summary>Invoked when a child Toplevel's <see cref="RunState"/> has been loaded.</summary>
-    public event EventHandler<ToplevelEventArgs>? ChildLoaded;
-
-    // TODO: Overlapped - Rename to *Subviews* - Move to View?
-    /// <summary>Invoked when a cjhild Toplevel's <see cref="RunState"/> has been unloaded.</summary>
-    public event EventHandler<ToplevelEventArgs>? ChildUnloaded;
-
     #endregion
 
     #region Life Cycle
@@ -116,11 +93,11 @@ public partial class Toplevel : View
     public bool IsLoaded { get; private set; }
 
     // TODO: IRunnable: Re-implement as an event on IRunnable; IRunnable.Activating/Activate
-    /// <summary>Invoked when the Toplevel <see cref="RunState"/> becomes the <see cref="Application.Current"/> Toplevel.</summary>
+    /// <summary>Invoked when the Toplevel <see cref="RunState"/> active.</summary>
     public event EventHandler<ToplevelEventArgs>? Activate;
 
     // TODO: IRunnable: Re-implement as an event on IRunnable; IRunnable.Deactivating/Deactivate?
-    /// <summary>Invoked when the Toplevel<see cref="RunState"/> ceases to be the <see cref="Application.Current"/> Toplevel.</summary>
+    /// <summary>Invoked when the Toplevel<see cref="RunState"/> ceases to be active.</summary>
     public event EventHandler<ToplevelEventArgs>? Deactivate;
 
     /// <summary>Invoked when the Toplevel's <see cref="RunState"/> is closed by <see cref="Application.End(RunState)"/>.</summary>
@@ -175,55 +152,7 @@ public partial class Toplevel : View
     /// </summary>
     public virtual void RequestStop ()
     {
-        if (IsOverlappedContainer
-            && Running
-            && (Application.Current == this
-                || Application.Current?.Modal == false
-                || (Application.Current?.Modal == true && Application.Current?.Running == false)))
-        {
-            foreach (Toplevel child in ApplicationOverlapped.OverlappedChildren!)
-            {
-                var ev = new ToplevelClosingEventArgs (this);
-
-                if (child.OnClosing (ev))
-                {
-                    return;
-                }
-
-                child.Running = false;
-                Application.RequestStop (child);
-            }
-
-            Running = false;
-            Application.RequestStop (this);
-        }
-        else if (IsOverlappedContainer && Running && Application.Current?.Modal == true && Application.Current?.Running == true)
-        {
-            var ev = new ToplevelClosingEventArgs (Application.Current);
-
-            if (OnClosing (ev))
-            {
-                return;
-            }
-
-            Application.RequestStop (Application.Current);
-        }
-        else if (!IsOverlappedContainer && Running && (!Modal || (Modal && Application.Current != this)))
-        {
-            var ev = new ToplevelClosingEventArgs (this);
-
-            if (OnClosing (ev))
-            {
-                return;
-            }
-
-            Running = false;
-            Application.RequestStop (this);
-        }
-        else
-        {
-            Application.RequestStop (Application.Current);
-        }
+        Application.RequestStop (Application.Top);
     }
 
     /// <summary>
@@ -234,28 +163,6 @@ public partial class Toplevel : View
 
     internal virtual void OnActivate (Toplevel deactivated) { Activate?.Invoke (this, new (deactivated)); }
 
-    /// <summary>
-    ///     Stops and closes the <see cref="Toplevel"/> specified by <paramref name="top"/>. If <paramref name="top"/> is
-    ///     the top-most Toplevel, <see cref="Application.RequestStop(Toplevel)"/> will be called, causing the application to
-    ///     exit.
-    /// </summary>
-    /// <param name="top">The Toplevel to request stop.</param>
-    public virtual void RequestStop (Toplevel top) { top.RequestStop (); }
-
-    internal virtual void OnAllChildClosed () { AllChildClosed?.Invoke (this, EventArgs.Empty); }
-
-    internal virtual void OnChildClosed (Toplevel top)
-    {
-        if (IsOverlappedContainer)
-        {
-            SetSubViewNeedsDisplay ();
-        }
-
-        ChildClosed?.Invoke (this, new (top));
-    }
-
-    internal virtual void OnChildLoaded (Toplevel top) { ChildLoaded?.Invoke (this, new (top)); }
-    internal virtual void OnChildUnloaded (Toplevel top) { ChildUnloaded?.Invoke (this, new (top)); }
     internal virtual void OnClosed (Toplevel top) { Closed?.Invoke (this, new (top)); }
 
     internal virtual bool OnClosing (ToplevelClosingEventArgs ev)
@@ -295,92 +202,11 @@ public partial class Toplevel : View
     }
 
     #endregion
-
-    #region Draw
-
-    /// <inheritdoc/>
-    public override void OnDrawContent (Rectangle viewport)
-    {
-        if (!Visible)
-        {
-            return;
-        }
-
-        if (NeedsDisplay || SubViewNeedsDisplay /*|| LayoutNeeded*/)
-        {
-            Clear ();
-
-            //LayoutSubviews ();
-            PositionToplevels ();
-
-            if (this == ApplicationOverlapped.OverlappedTop)
-            {
-                // This enables correct draw behavior when switching between overlapped subviews
-                foreach (Toplevel top in ApplicationOverlapped.OverlappedChildren!.AsEnumerable ().Reverse ())
-                {
-                    if (top.Frame.IntersectsWith (Viewport))
-                    {
-                        if (top != this && !top.IsCurrentTop && !OutsideTopFrame (top) && top.Visible)
-                        {
-                            top.SetNeedsLayout ();
-                            top.SetNeedsDisplay (top.Viewport);
-                            top.Draw ();
-                            top.OnRenderLineCanvas ();
-                        }
-                    }
-                }
-            }
-
-            // BUGBUG: This appears to be a hack to get ScrollBarViews to render correctly.
-            foreach (View view in Subviews)
-            {
-                if (view.Frame.IntersectsWith (Viewport) && !OutsideTopFrame (this))
-                {
-                    //view.SetNeedsLayout ();
-                    view.SetNeedsDisplay ();
-                    view.SetSubViewNeedsDisplay ();
-                }
-            }
-
-            base.OnDrawContent (viewport);
-        }
-    }
-
-    #endregion
-
+    
     #region Size / Position Management
 
     // TODO: Make cancelable?
-    internal virtual void OnSizeChanging (SizeChangedEventArgs size) { SizeChanging?.Invoke (this, size); }
-
-    /// <inheritdoc/>
-    public override Point? PositionCursor ()
-    {
-        if (!IsOverlappedContainer)
-        {
-            return null;
-        }
-
-        // This code path only happens when the Toplevel is an Overlapped container
-
-        if (Focused is null)
-        {
-            // TODO: this is an Overlapped hack
-            foreach (Toplevel top in ApplicationOverlapped.OverlappedChildren!)
-            {
-                if (top != this && top.Visible)
-                {
-                    top.SetFocus ();
-
-                    return null;
-                }
-            }
-        }
-
-        Point? cursor2 = base.PositionCursor ();
-
-        return null;
-    }
+    internal void OnSizeChanging (SizeChangedEventArgs size) { SizeChanging?.Invoke (this, size); }
 
     /// <summary>
     ///     Adjusts the location and size of <paramref name="top"/> within this Toplevel. Virtual method enabling
@@ -416,8 +242,10 @@ public partial class Toplevel : View
             maxWidth -= superView.GetAdornmentsThickness ().Left + superView.GetAdornmentsThickness ().Right;
         }
 
-        if ((superView != top || top?.SuperView is { } || (top != Application.Top && top!.Modal) || (top?.SuperView is null && ApplicationOverlapped.IsOverlapped (top)))
+        // BUGBUG: The && true is a temp hack
+        if ((superView != top || top?.SuperView is { } || (top != Application.Top && top!.Modal) || (top == Application.Top && top?.SuperView is null))
             && (top!.Frame.X + top.Frame.Width > maxWidth || ny > top.Frame.Y))
+
         {
             if (top?.X is null or PosAbsolute && top?.Frame.X != nx)
             {
@@ -457,30 +285,6 @@ public partial class Toplevel : View
     /// <summary>Invoked when the terminal has been resized. The new <see cref="Size"/> of the terminal is provided.</summary>
     public event EventHandler<SizeChangedEventArgs>? SizeChanging;
 
-    private bool OutsideTopFrame (Toplevel top)
-    {
-        if (top.Frame.X > Driver.Cols || top.Frame.Y > Driver.Rows)
-        {
-            return true;
-        }
-
-        return false;
-    }
-
-    // TODO: v2 - Not sure this is needed anymore.
-    internal void PositionToplevels ()
-    {
-        PositionToplevel (this);
-
-        foreach (View top in Subviews)
-        {
-            if (top is Toplevel)
-            {
-                PositionToplevel ((Toplevel)top);
-            }
-        }
-    }
-
     #endregion
 }
 
@@ -538,42 +342,3 @@ public class ToplevelEqualityComparer : IEqualityComparer<Toplevel>
         return hCode.GetHashCode ();
     }
 }
-
-/// <summary>
-///     Implements the <see cref="IComparer{T}"/> to sort the <see cref="Toplevel"/> from the
-///     <see cref="ApplicationOverlapped.OverlappedChildren"/> if needed.
-/// </summary>
-public sealed class ToplevelComparer : IComparer<Toplevel>
-{
-    /// <summary>
-    ///     Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the
-    ///     other.
-    /// </summary>
-    /// <param name="x">The first object to compare.</param>
-    /// <param name="y">The second object to compare.</param>
-    /// <returns>
-    ///     A signed integer that indicates the relative values of <paramref name="x"/> and <paramref name="y"/>, as shown
-    ///     in the following table.Value Meaning Less than zero <paramref name="x"/> is less than <paramref name="y"/>.Zero
-    ///     <paramref name="x"/> equals <paramref name="y"/> .Greater than zero <paramref name="x"/> is greater than
-    ///     <paramref name="y"/>.
-    /// </returns>
-    public int Compare (Toplevel? x, Toplevel? y)
-    {
-        if (ReferenceEquals (x, y))
-        {
-            return 0;
-        }
-
-        if (x is null)
-        {
-            return -1;
-        }
-
-        if (y is null)
-        {
-            return 1;
-        }
-
-        return string.CompareOrdinal (x.Id, y.Id);
-    }
-}

+ 0 - 8
Terminal.Gui/Views/ToplevelOverlapped.cs

@@ -1,8 +0,0 @@
-namespace Terminal.Gui;
-
-public partial class Toplevel
-{
-    /// <summary>Gets or sets if this Toplevel is a container for overlapped children.</summary>
-    public bool IsOverlappedContainer { get; set; }
-}
-

+ 23 - 14
Terminal.Gui/Views/TreeView/TreeView.cs

@@ -163,7 +163,7 @@ public class TreeView<T> : View, ITreeView where T : class
                    );
 
         AddCommand (
-                    Command.LineUp,
+                    Command.Up,
                     () =>
                     {
                         AdjustSelection (-1);
@@ -173,7 +173,7 @@ public class TreeView<T> : View, ITreeView where T : class
                    );
 
         AddCommand (
-                    Command.LineUpExtend,
+                    Command.UpExtend,
                     () =>
                     {
                         AdjustSelection (-1, true);
@@ -193,7 +193,7 @@ public class TreeView<T> : View, ITreeView where T : class
                    );
 
         AddCommand (
-                    Command.LineDown,
+                    Command.Down,
                     () =>
                     {
                         AdjustSelection (1);
@@ -203,7 +203,7 @@ public class TreeView<T> : View, ITreeView where T : class
                    );
 
         AddCommand (
-                    Command.LineDownExtend,
+                    Command.DownExtend,
                     () =>
                     {
                         AdjustSelection (1, true);
@@ -223,7 +223,7 @@ public class TreeView<T> : View, ITreeView where T : class
                    );
 
         AddCommand (
-                    Command.TopHome,
+                    Command.Start,
                     () =>
                     {
                         GoToFirst ();
@@ -233,7 +233,7 @@ public class TreeView<T> : View, ITreeView where T : class
                    );
 
         AddCommand (
-                    Command.BottomEnd,
+                    Command.End,
                     () =>
                     {
                         GoToEnd ();
@@ -285,16 +285,16 @@ public class TreeView<T> : View, ITreeView where T : class
         KeyBindings.Add (Key.CursorLeft, Command.Collapse);
         KeyBindings.Add (Key.CursorLeft.WithCtrl, Command.CollapseAll);
 
-        KeyBindings.Add (Key.CursorUp, Command.LineUp);
-        KeyBindings.Add (Key.CursorUp.WithShift, Command.LineUpExtend);
+        KeyBindings.Add (Key.CursorUp, Command.Up);
+        KeyBindings.Add (Key.CursorUp.WithShift, Command.UpExtend);
         KeyBindings.Add (Key.CursorUp.WithCtrl, Command.LineUpToFirstBranch);
 
-        KeyBindings.Add (Key.CursorDown, Command.LineDown);
-        KeyBindings.Add (Key.CursorDown.WithShift, Command.LineDownExtend);
+        KeyBindings.Add (Key.CursorDown, Command.Down);
+        KeyBindings.Add (Key.CursorDown.WithShift, Command.DownExtend);
         KeyBindings.Add (Key.CursorDown.WithCtrl, Command.LineDownToLastBranch);
 
-        KeyBindings.Add (Key.Home, Command.TopHome);
-        KeyBindings.Add (Key.End, Command.BottomEnd);
+        KeyBindings.Add (Key.Home, Command.Start);
+        KeyBindings.Add (Key.End, Command.End);
         KeyBindings.Add (Key.A.WithCtrl, Command.SelectAll);
         KeyBindings.Add (ObjectActivationKey, Command.Select);
     }
@@ -356,6 +356,7 @@ public class TreeView<T> : View, ITreeView where T : class
             {
                 KeyBindings.ReplaceKey (ObjectActivationKey, value);
                 objectActivationKey = value;
+                SetNeedsDisplay ();
             }
         }
     }
@@ -371,7 +372,11 @@ public class TreeView<T> : View, ITreeView where T : class
     public int ScrollOffsetHorizontal
     {
         get => scrollOffsetHorizontal;
-        set => scrollOffsetHorizontal = Math.Max (0, value);
+        set
+        {
+            scrollOffsetHorizontal = Math.Max (0, value);
+            SetNeedsDisplay ();
+        }
     }
 
     /// <summary>The amount of tree view that has been scrolled off the top of the screen (by the user scrolling down).</summary>
@@ -382,7 +387,11 @@ public class TreeView<T> : View, ITreeView where T : class
     public int ScrollOffsetVertical
     {
         get => scrollOffsetVertical;
-        set => scrollOffsetVertical = Math.Max (0, value);
+        set
+        {
+            scrollOffsetVertical = Math.Max (0, value);
+            SetNeedsDisplay ();
+        }
     }
 
     /// <summary>

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

@@ -27,7 +27,7 @@ public class Window : Toplevel
     {
         CanFocus = true;
         TabStop = TabBehavior.TabGroup;
-        Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped;
+        Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped | ViewArrangement.Resizable;
         ColorScheme = Colors.ColorSchemes ["Base"]; // TODO: make this a theme property
         BorderStyle = DefaultBorderStyle;
         ShadowStyle = DefaultShadow;

+ 16 - 10
Terminal.Gui/Views/Wizard/Wizard.cs

@@ -71,6 +71,7 @@ public class Wizard : Dialog
         BorderStyle = LineStyle.Double;
 
         BackButton = new () { Text = Strings.wzBack };
+
         NextFinishButton = new ()
         {
             Text = Strings.wzFinish,
@@ -91,12 +92,6 @@ public class Wizard : Dialog
         Closing += Wizard_Closing;
         TitleChanged += Wizard_TitleChanged;
 
-        if (Modal)
-        {
-            KeyBindings.Clear (Command.QuitToplevel);
-            KeyBindings.Add (Key.Esc, Command.QuitToplevel);
-        }
-
         SetNeedsLayout ();
     }
 
@@ -127,7 +122,8 @@ public class Wizard : Dialog
     ///             <description>Add the Wizard to a containing view with <see cref="View.Add(View)"/>.</description>
     ///         </item>
     ///     </list>
-    ///     If a non-Modal Wizard is added to the application after <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> has
+    ///     If a non-Modal Wizard is added to the application after
+    ///     <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> has
     ///     been called the first step must be explicitly set by setting <see cref="CurrentStep"/> to
     ///     <see cref="GetNextStep()"/>:
     ///     <code>
@@ -353,7 +349,6 @@ public class Wizard : Dialog
 
         UpdateButtonsAndTitle ();
 
-
         // Set focus on the contentview
         if (newStep is { })
         {
@@ -497,14 +492,24 @@ public class Wizard : Dialog
             // If we're modal, then we expand the WizardStep so that the top and side 
             // borders and not visible. The bottom border is the separator above the buttons.
             step.X = step.Y = 0;
-            step.Height = Dim.Fill (Dim.Func (() => IsInitialized ? Subviews.First (view => view.Y.Has<PosAnchorEnd> (out _)).Frame.Height + 1 : 1)); // for button frame (+1 for lineView)
+
+            step.Height = Dim.Fill (
+                                    Dim.Func (
+                                              () => IsInitialized
+                                                        ? Subviews.First (view => view.Y.Has<PosAnchorEnd> (out _)).Frame.Height + 1
+                                                        : 1)); // for button frame (+1 for lineView)
             step.Width = Dim.Fill ();
         }
         else
         {
             // If we're not a modal, then we show the border around the WizardStep
             step.X = step.Y = 0;
-            step.Height = Dim.Fill (Dim.Func (() => IsInitialized ? Subviews.First (view => view.Y.Has<PosAnchorEnd> (out _)).Frame.Height + 1 : 2)); // for button frame (+1 for lineView)
+
+            step.Height = Dim.Fill (
+                                    Dim.Func (
+                                              () => IsInitialized
+                                                        ? Subviews.First (view => view.Y.Has<PosAnchorEnd> (out _)).Frame.Height + 1
+                                                        : 2)); // for button frame (+1 for lineView)
             step.Width = Dim.Fill ();
         }
     }
@@ -542,6 +547,7 @@ public class Wizard : Dialog
 
         SetNeedsLayout ();
         LayoutSubviews ();
+
         //Draw ();
     }
 

+ 2 - 2
UICatalog/Resources/config.json

@@ -1,5 +1,5 @@
 {
-  "$schema": "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json",
+  "$schema": "https://gui-cs.github.io/Terminal.GuiV2Docs/schemas/tui-config-schema.json",
   "FileDialog.MaxSearchResults": 10000,
   "FileDialogStyle.DefaultUseColors": false,
   "FileDialogStyle.DefaultUseUnicodeCharacters": false,
@@ -75,7 +75,7 @@
               },
               "HotFocus": {
                 "Foreground": "#FFFF00",
-                "Background": "White"
+                "Background": "Black"
               },
               "Disabled": {
                 "Foreground": "BrightGreen",

+ 36 - 37
UICatalog/Scenario.cs

@@ -32,16 +32,10 @@ namespace UICatalog;
 ///             </item>
 ///             <item>
 ///                 <description>
-///                     Implement the <see cref="Setup"/> override which will be called when a user selects the
+///                     Implement the <see cref="Main"/> override which will be called when a user selects the
 ///                     scenario to run.
 ///                 </description>
 ///             </item>
-///             <item>
-///                 <description>
-///                     Optionally, implement the <see cref="Init()"/> and/or <see cref="Run"/> overrides to
-///                     provide a custom implementation.
-///                 </description>
-///             </item>
 ///         </list>
 ///     </para>
 ///     <para>
@@ -54,22 +48,36 @@ namespace UICatalog;
 ///     <code>
 /// using Terminal.Gui;
 /// 
-/// namespace UICatalog {
-/// 	[ScenarioMetadata (Name: "Generic", Description: "Generic sample - A template for creating new Scenarios")]
-/// 	[ScenarioCategory ("Controls")]
-/// 	class MyScenario : Scenario {
-/// 		public override void Setup ()
-/// 		{
-/// 			// Put your scenario code here, e.g.
-/// 			Win.Add (new Button () { Text = "Press me!", 
-/// 				X = Pos.Center (),
-/// 				Y = Pos.Center (),
-/// 				Clicked = () => MessageBox.Query (20, 7, "Hi", "Neat?", "Yes", "No")
-/// 			});
-/// 		}
-/// 	}
+/// namespace UICatalog.Scenarios;
+/// 
+/// [ScenarioMetadata ("Generic", "Generic sample - A template for creating new Scenarios")]
+/// [ScenarioCategory ("Controls")]
+/// public sealed class MyScenario : Scenario
+/// {
+///     public override void Main ()
+///     {
+///         // Init
+///         Application.Init ();
+/// 
+///         // Setup - Create a top-level application window and configure it.
+///         Window appWindow = new ()
+///         {
+///             Title = GetQuitKeyAndName (),
+///         };
+/// 
+///         var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Press me!" };
+///         button.Accept += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "Ok");
+///         appWindow.Add (button);
+/// 
+///         // Run - Start the application.
+///         Application.Run (appWindow);
+///         appWindow.Dispose ();
+/// 
+///         // Shutdown - Calling Application.Shutdown is required.
+///         Application.Shutdown ();
+///     }
 /// }
-/// </code>
+///  </code>
 /// </example>
 public class Scenario : IDisposable
 {
@@ -93,7 +101,10 @@ public class Scenario : IDisposable
     /// <returns></returns>
     public string GetName () { return ScenarioMetadata.GetName (GetType ()); }
 
-    /// <summary>Helper to get the <see cref="Application.QuitKey"/> and the <see cref="Scenario"/> Name (defined in <see cref="ScenarioMetadata"/>)</summary>
+    /// <summary>
+    ///     Helper to get the <see cref="Application.QuitKey"/> and the <see cref="Scenario"/> Name (defined in
+    ///     <see cref="ScenarioMetadata"/>)
+    /// </summary>
     /// <returns></returns>
     public string GetQuitKeyAndName () { return $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"; }
 
@@ -125,18 +136,7 @@ public class Scenario : IDisposable
     ///     Called by UI Catalog to run the <see cref="Scenario"/>. This is the main entry point for the <see cref="Scenario"/>
     ///     .
     /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         Scenario developers are encouraged to override this method as the primary way of authoring a new
-    ///         scenario.
-    ///     </para>
-    ///     <para>
-    ///         The base implementation calls <see cref="Init"/>, <see cref="Setup"/>, and <see cref="Run"/>.
-    ///     </para>
-    /// </remarks>
-    public virtual void Main ()
-    {
-    }
+    public virtual void Main () { }
 
     /// <summary>Gets the Scenario Name + Description with the Description padded based on the longest known Scenario name.</summary>
     /// <returns></returns>
@@ -156,8 +156,7 @@ public class Scenario : IDisposable
         if (!_disposedValue)
         {
             if (disposing)
-            {
-            }
+            { }
 
             _disposedValue = true;
         }

+ 2 - 2
UICatalog/Scenarios/AdornmentEditor.cs

@@ -61,8 +61,8 @@ public class AdornmentEditor : View
                 _adornment.Initialized += (sender, args) =>
                                           {
                                               var cs = _adornment.ColorScheme;
-                                              _foregroundColorPicker.SelectedColor = cs.Normal.Foreground.GetClosestNamedColor ();
-                                              _backgroundColorPicker.SelectedColor = cs.Normal.Background.GetClosestNamedColor ();
+                                              _foregroundColorPicker.SelectedColor = cs.Normal.Foreground.GetClosestNamedColor16 ();
+                                              _backgroundColorPicker.SelectedColor = cs.Normal.Background.GetClosestNamedColor16 ();
 
                                           };
             }

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