Tig hai 10 meses
pai
achega
c249e14ab6

+ 2 - 2
Terminal.Gui/View/Adornment/Border.cs

@@ -289,7 +289,7 @@ public class Border : Adornment
                 _dragPosition = mouseEvent.Position;
                 Application.GrabMouse (this);
 
-                SetHighlight (HighlightStyle);
+                SetPressedHighlight (HighlightStyle);
             }
 
             return true;
@@ -334,7 +334,7 @@ public class Border : Adornment
         {
             _dragPosition = null;
             Application.UngrabMouse ();
-            SetHighlight (HighlightStyle.None);
+            SetPressedHighlight (HighlightStyle.None);
 
             return true;
         }

+ 5 - 5
Terminal.Gui/View/HighlightStyle.cs

@@ -3,29 +3,29 @@
 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,
 
     /// <summary>
-    /// The mouse is hovering over the view (but not pressed). See <see cref="View.MouseEnter"/>.
+    ///     The mouse is hovering over the view (but not pressed). See <see cref="View.MouseEnter"/>.
     /// </summary>
     Hover = 1,
 
     /// <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
 }

+ 263 - 221
Terminal.Gui/View/View.Mouse.cs

@@ -1,129 +1,10 @@
 #nullable enable
-using System;
 using System.ComponentModel;
 
 namespace Terminal.Gui;
 
 public partial class View // Mouse APIs
 {
-    private ColorScheme? _savedHighlightColorScheme;
-
-
-    /// <summary>Event fired when a mouse click occurs.</summary>
-    /// <remarks>
-    ///     <para>
-    ///         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>
-    public event EventHandler<MouseEventEventArgs>? MouseClick;
-
-    /// <summary>Event fired when a mouse event occurs.</summary>
-    /// <remarks>
-    ///     <para>
-    ///         The coordinates are relative to <see cref="View.Viewport"/>.
-    ///     </para>
-    /// </remarks>
-    public event EventHandler<MouseEventEventArgs>? MouseEvent;
-
-    /// <summary>
-    ///     Processes a <see cref="MouseEvent"/>. This method is called by <see cref="Application.OnMouseEvent"/> when a mouse
-    ///     event occurs.
-    /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         A view must be both enabled and visible to receive mouse events.
-    ///     </para>
-    ///     <para>
-    ///         This method calls <see cref="OnMouseEvent"/> to process the event. If the event is not handled, and one of the
-    ///         mouse buttons was clicked, it calls <see cref="OnMouseClick"/> to process the click.
-    ///     </para>
-    ///     <para>
-    ///         See <see cref="SetHighlight"/> for more information.
-    ///     </para>
-    ///     <para>
-    ///         If <see cref="WantContinuousButtonPressed"/> is <see langword="true"/>, the <see cref="OnMouseClick"/> event
-    ///         will be invoked repeatedly while the button is pressed.
-    ///     </para>
-    /// </remarks>
-    /// <param name="mouseEvent"></param>
-    /// <returns><see langword="true"/> if the event was handled, <see langword="false"/> otherwise.</returns>
-    public bool? NewMouseEvent (MouseEvent mouseEvent)
-    {
-        if (!Enabled)
-        {
-            // A disabled view should not eat mouse events
-            return false;
-        }
-
-        if (!CanBeVisible (this))
-        {
-            return false;
-        }
-
-        if (!WantMousePositionReports && mouseEvent.Flags == MouseFlags.ReportMousePosition)
-        {
-            return false;
-        }
-
-        if (OnMouseEvent (mouseEvent))
-        {
-            // Technically mouseEvent.Handled should already be true if implementers of OnMouseEvent
-            // follow the rules. But we'll update it just in case.
-            return mouseEvent.Handled = true;
-        }
-
-        if (HighlightStyle != HighlightStyle.None || (WantContinuousButtonPressed && WantMousePositionReports))
-        {
-            if (HandlePressed (mouseEvent))
-            {
-                return mouseEvent.Handled;
-            }
-
-            if (HandleReleased (mouseEvent))
-            {
-                return mouseEvent.Handled;
-            }
-
-            if (HandleClicked (mouseEvent))
-            {
-                return mouseEvent.Handled;
-            }
-        }
-
-        if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button2Clicked)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button3Clicked)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button4Clicked)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button1DoubleClicked)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button2DoubleClicked)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button3DoubleClicked)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button4DoubleClicked)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button1TripleClicked)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button2TripleClicked)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button3TripleClicked)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button4TripleClicked)
-           )
-        {
-            // If it's a click, and we didn't handle it, then we'll call OnMouseClick
-            // 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));
-        }
-
-        return false;
-    }
-
-    /// <summary>Gets or sets whether the <see cref="View"/> wants continuous button pressed events.</summary>
-    public virtual bool WantContinuousButtonPressed { get; set; }
-
-    /// <summary>Gets or sets whether the <see cref="View"/> wants mouse position reports.</summary>
-    /// <value><see langword="true"/> if mouse position reports are wanted; otherwise, <see langword="false"/>.</value>
-    public virtual bool WantMousePositionReports { get; set; }
-
     #region MouseEnterLeave
 
     private bool _hovering;
@@ -143,11 +24,13 @@ public partial class View // Mouse APIs
     /// </returns>
     internal bool? NewMouseEnterEvent (CancelEventArgs eventArgs)
     {
+        // Pre-conditions
         if (!CanBeVisible (this))
         {
             return null;
         }
 
+        // Cancellable event
         if (OnMouseEnter (eventArgs))
         {
             return true;
@@ -162,12 +45,13 @@ public partial class View // Mouse APIs
             return true;
         }
 
-        if ((HighlightStyle.HasFlag (HighlightStyle.Hover) || Diagnostics.HasFlag (ViewDiagnosticFlags.Hover)))
+        // Post-conditions
+        if (HighlightStyle.HasFlag (HighlightStyle.Hover) || Diagnostics.HasFlag (ViewDiagnosticFlags.Hover))
         {
             HighlightStyle copy = HighlightStyle;
-            HighlightStyle hover = HighlightStyle.Hover;
+            var hover = HighlightStyle.Hover;
             CancelEventArgs<HighlightStyle> args = new (ref copy, ref hover);
-            
+
             if (RaiseHighlight (args) || args.Cancel)
             {
                 return args.Cancel;
@@ -189,7 +73,8 @@ public partial class View // Mouse APIs
     }
 
     /// <summary>
-    ///     Called when the mouse moves over the View's <see cref="Frame"/> and no other non-Subview occludes it. <see cref="MouseLeave"/> will
+    ///     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>
     /// <remarks>
@@ -204,7 +89,7 @@ public partial class View // Mouse APIs
     ///         Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
     ///     </para>
     ///     <para>
-    ///         See <see cref="SetHighlight"/> for more information.
+    ///         See <see cref="SetPressedHighlight"/> for more information.
     ///     </para>
     /// </remarks>
     /// <param name="eventArgs"></param>
@@ -235,13 +120,14 @@ public partial class View // Mouse APIs
     ///         prevents Views higher in the visible hierarchy from receiving Enter/Leave events.
     ///     </para>
     ///     <para>
-    ///         See <see cref="SetHighlight"/> for more information.
+    ///         See <see cref="SetPressedHighlight"/> for more information.
     ///     </para>
     /// </remarks>
     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.
+    ///     INTERNAL Called by <see cref="Application.OnMouseEvent"/> when the mouse leaves <see cref="Frame"/>, or is occluded
+    ///     by another non-SubView.
     /// </summary>
     /// <remarks>
     ///     <para>
@@ -251,21 +137,25 @@ public partial class View // Mouse APIs
     ///         Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
     ///     </para>
     ///     <para>
-    ///         See <see cref="SetHighlight"/> for more information.
+    ///         See <see cref="SetPressedHighlight"/> for more information.
     ///     </para>
     /// </remarks>
     internal void NewMouseLeaveEvent ()
     {
+        // Pre-conditions
+
+        // Non-cancellable event
         OnMouseLeave ();
 
         MouseLeave?.Invoke (this, EventArgs.Empty);
 
+        // Post-conditions
         _hovering = false;
 
-        if ((HighlightStyle.HasFlag (HighlightStyle.Hover) || Diagnostics.HasFlag (ViewDiagnosticFlags.Hover)))
+        if (HighlightStyle.HasFlag (HighlightStyle.Hover) || Diagnostics.HasFlag (ViewDiagnosticFlags.Hover))
         {
             HighlightStyle copy = HighlightStyle;
-            HighlightStyle hover = HighlightStyle.None;
+            var hover = HighlightStyle.None;
             RaiseHighlight (new (ref copy, ref hover));
 
             if (_savedNonHoverColorScheme is { })
@@ -284,26 +174,136 @@ public partial class View // Mouse APIs
     ///         Adornments receive MouseEnter/Leave events when the mouse is over the Adornment's <see cref="Thickness"/>.
     ///     </para>
     ///     <para>
-    ///         See <see cref="SetHighlight"/> for more information.
+    ///         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. 
+    ///     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="SetHighlight"/> for more information.
+    ///         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>
+    ///     <para>
+    ///         The coordinates are relative to <see cref="View.Viewport"/>.
+    ///     </para>
+    /// </remarks>
+    public event EventHandler<MouseEventEventArgs>? MouseEvent;
+
+    /// <summary>
+    ///     Processes a <see cref="MouseEvent"/>. This method is called by <see cref="Application.OnMouseEvent"/> when a mouse
+    ///     event occurs.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         A view must be both enabled and visible to receive mouse events.
+    ///     </para>
+    ///     <para>
+    ///         This method calls <see cref="OnMouseEvent"/> to process the event. If the event is not handled, and one of the
+    ///         mouse buttons was clicked, it calls <see cref="OnMouseClick"/> to process the click.
+    ///     </para>
+    ///     <para>
+    ///         See <see cref="SetPressedHighlight"/> for more information.
+    ///     </para>
+    ///     <para>
+    ///         If <see cref="WantContinuousButtonPressed"/> is <see langword="true"/>, the <see cref="OnMouseClick"/> event
+    ///         will be invoked repeatedly while the button is pressed.
+    ///     </para>
+    /// </remarks>
+    /// <param name="mouseEvent"></param>
+    /// <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
+            return false;
+        }
+
+        if (!CanBeVisible (this))
+        {
+            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
+            // follow the rules. But we'll update it just in case.
+            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 (WhenGrabbedHandlePressed (mouseEvent))
+            {
+                return mouseEvent.Handled;
+            }
+
+            if (WhenGrabbedHandleReleased (mouseEvent))
+            {
+                return mouseEvent.Handled;
+            }
+
+            if (WhenGrabbedHandleClicked (mouseEvent))
+            {
+                return mouseEvent.Handled;
+            }
+        }
+
+        if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button2Clicked)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button3Clicked)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button4Clicked)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button1DoubleClicked)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button2DoubleClicked)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button3DoubleClicked)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button4DoubleClicked)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button1TripleClicked)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button2TripleClicked)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button3TripleClicked)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button4TripleClicked)
+           )
+        {
+            // 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));
+        }
+
+        return false;
+    }
+
+    /// <summary>Gets or sets whether the <see cref="View"/> wants continuous button pressed events.</summary>
+    public virtual bool WantContinuousButtonPressed { get; set; }
+
+    /// <summary>Gets or sets whether the <see cref="View"/> wants mouse position reports.</summary>
+    /// <value><see langword="true"/> if mouse position reports are wanted; otherwise, <see langword="false"/>.</value>
+    public virtual bool WantMousePositionReports { get; set; }
+
     /// <summary>Called when a mouse event occurs within the view's <see cref="Viewport"/>.</summary>
     /// <remarks>
     ///     <para>
@@ -321,6 +321,23 @@ public partial class View // Mouse APIs
         return args.Handled;
     }
 
+    #endregion Low Level Mouse Events
+
+    #region Mouse Click Events
+
+    /// <summary>Event fired when a mouse click occurs.</summary>
+    /// 
+    /// <remarks>
+    ///     <para>
+    ///         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>
+    public event EventHandler<MouseEventEventArgs>? MouseClick;
+
     /// <summary>Invokes the MouseClick event.</summary>
     /// <remarks>
     ///     <para>
@@ -331,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)
@@ -344,6 +368,7 @@ public partial class View // Mouse APIs
             return true;
         }
 
+        // Post-conditions
         if (!HasFocus && CanFocus)
         {
             args.Handled = true;
@@ -354,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>
@@ -362,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)
@@ -373,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));
@@ -391,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>
@@ -399,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)
@@ -408,7 +433,7 @@ public partial class View // Mouse APIs
         {
             if (Application.MouseGrabView == this)
             {
-                SetHighlight (HighlightStyle.None);
+                SetPressedHighlight (HighlightStyle.None);
             }
 
             return mouseEvent.Handled = true;
@@ -417,16 +442,89 @@ public partial class View // Mouse APIs
         return false;
     }
 
-    #region Highlight
+    /// <summary>
+    ///     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>
+    ///         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 WhenGrabbedHandlePressed (MouseEvent mouseEvent)
+    {
+        if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button2Pressed)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button3Pressed)
+            || mouseEvent.Flags.HasFlag (MouseFlags.Button4Pressed))
+        {
+            // The first time we get pressed event, grab the mouse and set focus
+            if (Application.MouseGrabView != this)
+            {
+                Application.GrabMouse (this);
+
+                if (!HasFocus && CanFocus)
+                {
+                    // Set the focus, but don't invoke Accept
+                    SetFocus ();
+                }
+
+                mouseEvent.Handled = true;
+            }
+
+            if (Viewport.Contains (mouseEvent.Position))
+            {
+                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;
+        }
+
+        return false;
+    }
+
+    #endregion Mouse Click Events
+
+    #region Highlight Handling
+
+    // Used for Pressed highlighting
+    private ColorScheme? _savedHighlightColorScheme;
 
     /// <summary>
-    ///     Gets or sets whether the <see cref="View"/> will be highlighted visually while the mouse button is
-    ///     pressed.
+    ///     Gets or sets whether the <see cref="View"/> will be highlighted visually by mouse interaction.
     /// </summary>
     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 (OnHighlight (args) == true)
+        if (OnHighlight (args))
         {
             return true;
         }
@@ -437,36 +535,42 @@ public partial class View // Mouse APIs
     }
 
     /// <summary>
-    ///     Called when the view is to be highlighted. See <see cref="HighlightStyle"/>.
+    ///     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>
-    /// <returns><see langword="true"/>, if the event was handled, <see langword="false"/> otherwise.</returns>
-    protected virtual bool OnHighlight (CancelEventArgs<HighlightStyle> args)
-    {
-        return false;
-    }
+    /// <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>
-    ///     Fired when the view is highlighted. See <see cref="HighlightStyle"/>.
+    ///     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>
-    ///     Enables the highlight for the view when the mouse is pressed. Called from OnMouseEvent.
+    ///     INTERNAL Enables the highlight for the view when the mouse is pressed. Called from OnMouseEvent.
     /// </summary>
     /// <remarks>
     ///     <para>
-    ///         Set <see cref="HighlightStyle"/> to <see cref="HighlightStyle.Pressed"/> to enable.
+    ///         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)
@@ -474,17 +578,17 @@ public partial class View // Mouse APIs
             return false;
         }
 
-        // Enable override via virtual method and/or event
         HighlightStyle copy = HighlightStyle;
         CancelEventArgs<HighlightStyle> args = new (ref copy, ref newHighlightStyle);
 
-        if (RaiseHighlight (args))
+        if (RaiseHighlight (args) || args.Cancel)
         {
             return true;
         }
 
-        // For Shadow
+        // For 3D Pressed Style - Note we don't care about canceling the event here
         Margin?.RaiseHighlight (args);
+        args.Cancel = false; // Just in case
 
         if (args.NewValue.HasFlag (HighlightStyle.Pressed) || args.NewValue.HasFlag (HighlightStyle.PressedOutside))
         {
@@ -529,69 +633,7 @@ public partial class View // Mouse APIs
         return false;
     }
 
-    #endregion
-
-    /// <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).
-    /// </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)
-    {
-        if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button2Pressed)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button3Pressed)
-            || mouseEvent.Flags.HasFlag (MouseFlags.Button4Pressed))
-        {
-            // The first time we get pressed event, grab the mouse and set focus
-            if (Application.MouseGrabView != this)
-            {
-                Application.GrabMouse (this);
-
-                if (!HasFocus && CanFocus)
-                {
-                    // Set the focus, but don't invoke Accept
-                    SetFocus ();
-                }
-
-                mouseEvent.Handled = true;
-            }
-
-            if (Viewport.Contains (mouseEvent.Position))
-            {
-                if (this is not Adornment
-                    && SetHighlight (HighlightStyle.HasFlag (HighlightStyle.Pressed) ? HighlightStyle.Pressed : HighlightStyle.None))
-                {
-                    return true;
-                }
-            }
-            else
-            {
-                if (this is not Adornment
-                    && SetHighlight (HighlightStyle.HasFlag (HighlightStyle.PressedOutside) ? HighlightStyle.PressedOutside : HighlightStyle.None))
-
-                {
-                    return true;
-                }
-            }
-
-            if (WantContinuousButtonPressed && Application.MouseGrabView == this)
-            {
-                // If this is not the first pressed event, click
-                return OnMouseClick (new (mouseEvent));
-            }
-
-            return mouseEvent.Handled = true;
-        }
-
-        return false;
-    }
+    #endregion Highlight Handling
 
     /// <summary>
     ///     INTERNAL: Gets the Views that are under the mouse at <paramref name="location"/>, including Adornments.

+ 4 - 37
Terminal.Gui/Views/Shortcut.cs

@@ -146,19 +146,12 @@ 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))
         {
-            SetColors (highlight: true);
-        }
-        else
-        {
-            // If we have focus, act like we're highlighted
-            SetColors (highlight: HasFocus);
+            HasFocus = true;
         }
 
         return true;
@@ -339,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
@@ -522,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)
@@ -551,6 +516,7 @@ public class Shortcut : View, IOrientation, IDesignable
 
         HelpView.Visible = true;
         HelpView.VerticalTextAlignment = Alignment.Center;
+        HelpView.HighlightStyle = HighlightStyle.None;
     }
 
     /// <summary>
@@ -692,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)

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

+ 16 - 6
UICatalog/Scenarios/Bars.cs

@@ -116,7 +116,7 @@ public class Bars : Scenario
             //Width = Dim.Percent (40),
             Orientation = Orientation.Vertical,
         };
-            ConfigureMenu (bar);
+        ConfigureMenu (bar);
 
         menuLikeExamples.Add (bar);
 
@@ -390,22 +390,32 @@ public class Bars : Scenario
             HighlightStyle = HighlightStyle.Hover
         };
 
+        var shortcut3 = new Shortcut
+        {
+            Title = "_Three",
+            Text = "The 3rd item",
+            Key = Key.D3.WithAlt,
+            HighlightStyle = HighlightStyle.Hover
+        };
+
         var line = new Line ()
         {
             BorderStyle = LineStyle.Dotted,
             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 - 1
UICatalog/Scenarios/Shortcuts.cs

@@ -57,7 +57,6 @@ public class Shortcuts : Scenario
             Text = "Width is 35",
             KeyBindingScope = KeyBindingScope.Application,
         };
-        vShortcut1.HighlightStyle |= HighlightStyle.Hover;
 
         Application.Top.Add (vShortcut1);
 

+ 1 - 2
UnitTests/Views/MenuBarTests.cs

@@ -660,15 +660,14 @@ public class MenuBarTests (ITestOutputHelper output)
     [AutoInitShutdown (configLocation: ConfigurationManager.ConfigLocations.None)]
     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;
         Button.DefaultShadow = ShadowStyle.None;
 
-
         Assert.Equal (new (0, 0, 40, 15), Application.Driver?.Clip);
         TestHelpers.AssertDriverContentsWithFrameAre (@"", output);