mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Prototype
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
#nullable enable
|
||||
<<<<<<< Updated upstream
|
||||
=======
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
|
||||
|
||||
>>>>>>> Stashed changes
|
||||
namespace Terminal.Gui;
|
||||
|
||||
public static partial class Application // Mouse handling
|
||||
@@ -116,8 +122,8 @@ 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; }
|
||||
// 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>
|
||||
/// <remarks>
|
||||
@@ -139,17 +145,32 @@ public static partial class Application // Mouse handling
|
||||
return;
|
||||
}
|
||||
|
||||
<<<<<<< Updated upstream
|
||||
var view = View.FindDeepestView (Current, mouseEvent.Position);
|
||||
=======
|
||||
Stack<View?> viewsUnderMouse = View.GetViewsUnderMouse (mouseEvent.Position);
|
||||
|
||||
if (view is { })
|
||||
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 (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);
|
||||
@@ -177,7 +198,7 @@ public static partial class Application // Mouse handling
|
||||
Position = frameLoc,
|
||||
Flags = mouseEvent.Flags,
|
||||
ScreenPosition = mouseEvent.Position,
|
||||
View = view ?? MouseGrabView
|
||||
View = deepestViewUnderMouse ?? MouseGrabView
|
||||
};
|
||||
|
||||
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
|
||||
if (MouseGrabView is null && view is Adornment)
|
||||
if (MouseGrabView is null && deepestViewUnderMouse is Adornment)
|
||||
{
|
||||
// The view that grabbed the mouse has been disposed
|
||||
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
|
||||
// avoid one or two of these checks in the process, as well.
|
||||
<<<<<<< Updated upstream
|
||||
WantContinuousButtonPressedView = view switch
|
||||
{
|
||||
{ WantContinuousButtonPressed: true } => view,
|
||||
@@ -224,17 +246,24 @@ public static partial class Application // Mouse handling
|
||||
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.
|
||||
// So, the checking must be outside the prior condition.
|
||||
if (view is null)
|
||||
if (deepestViewUnderMouse is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MouseEvent? me;
|
||||
|
||||
if (view is Adornment adornment)
|
||||
if (deepestViewUnderMouse is Adornment adornment)
|
||||
{
|
||||
Point frameLoc = adornment.ScreenToFrame (mouseEvent.Position);
|
||||
|
||||
@@ -243,19 +272,19 @@ 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
|
||||
@@ -263,51 +292,97 @@ public static partial class Application // Mouse handling
|
||||
return;
|
||||
}
|
||||
|
||||
if (MouseEnteredView is null)
|
||||
{
|
||||
MouseEnteredView = view;
|
||||
view.NewMouseEnterEvent (me);
|
||||
}
|
||||
else if (MouseEnteredView != view)
|
||||
{
|
||||
MouseEnteredView.NewMouseLeaveEvent (me);
|
||||
view.NewMouseEnterEvent (me);
|
||||
MouseEnteredView = view;
|
||||
}
|
||||
// Mouse Enter/Leave events
|
||||
|
||||
if (!view.WantMousePositionReports && mouseEvent.Flags == MouseFlags.ReportMousePosition)
|
||||
// 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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (view is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
|
||||
|
||||
//Debug.WriteLine ($"OnMouseEvent: ({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags}");
|
||||
|
||||
while (view.NewMouseEvent (me) is not true && MouseGrabView is not { })
|
||||
{
|
||||
if (view is Adornment adornmentView)
|
||||
{
|
||||
view = adornmentView.Parent!.SuperView;
|
||||
Point frameLoc = adornmentView.ScreenToFrame (mouseEvent.Position);
|
||||
if (adornmentView.Parent is { } && !adornmentView.Contains (frameLoc))
|
||||
{
|
||||
ViewsUnderMouse.Remove (view);
|
||||
view.NewMouseLeaveEvent (me);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
view = view.SuperView;
|
||||
Point viewportLocation = view.ScreenToViewport (mouseEvent.Position);
|
||||
if (!view.Contains (viewportLocation))
|
||||
{
|
||||
ViewsUnderMouse.Remove (view);
|
||||
view.NewMouseLeaveEvent (me);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (view is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (view is null)
|
||||
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 = deepestViewUnderMouse.WantContinuousButtonPressed ? deepestViewUnderMouse : null;
|
||||
|
||||
//Debug.WriteLine ($"OnMouseEvent: ({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags}");
|
||||
if (deepestViewUnderMouse.Id == "mouseDemo")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
while (deepestViewUnderMouse.NewMouseEvent (me) is not true && MouseGrabView is not { })
|
||||
{
|
||||
if (deepestViewUnderMouse is Adornment adornmentView)
|
||||
{
|
||||
deepestViewUnderMouse = adornmentView.Parent!.SuperView;
|
||||
}
|
||||
else
|
||||
{
|
||||
deepestViewUnderMouse = deepestViewUnderMouse.SuperView;
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ public static partial class Application
|
||||
IsInitialized = false;
|
||||
|
||||
// Mouse
|
||||
MouseEnteredView = null;
|
||||
ViewsUnderMouse.Clear ();
|
||||
WantContinuousButtonPressedView = null;
|
||||
MouseEvent = null;
|
||||
GrabbedMouse = null;
|
||||
|
||||
@@ -205,6 +205,7 @@ public class Adornment : View
|
||||
#region Mouse Support
|
||||
|
||||
|
||||
// TODO: It's stoopid that this override changes the defn of the input coords from base.
|
||||
/// <summary>
|
||||
/// Indicates whether the specified Parent's SuperView-relative coordinates are within the Adornment's Thickness.
|
||||
/// </summary>
|
||||
@@ -229,15 +230,15 @@ public class Adornment : View
|
||||
/// <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;
|
||||
}
|
||||
//// 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);
|
||||
}
|
||||
@@ -245,15 +246,15 @@ public class Adornment : View
|
||||
/// <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;
|
||||
}
|
||||
//// 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);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
using System.Diagnostics;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
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.
|
||||
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;
|
||||
|
||||
while (start is { Visible: true } && start.Contains (currentLocation))
|
||||
@@ -51,6 +69,8 @@ public partial class View // Layout APIs
|
||||
|
||||
if (found is { })
|
||||
{
|
||||
//viewsUnderMouse.Push (found);
|
||||
|
||||
start = found;
|
||||
viewportOffset = found.Parent?.Frame.Location ?? Point.Empty;
|
||||
}
|
||||
@@ -70,6 +90,7 @@ public partial class View // Layout APIs
|
||||
currentLocation.Y = startOffsetY + start.Viewport.Y;
|
||||
|
||||
// start is the deepest subview under the mouse; stop searching the subviews
|
||||
viewsUnderMouse.Push (start);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -77,14 +98,16 @@ public partial class View // Layout APIs
|
||||
if (subview is null)
|
||||
{
|
||||
// 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...
|
||||
start = subview;
|
||||
//viewsUnderMouse.Push (subview);
|
||||
}
|
||||
|
||||
return null;
|
||||
return viewsUnderMouse;
|
||||
}
|
||||
|
||||
// BUGBUG: This method interferes with Dialog/MessageBox default min/max size.
|
||||
|
||||
@@ -80,6 +80,11 @@ public partial class View // Mouse APIs
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WantMousePositionReports && mouseEvent.Flags == MouseFlags.ReportMousePosition)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OnMouseEvent (mouseEvent))
|
||||
{
|
||||
// Technically mouseEvent.Handled should already be true if implementers of OnMouseEvent
|
||||
@@ -328,23 +333,24 @@ 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.
|
||||
/// 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>
|
||||
/// <remarks>
|
||||
/// <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>
|
||||
/// 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>
|
||||
/// 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>
|
||||
/// <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)
|
||||
{
|
||||
if (!Enabled)
|
||||
@@ -375,29 +381,23 @@ public partial class View // Mouse APIs
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <remarks>
|
||||
/// <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>
|
||||
/// 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>
|
||||
/// 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>
|
||||
/// <returns><see langword="true"/> if the event was handled, <see langword="false"/> otherwise. </returns>
|
||||
internal bool? NewMouseLeaveEvent (MouseEvent mouseEvent)
|
||||
{
|
||||
if (!Enabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!CanBeVisible (this))
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -187,13 +187,18 @@ public class Mouse : Scenario
|
||||
Application.Shutdown ();
|
||||
}
|
||||
|
||||
public class MouseDemo : View
|
||||
public class MouseDemo : Shortcut
|
||||
{
|
||||
private bool _button1PressedOnEnter;
|
||||
|
||||
public MouseDemo ()
|
||||
{
|
||||
CanFocus = true;
|
||||
Id = "mouseDemo";
|
||||
Title = "Hi";
|
||||
Key = Key.A.WithAlt;
|
||||
HelpText = "Help!";
|
||||
WantMousePositionReports = true;
|
||||
|
||||
MouseEvent += (s, e) =>
|
||||
{
|
||||
@@ -214,10 +219,14 @@ public class Mouse : Scenario
|
||||
|
||||
MouseLeave += (s, e) =>
|
||||
{
|
||||
ColorScheme = Colors.ColorSchemes ["Dialog"];
|
||||
ColorScheme = Colors.ColorSchemes ["Menu"];
|
||||
_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);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user