Browse Source

Rebased onto v2_2491-Overlapped

Tig 10 tháng trước cách đây
mục cha
commit
bff7794495
100 tập tin đã thay đổi với 3739 bổ sung2111 xóa
  1. 1 1
      Terminal.Gui/Application/Application.Driver.cs
  2. 1 1
      Terminal.Gui/Application/Application.Initialization.cs
  3. 143 85
      Terminal.Gui/Application/Application.Mouse.cs
  4. 1 1
      Terminal.Gui/Application/Application.cs
  5. 8 4
      Terminal.Gui/Configuration/ColorJsonConverter.cs
  6. 2 1
      Terminal.Gui/Configuration/SourceGenerationContext.cs
  7. 39 39
      Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
  8. 3 3
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
  9. 2 2
      Terminal.Gui/ConsoleDrivers/NetDriver.cs
  10. 1 1
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  11. 6 6
      Terminal.Gui/Drawing/Attribute.cs
  12. 47 44
      Terminal.Gui/Drawing/Color.ColorExtensions.cs
  13. 2 2
      Terminal.Gui/Drawing/Color.ColorName.cs
  14. 180 177
      Terminal.Gui/Drawing/Color.Formatting.cs
  15. 3 3
      Terminal.Gui/Drawing/Color.Operators.cs
  16. 40 38
      Terminal.Gui/Drawing/Color.cs
  17. 43 55
      Terminal.Gui/Drawing/ColorScheme.cs
  18. 30 27
      Terminal.Gui/Drawing/ColorStrings.cs
  19. 1 1
      Terminal.Gui/Input/GrabMouseEventArgs.cs
  20. 63 0
      Terminal.Gui/Resources/Strings.Designer.cs
  21. 21 0
      Terminal.Gui/Resources/Strings.resx
  22. 325 36
      Terminal.Gui/Resources/config.json
  23. 29 31
      Terminal.Gui/View/Adornment/Adornment.cs
  24. 5 22
      Terminal.Gui/View/Adornment/Border.cs
  25. 5 2
      Terminal.Gui/View/Adornment/Margin.cs
  26. 9 8
      Terminal.Gui/View/HighlightStyle.cs
  27. 2 3
      Terminal.Gui/View/View.Diagnostics.cs
  28. 43 9
      Terminal.Gui/View/View.Drawing.cs
  29. 1 76
      Terminal.Gui/View/View.Layout.cs
  30. 362 225
      Terminal.Gui/View/View.Mouse.cs
  31. 8 5
      Terminal.Gui/Views/Button.cs
  32. 7 1
      Terminal.Gui/Views/CheckBox.cs
  33. 5 5
      Terminal.Gui/Views/ColorPicker16.cs
  34. 1 0
      Terminal.Gui/Views/Menuv2.cs
  35. 2 2
      Terminal.Gui/Views/NumericUpDown.cs
  36. 4 4
      Terminal.Gui/Views/ProgressBar.cs
  37. 2 2
      Terminal.Gui/Views/RadioGroup.cs
  38. 4 2
      Terminal.Gui/Views/ScrollView.cs
  39. 15 33
      Terminal.Gui/Views/Shortcut.cs
  40. 2 0
      Terminal.Gui/Views/Slider.cs
  41. 1 8
      Terminal.Gui/Views/TextField.cs
  42. 1 8
      Terminal.Gui/Views/TextView.cs
  43. 1 1
      UICatalog/Resources/config.json
  44. 2 2
      UICatalog/Scenarios/AdornmentEditor.cs
  45. 24 8
      UICatalog/Scenarios/Bars.cs
  46. 0 116
      UICatalog/Scenarios/BasicColors.cs
  47. 4 0
      UICatalog/Scenarios/CharacterMap.cs
  48. 2 2
      UICatalog/Scenarios/ColorPicker.cs
  49. 2 2
      UICatalog/Scenarios/GraphViewExample.cs
  50. 3 3
      UICatalog/Scenarios/InvertColors.cs
  51. 140 44
      UICatalog/Scenarios/Mouse.cs
  52. 7 6
      UICatalog/Scenarios/Shortcuts.cs
  53. 1 1
      UICatalog/Scenarios/Sliders.cs
  54. 6 6
      UICatalog/Scenarios/TextEffectsScenario.cs
  55. 10 22
      UICatalog/UICatalog.cs
  56. 10 3
      UnitTests/Application/ApplicationTests.cs
  57. 481 0
      UnitTests/Application/Mouse/ApplicationMouseEnterLeaveTests.cs
  58. 15 12
      UnitTests/Application/Mouse/ApplicationMouseTests.cs
  59. 1 1
      UnitTests/Configuration/AppScopeTests.cs
  60. 15 2
      UnitTests/Configuration/ConfigurationMangerTests.cs
  61. 25 25
      UnitTests/Configuration/JsonConverterTests.cs
  62. 4 0
      UnitTests/Configuration/SerializableConfigurationPropertyTests.cs
  63. 6 2
      UnitTests/Configuration/SettingsScopeTests.cs
  64. 4 1
      UnitTests/Configuration/ThemeScopeTests.cs
  65. 2 1
      UnitTests/Configuration/ThemeTests.cs
  66. 33 1
      UnitTests/Dialogs/DialogTests.cs
  67. 10 1
      UnitTests/Dialogs/MessageBoxTests.cs
  68. 7 7
      UnitTests/Drawing/AttributeTests.cs
  69. 20 20
      UnitTests/Drawing/ColorTests.Constructors.cs
  70. 20 20
      UnitTests/Drawing/ColorTests.Operators.cs
  71. 2 2
      UnitTests/Drawing/ColorTests.ParsingAndFormatting.cs
  72. 20 20
      UnitTests/Drawing/ColorTests.TypeChecks.cs
  73. 18 18
      UnitTests/Drawing/ColorTests.cs
  74. 6 0
      UnitTests/FileServices/FileDialogTests.cs
  75. 0 2
      UnitTests/Input/ResponderTests.cs
  76. 11 1
      UnitTests/Resources/ResourceManagerTests.cs
  77. 28 77
      UnitTests/TestHelpers.cs
  78. 12 10
      UnitTests/Text/TextFormatterTests.cs
  79. 14 11
      UnitTests/View/Adornment/AdornmentSubViewTests.cs
  80. 4 4
      UnitTests/View/Adornment/BorderTests.cs
  81. 2 2
      UnitTests/View/Adornment/MarginTests.cs
  82. 1 1
      UnitTests/View/Adornment/PaddingTests.cs
  83. 0 536
      UnitTests/View/FindDeepestViewTests.cs
  84. 3 0
      UnitTests/View/Layout/Dim.Tests.cs
  85. 3 0
      UnitTests/View/Layout/Pos.AnchorEndTests.cs
  86. 9 6
      UnitTests/View/Layout/Pos.CombineTests.cs
  87. 74 71
      UnitTests/View/Layout/ToScreenTests.cs
  88. 805 0
      UnitTests/View/Mouse/GetViewsUnderMouseTests.cs
  89. 349 0
      UnitTests/View/Mouse/MouseEnterLeaveTests.cs
  90. 6 2
      UnitTests/View/Mouse/MouseTests.cs
  91. 1 3
      UnitTests/View/ViewTests.cs
  92. 10 34
      UnitTests/Views/ButtonTests.cs
  93. 13 13
      UnitTests/Views/ColorPicker16Tests.cs
  94. 1 1
      UnitTests/Views/ComboBoxTests.cs
  95. 4 4
      UnitTests/Views/ContextMenuTests.cs
  96. 9 4
      UnitTests/Views/MenuBarTests.cs
  97. 0 5
      UnitTests/Views/ProgressBarTests.cs
  98. 1 1
      UnitTests/Views/RuneCellTests.cs
  99. 7 0
      UnitTests/Views/ScrollBarViewTests.cs
  100. 1 1
      UnitTests/Views/ScrollViewTests.cs

+ 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))]

+ 1 - 1
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 ();

+ 143 - 85
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 (Top, 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,65 +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
-                                          };
+
+        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);
 
@@ -226,74 +192,166 @@ 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;
-        }
+        RaiseMouseEnterLeaveEvents (me.ScreenPosition, currentViewsUnderMouse);
 
-        WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
+        WantContinuousButtonPressedView = deepestViewUnderMouse.WantContinuousButtonPressed ? deepestViewUnderMouse : null;
 
         //Debug.WriteLine ($"OnMouseEvent: ({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags}");
+        if (deepestViewUnderMouse.Id == "mouseDemo")
+        {
+
+        }
 
-        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
             };
         }
     }
 
-    #endregion Mouse handling
+    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;
+    }
+
+    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;
+            }
+        }
+    }
 }

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

@@ -197,7 +197,7 @@ public static partial class Application
         IsInitialized = false;
 
         // Mouse
-        MouseEnteredView = null;
+        _cachedViewsUnderMouse.Clear ();
         WantContinuousButtonPressedView = null;
         MouseEvent = null;
         GrabbedMouse = null;

+ 8 - 4
Terminal.Gui/Configuration/ColorJsonConverter.cs

@@ -1,5 +1,6 @@
 using System.Text.Json;
 using System.Text.Json.Serialization;
+using ColorHelper;
 
 namespace Terminal.Gui;
 
@@ -15,7 +16,7 @@ internal class ColorJsonConverter : JsonConverter<Color>
         {
             if (_instance is null)
             {
-                _instance = new ColorJsonConverter ();
+                _instance = new ();
             }
 
             return _instance;
@@ -31,10 +32,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 +49,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 ());
+    }
 }

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

@@ -15,8 +15,9 @@ 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>))]
 internal partial class SourceGenerationContext : JsonSerializerContext

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

+ 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 of the <see cref="GetClosestNamedColor16(Terminal.Gui.Color)"/> 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 - 38
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,7 +222,7 @@ 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>
@@ -229,9 +231,9 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
     /// </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]
@@ -244,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)
@@ -282,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;
+        }
     }
 }

+ 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>

+ 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>

+ 325 - 36
Terminal.Gui/Resources/config.json

@@ -31,34 +31,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
               }
             }
           },
@@ -69,8 +70,8 @@
                 "Background": "Blue"
               },
               "Focus": {
-                "Foreground": "Black",
-                "Background": "Gray"
+                "Foreground": "DarkBlue",
+                "Background": "LightGray"
               },
               "HotNormal": {
                 "Foreground": "BrightCyan",
@@ -78,7 +79,7 @@
               },
               "HotFocus": {
                 "Foreground": "BrightBlue",
-                "Background": "Gray"
+                "Background": "LightGray"
               },
               "Disabled": {
                 "Foreground": "DarkGray",
@@ -90,19 +91,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",
@@ -114,19 +115,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",
@@ -138,18 +139,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": {
@@ -163,6 +164,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": {
@@ -239,16 +249,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",
@@ -289,6 +299,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": {
@@ -317,7 +336,7 @@
           {
             "Base": {
               "Normal": {
-                "Foreground": "DarkGray",
+                "Foreground": "#505050", // DarkerGray
                 "Background": "White"
               },
               "Focus": {
@@ -366,19 +385,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",
@@ -412,6 +431,276 @@
           }
         ]
       }
+    },
+    {
+      "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"
+              }
+            }
+          }
+        ]
+      }
     }
+
+
+
   ]
 }

+ 29 - 31
Terminal.Gui/View/Adornment/Adornment.cs

@@ -1,4 +1,5 @@
 #nullable enable
+using System.ComponentModel;
 using Terminal.Gui;
 using Attribute = Terminal.Gui.Attribute;
 
@@ -228,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
 }

+ 5 - 22
Terminal.Gui/View/Adornment/Border.cs

@@ -78,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
@@ -251,16 +243,7 @@ public class Border : Adornment
             };
             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)
         {
@@ -315,7 +298,7 @@ public class Border : Adornment
                 _dragPosition = mouseEvent.Position;
                 Application.GrabMouse (this);
 
-                SetHighlight (HighlightStyle);
+                SetPressedHighlight (HighlightStyle);
 
                 // Arrange Mode -
                 // TODO: This code can be refactored to be more readable and maintainable.
@@ -485,8 +468,8 @@ public class Border : Adornment
                                                            out _
                                                           );
 
-                        Parent.X = nx;
-                        Parent.Y = ny;
+                        Parent.X = parentLoc.X - _startGrabPoint.X;
+                        Parent.Y = parentLoc.Y - _startGrabPoint.Y;
 
                         break;
 
@@ -593,7 +576,7 @@ public class Border : Adornment
         {
             _dragPosition = null;
             Application.UngrabMouse ();
-            SetHighlight (HighlightStyle.None);
+            SetPressedHighlight (HighlightStyle.None);
 
             EndArrangeMode ();
 

+ 5 - 2
Terminal.Gui/View/Adornment/Margin.cs

@@ -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 { })

+ 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
 }

+ 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

+ 43 - 9
Terminal.Gui/View/View.Drawing.cs

@@ -334,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
                           );
         }
     }
@@ -366,7 +365,26 @@ public partial class View // Drawing APIs
             cs = new ();
         }
 
-        return Enabled ? cs.Focus : cs.Disabled;
+        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 ? GetColor (cs.HotFocus) : cs.Disabled;
     }
 
     /// <summary>Determines the current <see cref="ColorScheme"/> based on the <see cref="Enabled"/> value.</summary>
@@ -384,7 +402,7 @@ public partial class View // Drawing APIs
             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>
@@ -402,7 +420,23 @@ public partial class View // Drawing APIs
             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>
@@ -520,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 ();

+ 1 - 76
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.

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

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

+ 5 - 5
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;
             }

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

@@ -54,6 +54,7 @@ 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

+ 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;

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

@@ -265,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 ()

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

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

+ 4 - 2
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>
@@ -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);
 

+ 2 - 0
Terminal.Gui/Views/Slider.cs

@@ -1380,6 +1380,8 @@ public class Slider<T> : View, IOrientation
             SetNeedsDisplay ();
 
             mouseEvent.Handled = true;
+
+            // BUGBUG: OnMouseClick is/should be internal. 
             return OnMouseClick (new (mouseEvent));
         }
 

+ 1 - 8
Terminal.Gui/Views/TextField.cs

@@ -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>

+ 1 - 8
Terminal.Gui/Views/TextView.cs

@@ -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>

+ 1 - 1
UICatalog/Resources/config.json

@@ -75,7 +75,7 @@
               },
               "HotFocus": {
                 "Foreground": "#FFFF00",
-                "Background": "White"
+                "Background": "Black"
               },
               "Disabled": {
                 "Foreground": "BrightGreen",

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

+ 24 - 8
UICatalog/Scenarios/Bars.cs

@@ -117,7 +117,7 @@ public class Bars : Scenario
             //Width = Dim.Percent (40),
             Orientation = Orientation.Vertical,
         };
-            ConfigureMenu (bar);
+        ConfigureMenu (bar);
 
         menuLikeExamples.Add (bar);
 
@@ -415,20 +415,23 @@ public class Bars : Scenario
             Title = "_File",
             HelpText = "File Menu",
             Key = Key.D0.WithAlt,
+            HighlightStyle = HighlightStyle.Hover
         };
 
         var editMenuBarItem = new Shortcut
         {
             Title = "_Edit",
             HelpText = "Edit Menu",
-            Key = Key.D1.WithAlt
+            Key = Key.D1.WithAlt,
+            HighlightStyle = HighlightStyle.Hover
         };
 
         var helpMenuBarItem = new Shortcut
         {
             Title = "_Help",
             HelpText = "Halp Menu",
-            Key = Key.D2.WithAlt
+            Key = Key.D2.WithAlt,
+            HighlightStyle = HighlightStyle.Hover
         };
 
         bar.Add (fileMenuBarItem, editMenuBarItem, helpMenuBarItem);
@@ -442,6 +445,7 @@ public class Bars : Scenario
             Title = "Z_igzag",
             Key = Key.I.WithCtrl,
             Text = "Gonna zig zag",
+            HighlightStyle = HighlightStyle.Hover
         };
 
         var shortcut2 = new Shortcut
@@ -449,6 +453,15 @@ public class Bars : Scenario
             Title = "Za_G",
             Text = "Gonna zag",
             Key = Key.G.WithAlt,
+            HighlightStyle = HighlightStyle.Hover
+        };
+
+        var shortcut3 = new Shortcut
+        {
+            Title = "_Three",
+            Text = "The 3rd item",
+            Key = Key.D3.WithAlt,
+            HighlightStyle = HighlightStyle.Hover
         };
 
         var line = new Line ()
@@ -457,15 +470,18 @@ public class Bars : Scenario
             Orientation = Orientation.Horizontal,
             CanFocus = false,
         };
+        // HACK: Bug in Line
+        line.Orientation = Orientation.Vertical;
+        line.Orientation = Orientation.Horizontal;
 
-        var shortcut3 = new Shortcut
+        var shortcut4 = new Shortcut
         {
-            Title = "_Three",
-            Text = "The 3rd item",
+            Title = "_Four",
+            Text = "Below the line",
             Key = Key.D3.WithAlt,
+            HighlightStyle = HighlightStyle.Hover
         };
-
-        bar.Add (shortcut1, shortcut2, line, shortcut3);
+        bar.Add (shortcut1, shortcut2, shortcut3, line, shortcut4);
     }
 
     public void ConfigStatusBar (Bar bar)

+ 0 - 116
UICatalog/Scenarios/BasicColors.cs

@@ -1,116 +0,0 @@
-using System;
-using Terminal.Gui;
-
-namespace UICatalog.Scenarios;
-
-[ScenarioMetadata ("Basic Colors", "Show all basic colors.")]
-[ScenarioCategory ("Colors")]
-[ScenarioCategory ("Text and Formatting")]
-public class BasicColors : Scenario
-{
-    public override void Main ()
-    {
-        Application.Init ();
-
-        Window app = new ()
-        {
-            Title = GetQuitKeyAndName (),
-        };
-
-        var vx = 30;
-        var x = 30;
-        var y = 14;
-        Array colors = Enum.GetValues (typeof (ColorName));
-
-        foreach (ColorName bg in colors)
-        {
-            var attr = new Attribute (bg, colors.Length - 1 - bg);
-
-            var vl = new Label
-            {
-                X = vx,
-                Y = 0,
-                Width = 1,
-                Height = 13,
-                VerticalTextAlignment = Alignment.End,
-                ColorScheme = new ColorScheme { Normal = attr },
-                Text = bg.ToString (),
-                TextDirection = TextDirection.TopBottom_LeftRight
-            };
-            app.Add (vl);
-
-            var hl = new Label
-            {
-                X = 15,
-                Y = y,
-                Width = 13,
-                Height = 1,
-                TextAlignment = Alignment.End,
-                ColorScheme = new ColorScheme { Normal = attr },
-                Text = bg.ToString ()
-            };
-            app.Add (hl);
-            vx++;
-
-            foreach (ColorName fg in colors)
-            {
-                var c = new Attribute (fg, bg);
-                var t = x.ToString ();
-
-                var l = new Label
-                {
-                    ColorScheme = new ColorScheme { Normal = c }, X = x, Y = y, Text = t [^1].ToString ()
-                };
-                app.Add (l);
-                x++;
-            }
-
-            x = 30;
-            y++;
-        }
-
-        app.Add (
-                 new Label { X = Pos.AnchorEnd (36), Text = "Mouse over to get the Attribute:" }
-                );
-        app.Add (new Label { X = Pos.AnchorEnd (35), Y = 2, Text = "Foreground:" });
-
-        var lblForeground = new Label { X = Pos.AnchorEnd (23), Y = 2 };
-        app.Add (lblForeground);
-
-        var viewForeground = new View { X = Pos.AnchorEnd (2), Y = 2, ColorScheme = new ColorScheme (), Text = "  " };
-        app.Add (viewForeground);
-
-        app.Add (new Label { X = Pos.AnchorEnd (35), Y = 4, Text = "Background:" });
-
-        var lblBackground = new Label { X = Pos.AnchorEnd (23), Y = 4 };
-        app.Add (lblBackground);
-
-        var viewBackground = new View { X = Pos.AnchorEnd (2), Y = 4, ColorScheme = new ColorScheme (), Text = "  " };
-        app.Add (viewBackground);
-
-        Application.MouseEvent += (s, e) =>
-                                  {
-                                      if (e.View != null)
-                                      {
-                                          Color fore = e.View.GetNormalColor ().Foreground;
-                                          Color back = e.View.GetNormalColor ().Background;
-
-                                          lblForeground.Text =
-                                              $"#{fore.R:X2}{fore.G:X2}{fore.B:X2} {fore.GetClosestNamedColor ()} ";
-
-                                          viewForeground.ColorScheme =
-                                              new ColorScheme (viewForeground.ColorScheme) { Normal = new Attribute (fore, fore) };
-
-                                          lblBackground.Text =
-                                              $"#{back.R:X2}{back.G:X2}{back.B:X2} {back.GetClosestNamedColor ()} ";
-
-                                          viewBackground.ColorScheme =
-                                              new ColorScheme (viewBackground.ColorScheme) { Normal = new Attribute (back, back) };
-                                      }
-                                  };
-
-        Application.Run (app);
-        app.Dispose ();
-        Application.Shutdown ();
-    }
-}

+ 4 - 0
UICatalog/Scenarios/CharacterMap.cs

@@ -475,6 +475,7 @@ internal class CharMap : View
             NoDecorations = true,
             Title = CM.Glyphs.UpArrow.ToString (),
             WantContinuousButtonPressed = true,
+            ShadowStyle = ShadowStyle.None,
             CanFocus = false
         };
         up.Accept += (sender, args) => { args.Handled = ScrollVertical (-1) == true; };
@@ -489,6 +490,7 @@ internal class CharMap : View
             NoDecorations = true,
             Title = CM.Glyphs.DownArrow.ToString (),
             WantContinuousButtonPressed = true,
+            ShadowStyle = ShadowStyle.None,
             CanFocus = false
         };
         down.Accept += (sender, args) => { ScrollVertical (1); };
@@ -503,6 +505,7 @@ internal class CharMap : View
             NoDecorations = true,
             Title = CM.Glyphs.LeftArrow.ToString (),
             WantContinuousButtonPressed = true,
+            ShadowStyle = ShadowStyle.None,
             CanFocus = false
         };
         left.Accept += (sender, args) => { ScrollHorizontal (-1); };
@@ -517,6 +520,7 @@ internal class CharMap : View
             NoDecorations = true,
             Title = CM.Glyphs.RightArrow.ToString (),
             WantContinuousButtonPressed = true,
+            ShadowStyle = ShadowStyle.None,
             CanFocus = false
         };
         right.Accept += (sender, args) => { ScrollHorizontal (1); };

+ 2 - 2
UICatalog/Scenarios/ColorPicker.cs

@@ -216,8 +216,8 @@ public class ColorPickers : Scenario
         app.Add (cbShowName);
 
         // Set default colors.
-        foregroundColorPicker.SelectedColor = _demoView.SuperView.ColorScheme.Normal.Foreground.GetClosestNamedColor ();
-        backgroundColorPicker.SelectedColor = _demoView.SuperView.ColorScheme.Normal.Background.GetClosestNamedColor ();
+        foregroundColorPicker.SelectedColor = _demoView.SuperView.ColorScheme.Normal.Foreground.GetClosestNamedColor16 ();
+        backgroundColorPicker.SelectedColor = _demoView.SuperView.ColorScheme.Normal.Background.GetClosestNamedColor16 ();
         app.Initialized += (s, e) => app.LayoutSubviews ();
 
         Application.Run (app);

+ 2 - 2
UICatalog/Scenarios/GraphViewExample.cs

@@ -239,8 +239,8 @@ public class GraphViewExample : Scenario
 
         _about.Text = "Housing Expenditures by income thirds 1996-2003";
 
-        Color fore = _graphView.ColorScheme.Normal.Foreground == new Color (ColorName.Black)
-                         ? new (ColorName.White)
+        Color fore = _graphView.ColorScheme.Normal.Foreground == Color.Black
+                         ? Color.White
                          : _graphView.ColorScheme.Normal.Foreground;
         var black = new Attribute (fore, Color.Black);
         var cyan = new Attribute (Color.BrightCyan, Color.Black);

+ 3 - 3
UICatalog/Scenarios/InvertColors.cs

@@ -20,12 +20,12 @@ public class InvertColors : Scenario
         };
 
         List<Label> labels = new ();
-        ColorName [] foreColors = Enum.GetValues (typeof (ColorName)).Cast<ColorName> ().ToArray ();
+        ColorName16 [] foreColors = Enum.GetValues (typeof (ColorName16)).Cast<ColorName16> ().ToArray ();
 
         for (var y = 0; y < foreColors.Length; y++)
         {
-            ColorName fore = foreColors [y];
-            ColorName back = foreColors [(y + 1) % foreColors.Length];
+            ColorName16 fore = foreColors [y];
+            ColorName16 back = foreColors [(y + 1) % foreColors.Length];
             var color = new Attribute (fore, back);
 
             var label = new Label { ColorScheme = new ColorScheme (), Y = y, Text = $"{fore} on {back}" };

+ 140 - 44
UICatalog/Scenarios/Mouse.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.ObjectModel;
+using System.ComponentModel;
 using System.Linq;
 using Terminal.Gui;
 
@@ -43,7 +44,10 @@ public class Mouse : Scenario
 
         for (var i = 0; i < filterSlider.Options.Count; i++)
         {
-            filterSlider.SetOption (i);
+            if (filterSlider.Options [i].Data != MouseFlags.ReportMousePosition)
+            {
+                filterSlider.SetOption (i);
+            }
         }
 
         win.Add (filterSlider);
@@ -67,7 +71,6 @@ public class Mouse : Scenario
             Y = Pos.Bottom (ml),
             Title = "_Want Continuous Button Pressed"
         };
-        cbWantContinuousPresses.CheckedStateChanging += (s, e) => { win.WantContinuousButtonPressed = !win.WantContinuousButtonPressed; };
 
         win.Add (cbWantContinuousPresses);
 
@@ -77,35 +80,116 @@ public class Mouse : Scenario
             Y = Pos.Bottom (cbWantContinuousPresses),
             Title = "_Highlight on Press"
         };
-        cbHighlightOnPress.CheckedState = win.HighlightStyle == (HighlightStyle.Pressed | HighlightStyle.PressedOutside) ? CheckState.Checked : CheckState.UnChecked;
-
-        cbHighlightOnPress.CheckedStateChanging += (s, e) =>
-                                      {
-                                          if (e.NewValue == CheckState.Checked)
-                                          {
-                                              win.HighlightStyle = HighlightStyle.Pressed | HighlightStyle.PressedOutside;
-                                          }
-                                          else
-                                          {
-                                              win.HighlightStyle = HighlightStyle.None;
-                                          }
-                                      };
 
         win.Add (cbHighlightOnPress);
 
-        var demo = new MouseDemo
+        var demo = new MouseEventDemoView
         {
             X = Pos.Right (filterSlider),
             Y = Pos.Bottom (cbHighlightOnPress),
-            Width = 20,
-            Height = 3,
-            Text = "Enter/Leave Demo",
-            TextAlignment = Alignment.Center,
-            VerticalTextAlignment = Alignment.Center,
-            ColorScheme = Colors.ColorSchemes ["Dialog"]
+            Width = Dim.Fill (),
+            Height = 15,
+            Title = "Enter/Leave Demo",
+        };
+
+        demo.Padding.Initialized += DemoPaddingOnInitialized;
+
+        void DemoPaddingOnInitialized (object o, EventArgs eventArgs)
+        {
+            demo.Padding.Add (
+                              new MouseEventDemoView ()
+                              {
+                                  X = 0,
+                                  Y = 0,
+                                  Width = Dim.Fill (),
+                                  Height = Dim.Func (() => demo.Padding.Thickness.Top),
+                                  Title = "inPadding",
+                                  Id = "inPadding"
+                              });
+            demo.Padding.Thickness = demo.Padding.Thickness with { Top = 5 };
+        }
+
+        View sub1 = new MouseEventDemoView ()
+        {
+            X = 0,
+            Y = 0,
+            Width = Dim.Percent (20),
+            Height = Dim.Fill (),
+            Title = "sub1",
+            Id = "sub1",
         };
+        demo.Add (sub1);
+
+        demo.Add (
+                  new MouseEventDemoView ()
+                  {
+                      X = Pos.Right (sub1) - 4,
+                      Y = Pos.Top (sub1) + 1,
+                      Width = Dim.Percent (20),
+                      Height = Dim.Fill (1),
+                      Title = "sub2",
+                      Id = "sub2",
+                  });
+
         win.Add (demo);
 
+        cbHighlightOnPress.CheckedState = demo.HighlightStyle == (HighlightStyle.Pressed | HighlightStyle.PressedOutside) ? CheckState.Checked : CheckState.UnChecked;
+
+        // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/3753
+        cbHighlightOnPress.CheckedStateChanging += (s, e) =>
+                                                   {
+                                                       if (e.NewValue == CheckState.Checked)
+                                                       {
+                                                           demo.HighlightStyle = HighlightStyle.Pressed | HighlightStyle.PressedOutside;
+                                                       }
+                                                       else
+                                                       {
+                                                           demo.HighlightStyle = HighlightStyle.None;
+                                                       }
+
+                                                       foreach (View subview in demo.Subviews)
+                                                       {
+                                                           if (e.NewValue == CheckState.Checked)
+                                                           {
+                                                               subview.HighlightStyle = HighlightStyle.Pressed | HighlightStyle.PressedOutside;
+                                                           }
+                                                           else
+                                                           {
+                                                               subview.HighlightStyle = HighlightStyle.None;
+                                                           }
+                                                       }
+
+                                                       foreach (View subview in demo.Padding.Subviews)
+                                                       {
+                                                           if (e.NewValue == CheckState.Checked)
+                                                           {
+                                                               subview.HighlightStyle = HighlightStyle.Pressed | HighlightStyle.PressedOutside;
+                                                           }
+                                                           else
+                                                           {
+                                                               subview.HighlightStyle = HighlightStyle.None;
+                                                           }
+                                                       }
+
+                                                   };
+
+        cbWantContinuousPresses.CheckedStateChanging += (s, e) =>
+                                                        {
+                                                            demo.WantContinuousButtonPressed = !demo.WantContinuousButtonPressed;
+
+                                                            foreach (View subview in demo.Subviews)
+                                                            {
+                                                                subview.WantContinuousButtonPressed = demo.WantContinuousButtonPressed;
+                                                            }
+
+                                                            foreach (View subview in demo.Padding.Subviews)
+                                                            {
+                                                                subview.WantContinuousButtonPressed = demo.WantContinuousButtonPressed;
+                                                            }
+
+                                                        };
+
+
         var label = new Label
         {
             Text = "_App Events:",
@@ -187,37 +271,49 @@ public class Mouse : Scenario
         Application.Shutdown ();
     }
 
-    public class MouseDemo : View
+    public class MouseEventDemoView : View
     {
-        private bool _button1PressedOnEnter;
-
-        public MouseDemo ()
+        public MouseEventDemoView ()
         {
             CanFocus = true;
+            Id = "mouseEventDemoView";
 
-            MouseEvent += (s, e) =>
-                          {
-                              if (e.MouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
-                              {
-                                  if (!_button1PressedOnEnter)
-                                  {
-                                      ColorScheme = Colors.ColorSchemes ["Toplevel"];
-                                  }
-                              }
+            Padding.Thickness = new Thickness (1, 1, 1, 1);
 
-                              if (e.MouseEvent.Flags.HasFlag (MouseFlags.Button1Released))
-                              {
-                                  ColorScheme = Colors.ColorSchemes ["Dialog"];
-                                  _button1PressedOnEnter = false;
-                              }
-                          };
+            Initialized += OnInitialized;
+
+            void OnInitialized (object sender, EventArgs e)
+            {
+                TextAlignment = Alignment.Center;
+                VerticalTextAlignment = Alignment.Center;
+
+                Padding.ColorScheme = new ColorScheme (new Attribute (Color.Black));
+
+                Padding.MouseEnter += PaddingOnMouseEnter;
+                Padding.MouseLeave += PaddingOnMouseLeave;
+
+                void PaddingOnMouseEnter (object o, CancelEventArgs e)
+                {
+                    Padding.ColorScheme = Colors.ColorSchemes ["Error"];
+                }
+
+                void PaddingOnMouseLeave (object o, EventArgs e)
+                {
+                    Padding.ColorScheme = Colors.ColorSchemes ["Dialog"];
+                }
+
+                Border.Thickness = new Thickness (1);
+                Border.LineStyle = LineStyle.Rounded;
+            }
 
             MouseLeave += (s, e) =>
                           {
-                              ColorScheme = Colors.ColorSchemes ["Dialog"];
-                              _button1PressedOnEnter = false;
+                              Text = "Leave";
+                          };
+            MouseEnter += (s, e) =>
+                          {
+                              Text = "Enter";
                           };
-            MouseEnter += (s, e) => { _button1PressedOnEnter = e.MouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed); };
         }
     }
 }

+ 7 - 6
UICatalog/Scenarios/Shortcuts.cs

@@ -57,6 +57,7 @@ public class Shortcuts : Scenario
             Text = "Width is 35",
             KeyBindingScope = KeyBindingScope.Application,
         };
+
         Application.Top.Add (vShortcut1);
 
         var vShortcut2 = new Shortcut
@@ -72,14 +73,14 @@ public class Shortcuts : Scenario
             {
                 Orientation = Orientation.Vertical,
                 RadioLabels = ["O_ne", "T_wo", "Th_ree", "Fo_ur"]
-            }
+            },
         };
 
         ((RadioGroup)vShortcut2.CommandView).SelectedItemChanged += (o, args) =>
-                                                                   {
-                                                                       eventSource.Add ($"SelectedItemChanged: {o.GetType ().Name} - {args.SelectedItem}");
-                                                                       eventLog.MoveDown ();
-                                                                   };
+                                                                    {
+                                                                        eventSource.Add ($"SelectedItemChanged: {o.GetType ().Name} - {args.SelectedItem}");
+                                                                        eventLog.MoveDown ();
+                                                                    };
 
         vShortcut2.Accept += (o, args) =>
                             {
@@ -378,6 +379,6 @@ public class Shortcuts : Scenario
     private void Button_Clicked (object sender, HandledEventArgs e)
     {
         //e.Cancel = true;
-        MessageBox.Query ("Hi", $"You clicked {sender}"); 
+        MessageBox.Query ("Hi", $"You clicked {sender}");
     }
 }

+ 1 - 1
UICatalog/Scenarios/Sliders.cs

@@ -468,7 +468,7 @@ public class Sliders : Scenario
 
         List<SliderOption<(Color, Color)>> colorOptions = new ();
 
-        foreach (ColorName colorIndex in Enum.GetValues<ColorName> ())
+        foreach (ColorName16 colorIndex in Enum.GetValues<ColorName16> ())
         {
             var colorName = colorIndex.ToString ();
 

+ 6 - 6
UICatalog/Scenarios/TextEffectsScenario.cs

@@ -38,11 +38,11 @@ public class TextEffectsScenario : Scenario
 
         w.ColorScheme = new ()
         {
-            Normal = new (ColorName.White, ColorName.Black),
-            Focus = new (ColorName.Black, ColorName.White),
-            HotNormal = new (ColorName.White, ColorName.Black),
-            HotFocus = new (ColorName.White, ColorName.Black),
-            Disabled = new (ColorName.Gray, ColorName.Black)
+            Normal = new (ColorName16.White, ColorName16.Black),
+            Focus = new (ColorName16.Black, ColorName16.White),
+            HotNormal = new (ColorName16.White, ColorName16.Black),
+            HotFocus = new (ColorName16.White, ColorName16.Black),
+            Disabled = new (ColorName16.Gray, ColorName16.Black)
         };
 
         // Creates a window that occupies the entire terminal with a title.
@@ -100,7 +100,7 @@ public class TextEffectsScenario : Scenario
                                      new (0, 0, size.Width, size.Height),
                                      g,
                                      GradientDirection.Diagonal);
-        var back = new SolidFill (new (ColorName.Black));
+        var back = new SolidFill (new (ColorName16.Black));
 
         w.LineCanvas.Fill = new (
                                  fore,

+ 10 - 22
UICatalog/UICatalog.cs

@@ -542,18 +542,6 @@ public class UICatalogApp
                                                                       Application.Refresh ();
                                                                   };
 
-                //ShDiagnostics = new Shortcut ()
-                //{
-                //    HelpText = "Diagnostic flags",
-                //    CommandView = new RadioGroup()
-                //    {
-                //        RadioLabels = ["Off", "Ruler", "Padding", "MouseEnter"],
-
-                //        CanFocus = false,
-                //        Orientation = Orientation.Vertical,
-                //    }
-                //};
-
                 StatusBar.Add (
                                new Shortcut
                                {
@@ -818,7 +806,7 @@ public class UICatalogApp
             const string OFF = "View Diagnostics: _Off";
             const string RULER = "View Diagnostics: _Ruler";
             const string PADDING = "View Diagnostics: _Padding";
-            const string MOUSEENTER = "View Diagnostics: _MouseEnter";
+            const string Hover = "View Diagnostics: _Hover";
             var index = 0;
 
             List<MenuItem> menuItems = new ();
@@ -836,7 +824,7 @@ public class UICatalogApp
                 {
                     item.Checked = !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Padding)
                                    && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Ruler)
-                                   && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.MouseEnter);
+                                   && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Hover);
                 }
                 else
                 {
@@ -849,12 +837,12 @@ public class UICatalogApp
 
                                    if (item.Title == t && item.Checked == false)
                                    {
-                                       _diagnosticFlags &= ~(ViewDiagnosticFlags.Padding | ViewDiagnosticFlags.Ruler | ViewDiagnosticFlags.MouseEnter);
+                                       _diagnosticFlags &= ~(ViewDiagnosticFlags.Padding | ViewDiagnosticFlags.Ruler | ViewDiagnosticFlags.Hover);
                                        item.Checked = true;
                                    }
                                    else if (item.Title == t && item.Checked == true)
                                    {
-                                       _diagnosticFlags |= ViewDiagnosticFlags.Padding | ViewDiagnosticFlags.Ruler | ViewDiagnosticFlags.MouseEnter;
+                                       _diagnosticFlags |= ViewDiagnosticFlags.Padding | ViewDiagnosticFlags.Ruler | ViewDiagnosticFlags.Hover;
                                        item.Checked = false;
                                    }
                                    else
@@ -877,7 +865,7 @@ public class UICatalogApp
                                        {
                                            menuItem.Checked = !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Ruler)
                                                               && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Padding)
-                                                              && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.MouseEnter);
+                                                              && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Hover);
                                        }
                                        else if (menuItem.Title != t)
                                        {
@@ -900,7 +888,7 @@ public class UICatalogApp
                     "Off" => OFF,
                     "Ruler" => RULER,
                     "Padding" => PADDING,
-                    "MouseEnter" => MOUSEENTER,
+                    "Hover" => Hover,
                     _ => ""
                 };
             }
@@ -911,7 +899,7 @@ public class UICatalogApp
                 {
                     RULER => ViewDiagnosticFlags.Ruler,
                     PADDING => ViewDiagnosticFlags.Padding,
-                    MOUSEENTER => ViewDiagnosticFlags.MouseEnter,
+                    Hover => ViewDiagnosticFlags.Hover,
                     _ => null!
                 };
             }
@@ -942,14 +930,14 @@ public class UICatalogApp
                         }
 
                         break;
-                    case ViewDiagnosticFlags.MouseEnter:
+                    case ViewDiagnosticFlags.Hover:
                         if (add)
                         {
-                            _diagnosticFlags |= ViewDiagnosticFlags.MouseEnter;
+                            _diagnosticFlags |= ViewDiagnosticFlags.Hover;
                         }
                         else
                         {
-                            _diagnosticFlags &= ~ViewDiagnosticFlags.MouseEnter;
+                            _diagnosticFlags &= ~ViewDiagnosticFlags.Hover;
                         }
 
                         break;

+ 10 - 3
UnitTests/Application/ApplicationTests.cs

@@ -10,6 +10,7 @@ public class ApplicationTests
     {
         _output = output;
         ConsoleDriver.RunningUnitTests = true;
+        ConfigurationManager.Locations = ConfigurationManager.ConfigLocations.None;
 
 #if DEBUG_IDISPOSABLE
         Responder.Instances.Clear ();
@@ -312,7 +313,7 @@ public class ApplicationTests
             Assert.False (Application._forceFakeConsole);
             Assert.Equal (-1, Application.MainThreadId);
             Assert.Empty (Application.TopLevels);
-            Assert.Null (Application.MouseEnteredView);
+            Assert.Empty (Application._cachedViewsUnderMouse);
 
             // Keyboard
             Assert.Empty (Application.GetViewKeyBindings ());
@@ -342,7 +343,7 @@ public class ApplicationTests
         Application.MainThreadId = 1;
 
         //Application._topLevels = new List<Toplevel> ();
-        Application.MouseEnteredView = new ();
+        Application._cachedViewsUnderMouse.Clear ();
 
         //Application.SupportedCultures = new List<CultureInfo> ();
         Application.Force16Colors = true;
@@ -356,7 +357,7 @@ public class ApplicationTests
 
         //ApplicationOverlapped.OverlappedChildren = new List<View> ();
         //ApplicationOverlapped.OverlappedTop = 
-        Application.MouseEnteredView = new ();
+        Application._cachedViewsUnderMouse.Clear ();
 
         //Application.WantContinuousButtonPressedView = new View ();
 
@@ -392,6 +393,12 @@ public class ApplicationTests
 #endif
     }
 
+    [Fact]
+    public void Shutdown_Alone_Does_Nothing ()
+    {
+        Application.Shutdown ();
+    }
+
     [Theory]
     [InlineData (typeof (FakeDriver))]
     [InlineData (typeof (NetDriver))]

+ 481 - 0
UnitTests/Application/Mouse/ApplicationMouseEnterLeaveTests.cs

@@ -0,0 +1,481 @@
+using System.ComponentModel;
+
+namespace Terminal.Gui.ViewMouseTests;
+
+[Trait ("Category", "Input")]
+public class ApplicationMouseEnterLeaveTests
+{
+    private class TestView : View
+    {
+        public TestView ()
+        {
+            X = 1;
+            Y = 1;
+            Width = 1;
+            Height = 1;
+        }
+
+        public bool CancelOnEnter { get; }
+        public int OnMouseEnterCalled { get; private set; }
+        public int OnMouseLeaveCalled { get; private set; }
+
+        protected override bool OnMouseEnter (CancelEventArgs eventArgs)
+        {
+            OnMouseEnterCalled++;
+            eventArgs.Cancel = CancelOnEnter;
+
+            base.OnMouseEnter (eventArgs);
+
+            return eventArgs.Cancel;
+        }
+
+        protected override void OnMouseLeave ()
+        {
+            OnMouseLeaveCalled++;
+
+            base.OnMouseLeave ();
+        }
+    }
+
+    [Fact]
+    public void RaiseMouseEnterLeaveEvents_MouseEntersView_CallsOnMouseEnter ()
+    {
+        // Arrange
+        Application.Top = new () { Frame = new (0, 0, 10, 10) };
+        var view = new TestView ();
+        Application.Top.Add (view);
+        var mousePosition = new Point (1, 1);
+        List<View> currentViewsUnderMouse = new () { view };
+
+        var mouseEvent = new MouseEvent
+        {
+            Position = mousePosition,
+            ScreenPosition = mousePosition
+        };
+
+        Application._cachedViewsUnderMouse.Clear ();
+
+        try
+        {
+            // Act
+            Application.RaiseMouseEnterLeaveEvents (mousePosition, currentViewsUnderMouse);
+
+            // Assert
+            Assert.Equal (1, view.OnMouseEnterCalled);
+        }
+        finally
+        {
+            // Cleanup
+            Application.Top?.Dispose ();
+            Application.ResetState ();
+        }
+    }
+
+    [Fact]
+    public void RaiseMouseEnterLeaveEvents_MouseLeavesView_CallsOnMouseLeave ()
+    {
+        // Arrange
+        Application.Top = new () { Frame = new (0, 0, 10, 10) };
+        var view = new TestView ();
+        Application.Top.Add (view);
+        var mousePosition = new Point (0, 0);
+        List<View> currentViewsUnderMouse = new ();
+        var mouseEvent = new MouseEvent ();
+
+        Application._cachedViewsUnderMouse.Clear ();
+        Application._cachedViewsUnderMouse.Add (view);
+
+        try
+        {
+            // Act
+            Application.RaiseMouseEnterLeaveEvents (mousePosition, currentViewsUnderMouse);
+
+            // Assert
+            Assert.Equal (0, view.OnMouseEnterCalled);
+            Assert.Equal (1, view.OnMouseLeaveCalled);
+        }
+        finally
+        {
+            // Cleanup
+            Application.Top?.Dispose ();
+            Application.ResetState ();
+        }
+    }
+
+    [Fact]
+    public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMouseEnterAndLeave ()
+    {
+        // Arrange
+        Application.Top = new () { Frame = new (0, 0, 10, 10) };
+        var view1 = new TestView (); // at 1,1 to 2,2
+
+        var view2 = new TestView () // at 2,2 to 3,3
+        {
+            X = 2,
+            Y = 2
+        };
+        Application.Top.Add (view1);
+        Application.Top.Add (view2);
+
+        Application._cachedViewsUnderMouse.Clear ();
+
+        try
+        {
+            // Act
+            var mousePosition = new Point (0, 0);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (0, view1.OnMouseEnterCalled);
+            Assert.Equal (0, view1.OnMouseLeaveCalled);
+            Assert.Equal (0, view2.OnMouseEnterCalled);
+            Assert.Equal (0, view2.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (1, 1);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (1, view1.OnMouseEnterCalled);
+            Assert.Equal (0, view1.OnMouseLeaveCalled);
+            Assert.Equal (0, view2.OnMouseEnterCalled);
+            Assert.Equal (0, view2.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (2, 2);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (1, view1.OnMouseEnterCalled);
+            Assert.Equal (1, view1.OnMouseLeaveCalled);
+            Assert.Equal (1, view2.OnMouseEnterCalled);
+            Assert.Equal (0, view2.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (3, 3);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (1, view1.OnMouseEnterCalled);
+            Assert.Equal (1, view1.OnMouseLeaveCalled);
+            Assert.Equal (1, view2.OnMouseEnterCalled);
+            Assert.Equal (1, view2.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (0, 0);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (1, view1.OnMouseEnterCalled);
+            Assert.Equal (1, view1.OnMouseLeaveCalled);
+            Assert.Equal (1, view2.OnMouseEnterCalled);
+            Assert.Equal (1, view2.OnMouseLeaveCalled);
+        }
+        finally
+        {
+            // Cleanup
+            Application.Top?.Dispose ();
+            Application.ResetState ();
+        }
+    }
+
+    [Fact]
+    public void RaiseMouseEnterLeaveEvents_NoViewsUnderMouse_DoesNotCallOnMouseEnterOrLeave ()
+    {
+        // Arrange
+        Application.Top = new () { Frame = new (0, 0, 10, 10) };
+        var view = new TestView ();
+        Application.Top.Add (view);
+        var mousePosition = new Point (0, 0);
+        List<View> currentViewsUnderMouse = new ();
+        var mouseEvent = new MouseEvent ();
+
+        Application._cachedViewsUnderMouse.Clear ();
+
+        try
+        {
+            // Act
+            Application.RaiseMouseEnterLeaveEvents (mousePosition, currentViewsUnderMouse);
+
+            // Assert
+            Assert.Equal (0, view.OnMouseEnterCalled);
+            Assert.Equal (0, view.OnMouseLeaveCalled);
+        }
+        finally
+        {
+            // Cleanup
+            Application.Top?.Dispose ();
+            Application.ResetState ();
+        }
+    }
+
+    [Fact]
+    public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingPeerViews_CallsOnMouseEnterAndLeave ()
+    {
+        // Arrange
+        Application.Top = new () { Frame = new (0, 0, 10, 10) };
+
+        var view1 = new TestView
+        {
+            Width = 2
+        }; // at 1,1 to 3,2
+
+        var view2 = new TestView () // at 2,2 to 4,3
+        {
+            Width = 2,
+            X = 2,
+            Y = 2
+        };
+        Application.Top.Add (view1);
+        Application.Top.Add (view2);
+
+        Application._cachedViewsUnderMouse.Clear ();
+
+        try
+        {
+            // Act
+            var mousePosition = new Point (0, 0);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (0, view1.OnMouseEnterCalled);
+            Assert.Equal (0, view1.OnMouseLeaveCalled);
+            Assert.Equal (0, view2.OnMouseEnterCalled);
+            Assert.Equal (0, view2.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (1, 1);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (1, view1.OnMouseEnterCalled);
+            Assert.Equal (0, view1.OnMouseLeaveCalled);
+            Assert.Equal (0, view2.OnMouseEnterCalled);
+            Assert.Equal (0, view2.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (2, 2);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (1, view1.OnMouseEnterCalled);
+            Assert.Equal (1, view1.OnMouseLeaveCalled);
+            Assert.Equal (1, view2.OnMouseEnterCalled);
+            Assert.Equal (0, view2.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (3, 3);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (1, view1.OnMouseEnterCalled);
+            Assert.Equal (1, view1.OnMouseLeaveCalled);
+            Assert.Equal (1, view2.OnMouseEnterCalled);
+            Assert.Equal (1, view2.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (0, 0);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (1, view1.OnMouseEnterCalled);
+            Assert.Equal (1, view1.OnMouseLeaveCalled);
+            Assert.Equal (1, view2.OnMouseEnterCalled);
+            Assert.Equal (1, view2.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (2, 2);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (1, view1.OnMouseEnterCalled);
+            Assert.Equal (1, view1.OnMouseLeaveCalled);
+            Assert.Equal (2, view2.OnMouseEnterCalled);
+            Assert.Equal (1, view2.OnMouseLeaveCalled);
+        }
+        finally
+        {
+            // Cleanup
+            Application.Top?.Dispose ();
+            Application.ResetState ();
+        }
+    }
+
+    [Fact]
+    public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingSubViews_CallsOnMouseEnterAndLeave ()
+    {
+        // Arrange
+        Application.Top = new () { Frame = new (0, 0, 10, 10) };
+
+        var view1 = new TestView
+        {
+            Id = "view1",
+            Width = 2,
+            Height = 2,
+            Arrangement = ViewArrangement.Overlapped
+        }; // at 1,1 to 3,3 (screen)
+
+        var subView = new TestView
+        {
+            Id = "subView",
+            Width = 2,
+            Height = 2,
+            X = 1,
+            Y = 1,
+            Arrangement = ViewArrangement.Overlapped
+        }; // at 2,2 to 4,4 (screen)
+        view1.Add (subView);
+        Application.Top.Add (view1);
+
+        Application._cachedViewsUnderMouse.Clear ();
+
+        try
+        {
+            Assert.Equal (1, view1.FrameToScreen ().X);
+            Assert.Equal (2, subView.FrameToScreen ().X);
+
+            // Act
+            var mousePosition = new Point (0, 0);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (0, view1.OnMouseEnterCalled);
+            Assert.Equal (0, view1.OnMouseLeaveCalled);
+            Assert.Equal (0, subView.OnMouseEnterCalled);
+            Assert.Equal (0, subView.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (1, 1);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (1, view1.OnMouseEnterCalled);
+            Assert.Equal (0, view1.OnMouseLeaveCalled);
+            Assert.Equal (0, subView.OnMouseEnterCalled);
+            Assert.Equal (0, subView.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (2, 2);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (1, view1.OnMouseEnterCalled);
+            Assert.Equal (0, view1.OnMouseLeaveCalled);
+            Assert.Equal (1, subView.OnMouseEnterCalled);
+            Assert.Equal (0, subView.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (0, 0);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (1, view1.OnMouseEnterCalled);
+            Assert.Equal (1, view1.OnMouseLeaveCalled);
+            Assert.Equal (1, subView.OnMouseEnterCalled);
+            Assert.Equal (1, subView.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (2, 2);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (2, view1.OnMouseEnterCalled);
+            Assert.Equal (1, view1.OnMouseLeaveCalled);
+            Assert.Equal (2, subView.OnMouseEnterCalled);
+            Assert.Equal (1, subView.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (3, 3);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (2, view1.OnMouseEnterCalled);
+            Assert.Equal (2, view1.OnMouseLeaveCalled);
+            Assert.Equal (2, subView.OnMouseEnterCalled);
+            Assert.Equal (2, subView.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (0, 0);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (2, view1.OnMouseEnterCalled);
+            Assert.Equal (2, view1.OnMouseLeaveCalled);
+            Assert.Equal (2, subView.OnMouseEnterCalled);
+            Assert.Equal (2, subView.OnMouseLeaveCalled);
+
+            // Act
+            mousePosition = new (2, 2);
+
+            Application.RaiseMouseEnterLeaveEvents (
+                                                    mousePosition,
+                                                    View.GetViewsUnderMouse (mousePosition));
+
+            // Assert
+            Assert.Equal (3, view1.OnMouseEnterCalled);
+            Assert.Equal (2, view1.OnMouseLeaveCalled);
+            Assert.Equal (3, subView.OnMouseEnterCalled);
+            Assert.Equal (2, subView.OnMouseLeaveCalled);
+        }
+        finally
+        {
+            // Cleanup
+            Application.Top?.Dispose ();
+            Application.ResetState ();
+        }
+    }
+}

+ 15 - 12
UnitTests/Application/MouseTests.cs → UnitTests/Application/Mouse/ApplicationMouseTests.cs

@@ -4,11 +4,12 @@
 
 namespace Terminal.Gui.ApplicationTests;
 
-public class MouseTests
+[Trait ("Category", "Input")]
+public class ApplicationMouseTests
 {
     private readonly ITestOutputHelper _output;
 
-    public MouseTests (ITestOutputHelper output)
+    public ApplicationMouseTests (ITestOutputHelper output)
     {
         _output = output;
 #if DEBUG_IDISPOSABLE
@@ -137,7 +138,7 @@ public class MouseTests
     ///     Tests that the mouse coordinates passed to the focused view are correct when the mouse is clicked. With
     ///     Frames; Frame != Viewport
     /// </summary>
-    [AutoInitShutdown]
+    //[AutoInitShutdown]
     [Theory]
 
     // click on border
@@ -199,12 +200,12 @@ public class MouseTests
 
         var clicked = false;
 
-        var top = new Toplevel ();
-        top.X = 0;
-        top.Y = 0;
-        top.Width = size.Width * 2;
-        top.Height = size.Height * 2;
-        top.BorderStyle = LineStyle.None;
+        Application.Top = new Toplevel ();
+        Application.Top.X = 0;
+        Application.Top.Y = 0;
+        Application.Top.Width = size.Width * 2;
+        Application.Top.Height = size.Height * 2;
+        Application.Top.BorderStyle = LineStyle.None;
 
         var view = new View { X = pos.X, Y = pos.Y, Width = size.Width, Height = size.Height };
 
@@ -212,8 +213,8 @@ public class MouseTests
         view.BorderStyle = LineStyle.Single;
         view.CanFocus = true;
 
-        top.Add (view);
-        Application.Begin (top);
+        Application.Top.Add (view);
+
         var mouseEvent = new MouseEvent { Position = new (clickX, clickY), Flags = MouseFlags.Button1Clicked };
 
         view.MouseClick += (s, e) =>
@@ -225,7 +226,8 @@ public class MouseTests
 
         Application.OnMouseEvent (mouseEvent);
         Assert.Equal (expectedClicked, clicked);
-        top.Dispose ();
+        Application.Top.Dispose ();
+        Application.ResetState (ignoreDisposed: true);
     }
 
     #endregion mouse coordinate tests
@@ -401,5 +403,6 @@ public class MouseTests
         Assert.Equal (0, count);
         top.Dispose ();
     }
+
     #endregion
 }

+ 1 - 1
UnitTests/Configuration/AppScopeTests.cs

@@ -15,7 +15,7 @@ public class AppScopeTests
     };
 
     [Fact]
-    [AutoInitShutdown]
+    [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
     public void Apply_ShouldApplyUpdatedProperties ()
     {
         Reset ();

+ 15 - 2
UnitTests/Configuration/ConfigurationMangerTests.cs

@@ -149,6 +149,8 @@ public class ConfigurationManagerTests
     [Fact]
     public void Load_FiresUpdated ()
     {
+        ConfigLocations savedLocations = Locations;
+        Locations = ConfigLocations.All;
         Reset ();
 
         Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
@@ -183,6 +185,7 @@ public class ConfigurationManagerTests
 
         Updated -= ConfigurationManager_Updated;
         Reset ();
+        Locations = savedLocations;
     }
 
     [Fact]
@@ -414,6 +417,9 @@ public class ConfigurationManagerTests
     [Fact]
     public void TestConfigPropertyOmitClassName ()
     {
+        ConfigLocations savedLocations = Locations;
+        Locations = ConfigLocations.All;
+
         // Color.ColorSchemes is serialized as "ColorSchemes", not "Colors.ColorSchemes"
         PropertyInfo pi = typeof (Colors).GetProperty ("ColorSchemes");
         var scp = (SerializableConfigurationProperty)pi!.GetCustomAttribute (typeof (SerializableConfigurationProperty));
@@ -422,6 +428,8 @@ public class ConfigurationManagerTests
 
         Reset ();
         Assert.Equal (pi, Themes! ["Default"] ["ColorSchemes"].PropertyInfo);
+
+        Locations = savedLocations;
     }
 
     [Fact]
@@ -563,7 +571,7 @@ public class ConfigurationManagerTests
 							{
 								""UserDefined"": {
 									""hotNormal"": {
-										""foreground"": ""brown"",
+										""foreground"": ""brownish"",
 										""background"": ""1234""
 									}
 								}
@@ -575,7 +583,7 @@ public class ConfigurationManagerTests
 			}";
 
         var jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
-        Assert.Equal ("Unexpected color name: brown.", jsonException.Message);
+        Assert.Equal ("Unexpected color name: brownish.", jsonException.Message);
 
         // AbNormal is not a ColorScheme attribute
         json = @"
@@ -652,6 +660,9 @@ public class ConfigurationManagerTests
     [Fact]
     public void TestConfigurationManagerUpdateFromJson ()
     {
+        ConfigLocations savedLocations = Locations;
+        Locations = ConfigLocations.All;
+
         // Arrange
         var json = @"
 {
@@ -816,6 +827,8 @@ public class ConfigurationManagerTests
         Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground);
         Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background);
         Reset ();
+
+        Locations = savedLocations;
     }
 
     [Fact]

+ 25 - 25
UnitTests/Configuration/JsonConverterTests.cs

@@ -41,23 +41,23 @@ public class ColorJsonConverterTests
     }
 
     [Theory]
-    [InlineData (ColorName.Black, "Black")]
-    [InlineData (ColorName.Blue, "Blue")]
-    [InlineData (ColorName.Green, "Green")]
-    [InlineData (ColorName.Cyan, "Cyan")]
-    [InlineData (ColorName.Gray, "Gray")]
-    [InlineData (ColorName.Red, "Red")]
-    [InlineData (ColorName.Magenta, "Magenta")]
-    [InlineData (ColorName.Yellow, "Yellow")]
-    [InlineData (ColorName.DarkGray, "DarkGray")]
-    [InlineData (ColorName.BrightBlue, "BrightBlue")]
-    [InlineData (ColorName.BrightGreen, "BrightGreen")]
-    [InlineData (ColorName.BrightCyan, "BrightCyan")]
-    [InlineData (ColorName.BrightRed, "BrightRed")]
-    [InlineData (ColorName.BrightMagenta, "BrightMagenta")]
-    [InlineData (ColorName.BrightYellow, "BrightYellow")]
-    [InlineData (ColorName.White, "White")]
-    public void SerializesEnumValuesAsStrings (ColorName colorName, string expectedJson)
+    [InlineData (ColorName16.Black, "Black")]
+    [InlineData (ColorName16.Blue, "Blue")]
+    [InlineData (ColorName16.Green, "Green")]
+    [InlineData (ColorName16.Cyan, "Cyan")]
+    [InlineData (ColorName16.Gray, "Gray")]
+    [InlineData (ColorName16.Red, "Red")]
+    [InlineData (ColorName16.Magenta, "Magenta")]
+    [InlineData (ColorName16.Yellow, "Yellow")]
+    [InlineData (ColorName16.DarkGray, "DarkGray")]
+    [InlineData (ColorName16.BrightBlue, "BrightBlue")]
+    [InlineData (ColorName16.BrightGreen, "BrightGreen")]
+    [InlineData (ColorName16.BrightCyan, "BrightCyan")]
+    [InlineData (ColorName16.BrightRed, "BrightRed")]
+    [InlineData (ColorName16.BrightMagenta, "BrightMagenta")]
+    [InlineData (ColorName16.BrightYellow, "BrightYellow")]
+    [InlineData (ColorName16.White, "White")]
+    public void SerializesEnumValuesAsStrings (ColorName16 colorName, string expectedJson)
     {
         var converter = new ColorJsonConverter ();
         var options = new JsonSerializerOptions { Converters = { converter } };
@@ -67,7 +67,7 @@ public class ColorJsonConverterTests
         Assert.Equal ($"\"{expectedJson}\"", serialized);
     }
 
-    [Theory]
+    [Theory (Skip = "Not anymore. If a W3C color matches, that's used")]
     [InlineData (0, 0, 0, "\"#000000\"")]
     [InlineData (0, 0, 1, "\"#000001\"")]
     public void SerializesToHexCode (int r, int g, int b, string expected)
@@ -101,7 +101,7 @@ public class ColorJsonConverterTests
     [InlineData ("Magenta", Color.Magenta)]
     [InlineData ("Red", Color.Red)]
     [InlineData ("White", Color.White)]
-    public void TestColorDeserializationFromHumanReadableColorNames (string colorName, ColorName expectedColor)
+    public void TestColorDeserializationFromHumanReadableColorName16 (string colorName, ColorName16 expectedColor)
     {
         // Arrange
         var json = $"\"{colorName}\"";
@@ -118,7 +118,7 @@ public class ColorJsonConverterTests
     {
         // Arrange
         var json = "\"Black\"";
-        var expectedColor = new Color (ColorName.Black);
+        var expectedColor = new Color ("Black");
 
         // Act
         var color = JsonSerializer.Deserialize<Color> (
@@ -135,7 +135,7 @@ public class ColorJsonConverterTests
     {
         // Arrange
         var json = "\"BrightRed\"";
-        var expectedColor = new Color (ColorName.BrightRed);
+        var expectedColor = Color.BrightRed;
 
         // Act
         var color = JsonSerializer.Deserialize<Color> (
@@ -188,14 +188,14 @@ public class AttributeJsonConverterTests
         // Test deserializing from human-readable color names
         var json = "{\"Foreground\":\"Blue\",\"Background\":\"Green\"}";
         var attribute = JsonSerializer.Deserialize<Attribute> (json, ConfigurationManagerTests._jsonOptions);
-        Assert.Equal (Color.Blue, attribute.Foreground.GetClosestNamedColor ());
-        Assert.Equal (Color.Green, attribute.Background.GetClosestNamedColor ());
+        Assert.Equal (Color.Blue, attribute.Foreground.GetClosestNamedColor16 ());
+        Assert.Equal (Color.Green, attribute.Background.GetClosestNamedColor16 ());
 
         // Test deserializing from RGB values
         json = "{\"Foreground\":\"rgb(255,0,0)\",\"Background\":\"rgb(0,255,0)\"}";
         attribute = JsonSerializer.Deserialize<Attribute> (json, ConfigurationManagerTests._jsonOptions);
-        Assert.Equal (Color.Red, attribute.Foreground.GetClosestNamedColor ());
-        Assert.Equal (Color.BrightGreen, attribute.Background.GetClosestNamedColor ());
+        Assert.Equal (Color.Red, attribute.Foreground.GetClosestNamedColor16 ());
+        Assert.Equal (Color.BrightGreen, attribute.Background.GetClosestNamedColor16 ());
     }
 
     [Fact]

+ 4 - 0
UnitTests/Configuration/SerializableConfigurationPropertyTests.cs

@@ -8,6 +8,10 @@ namespace Terminal.Gui.ConfigurationTests;
 
 public class SerializableConfigurationPropertyTests
 {
+
+    /// <summary>
+    ///     If this test fails, you need to add a new property with the SerializableConfigurationProperty attribute.
+    /// </summary>
     [Fact]
     public void Test_SerializableConfigurationProperty_Types_Added_To_JsonSerializerContext ()
     {

+ 6 - 2
UnitTests/Configuration/SettingsScopeTests.cs

@@ -5,7 +5,7 @@ namespace Terminal.Gui.ConfigurationTests;
 public class SettingsScopeTests
 {
     [Fact]
-    [AutoInitShutdown]
+    [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
     public void Apply_ShouldApplyProperties ()
     {
         // arrange
@@ -55,9 +55,11 @@ public class SettingsScopeTests
     [Fact]
     public void GetHardCodedDefaults_ShouldSetProperties ()
     {
+        ConfigLocations savedLocations = Locations;
+        Locations = ConfigLocations.DefaultOnly;
         Reset ();
 
-        Assert.Equal (3, ((Dictionary<string, ThemeScope>)Settings ["Themes"].PropertyValue).Count);
+        Assert.Equal (5, ((Dictionary<string, ThemeScope>)Settings ["Themes"].PropertyValue).Count);
 
         GetHardCodedDefaults ();
         Assert.NotEmpty (Themes);
@@ -72,5 +74,7 @@ public class SettingsScopeTests
 
         Assert.True (Settings ["Themes"].PropertyValue is Dictionary<string, ThemeScope>);
         Assert.Single ((Dictionary<string, ThemeScope>)Settings ["Themes"].PropertyValue);
+
+        Locations = savedLocations;
     }
 }

+ 4 - 1
UnitTests/Configuration/ThemeScopeTests.cs

@@ -15,6 +15,7 @@ public class ThemeScopeTests
     };
 
     [Fact]
+    [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
     public void AllThemesPresent ()
     {
         Reset ();
@@ -24,7 +25,7 @@ public class ThemeScopeTests
     }
 
     [Fact]
-    [AutoInitShutdown]
+    [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
     public void Apply_ShouldApplyUpdatedProperties ()
     {
         Reset ();
@@ -53,6 +54,7 @@ public class ThemeScopeTests
     }
 
     [Fact]
+    [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
     public void TestSerialize_RoundTrip ()
     {
         Reset ();
@@ -69,6 +71,7 @@ public class ThemeScopeTests
     }
 
     [Fact]
+    [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
     public void ThemeManager_ClassMethodsWork ()
     {
         Reset ();

+ 2 - 1
UnitTests/Configuration/ThemeTests.cs

@@ -10,7 +10,7 @@ public class ThemeTests
         Converters = { new AttributeJsonConverter (), new ColorJsonConverter () }
     };
 
-    [Fact]
+    [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
     public void TestApply ()
     {
         Reset ();
@@ -32,6 +32,7 @@ public class ThemeTests
     }
 
     [Fact]
+    [AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
     public void TestApply_UpdatesColors ()
     {
         // Arrange

+ 33 - 1
UnitTests/Dialogs/DialogTests.cs

@@ -29,6 +29,8 @@ public class DialogTests
         // Override CM
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         // Default (center)
         var dlg = new Dialog
@@ -138,6 +140,8 @@ public class DialogTests
         RunState runstate = null;
 
         var d = (FakeDriver)Driver;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var title = "1234";
 
@@ -229,6 +233,8 @@ public class DialogTests
         RunState runstate = null;
 
         var d = (FakeDriver)Driver;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var title = "1234";
 
@@ -322,6 +328,8 @@ public class DialogTests
         RunState runstate = null;
 
         var d = (FakeDriver)Driver;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var title = "1234";
 
@@ -414,6 +422,8 @@ public class DialogTests
         RunState runstate = null;
 
         var d = (FakeDriver)Driver;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var title = "1234";
 
@@ -507,6 +517,8 @@ public class DialogTests
     {
         var d = (FakeDriver)Driver;
         RunState runstate = null;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var title = "1234";
 
@@ -646,6 +658,8 @@ public class DialogTests
         RunState runstate = null;
 
         var d = (FakeDriver)Driver;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var title = "1234";
 
@@ -730,6 +744,8 @@ public class DialogTests
         RunState runstate = null;
 
         var d = (FakeDriver)Driver;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var title = "1234";
 
@@ -809,6 +825,8 @@ public class DialogTests
         var firstIteration = false;
 
         var d = (FakeDriver)Driver;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var title = "1234";
 
@@ -883,6 +901,8 @@ public class DialogTests
         Window.DefaultBorderStyle = LineStyle.Single;
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var win = new Window ();
 
@@ -988,7 +1008,9 @@ public class DialogTests
         // Override CM
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultBorderStyle = LineStyle.Single;
-
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
+ 
         Iteration += (s, a) =>
                      {
                          iterations++;
@@ -1028,6 +1050,8 @@ public class DialogTests
         // Override CM
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var btn1 = new Button { Text = "press me 1" };
         Button btn2 = null;
@@ -1178,6 +1202,8 @@ public class DialogTests
         Window.DefaultBorderStyle = LineStyle.Single;
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var expected = 5;
         var d = new Dialog { X = expected, Y = expected, Height = 5, Width = 5 };
@@ -1287,6 +1313,8 @@ public class DialogTests
 
         var d = (FakeDriver)Driver;
 
+        Button.DefaultShadow = ShadowStyle.None;
+
         var title = "";
         var btnText = "ok";
 
@@ -1329,6 +1357,8 @@ public class DialogTests
     [AutoInitShutdown]
     public void Size_Not_Default ()
     {
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
         var d = new Dialog { Width = 50, Height = 50 };
 
         Begin (d);
@@ -1370,6 +1400,8 @@ public class DialogTests
         // Override CM
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var dlg = new Dialog
         {

+ 10 - 1
UnitTests/Dialogs/MessageBoxTests.cs

@@ -138,6 +138,8 @@ public class MessageBoxTests
         int iterations = -1;
 
         ((FakeDriver)Application.Driver!).SetBufferSize (15, 15); // 15 x 15 gives us enough room for a button with one char (9x1)
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         Rectangle mbFrame = Rectangle.Empty;
 
@@ -177,6 +179,8 @@ public class MessageBoxTests
         // Override CM
         MessageBox.DefaultButtonAlignment = Alignment.End;
         MessageBox.DefaultBorderStyle = LineStyle.Double;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         Application.Iteration += (s, a) =>
                                  {
@@ -229,6 +233,7 @@ public class MessageBoxTests
                                  };
 
         Application.Run (top);
+        top.Dispose ();
     }
 
     [Fact]
@@ -246,6 +251,8 @@ public class MessageBoxTests
         // Override CM
         MessageBox.DefaultButtonAlignment = Alignment.End;
         MessageBox.DefaultBorderStyle = LineStyle.Double;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         Application.Iteration += (s, a) =>
                                  {
@@ -426,6 +433,8 @@ public class MessageBoxTests
         // Override CM
         MessageBox.DefaultButtonAlignment = Alignment.End;
         MessageBox.DefaultBorderStyle = LineStyle.Double;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         Application.Iteration += (s, a) =>
                                  {
@@ -473,7 +482,7 @@ public class MessageBoxTests
         var top = new Toplevel ();
         top.BorderStyle = LineStyle.Single;
         Application.Run (top);
-
+        top.Dispose ();
     }
 }
 

+ 7 - 7
UnitTests/Drawing/AttributeTests.cs

@@ -16,7 +16,7 @@ public class AttributeTests
     {
         // Arrange & Act
         var foregroundColor = new Color (0, 0, 255);
-        var backgroundColorName = ColorName.Black;
+        var backgroundColorName = ColorName16.Black;
         var attribute = new Attribute (foregroundColor, backgroundColorName);
 
         // Assert
@@ -41,7 +41,7 @@ public class AttributeTests
     public void ColorNamesAndColorConstructor ()
     {
         // Arrange & Act
-        var foregroundColorName = ColorName.BrightYellow;
+        var foregroundColorName = ColorName16.BrightYellow;
         var backgroundColor = new Color (128, 128, 128);
         var attribute = new Attribute (foregroundColorName, backgroundColor);
 
@@ -54,7 +54,7 @@ public class AttributeTests
     public void ColorNamesConstructor ()
     {
         // Arrange & Act
-        var attribute = new Attribute (ColorName.Blue);
+        var attribute = new Attribute (ColorName16.Blue);
 
         // Assert
         Assert.Equal (new Color (Color.Blue), attribute.Foreground);
@@ -278,7 +278,7 @@ public class AttributeTests
     {
         // Arrange
         var foregroundColor = new Color (255, 0);
-        var backgroundColorName = ColorName.White;
+        var backgroundColorName = ColorName16.White;
 
         // Act
         var attribute = new Attribute (foregroundColor, backgroundColorName);
@@ -292,7 +292,7 @@ public class AttributeTests
     public void MakeColorNamesAndColor_ForegroundAndBackgroundShouldMatchInput ()
     {
         // Arrange
-        var foregroundColorName = ColorName.Green;
+        var foregroundColorName = ColorName16.Green;
         var backgroundColor = new Color (128, 128, 128);
 
         // Act
@@ -307,8 +307,8 @@ public class AttributeTests
     public void MakeColorNamesAndColorNames_ForegroundAndBackgroundShouldMatchInput ()
     {
         // Arrange
-        var foregroundColorName = ColorName.BrightYellow;
-        var backgroundColorName = ColorName.Black;
+        var foregroundColorName = ColorName16.BrightYellow;
+        var backgroundColorName = ColorName16.Black;
 
         // Act
         var attribute = new Attribute (foregroundColorName, backgroundColorName);

+ 20 - 20
UnitTests/Drawing/ColorTests.Constructors.cs

@@ -66,13 +66,13 @@ public partial class ColorTests
                         );
     }
 
-    [Theory]
+    [Theory (Skip = "Relies on old ColorName mapping")]
     [MemberData (
                     nameof (ColorTestsTheoryDataGenerators.Constructor_WithColorName_AllChannelsCorrect),
                     MemberType = typeof (ColorTestsTheoryDataGenerators)
                 )]
     public void Constructor_WithColorName_AllChannelsCorrect (
-        ColorName cname,
+        ColorName16 cname,
         ValueTuple<byte, byte, byte> expectedColorValues
     )
     {
@@ -195,25 +195,25 @@ public partial class ColorTests
 
 public static partial class ColorTestsTheoryDataGenerators
 {
-    public static TheoryData<ColorName, ValueTuple<byte, byte, byte>> Constructor_WithColorName_AllChannelsCorrect ()
+    public static TheoryData<ColorName16, ValueTuple<byte, byte, byte>> Constructor_WithColorName_AllChannelsCorrect ()
     {
-        TheoryData<ColorName, ValueTuple<byte, byte, byte>> data = [];
-        data.Add (ColorName.Black, new ValueTuple<byte, byte, byte> (12, 12, 12));
-        data.Add (ColorName.Blue, new ValueTuple<byte, byte, byte> (0, 55, 218));
-        data.Add (ColorName.Green, new ValueTuple<byte, byte, byte> (19, 161, 14));
-        data.Add (ColorName.Cyan, new ValueTuple<byte, byte, byte> (58, 150, 221));
-        data.Add (ColorName.Red, new ValueTuple<byte, byte, byte> (197, 15, 31));
-        data.Add (ColorName.Magenta, new ValueTuple<byte, byte, byte> (136, 23, 152));
-        data.Add (ColorName.Yellow, new ValueTuple<byte, byte, byte> (128, 64, 32));
-        data.Add (ColorName.Gray, new ValueTuple<byte, byte, byte> (204, 204, 204));
-        data.Add (ColorName.DarkGray, new ValueTuple<byte, byte, byte> (118, 118, 118));
-        data.Add (ColorName.BrightBlue, new ValueTuple<byte, byte, byte> (59, 120, 255));
-        data.Add (ColorName.BrightGreen, new ValueTuple<byte, byte, byte> (22, 198, 12));
-        data.Add (ColorName.BrightCyan, new ValueTuple<byte, byte, byte> (97, 214, 214));
-        data.Add (ColorName.BrightRed, new ValueTuple<byte, byte, byte> (231, 72, 86));
-        data.Add (ColorName.BrightMagenta, new ValueTuple<byte, byte, byte> (180, 0, 158));
-        data.Add (ColorName.BrightYellow, new ValueTuple<byte, byte, byte> (249, 241, 165));
-        data.Add (ColorName.White, new ValueTuple<byte, byte, byte> (242, 242, 242));
+        TheoryData<ColorName16, ValueTuple<byte, byte, byte>> data = [];
+        data.Add (ColorName16.Black, new ValueTuple<byte, byte, byte> (12, 12, 12));
+        data.Add (ColorName16.Blue, new ValueTuple<byte, byte, byte> (0, 55, 218));
+        data.Add (ColorName16.Green, new ValueTuple<byte, byte, byte> (19, 161, 14));
+        data.Add (ColorName16.Cyan, new ValueTuple<byte, byte, byte> (58, 150, 221));
+        data.Add (ColorName16.Red, new ValueTuple<byte, byte, byte> (197, 15, 31));
+        data.Add (ColorName16.Magenta, new ValueTuple<byte, byte, byte> (136, 23, 152));
+        data.Add (ColorName16.Yellow, new ValueTuple<byte, byte, byte> (128, 64, 32));
+        data.Add (ColorName16.Gray, new ValueTuple<byte, byte, byte> (204, 204, 204));
+        data.Add (ColorName16.DarkGray, new ValueTuple<byte, byte, byte> (118, 118, 118));
+        data.Add (ColorName16.BrightBlue, new ValueTuple<byte, byte, byte> (59, 120, 255));
+        data.Add (ColorName16.BrightGreen, new ValueTuple<byte, byte, byte> (22, 198, 12));
+        data.Add (ColorName16.BrightCyan, new ValueTuple<byte, byte, byte> (97, 214, 214));
+        data.Add (ColorName16.BrightRed, new ValueTuple<byte, byte, byte> (231, 72, 86));
+        data.Add (ColorName16.BrightMagenta, new ValueTuple<byte, byte, byte> (180, 0, 158));
+        data.Add (ColorName16.BrightYellow, new ValueTuple<byte, byte, byte> (249, 241, 165));
+        data.Add (ColorName16.White, new ValueTuple<byte, byte, byte> (242, 242, 242));
 
         return data;
     }

+ 20 - 20
UnitTests/Drawing/ColorTests.Operators.cs

@@ -58,13 +58,13 @@ public partial class ColorTests
         Assert.Equal (rgba.GetHashCode (), color.GetHashCode ());
     }
 
-    [Theory]
+    [Theory (Skip = "Relies on old ColorName mapping")]
     [Trait ("Category", "Operators")]
     [MemberData (
                     nameof (ColorTestsTheoryDataGenerators.ExplicitOperator_FromColorName_RoundTripsCorrectly),
                     MemberType = typeof (ColorTestsTheoryDataGenerators)
                 )]
-    public void ImplicitOperator_FromColorName_ReturnsCorrectColorValue (ColorName cname, Color expectedColor)
+    public void ImplicitOperator_FromColorName_ReturnsCorrectColorValue (ColorName16 cname, Color expectedColor)
     {
         Color color = cname;
 
@@ -182,26 +182,26 @@ public partial class ColorTests
 
 public static partial class ColorTestsTheoryDataGenerators
 {
-    public static TheoryData<ColorName, Color> ExplicitOperator_FromColorName_RoundTripsCorrectly ()
+    public static TheoryData<ColorName16, Color> ExplicitOperator_FromColorName_RoundTripsCorrectly ()
     {
-        TheoryData<ColorName, Color> data = []
+        TheoryData<ColorName16, Color> data = []
             ;
-        data.Add (ColorName.Black, new Color (12, 12, 12));
-        data.Add (ColorName.Blue, new Color (0, 55, 218));
-        data.Add (ColorName.Green, new Color (19, 161, 14));
-        data.Add (ColorName.Cyan, new Color (58, 150, 221));
-        data.Add (ColorName.Red, new Color (197, 15, 31));
-        data.Add (ColorName.Magenta, new Color (136, 23, 152));
-        data.Add (ColorName.Yellow, new Color (128, 64, 32));
-        data.Add (ColorName.Gray, new Color (204, 204, 204));
-        data.Add (ColorName.DarkGray, new Color (118, 118, 118));
-        data.Add (ColorName.BrightBlue, new Color (59, 120, 255));
-        data.Add (ColorName.BrightGreen, new Color (22, 198, 12));
-        data.Add (ColorName.BrightCyan, new Color (97, 214, 214));
-        data.Add (ColorName.BrightRed, new Color (231, 72, 86));
-        data.Add (ColorName.BrightMagenta, new Color (180, 0, 158));
-        data.Add (ColorName.BrightYellow, new Color (249, 241, 165));
-        data.Add (ColorName.White, new Color (242, 242, 242));
+        data.Add (ColorName16.Black, new Color (12, 12, 12));
+        data.Add (ColorName16.Blue, new Color (0, 55, 218));
+        data.Add (ColorName16.Green, new Color (19, 161, 14));
+        data.Add (ColorName16.Cyan, new Color (58, 150, 221));
+        data.Add (ColorName16.Red, new Color (197, 15, 31));
+        data.Add (ColorName16.Magenta, new Color (136, 23, 152));
+        data.Add (ColorName16.Yellow, new Color (128, 64, 32));
+        data.Add (ColorName16.Gray, new Color (204, 204, 204));
+        data.Add (ColorName16.DarkGray, new Color (118, 118, 118));
+        data.Add (ColorName16.BrightBlue, new Color (59, 120, 255));
+        data.Add (ColorName16.BrightGreen, new Color (22, 198, 12));
+        data.Add (ColorName16.BrightCyan, new Color (97, 214, 214));
+        data.Add (ColorName16.BrightRed, new Color (231, 72, 86));
+        data.Add (ColorName16.BrightMagenta, new Color (180, 0, 158));
+        data.Add (ColorName16.BrightYellow, new Color (249, 241, 165));
+        data.Add (ColorName16.White, new Color (242, 242, 242));
 
         return data;
     }

+ 2 - 2
UnitTests/Drawing/ColorTests.ParsingAndFormatting.cs

@@ -10,7 +10,7 @@ public partial class ColorTests
     public void Color_ToString_WithNamedColor ()
     {
         // Arrange
-        var color = new Color (0, 55, 218); // Blue
+        var color = new Color (ColorName16.Blue);// Blue
 
         // Act
         var colorString = color.ToString ();
@@ -59,7 +59,7 @@ public partial class ColorTests
         Assert.Equal (constructedColor.Argb, parsedColor.Argb);
     }
 
-    [Theory]
+    [Theory (Skip = "Doesn't utilize W3ColorNames")]
     [CombinatorialData]
     public void ToString_WithInvariantCultureAndNullString_IsSameAsParameterless (
         [CombinatorialValues (0, 64, 128, 255)] byte r,

+ 20 - 20
UnitTests/Drawing/ColorTests.TypeChecks.cs

@@ -8,7 +8,7 @@ public partial class ColorTests
     [Fact]
     [Trait ("Category", "Type Checks")]
     [Trait ("Category", "Change Control")]
-    public void ColorName_Has_Exactly_16_Defined_Values () { Assert.Equal (16, Enum.GetValues<ColorName> ().DistinctBy (static cname => (int)cname).Count ()); }
+    public void ColorName_Has_Exactly_16_Defined_Values () { Assert.Equal (16, Enum.GetValues<ColorName16> ().DistinctBy (static cname => (int)cname).Count ()); }
 
     [Theory]
     [Trait ("Category", "Type Checks")]
@@ -17,7 +17,7 @@ public partial class ColorTests
                     nameof (ColorTestsTheoryDataGenerators.ColorName_HasCorrectOrdinals),
                     MemberType = typeof (ColorTestsTheoryDataGenerators)
                 )]
-    public void ColorName_HasCorrectOrdinals (ColorName cname, int ordinal) { Assert.Equal ((int)cname, ordinal); }
+    public void ColorName_HasCorrectOrdinals (ColorName16 cname, int ordinal) { Assert.Equal ((int)cname, ordinal); }
 
     [Fact]
     [Trait ("Category", "Type Checks")]
@@ -117,26 +117,26 @@ public partial class ColorTests
 
 public static partial class ColorTestsTheoryDataGenerators
 {
-    public static TheoryData<ColorName, int> ColorName_HasCorrectOrdinals ()
+    public static TheoryData<ColorName16, int> ColorName_HasCorrectOrdinals ()
     {
-        TheoryData<ColorName, int> data = []
+        TheoryData<ColorName16, int> data = []
             ;
-        data.Add (ColorName.Black, 0);
-        data.Add (ColorName.Blue, 1);
-        data.Add (ColorName.Green, 2);
-        data.Add (ColorName.Cyan, 3);
-        data.Add (ColorName.Red, 4);
-        data.Add (ColorName.Magenta, 5);
-        data.Add (ColorName.Yellow, 6);
-        data.Add (ColorName.Gray, 7);
-        data.Add (ColorName.DarkGray, 8);
-        data.Add (ColorName.BrightBlue, 9);
-        data.Add (ColorName.BrightGreen, 10);
-        data.Add (ColorName.BrightCyan, 11);
-        data.Add (ColorName.BrightRed, 12);
-        data.Add (ColorName.BrightMagenta, 13);
-        data.Add (ColorName.BrightYellow, 14);
-        data.Add (ColorName.White, 15);
+        data.Add (ColorName16.Black, 0);
+        data.Add (ColorName16.Blue, 1);
+        data.Add (ColorName16.Green, 2);
+        data.Add (ColorName16.Cyan, 3);
+        data.Add (ColorName16.Red, 4);
+        data.Add (ColorName16.Magenta, 5);
+        data.Add (ColorName16.Yellow, 6);
+        data.Add (ColorName16.Gray, 7);
+        data.Add (ColorName16.DarkGray, 8);
+        data.Add (ColorName16.BrightBlue, 9);
+        data.Add (ColorName16.BrightGreen, 10);
+        data.Add (ColorName16.BrightCyan, 11);
+        data.Add (ColorName16.BrightRed, 12);
+        data.Add (ColorName16.BrightMagenta, 13);
+        data.Add (ColorName16.BrightYellow, 14);
+        data.Add (ColorName16.White, 15);
 
         return data;
     }

+ 18 - 18
UnitTests/Drawing/ColorTests.cs

@@ -21,15 +21,15 @@ public partial class ColorTests
         Assert.Equal (expectedArgb, color.Argb);
     }
 
-    [Fact]
+    [Fact (Skip = "Relies on old ColorName mapping")]
     public void Color_ColorName_Get_ReturnsClosestColorName ()
     {
         // Arrange
         var color = new Color (128, 64, 40); // Custom RGB color, closest to Yellow
-        var expectedColorName = ColorName.Yellow;
+        var expectedColorName = ColorName16.Yellow;
 
         // Act
-        ColorName colorName = color.GetClosestNamedColor ();
+        ColorName16 colorName = color.GetClosestNamedColor16 ();
 
         // Assert
         Assert.Equal (expectedColorName, colorName);
@@ -39,22 +39,22 @@ public partial class ColorTests
     public void Color_IsClosestToNamedColor_ReturnsExpectedValue ()
     {
         // Arrange
-        var color1 = new Color (ColorName.Red);
+        var color1 = new Color (ColorName16.Red);
         var color2 = new Color (197, 15, 31); // Red in RGB
 
-        Assert.True (color1.IsClosestToNamedColor (ColorName.Red));
+        Assert.True (color1.IsClosestToNamedColor16 (ColorName16.Red));
 
-        Assert.True (color2.IsClosestToNamedColor (ColorName.Red));
+        Assert.True (color2.IsClosestToNamedColor16 (ColorName16.Red));
     }
 
-    [Theory]
+    [Theory (Skip = "Test data is now bogus")]
     [MemberData (
                     nameof (ColorTestsTheoryDataGenerators.FindClosestColor_ReturnsClosestColor),
                     MemberType = typeof (ColorTestsTheoryDataGenerators)
                 )]
-    public void FindClosestColor_ReturnsClosestColor (Color inputColor, ColorName expectedColorName)
+    public void FindClosestColor_ReturnsClosestColor (Color inputColor, ColorName16 expectedColorName)
     {
-        ColorName actualColorName = Color.GetClosestNamedColor (inputColor);
+        ColorName16 actualColorName = Color.GetClosestNamedColor16 (inputColor);
 
         Assert.Equal (expectedColorName, actualColorName);
     }
@@ -79,16 +79,16 @@ public partial class ColorTests
 
 public static partial class ColorTestsTheoryDataGenerators
 {
-    public static TheoryData<Color, ColorName> FindClosestColor_ReturnsClosestColor ()
+    public static TheoryData<Color, ColorName16> FindClosestColor_ReturnsClosestColor ()
     {
-        TheoryData<Color, ColorName> data = [];
-        data.Add (new Color (0, 0), ColorName.Black);
-        data.Add (new Color (255, 255, 255), ColorName.White);
-        data.Add (new Color (5, 100, 255), ColorName.BrightBlue);
-        data.Add (new Color (0, 255), ColorName.BrightGreen);
-        data.Add (new Color (255, 70, 8), ColorName.BrightRed);
-        data.Add (new Color (0, 128, 128), ColorName.Cyan);
-        data.Add (new Color (128, 64, 32), ColorName.Yellow);
+        TheoryData<Color, ColorName16> data = [];
+        data.Add (new Color (0, 0), ColorName16.Black);
+        data.Add (new Color (255, 255, 255), ColorName16.White);
+        data.Add (new Color (5, 100, 255), ColorName16.BrightBlue);
+        data.Add (new Color (0, 255), ColorName16.BrightGreen);
+        data.Add (new Color (255, 70, 8), ColorName16.BrightRed);
+        data.Add (new Color (0, 128, 128), ColorName16.Cyan);
+        data.Add (new Color (128, 64, 32), ColorName16.Yellow);
 
         return data;
     }

+ 6 - 0
UnitTests/FileServices/FileDialogTests.cs

@@ -616,6 +616,8 @@ public class FileDialogTests ()
         Window.DefaultBorderStyle = LineStyle.Single;
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         var dlg = new FileDialog ();
         Begin (dlg);
@@ -628,6 +630,8 @@ public class FileDialogTests ()
         Window.DefaultBorderStyle = LineStyle.Single;
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         // Arrange
         var fileSystem = new MockFileSystem (new Dictionary<string, MockFileData> (), "/");
@@ -676,6 +680,8 @@ public class FileDialogTests ()
         Window.DefaultBorderStyle = LineStyle.Single;
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         // Arrange
         var fileSystem = new MockFileSystem (new Dictionary<string, MockFileData> (), @"c:\");

+ 0 - 2
UnitTests/Input/ResponderTests.cs

@@ -235,8 +235,6 @@ public class ResponderTests
         Assert.False (r.OnKeyDown (new Key { KeyCode = KeyCode.Null }));
         Assert.False (r.OnKeyUp (new Key { KeyCode = KeyCode.Null }));
         Assert.False (r.NewMouseEvent (new MouseEvent { Flags = MouseFlags.AllEvents }));
-        Assert.False (r.NewMouseEnterEvent (new MouseEvent { Flags = MouseFlags.AllEvents }));
-        Assert.False (r.NewMouseLeaveEvent (new MouseEvent { Flags = MouseFlags.AllEvents }));
 
         var v = new View ();
         //Assert.False (r.OnEnter (v));

+ 11 - 1
UnitTests/Resources/ResourceManagerTests.cs

@@ -9,8 +9,10 @@ namespace Terminal.Gui.ResourcesTests;
 
 public class ResourceManagerTests
 {
-    private const string DODGER_BLUE_COLOR_KEY = "#1E90FF";
+    private const string DODGER_BLUE_COLOR_KEY = "DodgerBlue";
     private const string DODGER_BLUE_COLOR_NAME = "DodgerBlue";
+    private const string NO_NAMED_COLOR_KEY = "#1E80FF";
+    private const string NO_NAMED_COLOR_NAME = "#1E80FF";
     private const string EXISTENT_CULTURE = "pt-PT";
     private const string NO_EXISTENT_CULTURE = "de-DE";
     private const string NO_EXISTENT_KEY = "blabla";
@@ -82,6 +84,14 @@ public class ResourceManagerTests
         Assert.True (ColorStrings.TryParseW3CColorName (DODGER_BLUE_COLOR_NAME, out Color color));
         Assert.Equal (DODGER_BLUE_COLOR_KEY, color.ToString ());
 
+        // W3CColors.GetColorNames also calls ColorStrings.GetW3CColorNames for no-named colors
+        colorNames = new W3CColors ().GetColorNames ().ToArray ();
+        Assert.DoesNotContain (NO_NAMED_COLOR_NAME, colorNames);
+
+        // ColorStrings.TryParseW3CColorName method uses GetResourceSet method to retrieve a color value for no-named colors
+        Assert.True (ColorStrings.TryParseW3CColorName (NO_NAMED_COLOR_NAME, out color));
+        Assert.Equal (NO_NAMED_COLOR_KEY, color.ToString ());
+
         RestoreCurrentCultures ();
     }
 

+ 28 - 77
UnitTests/TestHelpers.cs

@@ -5,6 +5,7 @@ using System.Text;
 using System.Text.RegularExpressions;
 using Xunit.Abstractions;
 using Xunit.Sdk;
+using static Terminal.Gui.ConfigurationManager;
 
 namespace Terminal.Gui;
 
@@ -47,7 +48,7 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
         bool useFakeClipboard = true,
         bool fakeClipboardAlwaysThrowsNotSupportedException = false,
         bool fakeClipboardIsSupportedAlwaysTrue = false,
-        ConfigurationManager.ConfigLocations configLocation = ConfigurationManager.ConfigLocations.DefaultOnly
+        ConfigurationManager.ConfigLocations configLocation = ConfigurationManager.ConfigLocations.None
     )
     {
         AutoInit = autoInit;
@@ -72,25 +73,37 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
 
         if (AutoInit)
         {
-            // TODO: This Dispose call is here until all unit tests that don't correctly dispose Toplevel's they create are fixed.
-            Application.Top?.Dispose ();
-            Application.Shutdown ();
+            try
+            {
+                // TODO: This Dispose call is here until all unit tests that don't correctly dispose Toplevel's they create are fixed.
+                //Application.Top?.Dispose ();
+                Application.Shutdown ();
 #if DEBUG_IDISPOSABLE
-            if (Responder.Instances.Count == 0)
+                if (Responder.Instances.Count == 0)
+                {
+                    Assert.Empty (Responder.Instances);
+                }
+                else
+                {
+                    Responder.Instances.Clear ();
+                }
+#endif
+            }
+            catch (Exception e)
             {
-                Assert.Empty (Responder.Instances);
+                Assert.Fail ($"Application.Shutdown threw an exception after the test exited: {e}");
             }
-            else
+            finally
             {
+#if DEBUG_IDISPOSABLE
                 Responder.Instances.Clear ();
-            }
+                Application.ResetState (ignoreDisposed: true);
 #endif
-            ConfigurationManager.Reset ();
-
-            if (CM.Locations != CM.ConfigLocations.None)
-            {
-                SetCurrentConfig (_savedValues);
+                ConfigurationManager.Reset ();
+                ConfigurationManager.Locations = CM.ConfigLocations.None;
             }
+
+
         }
     }
 
@@ -115,11 +128,6 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
             }
 #endif
             Application.Init ((ConsoleDriver)Activator.CreateInstance (_driverType));
-
-            if (CM.Locations != CM.ConfigLocations.None)
-            {
-                _savedValues = GetCurrentConfig ();
-            }
         }
     }
 
@@ -127,65 +135,7 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
 
     private List<object> _savedValues;
 
-    private List<object> GetCurrentConfig ()
-    {
-        CM.Reset ();
-
-        List<object> savedValues =
-        [
-            Dialog.DefaultButtonAlignment,
-            Dialog.DefaultButtonAlignmentModes,
-            MessageBox.DefaultBorderStyle
-        ];
-        CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = Alignment.End;
-        CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignmentModes"].PropertyValue = AlignmentModes.AddSpaceBetweenItems;
-        CM.Themes! ["Default"] ["MessageBox.DefaultBorderStyle"].PropertyValue = LineStyle.Double;
-        ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
-
-        return savedValues;
-    }
-
-    private void SetCurrentConfig (List<object> values)
-    {
-        CM.Reset ();
-        bool needApply = false;
-
-        foreach (object value in values)
-        {
-            switch (value)
-            {
-                case Alignment alignment:
-                    if ((Alignment)CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue! != alignment)
-                    {
-                        needApply = true;
-                        CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = alignment;
-                    }
-
-                    break;
-                case AlignmentModes alignmentModes:
-                    if ((AlignmentModes)CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignmentModes"].PropertyValue! != alignmentModes)
-                    {
-                        needApply = true;
-                        CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignmentModes"].PropertyValue = alignmentModes;
-                    }
-
-                    break;
-                case LineStyle lineStyle:
-                    if ((LineStyle)CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue! != lineStyle)
-                    {
-                        needApply = true;
-                        CM.Themes! ["Default"] ["MessageBox.DefaultBorderStyle"].PropertyValue = lineStyle;
-                    }
-
-                    break;
-            }
-        }
-
-        if (needApply)
-        {
-            ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
-        }
-    }
+   
 }
 
 [AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]
@@ -243,6 +193,7 @@ public class SetupFakeDriverAttribute : BeforeAfterTestAttribute
         Application.ResetState (ignoreDisposed: true);
         Assert.Null (Application.Driver);
         Application.Driver = new FakeDriver { Rows = 25, Cols = 25 };
+
         base.Before (methodUnderTest);
     }
 }

+ 12 - 10
UnitTests/Text/TextFormatterTests.cs

@@ -3920,6 +3920,8 @@ public class TextFormatterTests
     public void Draw_Vertical_Throws_IndexOutOfRangeException_With_Negative_Bounds ()
     {
         Application.Init (new FakeDriver ());
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         Toplevel top = new ();
 
@@ -4141,8 +4143,8 @@ ssb
 
         tf.Draw (
                  new (0, 0, width, height),
-                 new (ColorName.White, ColorName.Black),
-                 new (ColorName.Blue, ColorName.Black),
+                 new (ColorName16.White, ColorName16.Black),
+                 new (ColorName16.Blue, ColorName16.Black),
                  default (Rectangle),
                  driver
                 );
@@ -4159,8 +4161,8 @@ ssb
 
         Attribute [] attrs =
         {
-            Attribute.Default, new (ColorName.Green, ColorName.BrightMagenta),
-            new (ColorName.Blue, ColorName.Cyan)
+            Attribute.Default, new (ColorName16.Green, ColorName16.BrightMagenta),
+            new (ColorName16.Blue, ColorName16.Cyan)
         };
         var tf = new TextFormatter { ConstrainToSize = new (14, 3), Text = "Test\nTest long\nTest long long\n", MultiLine = true };
 
@@ -5963,8 +5965,8 @@ ek")]
 
         tf.Draw (
                  new (0, 0, width, height),
-                 new (ColorName.White, ColorName.Black),
-                 new (ColorName.Blue, ColorName.Black),
+                 new (ColorName16.White, ColorName16.Black),
+                 new (ColorName16.Blue, ColorName16.Black),
                  default (Rectangle),
                  driver
                 );
@@ -6003,8 +6005,8 @@ ek")]
 
         tf.Draw (
                  new (0, 0, width, height),
-                 new (ColorName.White, ColorName.Black),
-                 new (ColorName.Blue, ColorName.Black),
+                 new (ColorName16.White, ColorName16.Black),
+                 new (ColorName16.Blue, ColorName16.Black),
                  default (Rectangle),
                  driver
                 );
@@ -6043,8 +6045,8 @@ ek")]
 
         tf.Draw (
                  new (0, 0, width, height),
-                 new (ColorName.White, ColorName.Black),
-                 new (ColorName.Blue, ColorName.Black),
+                 new (ColorName16.White, ColorName16.Black),
+                 new (ColorName16.Blue, ColorName16.Black),
                  default (Rectangle),
                  driver
                 );

+ 14 - 11
UnitTests/View/Adornment/AdornmentSubViewTests.cs

@@ -12,14 +12,14 @@ public class AdornmentSubViewTests (ITestOutputHelper output)
     [InlineData (1, 0, true)]
     [InlineData (1, 1, true)]
     [InlineData (2, 1, true)]
-    public void Adornment_WithSubView_FindDeepestView_Finds (int viewMargin, int subViewMargin, bool expectedFound)
+    public void Adornment_WithSubView_GetViewsUnderMouse_Finds (int viewMargin, int subViewMargin, bool expectedFound)
     {
-        var view = new View ()
+        Application.Top = new Toplevel()
         {
             Width = 10,
             Height = 10
         };
-        view.Margin.Thickness = new Thickness (viewMargin);
+        Application.Top.Margin.Thickness = new Thickness (viewMargin);
 
         var subView = new View ()
         {
@@ -29,24 +29,25 @@ public class AdornmentSubViewTests (ITestOutputHelper output)
             Height = 5
         };
         subView.Margin.Thickness = new Thickness (subViewMargin);
-        view.Margin.Add (subView);
+        Application.Top.Margin.Add (subView);
 
-        var foundView = View.FindDeepestView (view, new (0, 0));
+        var foundView = View.GetViewsUnderMouse (new Point(0, 0)).LastOrDefault ();
 
         bool found = foundView == subView || foundView == subView.Margin;
         Assert.Equal (expectedFound, found);
+        Application.Top.Dispose ();
+        Application.ResetState (ignoreDisposed: true);
     }
 
     [Fact]
-    public void Adornment_WithNonVisibleSubView_FindDeepestView_Finds_Adornment ()
+    public void Adornment_WithNonVisibleSubView_GetViewsUnderMouse_Finds_Adornment ()
     {
-        var view = new View ()
+        Application.Top = new Toplevel ()
         {
             Width = 10,
             Height = 10
-
         };
-        view.Padding.Thickness = new Thickness (1);
+        Application.Top.Padding.Thickness = new Thickness (1);
 
         var subView = new View ()
         {
@@ -56,9 +57,11 @@ public class AdornmentSubViewTests (ITestOutputHelper output)
             Height = 1,
             Visible = false
         };
-        view.Padding.Add (subView);
+        Application.Top.Padding.Add (subView);
 
-        Assert.Equal (view.Padding, View.FindDeepestView (view, new (0, 0)));
+        Assert.Equal (Application.Top.Padding, View.GetViewsUnderMouse (new Point(0, 0)).LastOrDefault ());
+        Application.Top?.Dispose ();
+        Application.ResetState (ignoreDisposed: true);
     }
 
     [Fact]

+ 4 - 4
UnitTests/View/Adornment/BorderTests.cs

@@ -24,8 +24,8 @@ public class BorderTests (ITestOutputHelper output)
             Focus = new (Color.Green, Color.Red)
         };
         Assert.NotEqual (view.ColorScheme.Normal.Foreground, view.ColorScheme.Focus.Foreground);
-        Assert.Equal (ColorName.Red, view.Border.GetNormalColor ().Foreground.GetClosestNamedColor ());
-        Assert.Equal (ColorName.Green, view.Border.GetFocusColor ().Foreground.GetClosestNamedColor ());
+        Assert.Equal (ColorName16.Red, view.Border.GetNormalColor ().Foreground.GetClosestNamedColor16 ());
+        Assert.Equal (ColorName16.Green, view.Border.GetFocusColor ().Foreground.GetClosestNamedColor16 ());
         Assert.Equal (view.GetFocusColor (), view.Border.GetFocusColor ());
 
         superView.BeginInit ();
@@ -57,8 +57,8 @@ public class BorderTests (ITestOutputHelper output)
         {
             Normal = new (Color.Red, Color.Green), Focus = new (Color.Green, Color.Red)
         };
-        Assert.Equal (ColorName.Red, view.Border.GetNormalColor ().Foreground.GetClosestNamedColor ());
-        Assert.Equal (ColorName.Green, view.Border.GetFocusColor ().Foreground.GetClosestNamedColor ());
+        Assert.Equal (ColorName16.Red, view.Border.GetNormalColor ().Foreground.GetClosestNamedColor16 ());
+        Assert.Equal (ColorName16.Green, view.Border.GetFocusColor ().Foreground.GetClosestNamedColor16 ());
         Assert.Equal (view.GetNormalColor (), view.Border.GetNormalColor ());
         Assert.Equal (view.GetFocusColor (), view.Border.GetFocusColor ());
 

+ 2 - 2
UnitTests/View/Adornment/MarginTests.cs

@@ -20,8 +20,8 @@ public class MarginTests (ITestOutputHelper output)
         };
 
         superView.Add (view);
-        Assert.Equal (ColorName.Red, view.Margin.GetNormalColor ().Foreground.GetClosestNamedColor ());
-        Assert.Equal (ColorName.Red, superView.GetNormalColor ().Foreground.GetClosestNamedColor ());
+        Assert.Equal (ColorName16.Red, view.Margin.GetNormalColor ().Foreground.GetClosestNamedColor16 ());
+        Assert.Equal (ColorName16.Red, superView.GetNormalColor ().Foreground.GetClosestNamedColor16 ());
         Assert.Equal (superView.GetNormalColor (), view.Margin.GetNormalColor ());
         Assert.Equal (superView.GetFocusColor (), view.Margin.GetFocusColor ());
 

+ 1 - 1
UnitTests/View/Adornment/PaddingTests.cs

@@ -17,7 +17,7 @@ public class PaddingTests (ITestOutputHelper output)
             Normal = new (Color.Red, Color.Green), Focus = new (Color.Green, Color.Red)
         };
 
-        Assert.Equal (ColorName.Red, view.Padding.GetNormalColor ().Foreground.GetClosestNamedColor ());
+        Assert.Equal (ColorName16.Red, view.Padding.GetNormalColor ().Foreground.GetClosestNamedColor16 ());
         Assert.Equal (view.GetNormalColor (), view.Padding.GetNormalColor ());
 
         view.BeginInit ();

+ 0 - 536
UnitTests/View/FindDeepestViewTests.cs

@@ -1,536 +0,0 @@
-
-#nullable enable
-using Microsoft.VisualStudio.TestPlatform.Utilities;
-using Xunit.Abstractions;
-
-namespace Terminal.Gui.ViewTests;
-
-/// <summary>
-/// Tests View.FindDeepestView
-/// </summary>
-/// <param name="output"></param>
-public class FindDeepestViewTests ()
-{
-    [Theory]
-    [InlineData (0, 0, 0, 0, 0, -1, -1, null)]
-    [InlineData (0, 0, 0, 0, 0, 0, 0, typeof (View))]
-    [InlineData (0, 0, 0, 0, 0, 1, 1, typeof (View))]
-    [InlineData (0, 0, 0, 0, 0, 4, 4, typeof (View))]
-    [InlineData (0, 0, 0, 0, 0, 9, 9, typeof (View))]
-    [InlineData (0, 0, 0, 0, 0, 10, 10, null)]
-
-    [InlineData (1, 1, 0, 0, 0, -1, -1, null)]
-    [InlineData (1, 1, 0, 0, 0, 0, 0, null)]
-    [InlineData (1, 1, 0, 0, 0, 1, 1, typeof (View))]
-    [InlineData (1, 1, 0, 0, 0, 4, 4, typeof (View))]
-    [InlineData (1, 1, 0, 0, 0, 9, 9, typeof (View))]
-    [InlineData (1, 1, 0, 0, 0, 10, 10, typeof (View))]
-
-    [InlineData (0, 0, 1, 0, 0, -1, -1, null)]
-    [InlineData (0, 0, 1, 0, 0, 0, 0, typeof (Margin))]
-    [InlineData (0, 0, 1, 0, 0, 1, 1, typeof (View))]
-    [InlineData (0, 0, 1, 0, 0, 4, 4, typeof (View))]
-    [InlineData (0, 0, 1, 0, 0, 9, 9, typeof (Margin))]
-    [InlineData (0, 0, 1, 0, 0, 10, 10, null)]
-
-    [InlineData (0, 0, 1, 1, 0, -1, -1, null)]
-    [InlineData (0, 0, 1, 1, 0, 0, 0, typeof (Margin))]
-    [InlineData (0, 0, 1, 1, 0, 1, 1, typeof (Border))]
-    [InlineData (0, 0, 1, 1, 0, 4, 4, typeof (View))]
-    [InlineData (0, 0, 1, 1, 0, 9, 9, typeof (Margin))]
-    [InlineData (0, 0, 1, 1, 0, 10, 10, null)]
-
-    [InlineData (0, 0, 1, 1, 1, -1, -1, null)]
-    [InlineData (0, 0, 1, 1, 1, 0, 0, typeof (Margin))]
-    [InlineData (0, 0, 1, 1, 1, 1, 1, typeof (Border))]
-    [InlineData (0, 0, 1, 1, 1, 2, 2, typeof (Padding))]
-    [InlineData (0, 0, 1, 1, 1, 4, 4, typeof (View))]
-    [InlineData (0, 0, 1, 1, 1, 9, 9, typeof (Margin))]
-    [InlineData (0, 0, 1, 1, 1, 10, 10, null)]
-
-    [InlineData (1, 1, 1, 0, 0, -1, -1, null)]
-    [InlineData (1, 1, 1, 0, 0, 0, 0, null)]
-    [InlineData (1, 1, 1, 0, 0, 1, 1, typeof (Margin))]
-    [InlineData (1, 1, 1, 0, 0, 4, 4, typeof (View))]
-    [InlineData (1, 1, 1, 0, 0, 9, 9, typeof (View))]
-    [InlineData (1, 1, 1, 0, 0, 10, 10, typeof (Margin))]
-    [InlineData (1, 1, 1, 1, 0, -1, -1, null)]
-    [InlineData (1, 1, 1, 1, 0, 0, 0, null)]
-    [InlineData (1, 1, 1, 1, 0, 1, 1, typeof (Margin))]
-    [InlineData (1, 1, 1, 1, 0, 4, 4, typeof (View))]
-    [InlineData (1, 1, 1, 1, 0, 9, 9, typeof (Border))]
-    [InlineData (1, 1, 1, 1, 0, 10, 10, typeof (Margin))]
-    [InlineData (1, 1, 1, 1, 1, -1, -1, null)]
-    [InlineData (1, 1, 1, 1, 1, 0, 0, null)]
-    [InlineData (1, 1, 1, 1, 1, 1, 1, typeof (Margin))]
-    [InlineData (1, 1, 1, 1, 1, 2, 2, typeof (Border))]
-    [InlineData (1, 1, 1, 1, 1, 3, 3, typeof (Padding))]
-    [InlineData (1, 1, 1, 1, 1, 4, 4, typeof (View))]
-    [InlineData (1, 1, 1, 1, 1, 8, 8, typeof (Padding))]
-    [InlineData (1, 1, 1, 1, 1, 9, 9, typeof (Border))]
-    [InlineData (1, 1, 1, 1, 1, 10, 10, typeof (Margin))]
-    public void Contains (int frameX, int frameY, int marginThickness, int borderThickness, int paddingThickness, int testX, int testY, Type? expectedAdornmentType)
-    {
-        var view = new View ()
-        {
-            X = frameX, Y = frameY,
-            Width = 10, Height = 10,
-        };
-        view.Margin.Thickness = new Thickness (marginThickness);
-        view.Border.Thickness = new Thickness (borderThickness);
-        view.Padding.Thickness = new Thickness (paddingThickness);
-
-        Type? containedType = null;
-        if (view.Contains (new (testX, testY)))
-        {
-            containedType = view.GetType ();
-        }
-
-        if (view.Margin.Contains (new (testX, testY)))
-        {
-            containedType = view.Margin.GetType ();
-        }
-
-        if (view.Border.Contains (new (testX, testY)))
-        {
-            containedType = view.Border.GetType ();
-        }
-
-        if (view.Padding.Contains (new (testX, testY)))
-        {
-            containedType = view.Padding.GetType ();
-        }
-        Assert.Equal (expectedAdornmentType, containedType);
-
-    }
-
-    // Test that FindDeepestView returns the correct view if the start view has no subviews
-    [Theory]
-    [InlineData (0, 0)]
-    [InlineData (1, 1)]
-    [InlineData (2, 2)]
-    public void Returns_Start_If_No_SubViews (int testX, int testY)
-    {
-        var start = new View ()
-        {
-            Width = 10, Height = 10,
-        };
-
-        Assert.Same (start, View.FindDeepestView (start, new (testX, testY)));
-    }
-
-    // Test that FindDeepestView returns null if the start view has no subviews and coords are outside the view
-    [Theory]
-    [InlineData (0, 0)]
-    [InlineData (2, 1)]
-    [InlineData (20, 20)]
-    public void Returns_Null_If_No_SubViews_Coords_Outside (int testX, int testY)
-    {
-        var start = new View ()
-        {
-            X = 1, Y = 2,
-            Width = 10, Height = 10,
-        };
-
-        Assert.Null (View.FindDeepestView (start, new (testX, testY)));
-    }
-
-    [Theory]
-    [InlineData (0, 0)]
-    [InlineData (2, 1)]
-    [InlineData (20, 20)]
-    public void Returns_Null_If_Start_Not_Visible (int testX, int testY)
-    {
-        var start = new View ()
-        {
-            X = 1, Y = 2,
-            Width = 10, Height = 10,
-            Visible = false,
-        };
-
-        Assert.Null (View.FindDeepestView (start, new (testX, testY)));
-    }
-
-    // Test that FindDeepestView returns the correct view if the start view has subviews
-    [Theory]
-    [InlineData (0, 0, false)]
-    [InlineData (1, 1, false)]
-    [InlineData (9, 9, false)]
-    [InlineData (10, 10, false)]
-    [InlineData (6, 7, false)]
-
-    [InlineData (1, 2, true)]
-    [InlineData (5, 6, true)]
-    public void Returns_Correct_If_SubViews (int testX, int testY, bool expectedSubViewFound)
-    {
-        var start = new View ()
-        {
-            Width = 10, Height = 10,
-        };
-
-        var subview = new View ()
-        {
-            X = 1, Y = 2,
-            Width = 5, Height = 5,
-        };
-        start.Add (subview);
-
-        var found = View.FindDeepestView (start, new (testX, testY));
-
-        Assert.Equal (expectedSubViewFound, found == subview);
-    }
-
-    [Theory]
-    [InlineData (0, 0, false)]
-    [InlineData (1, 1, false)]
-    [InlineData (9, 9, false)]
-    [InlineData (10, 10, false)]
-    [InlineData (6, 7, false)]
-    [InlineData (1, 2, false)]
-    [InlineData (5, 6, false)]
-    public void Returns_Null_If_SubView_NotVisible (int testX, int testY, bool expectedSubViewFound)
-    {
-        var start = new View ()
-        {
-            Width = 10, Height = 10,
-        };
-
-        var subview = new View ()
-        {
-            X = 1, Y = 2,
-            Width = 5, Height = 5,
-            Visible = false
-        };
-        start.Add (subview);
-
-        var found = View.FindDeepestView (start, new (testX, testY));
-
-        Assert.Equal (expectedSubViewFound, found == subview);
-    }
-
-
-    [Theory]
-    [InlineData (0, 0, false)]
-    [InlineData (1, 1, false)]
-    [InlineData (9, 9, false)]
-    [InlineData (10, 10, false)]
-    [InlineData (6, 7, false)]
-    [InlineData (1, 2, false)]
-    [InlineData (5, 6, false)]
-    public void Returns_Null_If_Not_Visible_And_SubView_Visible (int testX, int testY, bool expectedSubViewFound)
-    {
-        var start = new View ()
-        {
-            Width = 10, Height = 10,
-            Visible = false
-        };
-
-        var subview = new View ()
-        {
-            X = 1, Y = 2,
-            Width = 5, Height = 5,
-        };
-        start.Add (subview);
-        subview.Visible = true;
-        Assert.True (subview.Visible);
-        Assert.False (start.Visible);
-        var found = View.FindDeepestView (start, new (testX, testY));
-
-        Assert.Equal (expectedSubViewFound, found == subview);
-    }
-
-    // Test that FindDeepestView works if the start view has positive Adornments
-    [Theory]
-    [InlineData (0, 0, false)]
-    [InlineData (1, 1, false)]
-    [InlineData (9, 9, false)]
-    [InlineData (10, 10, false)]
-    [InlineData (7, 8, false)]
-    [InlineData (1, 2, false)]
-
-    [InlineData (2, 3, true)]
-    [InlineData (5, 6, true)]
-    [InlineData (6, 7, true)]
-    public void Returns_Correct_If_Start_Has_Adornments (int testX, int testY, bool expectedSubViewFound)
-    {
-        var start = new View ()
-        {
-            Width = 10, Height = 10,
-        };
-        start.Margin.Thickness = new Thickness (1);
-
-        var subview = new View ()
-        {
-            X = 1, Y = 2,
-            Width = 5, Height = 5,
-        };
-        start.Add (subview);
-
-        var found = View.FindDeepestView (start, new (testX, testY));
-
-        Assert.Equal (expectedSubViewFound, found == subview);
-    }
-
-    // Test that FindDeepestView works if the start view has offset Viewport location
-    [Theory]
-    [InlineData (1, 0, 0, true)]
-    [InlineData (1, 1, 1, true)]
-    [InlineData (1, 2, 2, false)]
-
-    [InlineData (-1, 3, 3, true)]
-    [InlineData (-1, 2, 2, true)]
-    [InlineData (-1, 1, 1, false)]
-    [InlineData (-1, 0, 0, false)]
-    public void Returns_Correct_If_Start_Has_Offset_Viewport (int offset, int testX, int testY, bool expectedSubViewFound)
-    {
-        var start = new View ()
-        {
-            Width = 10, Height = 10,
-            ViewportSettings = ViewportSettings.AllowNegativeLocation
-        };
-        start.Viewport = new (offset, offset, 10, 10);
-
-        var subview = new View ()
-        {
-            X = 1, Y = 1,
-            Width = 2, Height = 2,
-        };
-        start.Add (subview);
-
-        var found = View.FindDeepestView (start, new (testX, testY));
-
-        Assert.Equal (expectedSubViewFound, found == subview);
-    }
-
-    [Theory]
-    [InlineData (9, 9, true)]
-    [InlineData (0, 0, false)]
-    [InlineData (1, 1, false)]
-    [InlineData (10, 10, false)]
-    [InlineData (7, 8, false)]
-    [InlineData (1, 2, false)]
-    [InlineData (2, 3, false)]
-    [InlineData (5, 6, false)]
-    [InlineData (6, 7, false)]
-    public void Returns_Correct_If_Start_Has_Adornment_WithSubview (int testX, int testY, bool expectedSubViewFound)
-    {
-        var start = new View ()
-        {
-            Width = 10, Height = 10,
-        };
-        start.Padding.Thickness = new Thickness (1);
-
-        var subview = new View ()
-        {
-            X = Pos.AnchorEnd(1), Y = Pos.AnchorEnd(1),
-            Width = 1, Height = 1,
-        };
-        start.Padding.Add (subview);
-        start.BeginInit();
-        start.EndInit();
-
-        var found = View.FindDeepestView (start, new (testX, testY));
-
-        Assert.Equal (expectedSubViewFound, found == subview);
-    }
-
-
-    [Theory]
-    [InlineData (0, 0, typeof (Margin))]
-    [InlineData (9, 9, typeof (Margin))]
-
-    [InlineData (1, 1, typeof (Border))]
-    [InlineData (8, 8, typeof (Border))]
-
-    [InlineData (2, 2, typeof (Padding))]
-    [InlineData (7, 7, typeof (Padding))]
-
-    [InlineData (5, 5, typeof (View))]
-    public void Returns_Adornment_If_Start_Has_Adornments (int testX, int testY, Type expectedAdornmentType)
-    {
-        var start = new View ()
-        {
-            Width = 10, Height = 10,
-        };
-        start.Margin.Thickness = new Thickness (1);
-        start.Border.Thickness = new Thickness (1);
-        start.Padding.Thickness = new Thickness (1);
-
-        var subview = new View ()
-        {
-            X = 1, Y = 1,
-            Width = 1, Height = 1,
-        };
-        start.Add (subview);
-
-        var found = View.FindDeepestView (start, new (testX, testY));
-        Assert.Equal (expectedAdornmentType, found!.GetType ());
-    }
-
-    // Test that FindDeepestView works if the subview has positive Adornments
-    [Theory]
-    [InlineData (0, 0, false)]
-    [InlineData (1, 1, false)]
-    [InlineData (9, 9, false)]
-    [InlineData (10, 10, false)]
-    [InlineData (7, 8, false)]
-    [InlineData (6, 7, false)]
-    [InlineData (1, 2, false)]
-    [InlineData (5, 6, false)]
-
-    [InlineData (2, 3, true)]
-    public void Returns_Correct_If_SubView_Has_Adornments (int testX, int testY, bool expectedSubViewFound)
-    {
-        var start = new View ()
-        {
-            Width = 10, Height = 10,
-        };
-
-        var subview = new View ()
-        {
-            X = 1, Y = 2,
-            Width = 5, Height = 5,
-        };
-        subview.Margin.Thickness = new Thickness (1);
-        start.Add (subview);
-
-        var found = View.FindDeepestView (start, new (testX, testY));
-
-        Assert.Equal (expectedSubViewFound, found == subview);
-    }
-
-    [Theory]
-    [InlineData (0, 0, false)]
-    [InlineData (1, 1, false)]
-    [InlineData (9, 9, false)]
-    [InlineData (10, 10, false)]
-    [InlineData (7, 8, false)]
-    [InlineData (6, 7, false)]
-    [InlineData (1, 2, false)]
-    [InlineData (5, 6, false)]
-    [InlineData (6, 5, false)]
-    [InlineData (5, 5, true)]
-    public void Returns_Correct_If_SubView_Has_Adornment_WithSubview (int testX, int testY, bool expectedSubViewFound)
-    {
-        var start = new View ()
-        {
-            Width = 10, Height = 10,
-        };
-
-        // A subview with + Padding
-        var subview = new View ()
-        {
-            X = 1, Y = 1,
-            Width = 5, Height = 5,
-        };
-        subview.Padding.Thickness = new (1);
-
-        // This subview will be at the bottom-right-corner of subview
-        // So screen-relative location will be X + Width - 1 = 5
-        var paddingSubview = new View ()
-        {
-            X = Pos.AnchorEnd (1),
-            Y = Pos.AnchorEnd (1),
-            Width = 1,
-            Height = 1,
-        };
-        subview.Padding.Add (paddingSubview);
-        start.Add (subview);
-        start.BeginInit();
-        start.EndInit();
-
-        var found = View.FindDeepestView (start, new (testX, testY));
-
-        Assert.Equal (expectedSubViewFound, found == paddingSubview);
-    }
-
-    [Theory]
-    [InlineData (0, 0, false)]
-    [InlineData (1, 1, false)]
-    [InlineData (9, 9, false)]
-    [InlineData (10, 10, false)]
-    [InlineData (7, 8, false)]
-    [InlineData (6, 7, false)]
-    [InlineData (1, 2, false)]
-    [InlineData (5, 6, false)]
-    [InlineData (6, 5, false)]
-    [InlineData (5, 5, true)]
-    public void Returns_Correct_If_SubView_Is_Scrolled_And_Has_Adornment_WithSubview (int testX, int testY, bool expectedSubViewFound)
-    {
-        var start = new View ()
-        {
-            Width = 10, Height = 10,
-        };
-
-        // A subview with + Padding
-        var subview = new View ()
-        {
-            X = 1, Y = 1,
-            Width = 5, Height = 5,
-        };
-        subview.Padding.Thickness = new (1);
-
-        // Scroll the subview
-        subview.SetContentSize (new (10, 10));
-        subview.Viewport = subview.Viewport with { Location = new (1, 1) };
-
-        // This subview will be at the bottom-right-corner of subview
-        // So screen-relative location will be X + Width - 1 = 5
-        var paddingSubview = new View ()
-        {
-            X = Pos.AnchorEnd (1),
-            Y = Pos.AnchorEnd (1),
-            Width = 1,
-            Height = 1,
-        };
-        subview.Padding.Add (paddingSubview);
-        start.Add (subview);
-        start.BeginInit ();
-        start.EndInit ();
-
-        var found = View.FindDeepestView (start, new (testX, testY));
-
-        Assert.Equal (expectedSubViewFound, found == paddingSubview);
-    }
-
-    // Test that FindDeepestView works with nested subviews
-    [Theory]
-    [InlineData (0, 0, -1)]
-    [InlineData (9, 9, -1)]
-    [InlineData (10, 10, -1)]
-
-    [InlineData (1, 1, 0)]
-    [InlineData (1, 2, 0)]
-    [InlineData (2, 2, 1)]
-    [InlineData (3, 3, 2)]
-    [InlineData (5, 5, 2)]
-    public void Returns_Correct_With_NestedSubViews (int testX, int testY, int expectedSubViewFound)
-    {
-        var start = new View ()
-        {
-            Width = 10, Height = 10
-        };
-
-        int numSubViews = 3;
-        List<View> subviews = new List<View> ();
-        for (int i = 0; i < numSubViews; i++)
-        {
-            var subview = new View ()
-            {
-                X = 1, Y = 1,
-                Width = 5, Height = 5,
-            };
-            subviews.Add (subview);
-
-            if (i > 0)
-            {
-                subviews [i - 1].Add (subview);
-            }
-        }
-
-        start.Add (subviews [0]);
-
-        var found = View.FindDeepestView (start, new (testX, testY));
-        Assert.Equal (expectedSubViewFound, subviews.IndexOf (found!));
-    }
-}

+ 3 - 0
UnitTests/View/Layout/Dim.Tests.cs

@@ -224,6 +224,9 @@ public class DimTests
     [AutoInitShutdown]
     public void Only_DimAbsolute_And_DimFactor_As_A_Different_Procedure_For_Assigning_Value_To_Width_Or_Height ()
     {
+        // Override CM
+        Button.DefaultShadow = ShadowStyle.None;
+
         // Testing with the Button because it properly handles the Dim class.
         Toplevel t = new ();
 

+ 3 - 0
UnitTests/View/Layout/Pos.AnchorEndTests.cs

@@ -177,6 +177,9 @@ public class PosAnchorEndTests (ITestOutputHelper output)
     {
         ((FakeDriver)Application.Driver!).SetBufferSize (20, 5);
 
+        // Override CM
+        Button.DefaultShadow = ShadowStyle.None;
+        
         var b = $"{CM.Glyphs.LeftBracket} Ok {CM.Glyphs.RightBracket}";
 
         var frame = new FrameView { Width = 18, Height = 3 };

+ 9 - 6
UnitTests/View/Layout/Pos.CombineTests.cs

@@ -66,7 +66,7 @@ public class PosCombineTests (ITestOutputHelper output)
     [SetupFakeDriver]
     public void PosCombine_DimCombine_View_With_SubViews ()
     {
-        Toplevel top = new Toplevel () { Width = 80, Height = 25 };
+        Application.Top = new Toplevel () { Width = 80, Height = 25 };
         var win1 = new Window { Id = "win1", Width = 20, Height = 10 };
         var view1 = new View
         {
@@ -85,18 +85,21 @@ public class PosCombineTests (ITestOutputHelper output)
         view2.Add (view3);
         win2.Add (view2);
         win1.Add (view1, win2);
-        top.Add (win1);
-        top.BeginInit ();
-        top.EndInit ();
+        Application.Top.Add (win1);
+        Application.Top.BeginInit ();
+        Application.Top.EndInit ();
 
-        Assert.Equal (new Rectangle (0, 0, 80, 25), top.Frame);
+        Assert.Equal (new Rectangle (0, 0, 80, 25), Application.Top.Frame);
         Assert.Equal (new Rectangle (0, 0, 5, 1), view1.Frame);
         Assert.Equal (new Rectangle (0, 0, 20, 10), win1.Frame);
         Assert.Equal (new Rectangle (0, 2, 10, 3), win2.Frame);
         Assert.Equal (new Rectangle (0, 0, 8, 1), view2.Frame);
         Assert.Equal (new Rectangle (0, 0, 7, 1), view3.Frame);
-        var foundView = View.FindDeepestView (top, new (9, 4));
+        var foundView = View.GetViewsUnderMouse (new Point(9, 4)).LastOrDefault ();
         Assert.Equal (foundView, view2);
+        Application.Top.Dispose ();
+        Application.ResetState (ignoreDisposed: true);
+
     }
 
     [Fact]

+ 74 - 71
UnitTests/View/Layout/ToScreenTests.cs

@@ -1,4 +1,5 @@
 using Xunit.Abstractions;
+using static System.Net.Mime.MediaTypeNames;
 
 namespace Terminal.Gui.LayoutTests;
 
@@ -944,10 +945,10 @@ public class ToScreenTests (ITestOutputHelper output)
 
     [Fact]
     [AutoInitShutdown]
-    public void ScreenToView_ViewToScreen_FindDeepestView_Full_Top ()
+    public void ScreenToView_ViewToScreen_GetViewsUnderMouse_Full_Top ()
     {
-        Toplevel top = new ();
-        top.BorderStyle = LineStyle.Single;
+        Application.Top = new ();
+        Application.Top.BorderStyle = LineStyle.Single;
 
         var view = new View
         {
@@ -957,17 +958,17 @@ public class ToScreenTests (ITestOutputHelper output)
             Height = 1,
             Text = "0123456789"
         };
-        top.Add (view);
+        Application.Top.Add (view);
 
-        Application.Begin (top);
+        Application.Begin (Application.Top);
 
         Assert.Equal (new (0, 0, 80, 25), new Rectangle (0, 0, View.Driver.Cols, View.Driver.Rows));
-        Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
-        Assert.Equal (new (0, 0, 80, 25), top.Frame);
+        Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), Application.Top.Frame);
+        Assert.Equal (new (0, 0, 80, 25), Application.Top.Frame);
 
         ((FakeDriver)Application.Driver!).SetBufferSize (20, 10);
-        Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
-        Assert.Equal (new (0, 0, 20, 10), top.Frame);
+        Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), Application.Top.Frame);
+        Assert.Equal (new (0, 0, 20, 10), Application.Top.Frame);
 
         _ = TestHelpers.AssertDriverContentsWithFrameAre (
                                                           @"
@@ -986,64 +987,64 @@ public class ToScreenTests (ITestOutputHelper output)
                                                          );
 
         // top
-        Assert.Equal (Point.Empty, top.ScreenToFrame (new (0, 0)));
-        Point screen = top.Margin.ViewportToScreen (new Point (0, 0));
+        Assert.Equal (Point.Empty, Application.Top.ScreenToFrame (new (0, 0)));
+        Point screen = Application.Top.Margin.ViewportToScreen (new Point (0, 0));
         Assert.Equal (0, screen.X);
         Assert.Equal (0, screen.Y);
-        screen = top.Border.ViewportToScreen (new Point (0, 0));
+        screen = Application.Top.Border.ViewportToScreen (new Point (0, 0));
         Assert.Equal (0, screen.X);
         Assert.Equal (0, screen.Y);
-        screen = top.Padding.ViewportToScreen (new Point (0, 0));
+        screen = Application.Top.Padding.ViewportToScreen (new Point (0, 0));
         Assert.Equal (1, screen.X);
         Assert.Equal (1, screen.Y);
-        screen = top.ViewportToScreen (new Point (0, 0));
+        screen = Application.Top.ViewportToScreen (new Point (0, 0));
         Assert.Equal (1, screen.X);
         Assert.Equal (1, screen.Y);
-        screen = top.ViewportToScreen (new Point (-1, -1));
+        screen = Application.Top.ViewportToScreen (new Point (-1, -1));
         Assert.Equal (0, screen.X);
         Assert.Equal (0, screen.Y);
-        var found = View.FindDeepestView (top, new (0, 0));
-        Assert.Equal (top.Border, found);
+        var found = View.GetViewsUnderMouse (new Point(0, 0)).LastOrDefault ();
+        Assert.Equal (Application.Top.Border, found);
 
         Assert.Equal (0, found.Frame.X);
         Assert.Equal (0, found.Frame.Y);
-        Assert.Equal (new (3, 2), top.ScreenToFrame (new (3, 2)));
-        screen = top.ViewportToScreen (new Point (3, 2));
+        Assert.Equal (new (3, 2), Application.Top.ScreenToFrame (new (3, 2)));
+        screen = Application.Top.ViewportToScreen (new Point (3, 2));
         Assert.Equal (4, screen.X);
         Assert.Equal (3, screen.Y);
-        found = View.FindDeepestView (top, new (screen.X, screen.Y));
+        found = View.GetViewsUnderMouse (new Point(screen.X, screen.Y)).LastOrDefault ();
         Assert.Equal (view, found);
 
         //Assert.Equal (0, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
-        found = View.FindDeepestView (top, new (3, 2));
-        Assert.Equal (top, found);
+        found = View.GetViewsUnderMouse (new Point(3, 2)).LastOrDefault ();
+        Assert.Equal (Application.Top, found);
 
         //Assert.Equal (3, found.FrameToScreen ().X);
         //Assert.Equal (2, found.FrameToScreen ().Y);
-        Assert.Equal (new (13, 2), top.ScreenToFrame (new (13, 2)));
-        screen = top.ViewportToScreen (new Point (12, 2));
+        Assert.Equal (new (13, 2), Application.Top.ScreenToFrame (new (13, 2)));
+        screen = Application.Top.ViewportToScreen (new Point (12, 2));
         Assert.Equal (13, screen.X);
         Assert.Equal (3, screen.Y);
-        found = View.FindDeepestView (top, new (screen.X, screen.Y));
+        found = View.GetViewsUnderMouse (new Point(screen.X, screen.Y)).LastOrDefault ();
         Assert.Equal (view, found);
 
         //Assert.Equal (9, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
-        screen = top.ViewportToScreen (new Point (13, 2));
+        screen = Application.Top.ViewportToScreen (new Point (13, 2));
         Assert.Equal (14, screen.X);
         Assert.Equal (3, screen.Y);
-        found = View.FindDeepestView (top, new (13, 2));
-        Assert.Equal (top, found);
+        found = View.GetViewsUnderMouse (new Point(13, 2)).LastOrDefault ();
+        Assert.Equal (Application.Top, found);
 
         //Assert.Equal (13, found.FrameToScreen ().X);
         //Assert.Equal (2, found.FrameToScreen ().Y);
-        Assert.Equal (new (14, 3), top.ScreenToFrame (new (14, 3)));
-        screen = top.ViewportToScreen (new Point (14, 3));
+        Assert.Equal (new (14, 3), Application.Top.ScreenToFrame (new (14, 3)));
+        screen = Application.Top.ViewportToScreen (new Point (14, 3));
         Assert.Equal (15, screen.X);
         Assert.Equal (4, screen.Y);
-        found = View.FindDeepestView (top, new (14, 3));
-        Assert.Equal (top, found);
+        found = View.GetViewsUnderMouse (new Point(14, 3)).LastOrDefault ();
+        Assert.Equal (Application.Top, found);
 
         //Assert.Equal (14, found.FrameToScreen ().X);
         //Assert.Equal (3, found.FrameToScreen ().Y);
@@ -1065,37 +1066,39 @@ public class ToScreenTests (ITestOutputHelper output)
         screen = view.ViewportToScreen (new Point (-4, -3));
         Assert.Equal (0, screen.X);
         Assert.Equal (0, screen.Y);
-        found = View.FindDeepestView (top, new (0, 0));
-        Assert.Equal (top.Border, found);
+        found = View.GetViewsUnderMouse (new Point(0, 0)).LastOrDefault ();
+        Assert.Equal (Application.Top.Border, found);
 
         Assert.Equal (new (-1, -1), view.ScreenToFrame (new (3, 2)));
         screen = view.ViewportToScreen (new Point (0, 0));
         Assert.Equal (4, screen.X);
         Assert.Equal (3, screen.Y);
-        found = View.FindDeepestView (top, new (4, 3));
+        found = View.GetViewsUnderMouse (new Point(4, 3)).LastOrDefault ();
         Assert.Equal (view, found);
 
         Assert.Equal (new (9, -1), view.ScreenToFrame (new (13, 2)));
         screen = view.ViewportToScreen (new Point (10, 0));
         Assert.Equal (14, screen.X);
         Assert.Equal (3, screen.Y);
-        found = View.FindDeepestView (top, new (14, 3));
-        Assert.Equal (top, found);
+        found = View.GetViewsUnderMouse (new Point(14, 3)).LastOrDefault ();
+        Assert.Equal (Application.Top, found);
 
         Assert.Equal (new (10, 0), view.ScreenToFrame (new (14, 3)));
         screen = view.ViewportToScreen (new Point (11, 1));
         Assert.Equal (15, screen.X);
         Assert.Equal (4, screen.Y);
-        found = View.FindDeepestView (top, new (15, 4));
-        Assert.Equal (top, found);
-        top.Dispose ();
+        found = View.GetViewsUnderMouse (new Point(15, 4)).LastOrDefault ();
+        Assert.Equal (Application.Top, found);
+
+        Application.Top.Dispose ();
+        Application.ResetState (ignoreDisposed: true);
     }
 
     [Fact]
     [AutoInitShutdown]
-    public void ScreenToView_ViewToScreen_FindDeepestView_Smaller_Top ()
+    public void ScreenToView_ViewToScreen_GetViewsUnderMouse_Smaller_Top ()
     {
-        var top = new Toplevel
+        Application.Top = new ()
         {
             X = 3,
             Y = 2,
@@ -1112,18 +1115,18 @@ public class ToScreenTests (ITestOutputHelper output)
             Height = 1,
             Text = "0123456789"
         };
-        top.Add (view);
+        Application.Top.Add (view);
 
-        Application.Begin (top);
+        Application.Begin (Application.Top);
 
         Assert.Equal (new (0, 0, 80, 25), new Rectangle (0, 0, View.Driver.Cols, View.Driver.Rows));
-        Assert.NotEqual (new (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
-        Assert.Equal (new (3, 2, 20, 10), top.Frame);
+        Assert.NotEqual (new (0, 0, View.Driver.Cols, View.Driver.Rows), Application.Top.Frame);
+        Assert.Equal (new (3, 2, 20, 10), Application.Top.Frame);
 
         ((FakeDriver)Application.Driver!).SetBufferSize (30, 20);
         Assert.Equal (new (0, 0, 30, 20), new Rectangle (0, 0, View.Driver.Cols, View.Driver.Rows));
-        Assert.NotEqual (new (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
-        Assert.Equal (new (3, 2, 20, 10), top.Frame);
+        Assert.NotEqual (new (0, 0, View.Driver.Cols, View.Driver.Rows), Application.Top.Frame);
+        Assert.Equal (new (3, 2, 20, 10), Application.Top.Frame);
 
         Rectangle frame = TestHelpers.AssertDriverContentsWithFrameAre (
                                                                         @"
@@ -1146,45 +1149,45 @@ public class ToScreenTests (ITestOutputHelper output)
         Assert.Equal (new (3, 2, 23, 10), frame);
 
         // top
-        Assert.Equal (new (-3, -2), top.ScreenToFrame (new (0, 0)));
-        Point screen = top.Margin.ViewportToScreen (new Point (-3, -2));
+        Assert.Equal (new (-3, -2), Application.Top.ScreenToFrame (new (0, 0)));
+        Point screen = Application.Top.Margin.ViewportToScreen (new Point (-3, -2));
         Assert.Equal (0, screen.X);
         Assert.Equal (0, screen.Y);
-        screen = top.Border.ViewportToScreen (new Point (-3, -2));
+        screen = Application.Top.Border.ViewportToScreen (new Point (-3, -2));
         Assert.Equal (0, screen.X);
         Assert.Equal (0, screen.Y);
-        screen = top.Padding.ViewportToScreen (new Point (-3, -2));
+        screen = Application.Top.Padding.ViewportToScreen (new Point (-3, -2));
         Assert.Equal (1, screen.X);
         Assert.Equal (1, screen.Y);
-        screen = top.ViewportToScreen (new Point (-3, -2));
+        screen = Application.Top.ViewportToScreen (new Point (-3, -2));
         Assert.Equal (1, screen.X);
         Assert.Equal (1, screen.Y);
-        screen = top.ViewportToScreen (new Point (-4, -3));
+        screen = Application.Top.ViewportToScreen (new Point (-4, -3));
         Assert.Equal (0, screen.X);
         Assert.Equal (0, screen.Y);
-        var found = View.FindDeepestView (top, new (-4, -3));
+        var found = View.GetViewsUnderMouse (new Point(-4, -3)).LastOrDefault ();
         Assert.Null (found);
-        Assert.Equal (Point.Empty, top.ScreenToFrame (new (3, 2)));
-        screen = top.ViewportToScreen (new Point (0, 0));
+        Assert.Equal (Point.Empty, Application.Top.ScreenToFrame (new (3, 2)));
+        screen = Application.Top.ViewportToScreen (new Point (0, 0));
         Assert.Equal (4, screen.X);
         Assert.Equal (3, screen.Y);
-        Assert.Equal (top.Border, View.FindDeepestView (top, new (3, 2)));
+        Assert.Equal (Application.Top.Border, View.GetViewsUnderMouse (new Point(3, 2)).LastOrDefault ());
 
         //Assert.Equal (0, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
-        Assert.Equal (new (10, 0), top.ScreenToFrame (new (13, 2)));
-        screen = top.ViewportToScreen (new Point (10, 0));
+        Assert.Equal (new (10, 0), Application.Top.ScreenToFrame (new (13, 2)));
+        screen = Application.Top.ViewportToScreen (new Point (10, 0));
         Assert.Equal (14, screen.X);
         Assert.Equal (3, screen.Y);
-        Assert.Equal (top.Border, View.FindDeepestView (top, new (13, 2)));
+        Assert.Equal (Application.Top.Border, View.GetViewsUnderMouse (new Point(13, 2)).LastOrDefault ());
 
         //Assert.Equal (10, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
-        Assert.Equal (new (11, 1), top.ScreenToFrame (new (14, 3)));
-        screen = top.ViewportToScreen (new Point (11, 1));
+        Assert.Equal (new (11, 1), Application.Top.ScreenToFrame (new (14, 3)));
+        screen = Application.Top.ViewportToScreen (new Point (11, 1));
         Assert.Equal (15, screen.X);
         Assert.Equal (4, screen.Y);
-        Assert.Equal (top, View.FindDeepestView (top, new (14, 3)));
+        Assert.Equal (Application.Top, View.GetViewsUnderMouse (new Point(14, 3)).LastOrDefault ());
 
         // view
         Assert.Equal (new (-7, -5), view.ScreenToFrame (new (0, 0)));
@@ -1200,32 +1203,32 @@ public class ToScreenTests (ITestOutputHelper output)
         screen = view.ViewportToScreen (new Point (-6, -4));
         Assert.Equal (1, screen.X);
         Assert.Equal (1, screen.Y);
-        Assert.Null (View.FindDeepestView (top, new (1, 1)));
+        Assert.Null (View.GetViewsUnderMouse (new Point(1, 1)).LastOrDefault ());
         Assert.Equal (new (-4, -3), view.ScreenToFrame (new (3, 2)));
         screen = view.ViewportToScreen (new Point (-3, -2));
         Assert.Equal (4, screen.X);
         Assert.Equal (3, screen.Y);
-        Assert.Equal (top, View.FindDeepestView (top, new (4, 3)));
+        Assert.Equal (Application.Top, View.GetViewsUnderMouse (new Point(4, 3)).LastOrDefault ());
         Assert.Equal (new (-1, -1), view.ScreenToFrame (new (6, 4)));
         screen = view.ViewportToScreen (new Point (0, 0));
         Assert.Equal (7, screen.X);
         Assert.Equal (5, screen.Y);
-        Assert.Equal (view, View.FindDeepestView (top, new (7, 5)));
+        Assert.Equal (view, View.GetViewsUnderMouse (new Point(7, 5)).LastOrDefault ());
         Assert.Equal (new (6, -1), view.ScreenToFrame (new (13, 4)));
         screen = view.ViewportToScreen (new Point (7, 0));
         Assert.Equal (14, screen.X);
         Assert.Equal (5, screen.Y);
-        Assert.Equal (view, View.FindDeepestView (top, new (14, 5)));
+        Assert.Equal (view, View.GetViewsUnderMouse (new Point(14, 5)).LastOrDefault ());
         Assert.Equal (new (7, -2), view.ScreenToFrame (new (14, 3)));
         screen = view.ViewportToScreen (new Point (8, -1));
         Assert.Equal (15, screen.X);
         Assert.Equal (4, screen.Y);
-        Assert.Equal (top, View.FindDeepestView (top, new (15, 4)));
+        Assert.Equal (Application.Top, View.GetViewsUnderMouse (new Point(15, 4)).LastOrDefault ());
         Assert.Equal (new (16, -2), view.ScreenToFrame (new (23, 3)));
         screen = view.ViewportToScreen (new Point (17, -1));
         Assert.Equal (24, screen.X);
         Assert.Equal (4, screen.Y);
-        Assert.Null (View.FindDeepestView (top, new (24, 4)));
-        top.Dispose ();
+        Assert.Null (View.GetViewsUnderMouse (new Point(24, 4)).LastOrDefault ());
+        Application.Top.Dispose ();
     }
 }

+ 805 - 0
UnitTests/View/Mouse/GetViewsUnderMouseTests.cs

@@ -0,0 +1,805 @@
+#nullable enable
+
+namespace Terminal.Gui.ViewMouseTests;
+
+[Trait ("Category", "Input")]
+public class GetViewsUnderMouseTests
+{
+    [Theory]
+    [InlineData (0, 0, 0, 0, 0, -1, -1, null)]
+    [InlineData (0, 0, 0, 0, 0, 0, 0, typeof (Toplevel))]
+    [InlineData (0, 0, 0, 0, 0, 1, 1, typeof (Toplevel))]
+    [InlineData (0, 0, 0, 0, 0, 4, 4, typeof (Toplevel))]
+    [InlineData (0, 0, 0, 0, 0, 9, 9, typeof (Toplevel))]
+    [InlineData (0, 0, 0, 0, 0, 10, 10, null)]
+    [InlineData (1, 1, 0, 0, 0, -1, -1, null)]
+    [InlineData (1, 1, 0, 0, 0, 0, 0, null)]
+    [InlineData (1, 1, 0, 0, 0, 1, 1, typeof (Toplevel))]
+    [InlineData (1, 1, 0, 0, 0, 4, 4, typeof (Toplevel))]
+    [InlineData (1, 1, 0, 0, 0, 9, 9, typeof (Toplevel))]
+    [InlineData (1, 1, 0, 0, 0, 10, 10, typeof (Toplevel))]
+    [InlineData (0, 0, 1, 0, 0, -1, -1, null)]
+    [InlineData (0, 0, 1, 0, 0, 0, 0, typeof (Margin))]
+    [InlineData (0, 0, 1, 0, 0, 1, 1, typeof (Toplevel))]
+    [InlineData (0, 0, 1, 0, 0, 4, 4, typeof (Toplevel))]
+    [InlineData (0, 0, 1, 0, 0, 9, 9, typeof (Margin))]
+    [InlineData (0, 0, 1, 0, 0, 10, 10, null)]
+    [InlineData (0, 0, 1, 1, 0, -1, -1, null)]
+    [InlineData (0, 0, 1, 1, 0, 0, 0, typeof (Margin))]
+    [InlineData (0, 0, 1, 1, 0, 1, 1, typeof (Border))]
+    [InlineData (0, 0, 1, 1, 0, 4, 4, typeof (Toplevel))]
+    [InlineData (0, 0, 1, 1, 0, 9, 9, typeof (Margin))]
+    [InlineData (0, 0, 1, 1, 0, 10, 10, null)]
+    [InlineData (0, 0, 1, 1, 1, -1, -1, null)]
+    [InlineData (0, 0, 1, 1, 1, 0, 0, typeof (Margin))]
+    [InlineData (0, 0, 1, 1, 1, 1, 1, typeof (Border))]
+    [InlineData (0, 0, 1, 1, 1, 2, 2, typeof (Padding))]
+    [InlineData (0, 0, 1, 1, 1, 4, 4, typeof (Toplevel))]
+    [InlineData (0, 0, 1, 1, 1, 9, 9, typeof (Margin))]
+    [InlineData (0, 0, 1, 1, 1, 10, 10, null)]
+    [InlineData (1, 1, 1, 0, 0, -1, -1, null)]
+    [InlineData (1, 1, 1, 0, 0, 0, 0, null)]
+    [InlineData (1, 1, 1, 0, 0, 1, 1, typeof (Margin))]
+    [InlineData (1, 1, 1, 0, 0, 4, 4, typeof (Toplevel))]
+    [InlineData (1, 1, 1, 0, 0, 9, 9, typeof (Toplevel))]
+    [InlineData (1, 1, 1, 0, 0, 10, 10, typeof (Margin))]
+    [InlineData (1, 1, 1, 1, 0, -1, -1, null)]
+    [InlineData (1, 1, 1, 1, 0, 0, 0, null)]
+    [InlineData (1, 1, 1, 1, 0, 1, 1, typeof (Margin))]
+    [InlineData (1, 1, 1, 1, 0, 4, 4, typeof (Toplevel))]
+    [InlineData (1, 1, 1, 1, 0, 9, 9, typeof (Border))]
+    [InlineData (1, 1, 1, 1, 0, 10, 10, typeof (Margin))]
+    [InlineData (1, 1, 1, 1, 1, -1, -1, null)]
+    [InlineData (1, 1, 1, 1, 1, 0, 0, null)]
+    [InlineData (1, 1, 1, 1, 1, 1, 1, typeof (Margin))]
+    [InlineData (1, 1, 1, 1, 1, 2, 2, typeof (Border))]
+    [InlineData (1, 1, 1, 1, 1, 3, 3, typeof (Padding))]
+    [InlineData (1, 1, 1, 1, 1, 4, 4, typeof (Toplevel))]
+    [InlineData (1, 1, 1, 1, 1, 8, 8, typeof (Padding))]
+    [InlineData (1, 1, 1, 1, 1, 9, 9, typeof (Border))]
+    [InlineData (1, 1, 1, 1, 1, 10, 10, typeof (Margin))]
+    public void GetViewsUnderMouse_Top_Adornments_Returns_Correct_View (
+        int frameX,
+        int frameY,
+        int marginThickness,
+        int borderThickness,
+        int paddingThickness,
+        int testX,
+        int testY,
+        Type? expectedViewType
+    )
+    {
+        // Arrange
+        Application.Top = new ()
+        {
+            Frame = new (frameX, frameY, 10, 10)
+        };
+        Application.Top.Margin.Thickness = new (marginThickness);
+        Application.Top.Border.Thickness = new (borderThickness);
+        Application.Top.Padding.Thickness = new (paddingThickness);
+
+        var location = new Point (testX, testY);
+
+        // Act
+        List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
+
+        // Assert
+        if (expectedViewType == null)
+        {
+            Assert.Empty (viewsUnderMouse);
+        }
+        else
+        {
+            Assert.Contains (viewsUnderMouse, v => v?.GetType () == expectedViewType);
+        }
+
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (1, 1)]
+    [InlineData (2, 2)]
+    public void GetViewsUnderMouse_Returns_Top_If_No_SubViews (int testX, int testY)
+    {
+        // Arrange
+        Application.Top = new ()
+        {
+            Frame = new (0, 0, 10, 10)
+        };
+
+        var location = new Point (testX, testY);
+
+        // Act
+        List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
+
+        // Assert
+        Assert.Contains (viewsUnderMouse, v => v == Application.Top);
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (2, 1)]
+    [InlineData (20, 20)]
+    public void GetViewsUnderMouse_Returns_Null_If_No_SubViews_Coords_Outside (int testX, int testY)
+    {
+        // Arrange
+        var view = new View
+        {
+            Frame = new (0, 0, 10, 10)
+        };
+
+        var location = new Point (testX, testY);
+
+        // Act
+        List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
+
+        // Assert
+        Assert.Empty (viewsUnderMouse);
+    }
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (2, 1)]
+    [InlineData (20, 20)]
+    public void GetViewsUnderMouse_Returns_Null_If_Start_Not_Visible (int testX, int testY)
+    {
+        // Arrange
+        var view = new View
+        {
+            Frame = new (0, 0, 10, 10),
+            Visible = false
+        };
+
+        var location = new Point (testX, testY);
+
+        // Act
+        List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
+
+        // Assert
+        Assert.Empty (viewsUnderMouse);
+    }
+
+    [Theory]
+    [InlineData (0, 0, false)]
+    [InlineData (1, 1, true)]
+    [InlineData (9, 9, false)]
+    [InlineData (10, 10, false)]
+    [InlineData (6, 7, true)]
+    [InlineData (1, 2, true)]
+    [InlineData (5, 6, true)]
+    public void GetViewsUnderMouse_Returns_Correct_If_SubViews (int testX, int testY, bool expected)
+    {
+        // Arrange
+        Application.Top = new ()
+        {
+            Frame = new (0, 0, 10, 10)
+        };
+
+        var subView = new View
+        {
+            Frame = new (1, 1, 8, 8)
+        };
+
+        Application.Top.Add (subView);
+
+        var location = new Point (testX, testY);
+
+        // Act
+        List<View?> viewsUnderMouse = View.GetViewsUnderMouse (location);
+
+        // Assert
+        if (expected)
+        {
+            Assert.Contains (viewsUnderMouse, v => v == subView);
+        }
+        else
+        {
+            Assert.DoesNotContain (viewsUnderMouse, v => v == subView);
+        }
+
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    [Theory]
+    [InlineData (0, 0, 0, 0, 0, -1, -1, null)]
+    [InlineData (0, 0, 0, 0, 0, 0, 0, typeof (View))]
+    [InlineData (0, 0, 0, 0, 0, 1, 1, typeof (View))]
+    [InlineData (0, 0, 0, 0, 0, 4, 4, typeof (View))]
+    [InlineData (0, 0, 0, 0, 0, 9, 9, typeof (View))]
+    [InlineData (0, 0, 0, 0, 0, 10, 10, null)]
+    [InlineData (1, 1, 0, 0, 0, -1, -1, null)]
+    [InlineData (1, 1, 0, 0, 0, 0, 0, null)]
+    [InlineData (1, 1, 0, 0, 0, 1, 1, typeof (View))]
+    [InlineData (1, 1, 0, 0, 0, 4, 4, typeof (View))]
+    [InlineData (1, 1, 0, 0, 0, 9, 9, typeof (View))]
+    [InlineData (1, 1, 0, 0, 0, 10, 10, typeof (View))]
+    [InlineData (0, 0, 1, 0, 0, -1, -1, null)]
+    [InlineData (0, 0, 1, 0, 0, 0, 0, typeof (Margin))]
+    [InlineData (0, 0, 1, 0, 0, 1, 1, typeof (View))]
+    [InlineData (0, 0, 1, 0, 0, 4, 4, typeof (View))]
+    [InlineData (0, 0, 1, 0, 0, 9, 9, typeof (Margin))]
+    [InlineData (0, 0, 1, 0, 0, 10, 10, null)]
+    [InlineData (0, 0, 1, 1, 0, -1, -1, null)]
+    [InlineData (0, 0, 1, 1, 0, 0, 0, typeof (Margin))]
+    [InlineData (0, 0, 1, 1, 0, 1, 1, typeof (Border))]
+    [InlineData (0, 0, 1, 1, 0, 4, 4, typeof (View))]
+    [InlineData (0, 0, 1, 1, 0, 9, 9, typeof (Margin))]
+    [InlineData (0, 0, 1, 1, 0, 10, 10, null)]
+    [InlineData (0, 0, 1, 1, 1, -1, -1, null)]
+    [InlineData (0, 0, 1, 1, 1, 0, 0, typeof (Margin))]
+    [InlineData (0, 0, 1, 1, 1, 1, 1, typeof (Border))]
+    [InlineData (0, 0, 1, 1, 1, 2, 2, typeof (Padding))]
+    [InlineData (0, 0, 1, 1, 1, 4, 4, typeof (View))]
+    [InlineData (0, 0, 1, 1, 1, 9, 9, typeof (Margin))]
+    [InlineData (0, 0, 1, 1, 1, 10, 10, null)]
+    [InlineData (1, 1, 1, 0, 0, -1, -1, null)]
+    [InlineData (1, 1, 1, 0, 0, 0, 0, null)]
+    [InlineData (1, 1, 1, 0, 0, 1, 1, typeof (Margin))]
+    [InlineData (1, 1, 1, 0, 0, 4, 4, typeof (View))]
+    [InlineData (1, 1, 1, 0, 0, 9, 9, typeof (View))]
+    [InlineData (1, 1, 1, 0, 0, 10, 10, typeof (Margin))]
+    [InlineData (1, 1, 1, 1, 0, -1, -1, null)]
+    [InlineData (1, 1, 1, 1, 0, 0, 0, null)]
+    [InlineData (1, 1, 1, 1, 0, 1, 1, typeof (Margin))]
+    [InlineData (1, 1, 1, 1, 0, 4, 4, typeof (View))]
+    [InlineData (1, 1, 1, 1, 0, 9, 9, typeof (Border))]
+    [InlineData (1, 1, 1, 1, 0, 10, 10, typeof (Margin))]
+    [InlineData (1, 1, 1, 1, 1, -1, -1, null)]
+    [InlineData (1, 1, 1, 1, 1, 0, 0, null)]
+    [InlineData (1, 1, 1, 1, 1, 1, 1, typeof (Margin))]
+    [InlineData (1, 1, 1, 1, 1, 2, 2, typeof (Border))]
+    [InlineData (1, 1, 1, 1, 1, 3, 3, typeof (Padding))]
+    [InlineData (1, 1, 1, 1, 1, 4, 4, typeof (View))]
+    [InlineData (1, 1, 1, 1, 1, 8, 8, typeof (Padding))]
+    [InlineData (1, 1, 1, 1, 1, 9, 9, typeof (Border))]
+    [InlineData (1, 1, 1, 1, 1, 10, 10, typeof (Margin))]
+    public void Contains (
+        int frameX,
+        int frameY,
+        int marginThickness,
+        int borderThickness,
+        int paddingThickness,
+        int testX,
+        int testY,
+        Type? expectedAdornmentType
+    )
+    {
+        var view = new View
+        {
+            X = frameX, Y = frameY,
+            Width = 10, Height = 10
+        };
+        view.Margin.Thickness = new (marginThickness);
+        view.Border.Thickness = new (borderThickness);
+        view.Padding.Thickness = new (paddingThickness);
+
+        Type? containedType = null;
+
+        if (view.Contains (new (testX, testY)))
+        {
+            containedType = view.GetType ();
+        }
+
+        if (view.Margin.Contains (new (testX, testY)))
+        {
+            containedType = view.Margin.GetType ();
+        }
+
+        if (view.Border.Contains (new (testX, testY)))
+        {
+            containedType = view.Border.GetType ();
+        }
+
+        if (view.Padding.Contains (new (testX, testY)))
+        {
+            containedType = view.Padding.GetType ();
+        }
+
+        Assert.Equal (expectedAdornmentType, containedType);
+    }
+
+    // Test that GetViewsUnderMouse returns the correct view if the start view has no subviews
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (1, 1)]
+    [InlineData (2, 2)]
+    public void Returns_Start_If_No_SubViews (int testX, int testY)
+    {
+        Application.Top = new ()
+        {
+            Width = 10, Height = 10
+        };
+
+        Assert.Same (Application.Top, View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ());
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    // Test that GetViewsUnderMouse returns null if the start view has no subviews and coords are outside the view
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (2, 1)]
+    [InlineData (20, 20)]
+    public void Returns_Null_If_No_SubViews_Coords_Outside (int testX, int testY)
+    {
+        Application.Top = new ()
+        {
+            X = 1, Y = 2,
+            Width = 10, Height = 10
+        };
+
+        Assert.Null (View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ());
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (2, 1)]
+    [InlineData (20, 20)]
+    public void Returns_Null_If_Start_Not_Visible (int testX, int testY)
+    {
+        Application.Top = new ()
+        {
+            X = 1, Y = 2,
+            Width = 10, Height = 10,
+            Visible = false
+        };
+
+        Assert.Null (View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ());
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    // Test that GetViewsUnderMouse returns the correct view if the start view has subviews
+    [Theory]
+    [InlineData (0, 0, false)]
+    [InlineData (1, 1, false)]
+    [InlineData (9, 9, false)]
+    [InlineData (10, 10, false)]
+    [InlineData (6, 7, false)]
+    [InlineData (1, 2, true)]
+    [InlineData (5, 6, true)]
+    public void Returns_Correct_If_SubViews (int testX, int testY, bool expectedSubViewFound)
+    {
+        Application.Top = new ()
+        {
+            Width = 10, Height = 10
+        };
+
+        var subview = new View
+        {
+            X = 1, Y = 2,
+            Width = 5, Height = 5
+        };
+        Application.Top.Add (subview);
+
+        View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
+
+        Assert.Equal (expectedSubViewFound, found == subview);
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    [Theory]
+    [InlineData (0, 0, false)]
+    [InlineData (1, 1, false)]
+    [InlineData (9, 9, false)]
+    [InlineData (10, 10, false)]
+    [InlineData (6, 7, false)]
+    [InlineData (1, 2, false)]
+    [InlineData (5, 6, false)]
+    public void Returns_Null_If_SubView_NotVisible (int testX, int testY, bool expectedSubViewFound)
+    {
+        Application.Top = new ()
+        {
+            Width = 10, Height = 10
+        };
+
+        var subview = new View
+        {
+            X = 1, Y = 2,
+            Width = 5, Height = 5,
+            Visible = false
+        };
+        Application.Top.Add (subview);
+
+        View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
+
+        Assert.Equal (expectedSubViewFound, found == subview);
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    [Theory]
+    [InlineData (0, 0, false)]
+    [InlineData (1, 1, false)]
+    [InlineData (9, 9, false)]
+    [InlineData (10, 10, false)]
+    [InlineData (6, 7, false)]
+    [InlineData (1, 2, false)]
+    [InlineData (5, 6, false)]
+    public void Returns_Null_If_Not_Visible_And_SubView_Visible (int testX, int testY, bool expectedSubViewFound)
+    {
+        Application.Top = new ()
+        {
+            Width = 10, Height = 10,
+            Visible = false
+        };
+
+        var subview = new View
+        {
+            X = 1, Y = 2,
+            Width = 5, Height = 5
+        };
+        Application.Top.Add (subview);
+        subview.Visible = true;
+        Assert.True (subview.Visible);
+        Assert.False (Application.Top.Visible);
+        View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
+
+        Assert.Equal (expectedSubViewFound, found == subview);
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    // Test that GetViewsUnderMouse works if the start view has positive Adornments
+    [Theory]
+    [InlineData (0, 0, false)]
+    [InlineData (1, 1, false)]
+    [InlineData (9, 9, false)]
+    [InlineData (10, 10, false)]
+    [InlineData (7, 8, false)]
+    [InlineData (1, 2, false)]
+    [InlineData (2, 3, true)]
+    [InlineData (5, 6, true)]
+    [InlineData (6, 7, true)]
+    public void Returns_Correct_If_Start_Has_Adornments (int testX, int testY, bool expectedSubViewFound)
+    {
+        Application.Top = new ()
+        {
+            Width = 10, Height = 10
+        };
+        Application.Top.Margin.Thickness = new (1);
+
+        var subview = new View
+        {
+            X = 1, Y = 2,
+            Width = 5, Height = 5
+        };
+        Application.Top.Add (subview);
+
+        View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
+
+        Assert.Equal (expectedSubViewFound, found == subview);
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    // Test that GetViewsUnderMouse works if the start view has offset Viewport location
+    [Theory]
+    [InlineData (1, 0, 0, true)]
+    [InlineData (1, 1, 1, true)]
+    [InlineData (1, 2, 2, false)]
+    [InlineData (-1, 3, 3, true)]
+    [InlineData (-1, 2, 2, true)]
+    [InlineData (-1, 1, 1, false)]
+    [InlineData (-1, 0, 0, false)]
+    public void Returns_Correct_If_Start_Has_Offset_Viewport (int offset, int testX, int testY, bool expectedSubViewFound)
+    {
+        Application.Top = new ()
+        {
+            Width = 10, Height = 10,
+            ViewportSettings = ViewportSettings.AllowNegativeLocation
+        };
+        Application.Top.Viewport = new (offset, offset, 10, 10);
+
+        var subview = new View
+        {
+            X = 1, Y = 1,
+            Width = 2, Height = 2
+        };
+        Application.Top.Add (subview);
+
+        View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
+
+        Assert.Equal (expectedSubViewFound, found == subview);
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    [Theory]
+    [InlineData (9, 9, true)]
+    [InlineData (0, 0, false)]
+    [InlineData (1, 1, false)]
+    [InlineData (10, 10, false)]
+    [InlineData (7, 8, false)]
+    [InlineData (1, 2, false)]
+    [InlineData (2, 3, false)]
+    [InlineData (5, 6, false)]
+    [InlineData (6, 7, false)]
+    public void Returns_Correct_If_Start_Has_Adornment_WithSubview (int testX, int testY, bool expectedSubViewFound)
+    {
+        Application.Top = new ()
+        {
+            Width = 10, Height = 10
+        };
+        Application.Top.Padding.Thickness = new (1);
+
+        var subview = new View
+        {
+            X = Pos.AnchorEnd (1), Y = Pos.AnchorEnd (1),
+            Width = 1, Height = 1
+        };
+        Application.Top.Padding.Add (subview);
+        Application.Top.BeginInit ();
+        Application.Top.EndInit ();
+
+        View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
+
+        Assert.Equal (expectedSubViewFound, found == subview);
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    [Theory]
+    [InlineData (0, 0, typeof (Margin))]
+    [InlineData (9, 9, typeof (Margin))]
+    [InlineData (1, 1, typeof (Border))]
+    [InlineData (8, 8, typeof (Border))]
+    [InlineData (2, 2, typeof (Padding))]
+    [InlineData (7, 7, typeof (Padding))]
+    [InlineData (5, 5, typeof (Toplevel))]
+    public void Returns_Adornment_If_Start_Has_Adornments (int testX, int testY, Type expectedAdornmentType)
+    {
+        Application.Top = new ()
+        {
+            Width = 10, Height = 10
+        };
+        Application.Top.Margin.Thickness = new (1);
+        Application.Top.Border.Thickness = new (1);
+        Application.Top.Padding.Thickness = new (1);
+
+        var subview = new View
+        {
+            X = 1, Y = 1,
+            Width = 1, Height = 1
+        };
+        Application.Top.Add (subview);
+
+        View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
+        Assert.Equal (expectedAdornmentType, found!.GetType ());
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    // Test that GetViewsUnderMouse works if the subview has positive Adornments
+    [Theory]
+    [InlineData (0, 0, false)]
+    [InlineData (1, 1, false)]
+    [InlineData (9, 9, false)]
+    [InlineData (10, 10, false)]
+    [InlineData (7, 8, false)]
+    [InlineData (6, 7, false)]
+    [InlineData (1, 2, false)]
+    [InlineData (5, 6, false)]
+    [InlineData (2, 3, true)]
+    public void Returns_Correct_If_SubView_Has_Adornments (int testX, int testY, bool expectedSubViewFound)
+    {
+        Application.Top = new ()
+        {
+            Width = 10, Height = 10
+        };
+
+        var subview = new View
+        {
+            X = 1, Y = 2,
+            Width = 5, Height = 5
+        };
+        subview.Margin.Thickness = new (1);
+        Application.Top.Add (subview);
+
+        View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
+
+        Assert.Equal (expectedSubViewFound, found == subview);
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    [Theory]
+    [InlineData (0, 0, false)]
+    [InlineData (1, 1, false)]
+    [InlineData (9, 9, false)]
+    [InlineData (10, 10, false)]
+    [InlineData (7, 8, false)]
+    [InlineData (6, 7, false)]
+    [InlineData (1, 2, false)]
+    [InlineData (5, 6, false)]
+    [InlineData (6, 5, false)]
+    [InlineData (5, 5, true)]
+    public void Returns_Correct_If_SubView_Has_Adornment_WithSubview (int testX, int testY, bool expectedSubViewFound)
+    {
+        Application.Top = new ()
+        {
+            Width = 10, Height = 10
+        };
+
+        // A subview with + Padding
+        var subview = new View
+        {
+            X = 1, Y = 1,
+            Width = 5, Height = 5
+        };
+        subview.Padding.Thickness = new (1);
+
+        // This subview will be at the bottom-right-corner of subview
+        // So screen-relative location will be X + Width - 1 = 5
+        var paddingSubview = new View
+        {
+            X = Pos.AnchorEnd (1),
+            Y = Pos.AnchorEnd (1),
+            Width = 1,
+            Height = 1
+        };
+        subview.Padding.Add (paddingSubview);
+        Application.Top.Add (subview);
+        Application.Top.BeginInit ();
+        Application.Top.EndInit ();
+
+        View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
+
+        Assert.Equal (expectedSubViewFound, found == paddingSubview);
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    [Theory]
+    [InlineData (0, 0, false)]
+    [InlineData (1, 1, false)]
+    [InlineData (9, 9, false)]
+    [InlineData (10, 10, false)]
+    [InlineData (7, 8, false)]
+    [InlineData (6, 7, false)]
+    [InlineData (1, 2, false)]
+    [InlineData (5, 6, false)]
+    [InlineData (6, 5, false)]
+    [InlineData (5, 5, true)]
+    public void Returns_Correct_If_SubView_Is_Scrolled_And_Has_Adornment_WithSubview (int testX, int testY, bool expectedSubViewFound)
+    {
+        Application.Top = new ()
+        {
+            Width = 10, Height = 10
+        };
+
+        // A subview with + Padding
+        var subview = new View
+        {
+            X = 1, Y = 1,
+            Width = 5, Height = 5
+        };
+        subview.Padding.Thickness = new (1);
+
+        // Scroll the subview
+        subview.SetContentSize (new (10, 10));
+        subview.Viewport = subview.Viewport with { Location = new (1, 1) };
+
+        // This subview will be at the bottom-right-corner of subview
+        // So screen-relative location will be X + Width - 1 = 5
+        var paddingSubview = new View
+        {
+            X = Pos.AnchorEnd (1),
+            Y = Pos.AnchorEnd (1),
+            Width = 1,
+            Height = 1
+        };
+        subview.Padding.Add (paddingSubview);
+        Application.Top.Add (subview);
+        Application.Top.BeginInit ();
+        Application.Top.EndInit ();
+
+        View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
+
+        Assert.Equal (expectedSubViewFound, found == paddingSubview);
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    // Test that GetViewsUnderMouse works with nested subviews
+    [Theory]
+    [InlineData (0, 0, -1)]
+    [InlineData (9, 9, -1)]
+    [InlineData (10, 10, -1)]
+    [InlineData (1, 1, 0)]
+    [InlineData (1, 2, 0)]
+    [InlineData (2, 2, 1)]
+    [InlineData (3, 3, 2)]
+    [InlineData (5, 5, 2)]
+    public void Returns_Correct_With_NestedSubViews (int testX, int testY, int expectedSubViewFound)
+    {
+        Application.Top = new ()
+        {
+            Width = 10, Height = 10
+        };
+
+        var numSubViews = 3;
+        List<View> subviews = new ();
+
+        for (var i = 0; i < numSubViews; i++)
+        {
+            var subview = new View
+            {
+                X = 1, Y = 1,
+                Width = 5, Height = 5
+            };
+            subviews.Add (subview);
+
+            if (i > 0)
+            {
+                subviews [i - 1].Add (subview);
+            }
+        }
+
+        Application.Top.Add (subviews [0]);
+
+        View? found = View.GetViewsUnderMouse (new (testX, testY)).LastOrDefault ();
+        Assert.Equal (expectedSubViewFound, subviews.IndexOf (found!));
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+
+    [Theory]
+    [InlineData (0, 0, new [] { "top" })]
+    [InlineData (9, 9, new [] { "top" })]
+    [InlineData (10, 10, new string [] { })]
+    [InlineData (1, 1, new [] { "top", "view" })]
+    [InlineData (1, 2, new [] { "top", "view" })]
+    [InlineData (2, 1, new [] { "top", "view" })]
+    [InlineData (2, 2, new [] { "top", "view", "subView" })]
+    [InlineData (3, 3, new [] { "top" })] // clipped
+    [InlineData (2, 3, new [] { "top" })] // clipped
+    public void GetViewsUnderMouse_Tiled_Subviews (int mouseX, int mouseY, string [] viewIdStrings)
+    {
+        // Arrange
+        Application.Top = new ()
+        {
+            Frame = new (0, 0, 10, 10),
+            Id = "top"
+        };
+
+        var view = new View
+        {
+            Id = "view",
+            X = 1,
+            Y = 1,
+            Width = 2,
+            Height = 2,
+            Arrangement = ViewArrangement.Overlapped
+        }; // at 1,1 to 3,2 (screen)
+
+        var subView = new View
+        {
+            Id = "subView",
+            X = 1,
+            Y = 1,
+            Width = 2,
+            Height = 2,
+            Arrangement = ViewArrangement.Overlapped
+        }; // at 2,2 to 4,3 (screen)
+        view.Add (subView);
+        Application.Top.Add (view);
+
+        List<View?> found = View.GetViewsUnderMouse (new (mouseX, mouseY));
+
+        string [] foundIds = found.Select (v => v!.Id).ToArray ();
+
+        Assert.Equal (viewIdStrings, foundIds);
+
+        Application.Top.Dispose ();
+        Application.ResetState (true);
+    }
+}

+ 349 - 0
UnitTests/View/Mouse/MouseEnterLeaveTests.cs

@@ -0,0 +1,349 @@
+using System.ComponentModel;
+
+namespace Terminal.Gui.ViewMouseTests;
+
+[Trait ("Category", "Input")]
+public class MouseEnterLeaveTests
+{
+    private class TestView : View
+    {
+        public TestView ()
+        {
+            MouseEnter += OnMouseEnterHandler;
+            MouseLeave += OnMouseLeaveHandler;
+        }
+
+        public bool CancelOnEnter { get; init; }
+
+        public bool CancelEnterEvent { get; init; }
+
+        public bool OnMouseEnterCalled { get; private set; }
+        public bool OnMouseLeaveCalled { get; private set; }
+
+        protected override bool OnMouseEnter (CancelEventArgs eventArgs)
+        {
+            OnMouseEnterCalled = true;
+            eventArgs.Cancel = CancelOnEnter;
+
+            base.OnMouseEnter (eventArgs);
+
+            return eventArgs.Cancel;
+        }
+
+        protected override void OnMouseLeave ()
+        {
+            OnMouseLeaveCalled = true;
+
+            base.OnMouseLeave ();
+        }
+
+        public bool MouseEnterRaised { get; private set; }
+        public bool MouseLeaveRaised { get; private set; }
+
+        private void OnMouseEnterHandler (object s, CancelEventArgs e)
+        {
+            MouseEnterRaised = true;
+
+            if (CancelEnterEvent)
+            {
+                e.Cancel = true;
+            }
+        }
+
+        private void OnMouseLeaveHandler (object s, EventArgs e) { MouseLeaveRaised = true; }
+    }
+
+    [Fact]
+    public void NewMouseEnterEvent_ViewIsEnabledAndVisible_CallsOnMouseEnter ()
+    {
+        // Arrange
+        var view = new TestView
+        {
+            Enabled = true,
+            Visible = true
+        };
+
+        var mouseEvent = new MouseEvent ();
+
+        var eventArgs = new CancelEventArgs ();
+
+        // Act
+        bool? cancelled = view.NewMouseEnterEvent (eventArgs);
+
+        // Assert
+        Assert.True (view.OnMouseEnterCalled);
+        Assert.False (cancelled);
+        Assert.False (eventArgs.Cancel);
+
+        // Cleanup
+        view.Dispose ();
+    }
+
+    [Fact]
+    public void NewMouseEnterEvent_ViewIsDisabled_CallsOnMouseEnter ()
+    {
+        // Arrange
+        var view = new TestView
+        {
+            Enabled = false,
+            Visible = true
+        };
+
+        var eventArgs = new CancelEventArgs ();
+
+        // Act
+        bool? cancelled = view.NewMouseEnterEvent (eventArgs);
+
+        // Assert
+        Assert.True (view.OnMouseEnterCalled);
+        Assert.False (cancelled);
+        Assert.False (eventArgs.Cancel);
+
+        // Cleanup
+        view.Dispose ();
+    }
+
+    [Fact]
+    public void NewMouseEnterEvent_ViewIsNotVisible_DoesNotCallOnMouseEnter ()
+    {
+        // Arrange
+        var view = new TestView
+        {
+            Enabled = true,
+            Visible = false
+        };
+
+        var eventArgs = new CancelEventArgs ();
+
+        // Act
+        bool? cancelled = view.NewMouseEnterEvent (eventArgs);
+
+        // Assert
+        Assert.False (view.OnMouseEnterCalled);
+        Assert.Null (cancelled);
+        Assert.False (eventArgs.Cancel);
+
+        // Cleanup
+        view.Dispose ();
+    }
+
+    [Fact]
+    public void NewMouseLeaveEvent_ViewIsVisible_CallsOnMouseLeave ()
+    {
+        // Arrange
+        var view = new TestView
+        {
+            Enabled = true, Visible = true
+        };
+
+        var mouseEvent = new MouseEvent ();
+
+        // Act
+        view.NewMouseLeaveEvent ();
+
+        // Assert
+        Assert.True (view.OnMouseLeaveCalled);
+        Assert.False (mouseEvent.Handled);
+
+        // Cleanup
+        view.Dispose ();
+    }
+
+    [Fact]
+    public void NewMouseLeaveEvent_ViewIsNotVisible_CallsOnMouseLeave ()
+    {
+        // Arrange
+        var view = new TestView
+        {
+            Enabled = true,
+            Visible = false
+        };
+
+        var mouseEvent = new MouseEvent ();
+
+        // Act
+        view.NewMouseLeaveEvent ();
+
+        // Assert
+        Assert.True (view.OnMouseLeaveCalled);
+        Assert.False (mouseEvent.Handled);
+
+        // Cleanup
+        view.Dispose ();
+    }
+
+    // Events
+
+    [Fact]
+    public void NewMouseEnterEvent_ViewIsEnabledAndVisible_RaisesMouseEnter ()
+    {
+        // Arrange
+        var view = new TestView
+        {
+            Enabled = true,
+            Visible = true
+        };
+
+        var eventArgs = new CancelEventArgs ();
+
+        // Act
+        bool? cancelled = view.NewMouseEnterEvent (eventArgs);
+
+        // Assert
+        Assert.True (view.MouseEnterRaised);
+        Assert.False (cancelled);
+        Assert.False (eventArgs.Cancel);
+
+        // Cleanup
+        view.Dispose ();
+    }
+
+    [Fact]
+    public void NewMouseEnterEvent_ViewIsDisabled_RaisesMouseEnter ()
+    {
+        // Arrange
+        var view = new TestView
+        {
+            Enabled = false,
+            Visible = true
+        };
+
+        var eventArgs = new CancelEventArgs ();
+
+        // Act
+        bool? cancelled = view.NewMouseEnterEvent (eventArgs);
+
+        // Assert
+        Assert.True (view.MouseEnterRaised);
+        Assert.False (cancelled);
+        Assert.False (eventArgs.Cancel);
+
+        // Cleanup
+        view.Dispose ();
+    }
+
+    [Fact]
+    public void NewMouseEnterEvent_ViewIsNotVisible_DoesNotRaiseMouseEnter ()
+    {
+        // Arrange
+        var view = new TestView
+        {
+            Enabled = true,
+            Visible = false
+        };
+
+        var eventArgs = new CancelEventArgs ();
+
+        // Act
+        bool? cancelled = view.NewMouseEnterEvent (eventArgs);
+
+        // Assert
+        Assert.False (view.MouseEnterRaised);
+        Assert.Null (cancelled);
+        Assert.False (eventArgs.Cancel);
+
+        // Cleanup
+        view.Dispose ();
+    }
+
+    [Fact]
+    public void NewMouseLeaveEvent_ViewIsVisible_RaisesMouseLeave ()
+    {
+        // Arrange
+        var view = new TestView
+        {
+            Enabled = true,
+            Visible = true
+        };
+
+        var mouseEvent = new MouseEvent ();
+
+        // Act
+        view.NewMouseLeaveEvent ();
+
+        // Assert
+        Assert.True (view.MouseLeaveRaised);
+        Assert.False (mouseEvent.Handled);
+
+        // Cleanup
+        view.Dispose ();
+    }
+
+    [Fact]
+    public void NewMouseLeaveEvent_ViewIsNotVisible_RaisesMouseLeave ()
+    {
+        // Arrange
+        var view = new TestView
+        {
+            Enabled = true,
+            Visible = false
+        };
+
+        var mouseEvent = new MouseEvent ();
+
+        // Act
+        view.NewMouseLeaveEvent ();
+
+        // Assert
+        Assert.True (view.MouseLeaveRaised);
+        Assert.False (mouseEvent.Handled);
+
+        // Cleanup
+        view.Dispose ();
+    }
+
+    // Handled tests
+    [Fact]
+    public void NewMouseEnterEvent_HandleOnMouseEnter_Event_Not_Raised ()
+    {
+        // Arrange
+        var view = new TestView
+        {
+            Enabled = true,
+            Visible = true,
+            CancelOnEnter = true
+        };
+
+        var eventArgs = new CancelEventArgs ();
+
+        // Act
+        bool? cancelled = view.NewMouseEnterEvent (eventArgs);
+
+        // Assert
+        Assert.True (view.OnMouseEnterCalled);
+        Assert.True (cancelled);
+        Assert.True (eventArgs.Cancel);
+
+        Assert.False (view.MouseEnterRaised);
+
+        // Cleanup
+        view.Dispose ();
+    }
+
+    [Fact]
+    public void NewMouseEnterEvent_HandleMouseEnterEvent ()
+    {
+        // Arrange
+        var view = new TestView
+        {
+            Enabled = true,
+            Visible = true,
+            CancelEnterEvent = true
+        };
+
+        var eventArgs = new CancelEventArgs ();
+
+        // Act
+        bool? cancelled = view.NewMouseEnterEvent (eventArgs);
+
+        // Assert
+        Assert.True (view.OnMouseEnterCalled);
+        Assert.True (cancelled);
+        Assert.True (eventArgs.Cancel);
+
+        Assert.True (view.MouseEnterRaised);
+
+        // Cleanup
+        view.Dispose ();
+    }
+}

+ 6 - 2
UnitTests/View/MouseTests.cs → UnitTests/View/Mouse/MouseTests.cs

@@ -1,6 +1,8 @@
 using Xunit.Abstractions;
 
-namespace Terminal.Gui.ViewTests;
+namespace Terminal.Gui.ViewMouseTests;
+
+[Trait ("Category", "Input")]
 
 public class MouseTests (ITestOutputHelper output) : TestsAllViews
 {
@@ -359,7 +361,7 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
         int enablingHighlight = 0;
         int disablingHighlight = 0;
         view.Highlight += View_Highlight;
-        view.ColorScheme = new ColorScheme (new Attribute (ColorName.Red, ColorName.Blue));
+        view.ColorScheme = new ColorScheme (new Attribute (ColorName16.Red, ColorName16.Blue));
         ColorScheme originalColorScheme = view.ColorScheme;
 
         view.NewMouseEvent (new () { Flags = MouseFlags.Button1Pressed, });
@@ -463,4 +465,6 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
             }
         }
     }
+
+
 }

+ 1 - 3
UnitTests/View/ViewTests.cs

@@ -136,7 +136,7 @@ public class ViewTests (ITestOutputHelper output)
     }
 
     [Theory]
-    [AutoInitShutdown]
+    [AutoInitShutdown (configLocation: ConfigurationManager.ConfigLocations.DefaultOnly)]
     [InlineData (true)]
     [InlineData (false)]
     public void Clear_Does_Not_Spillover_Its_Parent (bool label)
@@ -875,8 +875,6 @@ At 0,0
         //Assert.False (r.OnKeyDown (new KeyEventArgs () { Key = Key.Unknown }));
         Assert.False (r.OnKeyUp (new() { KeyCode = KeyCode.Null }));
         Assert.False (r.NewMouseEvent (new() { Flags = MouseFlags.AllEvents }));
-        Assert.False (r.NewMouseEnterEvent (new() { Flags = MouseFlags.AllEvents }));
-        Assert.False (r.NewMouseLeaveEvent (new() { Flags = MouseFlags.AllEvents }));
 
         r.Dispose ();
 

+ 10 - 34
UnitTests/Views/ButtonTests.cs

@@ -52,6 +52,9 @@ public class ButtonTests (ITestOutputHelper output)
     [InlineData ("0_12你", 10, 1, 10, 1)]
     public void Button_AbsoluteSize_Text (string text, int width, int height, int expectedWidth, int expectedHeight)
     {
+        // Override CM
+        Button.DefaultShadow = ShadowStyle.None;
+
         var btn1 = new Button
         {
             Text = text,
@@ -76,6 +79,9 @@ public class ButtonTests (ITestOutputHelper output)
     [InlineData (10, 3, 10, 3)]
     public void Button_AbsoluteSize_DefaultText (int width, int height, int expectedWidth, int expectedHeight)
     {
+        // Override CM
+        Button.DefaultShadow = ShadowStyle.None;
+
         var btn1 = new Button ();
         btn1.Width = width;
         btn1.Height = height;
@@ -143,6 +149,9 @@ public class ButtonTests (ITestOutputHelper output)
     [SetupFakeDriver]
     public void Constructors_Defaults ()
     {
+        // Override CM
+        Button.DefaultShadow = ShadowStyle.None;
+
         var btn = new Button ();
         Assert.Equal (string.Empty, btn.Text);
         btn.BeginInit ();
@@ -548,44 +557,11 @@ public class ButtonTests (ITestOutputHelper output)
         b.Dispose ();
     }
 
-    [Fact]
-    [AutoInitShutdown]
-    public void Update_Only_On_Or_After_Initialize ()
-    {
-        var btn = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Say Hello 你" };
-        var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
-        win.Add (btn);
-        var top = new Toplevel ();
-        top.Add (win);
-
-        Assert.False (btn.IsInitialized);
-
-        Application.Begin (top);
-        ((FakeDriver)Application.Driver!).SetBufferSize (30, 5);
-
-        Assert.True (btn.IsInitialized);
-        Assert.Equal ("Say Hello 你", btn.Text);
-        Assert.Equal ($"{CM.Glyphs.LeftBracket} {btn.Text} {CM.Glyphs.RightBracket}", btn.TextFormatter.Text);
-        Assert.Equal (new (0, 0, 16, 1), btn.Viewport);
-        var btnTxt = $"{CM.Glyphs.LeftBracket} {btn.Text} {CM.Glyphs.RightBracket}";
-
-        var expected = @$"
-┌────────────────────────────┐
-│                            │
-│      {btnTxt}      │
-│                            │
-└────────────────────────────┘
-";
-
-        Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
-        Assert.Equal (new (0, 0, 30, 5), pos);
-        top.Dispose ();
-    }
-
     [Fact]
     [AutoInitShutdown]
     public void Update_Parameterless_Only_On_Or_After_Initialize ()
     {
+        Button.DefaultShadow = ShadowStyle.None;
         var btn = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Say Hello 你" };
         var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
         win.Add (btn);

+ 13 - 13
UnitTests/Views/ColorPicker16Tests.cs

@@ -6,7 +6,7 @@ public class ColorPicker16Tests
     public void Constructors ()
     {
         var colorPicker = new ColorPicker16 ();
-        Assert.Equal (ColorName.Black, colorPicker.SelectedColor);
+        Assert.Equal (ColorName16.Black, colorPicker.SelectedColor);
         Assert.Equal (Point.Empty, colorPicker.Cursor);
         Assert.True (colorPicker.CanFocus);
 
@@ -20,25 +20,25 @@ public class ColorPicker16Tests
     public void KeyBindings_Command ()
     {
         var colorPicker = new ColorPicker16 ();
-        Assert.Equal (ColorName.Black, colorPicker.SelectedColor);
+        Assert.Equal (ColorName16.Black, colorPicker.SelectedColor);
 
         Assert.True (colorPicker.NewKeyDownEvent (Key.CursorRight));
-        Assert.Equal (ColorName.Blue, colorPicker.SelectedColor);
+        Assert.Equal (ColorName16.Blue, colorPicker.SelectedColor);
 
         Assert.True (colorPicker.NewKeyDownEvent (Key.CursorDown));
-        Assert.Equal (ColorName.BrightBlue, colorPicker.SelectedColor);
+        Assert.Equal (ColorName16.BrightBlue, colorPicker.SelectedColor);
 
         Assert.True (colorPicker.NewKeyDownEvent (Key.CursorLeft));
-        Assert.Equal (ColorName.DarkGray, colorPicker.SelectedColor);
+        Assert.Equal (ColorName16.DarkGray, colorPicker.SelectedColor);
 
         Assert.True (colorPicker.NewKeyDownEvent (Key.CursorUp));
-        Assert.Equal (ColorName.Black, colorPicker.SelectedColor);
+        Assert.Equal (ColorName16.Black, colorPicker.SelectedColor);
 
         Assert.True (colorPicker.NewKeyDownEvent (Key.CursorLeft));
-        Assert.Equal (ColorName.Black, colorPicker.SelectedColor);
+        Assert.Equal (ColorName16.Black, colorPicker.SelectedColor);
 
         Assert.True (colorPicker.NewKeyDownEvent (Key.CursorUp));
-        Assert.Equal (ColorName.Black, colorPicker.SelectedColor);
+        Assert.Equal (ColorName16.Black, colorPicker.SelectedColor);
     }
 
     [Fact]
@@ -46,7 +46,7 @@ public class ColorPicker16Tests
     public void MouseEvents ()
     {
         var colorPicker = new ColorPicker16 { X = 0, Y = 0, Height = 4, Width = 32 };
-        Assert.Equal (ColorName.Black, colorPicker.SelectedColor);
+        Assert.Equal (ColorName16.Black, colorPicker.SelectedColor);
         var top = new Toplevel ();
         top.Add (colorPicker);
         Application.Begin (top);
@@ -54,7 +54,7 @@ public class ColorPicker16Tests
         Assert.False (colorPicker.NewMouseEvent (new ()));
 
         Assert.True (colorPicker.NewMouseEvent (new () { Position = new (4, 1), Flags = MouseFlags.Button1Clicked }));
-        Assert.Equal (ColorName.Blue, colorPicker.SelectedColor);
+        Assert.Equal (ColorName16.Blue, colorPicker.SelectedColor);
         top.Dispose ();
     }
 
@@ -62,7 +62,7 @@ public class ColorPicker16Tests
     public void SelectedColorAndCursor ()
     {
         var colorPicker = new ColorPicker16 ();
-        colorPicker.SelectedColor = ColorName.White;
+        colorPicker.SelectedColor = ColorName16.White;
         Assert.Equal (7, colorPicker.Cursor.X);
         Assert.Equal (1, colorPicker.Cursor.Y);
 
@@ -71,9 +71,9 @@ public class ColorPicker16Tests
         Assert.Equal (0, colorPicker.Cursor.Y);
 
         colorPicker.Cursor = new (7, 1);
-        Assert.Equal (ColorName.White, colorPicker.SelectedColor);
+        Assert.Equal (ColorName16.White, colorPicker.SelectedColor);
 
         colorPicker.Cursor = Point.Empty;
-        Assert.Equal (ColorName.Black, colorPicker.SelectedColor);
+        Assert.Equal (ColorName16.Black, colorPicker.SelectedColor);
     }
 }

+ 1 - 1
UnitTests/Views/ComboBoxTests.cs

@@ -492,7 +492,7 @@ public class ComboBoxTests (ITestOutputHelper output)
     }
 
     [Fact]
-    [AutoInitShutdown]
+    [AutoInitShutdown (configLocation: ConfigurationManager.ConfigLocations.DefaultOnly)]
     public void HideDropdownListOnClick_True_Highlight_Current_Item ()
     {
         var selected = "";

+ 4 - 4
UnitTests/Views/ContextMenuTests.cs

@@ -1396,7 +1396,7 @@ public class ContextMenuTests (ITestOutputHelper output)
         Assert.True (tf1.HasFocus);
         Assert.False (tf2.HasFocus);
         Assert.Equal (4, win.Subviews.Count); // TF & TV add autocomplete popup's to their superviews.
-        Assert.Null (Application.MouseEnteredView);
+        Assert.Empty (Application._cachedViewsUnderMouse);
 
         // Right click on tf2 to open context menu
         Application.OnMouseEvent (new () { Position = new (1, 3), Flags = MouseFlags.Button3Clicked });
@@ -1406,7 +1406,7 @@ public class ContextMenuTests (ITestOutputHelper output)
         Assert.True (tf2.ContextMenu.MenuBar.IsMenuOpen);
         Assert.True (win.Focused is Menu);
         Assert.True (Application.MouseGrabView is MenuBar);
-        Assert.Equal (tf2, Application.MouseEnteredView);
+        Assert.Equal (tf2, Application._cachedViewsUnderMouse.LastOrDefault ());
 
         // Click on tf1 to focus it, which cause context menu being closed
         Application.OnMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.Button1Clicked });
@@ -1418,7 +1418,7 @@ public class ContextMenuTests (ITestOutputHelper output)
         Assert.NotNull (tf2.ContextMenu.MenuBar);
         Assert.Equal (win.Focused, tf1);
         Assert.Null (Application.MouseGrabView);
-        Assert.Equal (tf1, Application.MouseEnteredView);
+        Assert.Equal (tf1, Application._cachedViewsUnderMouse.LastOrDefault ());
 
         // Click on tf2 to focus it
         Application.OnMouseEvent (new () { Position = new (1, 3), Flags = MouseFlags.Button1Clicked });
@@ -1430,7 +1430,7 @@ public class ContextMenuTests (ITestOutputHelper output)
         Assert.NotNull (tf2.ContextMenu.MenuBar);
         Assert.Equal (win.Focused, tf2);
         Assert.Null (Application.MouseGrabView);
-        Assert.Equal (tf2, Application.MouseEnteredView);
+        Assert.Equal (tf2, Application._cachedViewsUnderMouse.LastOrDefault ());
 
         Application.End (rs);
         win.Dispose ();

+ 9 - 4
UnitTests/Views/MenuBarTests.cs

@@ -170,6 +170,7 @@ public class MenuBarTests (ITestOutputHelper output)
 
         Assert.True (menu.NewKeyDownEvent (menu.Key));
         Assert.True (menu.IsMenuOpen);
+        top.Dispose ();
     }
 
     [Fact]
@@ -314,7 +315,7 @@ public class MenuBarTests (ITestOutputHelper output)
     }
 
     [Fact]
-    [AutoInitShutdown]
+    [AutoInitShutdown (configLocation: ConfigurationManager.ConfigLocations.DefaultOnly)]
     public void Disabled_MenuBar_Is_Never_Opened ()
     {
         Toplevel top = new ();
@@ -340,7 +341,7 @@ public class MenuBarTests (ITestOutputHelper output)
     }
 
     [Fact]
-    [AutoInitShutdown]
+    [AutoInitShutdown (configLocation: ConfigurationManager.ConfigLocations.DefaultOnly)]
     public void Disabled_MenuItem_Is_Never_Selected ()
     {
         var menu = new MenuBar
@@ -455,6 +456,8 @@ public class MenuBarTests (ITestOutputHelper output)
         Window.DefaultBorderStyle = LineStyle.Single;
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         Toplevel top = new ();
         var win = new Window ();
@@ -684,12 +687,14 @@ public class MenuBarTests (ITestOutputHelper output)
     [AutoInitShutdown]
     public void Draw_A_Menu_Over_A_Top_Dialog ()
     {
+        ((FakeDriver)Application.Driver).SetBufferSize (40, 15);
+
         // Override CM
         Window.DefaultBorderStyle = LineStyle.Single;
         Dialog.DefaultButtonAlignment = Alignment.Center;
         Dialog.DefaultBorderStyle = LineStyle.Single;
-
-        ((FakeDriver)Application.Driver).SetBufferSize (40, 15);
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
 
         Assert.Equal (new (0, 0, 40, 15), Application.Driver?.Clip);
         TestHelpers.AssertDriverContentsWithFrameAre (@"", output);

+ 0 - 5
UnitTests/Views/ProgressBarTests.cs

@@ -15,11 +15,6 @@ public class ProgressBarTests
         Assert.False (pb.CanFocus);
         Assert.Equal (0, pb.Fraction);
 
-        Assert.Equal (
-                      new Attribute (Color.BrightGreen, Color.Gray),
-                      new Attribute (pb.ColorScheme.HotNormal.Foreground, pb.ColorScheme.HotNormal.Background)
-                     );
-        Assert.Equal (Colors.ColorSchemes ["Base"].Normal, pb.ColorScheme.Normal);
         Assert.Equal (1, pb.Frame.Height);
         Assert.Equal (ProgressBarStyle.Blocks, pb.ProgressBarStyle);
         Assert.Equal (ProgressBarFormat.Simple, pb.ProgressBarFormat);

+ 1 - 1
UnitTests/Views/RuneCellTests.cs

@@ -50,7 +50,7 @@ public class RuneCellTests (ITestOutputHelper output)
     }
 
     [Fact]
-    [AutoInitShutdown]
+    [AutoInitShutdown (configLocation: ConfigurationManager.ConfigLocations.DefaultOnly)]
     public void RuneCell_LoadRuneCells_InheritsPreviousColorScheme ()
     {
         List<RuneCell> runeCells = new ();

+ 7 - 0
UnitTests/Views/ScrollBarViewTests.cs

@@ -1143,6 +1143,13 @@ This is a test
     [AutoInitShutdown]
     public void ShowScrollIndicator_False_Must_Also_Set_Visible_To_False_To_Not_Respond_To_Events ()
     {
+        // Override CM
+        Window.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultButtonAlignment = Alignment.Center;
+        Dialog.DefaultBorderStyle = LineStyle.Single;
+        Dialog.DefaultShadow = ShadowStyle.None;
+        Button.DefaultShadow = ShadowStyle.None;
+
         var clicked = false;
         var text = "This is a test\nThis is a test\nThis is a test\nThis is a test\nThis is a test";
         var label = new Label { Width = 14, Height = 5, Text = text };

+ 1 - 1
UnitTests/Views/ScrollViewTests.cs

@@ -174,7 +174,7 @@ public class ScrollViewTests (ITestOutputHelper output)
 
     // There are still issue with the lower right corner of the scroll view
     [Fact]
-    [AutoInitShutdown]
+    [AutoInitShutdown (configLocation: ConfigurationManager.ConfigLocations.DefaultOnly)]
     public void Clear_Window_Inside_ScrollView ()
     {
         var topLabel = new Label { X = 15, Text = "At 15,0" };

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác