Tig vor 10 Monaten
Ursprung
Commit
12e3346c01

+ 108 - 33
Terminal.Gui/Application/Application.Mouse.cs

@@ -1,4 +1,10 @@
 #nullable enable
 #nullable enable
+<<<<<<< Updated upstream
+=======
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.VisualBasic.Syntax;
+
+>>>>>>> Stashed changes
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
 public static partial class Application // Mouse handling
 public static partial class Application // Mouse handling
@@ -116,8 +122,8 @@ public static partial class Application // Mouse handling
         UnGrabbedMouse?.Invoke (view, new (view));
         UnGrabbedMouse?.Invoke (view, new (view));
     }
     }
 
 
-    // Used by OnMouseEvent to track the last view that was clicked on.
-    internal static View? MouseEnteredView { get; set; }
+    // Used by OnMouseEvent to suppport MouseEnter and MouseLeave events
+    internal static List<View?> ViewsUnderMouse { get; } = new ();
 
 
     /// <summary>Event fired when a mouse move or click occurs. Coordinates are screen relative.</summary>
     /// <summary>Event fired when a mouse move or click occurs. Coordinates are screen relative.</summary>
     /// <remarks>
     /// <remarks>
@@ -139,17 +145,32 @@ public static partial class Application // Mouse handling
             return;
             return;
         }
         }
 
 
+<<<<<<< Updated upstream
         var view = View.FindDeepestView (Current, mouseEvent.Position);
         var view = View.FindDeepestView (Current, mouseEvent.Position);
+=======
+        Stack<View?> viewsUnderMouse = View.GetViewsUnderMouse (mouseEvent.Position);
+
+        View? deepestViewUnderMouse = viewsUnderMouse.TryPeek (out View? result) ? result : null;
+
+        if ((mouseEvent.Flags == MouseFlags.Button1Pressed
+             || mouseEvent.Flags == MouseFlags.Button2Pressed
+             || mouseEvent.Flags == MouseFlags.Button3Pressed
+             || mouseEvent.Flags == MouseFlags.Button4Pressed)
+            && Popover is { Visible: true } && !ApplicationNavigation.IsInHierarchy (Popover, deepestViewUnderMouse, includeAdornments: true))
+        {
+            Popover.Visible = false;
+        }
+>>>>>>> Stashed changes
 
 
-        if (view is { })
+        if (deepestViewUnderMouse is { })
         {
         {
 #if DEBUG_IDISPOSABLE
 #if DEBUG_IDISPOSABLE
-            if (view.WasDisposed)
+            if (deepestViewUnderMouse.WasDisposed)
             {
             {
-                throw new ObjectDisposedException (view.GetType ().FullName);
+                throw new ObjectDisposedException (deepestViewUnderMouse.GetType ().FullName);
             }
             }
 #endif
 #endif
-            mouseEvent.View = view;
+            mouseEvent.View = deepestViewUnderMouse;
         }
         }
 
 
         MouseEvent?.Invoke (null, mouseEvent);
         MouseEvent?.Invoke (null, mouseEvent);
@@ -177,7 +198,7 @@ public static partial class Application // Mouse handling
                 Position = frameLoc,
                 Position = frameLoc,
                 Flags = mouseEvent.Flags,
                 Flags = mouseEvent.Flags,
                 ScreenPosition = mouseEvent.Position,
                 ScreenPosition = mouseEvent.Position,
-                View = view ?? MouseGrabView
+                View = deepestViewUnderMouse ?? MouseGrabView
             };
             };
 
 
             if ((MouseGrabView.Viewport with { Location = Point.Empty }).Contains (viewRelativeMouseEvent.Position) is false)
             if ((MouseGrabView.Viewport with { Location = Point.Empty }).Contains (viewRelativeMouseEvent.Position) is false)
@@ -193,7 +214,7 @@ public static partial class Application // Mouse handling
             }
             }
 
 
             // ReSharper disable once ConditionIsAlwaysTrueOrFalse
             // ReSharper disable once ConditionIsAlwaysTrueOrFalse
-            if (MouseGrabView is null && view is Adornment)
+            if (MouseGrabView is null && deepestViewUnderMouse is Adornment)
             {
             {
                 // The view that grabbed the mouse has been disposed
                 // The view that grabbed the mouse has been disposed
                 return;
                 return;
@@ -202,6 +223,7 @@ public static partial class Application // Mouse handling
 
 
         // We can combine this into the switch expression to reduce cognitive complexity even more and likely
         // 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.
         // avoid one or two of these checks in the process, as well.
+<<<<<<< Updated upstream
         WantContinuousButtonPressedView = view switch
         WantContinuousButtonPressedView = view switch
                                           {
                                           {
                                               { WantContinuousButtonPressed: true } => view,
                                               { WantContinuousButtonPressed: true } => view,
@@ -224,17 +246,24 @@ public static partial class Application // Mouse handling
                 ApplicationOverlapped.MoveCurrent ((Toplevel)top);
                 ApplicationOverlapped.MoveCurrent ((Toplevel)top);
             }
             }
         }
         }
+=======
+        WantContinuousButtonPressedView = deepestViewUnderMouse switch
+        {
+            { WantContinuousButtonPressed: true } => deepestViewUnderMouse,
+            _ => null
+        };
+>>>>>>> Stashed changes
 
 
         // May be null before the prior condition or the condition may set it as 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.
         // So, the checking must be outside the prior condition.
-        if (view is null)
+        if (deepestViewUnderMouse is null)
         {
         {
             return;
             return;
         }
         }
 
 
         MouseEvent? me;
         MouseEvent? me;
 
 
-        if (view is Adornment adornment)
+        if (deepestViewUnderMouse is Adornment adornment)
         {
         {
             Point frameLoc = adornment.ScreenToFrame (mouseEvent.Position);
             Point frameLoc = adornment.ScreenToFrame (mouseEvent.Position);
 
 
@@ -243,19 +272,19 @@ public static partial class Application // Mouse handling
                 Position = frameLoc,
                 Position = frameLoc,
                 Flags = mouseEvent.Flags,
                 Flags = mouseEvent.Flags,
                 ScreenPosition = mouseEvent.Position,
                 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 ()
             me = new ()
             {
             {
                 Position = viewportLocation,
                 Position = viewportLocation,
                 Flags = mouseEvent.Flags,
                 Flags = mouseEvent.Flags,
                 ScreenPosition = mouseEvent.Position,
                 ScreenPosition = mouseEvent.Position,
-                View = view
+                View = deepestViewUnderMouse
             };
             };
         }
         }
         else
         else
@@ -263,51 +292,97 @@ public static partial class Application // Mouse handling
             return;
             return;
         }
         }
 
 
-        if (MouseEnteredView is null)
-        {
-            MouseEnteredView = view;
-            view.NewMouseEnterEvent (me);
-        }
-        else if (MouseEnteredView != view)
+        // Mouse Enter/Leave events
+
+        // Tell any views that are no longer under the mouse that the mouse has left and remove them from list
+        List<View?> viewsToLeave = ViewsUnderMouse.Where (v => v is { } && !viewsUnderMouse.Contains (v)).ToList ();
+        foreach (View? view in viewsToLeave)
         {
         {
-            MouseEnteredView.NewMouseLeaveEvent (me);
-            view.NewMouseEnterEvent (me);
-            MouseEnteredView = view;
+            if (view is null)
+            {
+                continue;
+            }
+
+            if (view is Adornment adornmentView)
+            {
+                Point frameLoc = adornmentView.ScreenToFrame (mouseEvent.Position);
+                if (adornmentView.Parent is { } && !adornmentView.Contains (frameLoc))
+                {
+                    ViewsUnderMouse.Remove (view);
+                    view.NewMouseLeaveEvent (me);
+                }
+            }
+            else
+            {
+                Point viewportLocation = view.ScreenToViewport (mouseEvent.Position);
+                if (!view.Contains (viewportLocation))
+                {
+                    ViewsUnderMouse.Remove (view);
+                    view.NewMouseLeaveEvent (me);
+                }
+
+            }
         }
         }
 
 
-        if (!view.WantMousePositionReports && mouseEvent.Flags == MouseFlags.ReportMousePosition)
+        // Tell any views that are now under the mouse (viewsUnderMouse) that the mouse has entered and add them to the list
+        foreach (View? view in viewsUnderMouse)
         {
         {
-            return;
+            if (view is null)
+            {
+                continue;
+            }
+
+            Point viewportLocation = view.ScreenToViewport (mouseEvent.Position);
+
+            if (view is Adornment adornmentView)
+            {
+
+                if (adornmentView.Parent is { } && !adornmentView.Contains (viewportLocation))
+                {
+                    ViewsUnderMouse.Add (view);
+                    view.NewMouseEnterEvent (me);
+                }
+            }
+            else if (view.Contains (viewportLocation))
+            {
+                ViewsUnderMouse.Add (view);
+                view.NewMouseEnterEvent (me);
+            }
         }
         }
 
 
-        WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
+
+        WantContinuousButtonPressedView = deepestViewUnderMouse.WantContinuousButtonPressed ? deepestViewUnderMouse : null;
 
 
         //Debug.WriteLine ($"OnMouseEvent: ({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags}");
         //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
             else
             {
             {
-                view = view.SuperView;
+                deepestViewUnderMouse = deepestViewUnderMouse.SuperView;
             }
             }
 
 
-            if (view is null)
+            if (deepestViewUnderMouse is null)
             {
             {
                 break;
                 break;
             }
             }
 
 
-            Point boundsPoint = view.ScreenToViewport (mouseEvent.Position);
+            Point boundsPoint = deepestViewUnderMouse.ScreenToViewport (mouseEvent.Position);
 
 
             me = new ()
             me = new ()
             {
             {
                 Position = boundsPoint,
                 Position = boundsPoint,
                 Flags = mouseEvent.Flags,
                 Flags = mouseEvent.Flags,
                 ScreenPosition = mouseEvent.Position,
                 ScreenPosition = mouseEvent.Position,
-                View = view
+                View = deepestViewUnderMouse
             };
             };
         }
         }
 
 

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

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

+ 19 - 18
Terminal.Gui/View/Adornment/Adornment.cs

@@ -205,6 +205,7 @@ public class Adornment : View
     #region Mouse Support
     #region Mouse Support
 
 
 
 
+    // TODO: It's stoopid that this override changes the defn of the input coords from base. 
     /// <summary>
     /// <summary>
     /// Indicates whether the specified Parent's SuperView-relative coordinates are within the Adornment's Thickness.
     /// Indicates whether the specified Parent's SuperView-relative coordinates are within the Adornment's Thickness.
     /// </summary>
     /// </summary>
@@ -229,15 +230,15 @@ public class Adornment : View
     /// <inheritdoc/>
     /// <inheritdoc/>
     protected internal override bool? OnMouseEnter (MouseEvent mouseEvent)
     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;
-        }
+        //// 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);
         return base.OnMouseEnter (mouseEvent);
     }
     }
@@ -245,15 +246,15 @@ public class Adornment : View
     /// <inheritdoc/>   
     /// <inheritdoc/>   
     protected internal override bool OnMouseLeave (MouseEvent mouseEvent)
     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;
-        }
+        //// 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);
         return base.OnMouseLeave (mouseEvent);
     }
     }

+ 25 - 2
Terminal.Gui/View/View.Layout.cs

@@ -1,5 +1,6 @@
 #nullable enable
 #nullable enable
 using System.Diagnostics;
 using System.Diagnostics;
+using Microsoft.CodeAnalysis;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -28,6 +29,23 @@ public partial class View // Layout APIs
     // CONCURRENCY: This method is not thread-safe. Undefined behavior and likely program crashes are exposed by unsynchronized access to InternalSubviews.
     // 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)
     internal static View? FindDeepestView (View? start, in Point location)
     {
     {
+<<<<<<< Updated upstream
+=======
+        return GetViewsUnderMouse (location).TryPeek (out View? result) ? result : null;
+    }
+
+    internal static Stack<View?> GetViewsUnderMouse (in Point location)
+    {
+        Stack<View> viewsUnderMouse = new ();
+
+        View? start = Application.Top;
+
+        if (Application.Popover?.Visible == true)
+        {
+            start = Application.Popover;
+        }
+
+>>>>>>> Stashed changes
         Point currentLocation = location;
         Point currentLocation = location;
 
 
         while (start is { Visible: true } && start.Contains (currentLocation))
         while (start is { Visible: true } && start.Contains (currentLocation))
@@ -51,6 +69,8 @@ public partial class View // Layout APIs
 
 
             if (found is { })
             if (found is { })
             {
             {
+                //viewsUnderMouse.Push (found);
+
                 start = found;
                 start = found;
                 viewportOffset = found.Parent?.Frame.Location ?? Point.Empty;
                 viewportOffset = found.Parent?.Frame.Location ?? Point.Empty;
             }
             }
@@ -70,6 +90,7 @@ public partial class View // Layout APIs
                     currentLocation.Y = startOffsetY + start.Viewport.Y;
                     currentLocation.Y = startOffsetY + start.Viewport.Y;
 
 
                     // start is the deepest subview under the mouse; stop searching the subviews
                     // start is the deepest subview under the mouse; stop searching the subviews
+                    viewsUnderMouse.Push (start);
                     break;
                     break;
                 }
                 }
             }
             }
@@ -77,14 +98,16 @@ public partial class View // Layout APIs
             if (subview is null)
             if (subview is null)
             {
             {
                 // No subview was found that's under the mouse, so we're done
                 // No subview was found that's under the mouse, so we're done
-                return start;
+                viewsUnderMouse.Push (start);
+                return viewsUnderMouse;
             }
             }
 
 
             // We found a subview of start that's under the mouse, continue...
             // We found a subview of start that's under the mouse, continue...
             start = subview;
             start = subview;
+            //viewsUnderMouse.Push (subview);
         }
         }
 
 
-        return null;
+        return viewsUnderMouse;
     }
     }
 
 
     // BUGBUG: This method interferes with Dialog/MessageBox default min/max size.
     // BUGBUG: This method interferes with Dialog/MessageBox default min/max size.

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

@@ -80,6 +80,11 @@ public partial class View // Mouse APIs
             return false;
             return false;
         }
         }
 
 
+        if (!WantMousePositionReports && mouseEvent.Flags == MouseFlags.ReportMousePosition)
+        {
+            return false;
+        }
+
         if (OnMouseEvent (mouseEvent))
         if (OnMouseEvent (mouseEvent))
         {
         {
             // Technically mouseEvent.Handled should already be true if implementers of OnMouseEvent
             // Technically mouseEvent.Handled should already be true if implementers of OnMouseEvent
@@ -328,23 +333,24 @@ public partial class View // Mouse APIs
     }
     }
 
 
     /// <summary>
     /// <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.
+    ///     Called by <see cref="Application.OnMouseEvent"/> when the mouse enters <see cref="Viewport"/>. <see cref="MouseLeave"/> will
+    ///     be raised when the mouse is no longer over the <see cref="Viewport"/>. If another View occludes the current one, the
+    ///     that View will also receive a MouseEnter event.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
     ///     <para>
     ///     <para>
-    ///         A view must be both enabled and visible to receive mouse events.
+    ///         A view must be visible to receive Enter/Leave events.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         This method calls <see cref="OnMouseEnter"/> to fire the event.
+    ///         This method calls <see cref="OnMouseEnter"/> to raise the <see cref="MouseEnter"/> event.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         See <see cref="SetHighlight"/> for more information.
     ///         See <see cref="SetHighlight"/> for more information.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
     /// <param name="mouseEvent"></param>
     /// <param name="mouseEvent"></param>
-    /// <returns><see langword="true"/> if the event was handled, <see langword="false"/> otherwise.</returns>
+    /// <returns><see langword="true"/> if the event was handled, <see langword="false"/> otherwise. Handling the event
+    /// prevents Views higher in the visible hierarchy from recieving Enter/Leave events.</returns>
     internal bool? NewMouseEnterEvent (MouseEvent mouseEvent)
     internal bool? NewMouseEnterEvent (MouseEvent mouseEvent)
     {
     {
         if (!Enabled)
         if (!Enabled)
@@ -375,29 +381,23 @@ public partial class View // Mouse APIs
     }
     }
 
 
     /// <summary>
     /// <summary>
-    ///     Called by <see cref="Application.OnMouseEvent"/> when the mouse leaves <see cref="Viewport"/>. The view will
-    ///     then no longer receive mouse events.
+    ///     Called by <see cref="Application.OnMouseEvent"/> when the mouse leaves <see cref="Viewport"/>.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
     ///     <para>
     ///     <para>
-    ///         A view must be both enabled and visible to receive mouse events.
+    ///         A view must be visible to receive Enter/Leave events.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         This method calls <see cref="OnMouseLeave"/> to fire the event.
+    ///         This method calls <see cref="OnMouseLeave"/> to raise the <see cref="MouseLeave"/> event.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         See <see cref="SetHighlight"/> for more information.
     ///         See <see cref="SetHighlight"/> for more information.
     ///     </para>
     ///     </para>
     /// </remarks>
     /// </remarks>
     /// <param name="mouseEvent"></param>
     /// <param name="mouseEvent"></param>
-    /// <returns><see langword="true"/> if the event was handled, <see langword="false"/> otherwise.</returns>
+    /// <returns><see langword="true"/> if the event was handled, <see langword="false"/> otherwise. </returns>
     internal bool? NewMouseLeaveEvent (MouseEvent mouseEvent)
     internal bool? NewMouseLeaveEvent (MouseEvent mouseEvent)
     {
     {
-        if (!Enabled)
-        {
-            return true;
-        }
-
         if (!CanBeVisible (this))
         if (!CanBeVisible (this))
         {
         {
             return false;
             return false;

+ 12 - 3
UICatalog/Scenarios/Mouse.cs

@@ -187,13 +187,18 @@ public class Mouse : Scenario
         Application.Shutdown ();
         Application.Shutdown ();
     }
     }
 
 
-    public class MouseDemo : View
+    public class MouseDemo : Shortcut
     {
     {
         private bool _button1PressedOnEnter;
         private bool _button1PressedOnEnter;
 
 
         public MouseDemo ()
         public MouseDemo ()
         {
         {
             CanFocus = true;
             CanFocus = true;
+            Id = "mouseDemo";
+            Title = "Hi";
+            Key = Key.A.WithAlt;
+            HelpText = "Help!";
+            WantMousePositionReports = true;
 
 
             MouseEvent += (s, e) =>
             MouseEvent += (s, e) =>
                           {
                           {
@@ -214,10 +219,14 @@ public class Mouse : Scenario
 
 
             MouseLeave += (s, e) =>
             MouseLeave += (s, e) =>
                           {
                           {
-                              ColorScheme = Colors.ColorSchemes ["Dialog"];
+                              ColorScheme = Colors.ColorSchemes ["Menu"];
                               _button1PressedOnEnter = false;
                               _button1PressedOnEnter = false;
                           };
                           };
-            MouseEnter += (s, e) => { _button1PressedOnEnter = e.MouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed); };
+            MouseEnter += (s, e) =>
+                          {
+                              ColorScheme = Colors.ColorSchemes ["Error"];
+                              _button1PressedOnEnter = e.MouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed);
+                          };
         }
         }
     }
     }
 }
 }