Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #3029 - Refactors MouseEvent and APIs to simplify and make consistent #3797

Merged
merged 21 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Terminal.Gui/Application/Application.Initialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ internal static void InternalInit (
private static void Driver_SizeChanged (object? sender, SizeChangedEventArgs e) { OnSizeChanging (e); }
private static void Driver_KeyDown (object? sender, Key e) { RaiseKeyDownEvent (e); }
private static void Driver_KeyUp (object? sender, Key e) { RaiseKeyUpEvent (e); }
private static void Driver_MouseEvent (object? sender, MouseEvent e) { OnMouseEvent (e); }
private static void Driver_MouseEvent (object? sender, MouseEventArgs e) { RaiseMouseEvent (e); }

/// <summary>Gets of list of <see cref="ConsoleDriver"/> types that are available.</summary>
/// <returns></returns>
Expand Down
63 changes: 37 additions & 26 deletions Terminal.Gui/Application/Application.Mouse.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#nullable enable
using System.ComponentModel;
using System.Diagnostics;

namespace Terminal.Gui;

Expand Down Expand Up @@ -45,12 +44,12 @@
/// <param name="view">View that will receive all mouse events until <see cref="UngrabMouse"/> is invoked.</param>
public static void GrabMouse (View? view)
{
if (view is null || OnGrabbingMouse (view))
if (view is null || RaiseGrabbingMouseEvent (view))
{
return;
}

OnGrabbedMouse (view);
RaiseGrabbedMouseEvent (view);
MouseGrabView = view;
}

Expand All @@ -66,16 +65,16 @@
ObjectDisposedException.ThrowIf (MouseGrabView.WasDisposed, MouseGrabView);
#endif

if (!OnUnGrabbingMouse (MouseGrabView))
if (!RaiseUnGrabbingMouseEvent (MouseGrabView))
{
View view = MouseGrabView;
MouseGrabView = null;
OnUnGrabbedMouse (view);
RaiseUnGrabbedMouseEvent (view);
}
}

/// <exception cref="Exception">A delegate callback throws an exception.</exception>
private static bool OnGrabbingMouse (View? view)
private static bool RaiseGrabbingMouseEvent (View? view)
{
if (view is null)
{
Expand All @@ -89,7 +88,7 @@
}

/// <exception cref="Exception">A delegate callback throws an exception.</exception>
private static bool OnUnGrabbingMouse (View? view)
private static bool RaiseUnGrabbingMouseEvent (View? view)
{
if (view is null)
{
Expand All @@ -103,7 +102,7 @@
}

/// <exception cref="Exception">A delegate callback throws an exception.</exception>
private static void OnGrabbedMouse (View? view)
private static void RaiseGrabbedMouseEvent (View? view)
{
if (view is null)
{
Expand All @@ -114,7 +113,7 @@
}

/// <exception cref="Exception">A delegate callback throws an exception.</exception>
private static void OnUnGrabbedMouse (View? view)
private static void RaiseUnGrabbedMouseEvent (View? view)
{
if (view is null)
{
Expand All @@ -124,20 +123,14 @@
UnGrabbedMouse?.Invoke (view, new (view));
}

/// <summary>Event fired when a mouse move or click occurs. Coordinates are screen relative.</summary>
/// <remarks>
/// <para>
/// Use this event to receive mouse events in screen coordinates. Use <see cref="MouseEvent"/> to
/// receive mouse events relative to a <see cref="View.Viewport"/>.
/// </para>
/// <para>The <see cref="MouseEvent.View"/> will contain the <see cref="View"/> that contains the mouse coordinates.</para>
/// </remarks>
public static event EventHandler<MouseEvent>? MouseEvent;

/// <summary>Called when a mouse event is raised by the driver.</summary>
/// <summary>
/// INTERNAL API: Called when a mouse event is raised by the driver. Determines the view under the mouse and
/// calls the appropriate View mouse event handlers.
/// </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)
internal static void RaiseMouseEvent (MouseEventArgs mouseEvent)
{
_lastMousePosition = mouseEvent.ScreenPosition;

Expand Down Expand Up @@ -194,7 +187,7 @@
}

// Create a view-relative mouse event to send to the view that is under the mouse.
MouseEvent? viewMouseEvent;
MouseEventArgs? viewMouseEvent;

if (deepestViewUnderMouse is Adornment adornment)
{
Expand All @@ -208,7 +201,7 @@
View = deepestViewUnderMouse
};
}
else if (deepestViewUnderMouse.ViewportToScreen (Rectangle.Empty with { Size = deepestViewUnderMouse.Viewport.Size }).Contains (mouseEvent.Position))
else if (deepestViewUnderMouse.ViewportToScreen (Rectangle.Empty with { Size = deepestViewUnderMouse.Viewport.Size }).Contains (mouseEvent.ScreenPosition))
{
Point viewportLocation = deepestViewUnderMouse.ScreenToViewport (mouseEvent.ScreenPosition);

Expand All @@ -224,7 +217,7 @@
{
// The mouse was outside any View's Viewport.

// Debug.Fail ("This should never happen. If it does please file an Issue!!");
// Debug.Fail ("This should never happen. If it does please file an Issue!!");

return;
}
Expand Down Expand Up @@ -261,7 +254,26 @@
}
}

internal static bool HandleMouseGrab (View? deepestViewUnderMouse, MouseEvent mouseEvent)
/// <summary>
/// Raised when a mouse event occurs. Can be cancelled by setting <see cref="MouseEventArgs.Handled"/> to <see langword="true"/>.

Check warning on line 258 in Terminal.Gui/Application/Application.Mouse.cs

View workflow job for this annotation

GitHub Actions / build_release

XML comment has cref attribute 'Handled' that could not be resolved

Check warning on line 258 in Terminal.Gui/Application/Application.Mouse.cs

View workflow job for this annotation

GitHub Actions / build_and_test_debug (ubuntu-latest)

XML comment has cref attribute 'Handled' that could not be resolved

Check warning on line 258 in Terminal.Gui/Application/Application.Mouse.cs

View workflow job for this annotation

GitHub Actions / build_and_test_debug (macos-latest)

XML comment has cref attribute 'Handled' that could not be resolved
/// </summary>
/// <remarks>
/// <para>
/// <see cref="MouseEventArgs.ScreenPosition"/> coordinates are screen-relative.
/// </para>
/// <para>
/// <see cref="MouseEventArgs.View"/> will be the deepest view under the under the mouse.
/// </para>
/// <para>
/// <see cref="MouseEventArgs.Position"/> coordinates are view-relative. Only valid if <see cref="MouseEventArgs.View"/> is set.
/// </para>
/// <para>
/// Use this evento to handle mouse events at the application level, before View-specific handling.
/// </para>
/// </remarks>
public static event EventHandler<MouseEventArgs>? MouseEvent;

internal static bool HandleMouseGrab (View? deepestViewUnderMouse, MouseEventArgs mouseEvent)
{
if (MouseGrabView is { })
{
Expand All @@ -276,7 +288,7 @@
// The coordinates are relative to the Bounds of the view that grabbed the mouse.
Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.ScreenPosition);

var viewRelativeMouseEvent = new MouseEvent
var viewRelativeMouseEvent = new MouseEventArgs
{
Position = frameLoc,
Flags = mouseEvent.Flags,
Expand All @@ -303,7 +315,6 @@

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>
Expand Down
4 changes: 2 additions & 2 deletions Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -588,11 +588,11 @@ public virtual Attribute MakeColor (in Color foreground, in Color background)
public void OnKeyUp (Key a) { KeyUp?.Invoke (this, a); }

/// <summary>Event fired when a mouse event occurs.</summary>
public event EventHandler<MouseEvent>? MouseEvent;
public event EventHandler<MouseEventArgs>? MouseEvent;

/// <summary>Called when a mouse event occurs. Fires the <see cref="MouseEvent"/> event.</summary>
/// <param name="a"></param>
public void OnMouseEvent (MouseEvent a)
public void OnMouseEvent (MouseEventArgs a)
{
// Ensure ScreenPosition is set
a.ScreenPosition = a.Position;
Expand Down
2 changes: 1 addition & 1 deletion Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,7 @@ bool IsButtonClickedOrDoubleClicked (MouseFlags flag)

_lastMouseFlags = mouseFlag;

var me = new MouseEvent { Flags = mouseFlag, Position = pos };
var me = new MouseEventArgs { Flags = mouseFlag, Position = pos };
//Debug.WriteLine ($"CursesDriver: ({me.Position}) - {me.Flags}");

OnMouseEvent (me);
Expand Down
6 changes: 3 additions & 3 deletions Terminal.Gui/ConsoleDrivers/NetDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1154,7 +1154,7 @@ private void ProcessInput (InputResult inputEvent)

break;
case EventType.Mouse:
MouseEvent me = ToDriverMouse (inputEvent.MouseEvent);
MouseEventArgs me = ToDriverMouse (inputEvent.MouseEvent);
//Debug.WriteLine ($"NetDriver: ({me.X},{me.Y}) - {me.Flags}");
OnMouseEvent (me);

Expand Down Expand Up @@ -1393,7 +1393,7 @@ public void StopReportingMouseMoves ()
}
}

private MouseEvent ToDriverMouse (NetEvents.MouseEvent me)
private MouseEventArgs ToDriverMouse (NetEvents.MouseEvent me)
{
//System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}");

Expand Down Expand Up @@ -1539,7 +1539,7 @@ private MouseEvent ToDriverMouse (NetEvents.MouseEvent me)
mouseFlag |= MouseFlags.ButtonAlt;
}

return new MouseEvent { Position = me.Position, Flags = mouseFlag };
return new MouseEventArgs { Position = me.Position, Flags = mouseFlag };
}

#endregion Mouse Handling
Expand Down
10 changes: 5 additions & 5 deletions Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1483,7 +1483,7 @@ internal void ProcessInput (WindowsConsole.InputRecord inputEvent)
break;

case WindowsConsole.EventType.Mouse:
MouseEvent me = ToDriverMouse (inputEvent.MouseEvent);
MouseEventArgs me = ToDriverMouse (inputEvent.MouseEvent);

if (me is null || me.Flags == MouseFlags.None)
{
Expand Down Expand Up @@ -1827,9 +1827,9 @@ private async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag)
}
await Task.Delay (delay);

var me = new MouseEvent
var me = new MouseEventArgs
{
Position = _pointMove,
ScreenPosition = _pointMove,
Flags = mouseFlag
};

Expand Down Expand Up @@ -1883,7 +1883,7 @@ private static MouseFlags SetControlKeyStates (WindowsConsole.MouseEventRecord m
}

[CanBeNull]
private MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
private MouseEventArgs ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
{
var mouseFlag = MouseFlags.AllEvents;

Expand Down Expand Up @@ -2127,7 +2127,7 @@ private MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
//System.Diagnostics.Debug.WriteLine (
// $"point.X:{(point is { } ? ((Point)point).X : -1)};point.Y:{(point is { } ? ((Point)point).Y : -1)}");

return new MouseEvent
return new MouseEventArgs
{
Position = new (mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y),
Flags = mouseFlag
Expand Down
32 changes: 32 additions & 0 deletions Terminal.Gui/Input/MouseEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#nullable enable
using System.ComponentModel;

namespace Terminal.Gui;

/// <summary>
/// Specifies the event arguments for <see cref="Terminal.Gui.MouseEventArgs"/>. This is a higher-level construct than
/// the wrapped <see cref="Terminal.Gui.MouseEventArgs"/> class and is used for the events defined on <see cref="View"/> and subclasses
/// of View (e.g. <see cref="View.MouseEnter"/> and <see cref="View.MouseClick"/>).
/// </summary>
public class MouseEventArgs : HandledEventArgs
{
/// <summary>
/// Flags indicating the state of the mouse buttons and the type of event that occurred.
/// </summary>
public MouseFlags Flags { get; set; }

/// <summary>
/// The screen-relative mouse position.
/// </summary>
public Point ScreenPosition { get; set; }

/// <summary>The deepest View who's <see cref="View.Frame"/> contains <see cref="ScreenPosition"/>.</summary>
public View? View { get; set; }

/// <summary>The position of the mouse in <see cref="View"/>'s Viewport-relative coordinates. Only valid if <see cref="View"/> is set.</summary>
public Point Position { get; set; }

/// <summary>Returns a <see cref="T:System.String"/> that represents the current <see cref="Terminal.Gui.MouseEventArgs"/>.</summary>
/// <returns>A <see cref="T:System.String"/> that represents the current <see cref="Terminal.Gui.MouseEventArgs"/>.</returns>
public override string ToString () { return $"({ScreenPosition}):{Flags}:{View?.Id}:{Position}"; }
}
32 changes: 0 additions & 32 deletions Terminal.Gui/Input/MouseEventEventArgs.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
namespace Terminal.Gui;

/// <summary>Mouse flags reported in <see cref="MouseEvent"/>.</summary>
/// <summary>Mouse flags reported in <see cref="Terminal.Gui.MouseEventArgs"/>.</summary>
/// <remarks>They just happen to map to the ncurses ones.</remarks>
[Flags]
public enum MouseFlags
{
/// <summary>
/// No mouse event. This is the default value for <see cref="MouseEvent.Flags"/> when no mouse event is being reported.
/// No mouse event. This is the default value for <see cref="Terminal.Gui.MouseEventArgs.Flags"/> when no mouse event is being reported.
/// </summary>
None = 0,

Expand Down Expand Up @@ -97,50 +97,3 @@ public enum MouseFlags
/// <summary>Mask that captures all the events.</summary>
AllEvents = 0x7ffffff
}

// TODO: Merge MouseEvent and MouseEventEventArgs into a single class.

/// <summary>
/// Conveys the details of mouse events, such as coordinates and button state, from
/// ConsoleDrivers up to <see cref="Application"/> and Views.
/// </summary>
/// <remarks>
/// The <see cref="Application"/> class includes the <see cref="Application.MouseEvent"/> event which takes a
/// MouseEvent argument.
/// </remarks>
public class MouseEvent
{
/// <summary>Flags indicating the kind of mouse event that is being posted.</summary>
public MouseFlags Flags { get; set; }

/// <summary>The View at the location for the mouse event.</summary>
public View View { get; set; }

/// <summary>The position of the mouse in <see cref="Gui.View.Viewport"/>-relative coordinates.</summary>
public Point Position { get; set; }

/// <summary>
/// The screen-relative mouse position.
/// </summary>
/// <remarks>
/// <para>
/// <see cref="Position"/> is <see cref="Gui.View.Viewport"/>-relative. When the mouse is grabbed by a view,
/// <see cref="ScreenPosition"/> provides the mouse position screen-relative coordinates, enabling the grabbed view to know how much the
/// mouse has moved.
/// </para>
/// <para>
/// Calculated and processed in <see cref="Application.OnMouseEvent(MouseEvent)"/>.
/// </para>
/// </remarks>
public Point ScreenPosition { get; set; }

/// <summary>
/// Indicates if the current mouse event has been processed. Set this value to <see langword="true"/> to indicate the mouse
/// event was handled.
/// </summary>
public bool Handled { get; set; }

/// <summary>Returns a <see cref="T:System.String"/> that represents the current <see cref="MouseEvent"/>.</summary>
/// <returns>A <see cref="T:System.String"/> that represents the current <see cref="MouseEvent"/>.</returns>
public override string ToString () { return $"({Position}):{Flags}"; }
}
Loading
Loading