Skip to content

Commit

Permalink
Refixed Adornment view drag. Still some issues.
Browse files Browse the repository at this point in the history
  • Loading branch information
tig committed Mar 4, 2024
1 parent a928317 commit 3a834ac
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 71 deletions.
30 changes: 15 additions & 15 deletions Terminal.Gui/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1404,19 +1404,19 @@ internal static void OnMouseEvent (MouseEventEventArgs a)
{
// If the mouse is grabbed, send the event to the view that grabbed it.
// The coordinates are relative to the Bounds of the view that grabbed the mouse.
Point newxy = MouseGrabView.ScreenToFrame (a.MouseEvent.X, a.MouseEvent.Y);
Point frameLoc = MouseGrabView.ScreenToFrame (a.MouseEvent.X, a.MouseEvent.Y);

var nme = new MouseEvent
var viewRelativeMouseEvent = new MouseEvent
{
X = newxy.X,
Y = newxy.Y,
X = frameLoc.X,
Y = frameLoc.Y,
Flags = a.MouseEvent.Flags,
OfX = a.MouseEvent.X - newxy.X,
OfY = a.MouseEvent.Y - newxy.Y,
ScreenX = a.MouseEvent.X,
ScreenY = a.MouseEvent.Y,
View = view
};

if (MouseGrabView.Bounds.Contains (nme.X, nme.Y) is false)
if (MouseGrabView.Bounds.Contains (viewRelativeMouseEvent.X, viewRelativeMouseEvent.Y) is false)
{
// The mouse has moved outside the bounds of the view that
// grabbed the mouse, so we tell the view that last got
Expand All @@ -1426,7 +1426,7 @@ internal static void OnMouseEvent (MouseEventEventArgs a)
}

//System.Diagnostics.Debug.WriteLine ($"{nme.Flags};{nme.X};{nme.Y};{mouseGrabView}");
if (MouseGrabView?.OnMouseEvent (nme) == true)
if (MouseGrabView?.OnMouseEvent (viewRelativeMouseEvent) == true)
{
return;
}
Expand Down Expand Up @@ -1461,15 +1461,15 @@ internal static void OnMouseEvent (MouseEventEventArgs a)

if (view is Adornment adornment)
{
Rectangle screen = adornment.FrameToScreen ();
var frameLoc = adornment.ScreenToFrame (a.MouseEvent.X, a.MouseEvent.Y);

me = new MouseEvent
{
X = a.MouseEvent.X - screen.X,
Y = a.MouseEvent.Y - screen.Y,
X = frameLoc.X,
Y = frameLoc.Y,
Flags = a.MouseEvent.Flags,
OfX = a.MouseEvent.X - screen.X,
OfY = a.MouseEvent.Y - screen.Y,
ScreenX = a.MouseEvent.X,
ScreenY = a.MouseEvent.Y,
View = view
};
}
Expand All @@ -1482,8 +1482,8 @@ internal static void OnMouseEvent (MouseEventEventArgs a)
X = boundsPoint.X,
Y = boundsPoint.Y,
Flags = a.MouseEvent.Flags,
OfX = boundsPoint.X,
OfY = boundsPoint.Y,
ScreenX = a.MouseEvent.X,
ScreenY = a.MouseEvent.X,
View = view
};
}
Expand Down
61 changes: 43 additions & 18 deletions Terminal.Gui/Input/Mouse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,40 +96,65 @@ public enum MouseFlags
// TODO: Merge MouseEvent and MouseEventEventArgs into a single class.

/// <summary>
/// Low-level construct that conveys the details of mouse events, such as coordinates and button state, from
/// 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"/> Action which takes a
/// 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>
/// Indicates if the current mouse event has already been processed and the driver should stop notifying any other
/// event subscriber. Its important to set this value to true specially when updating any View's layout from inside the
/// subscriber method.
/// </summary>
public bool Handled { get; set; }

/// <summary>The offset X (column) location for the mouse event.</summary>
public int OfX { get; set; }

/// <summary>The offset Y (column) location for the mouse event.</summary>
public int OfY { get; set; }

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

/// <summary>The X (column) location for the mouse event.</summary>
/// <summary>The X (column) location of the mouse in <see cref="View.Bounds"/>-relative coordinates.</summary>
public int X { get; set; }

/// <summary>The Y (column) location for the mouse event.</summary>
/// <summary>The Y (column) location of the mouse in <see cref="View.Bounds"/>-relative coordinates.</summary>
public int Y { 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>
/// The screen-relative mouse coordinate.
/// </summary>
/// <remarks>
/// <para>
/// Calculated and processed in <see cref="Application.OnMouseEvent(MouseEventEventArgs)"/>.
/// </para>
/// <para>
/// The View that has called <see cref="Application.GrabMouse"/> will receive all mouse events
/// with <see cref="View.Bounds"/>-relative coordinates. <see cref="ScreenX"/> and <see cref="ScreenY"/> provide
/// the screen-relative offset of these coordinates, enabling the grabbed view to know how much the
/// mouse has moved.
/// </para>
/// </remarks>
public int ScreenX { get; set; }

/// <summary>
/// The screen-relative mouse coordinate.
/// </summary>
/// <remarks>
/// <para>
/// Calculated and processed in <see cref="Application.OnMouseEvent(MouseEventEventArgs)"/>.
/// </para>
/// <para>
/// The View that has called <see cref="Application.GrabMouse"/> will receive all mouse events
/// with <see cref="View.Bounds"/>-relative coordinates. <see cref="ScreenX"/> and <see cref="ScreenY"/> provide
/// the screen-relative offset of these coordinates, enabling the grabbed view to know how much the
/// mouse has moved.
/// </para>
/// </remarks>
public int ScreenY { 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 $"({X},{Y}):{Flags}"; }
Expand Down
28 changes: 14 additions & 14 deletions Terminal.Gui/View/Adornment/Adornment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,30 +212,27 @@ protected internal override bool OnMouseEvent (MouseEvent mouseEvent)

int nx, ny;

if (!_dragPosition.HasValue
&& (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button2Pressed)
|| mouseEvent.Flags.HasFlag (MouseFlags.Button3Pressed)))
if (!_dragPosition.HasValue && (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed)))
{
Parent.SetFocus ();
Application.BringOverlappedTopToFront ();

// Only start grabbing if the user clicks on the title bar.
if (mouseEvent.Y == 0 && mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
// Only start grabbing if the user clicks in the Thickness area
if (Thickness.Contains (Frame, mouseEvent.X, mouseEvent.Y) && mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
{
_startGrabPoint = new (mouseEvent.X, mouseEvent.Y);
nx = mouseEvent.X - mouseEvent.OfX;
ny = mouseEvent.Y - mouseEvent.OfY;
nx = mouseEvent.ScreenX;
ny = mouseEvent.ScreenY;
_dragPosition = new Point (nx, ny);
Application.GrabMouse (this);
}

return true;
}

if (mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) || mouseEvent.Flags == MouseFlags.Button3Pressed)
if (mouseEvent.Flags is (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))
{
if (_dragPosition.HasValue)
if (Application.MouseGrabView == this && _dragPosition.HasValue)
{
if (Parent.SuperView is null)
{
Expand All @@ -247,20 +244,23 @@ protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
Parent.SuperView.SetNeedsDisplay ();
}

_dragPosition = new Point (mouseEvent.X, mouseEvent.Y);

var parentLoc = Parent.SuperView?.ScreenToBounds (mouseEvent.ScreenX, mouseEvent.ScreenY) ?? new (mouseEvent.ScreenX, mouseEvent.ScreenY);
GetLocationThatFits (
Parent,
mouseEvent.X + mouseEvent.OfX - _startGrabPoint.X,
mouseEvent.Y + mouseEvent.OfY - _startGrabPoint.Y,
parentLoc.X - _startGrabPoint.X,
parentLoc.Y - _startGrabPoint.Y,
out nx,
out ny,
out _,
out _
);

_dragPosition = new Point (nx, ny);

Parent.X = nx;
Parent.Y = ny;
Parent.SetNeedsDisplay ();
//Parent.SetNeedsDisplay ();

return true;
}
Expand Down
39 changes: 23 additions & 16 deletions Terminal.Gui/View/Layout/ViewLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,13 @@ public Point ScreenToBounds (int x, int y)
public Point ScreenToFrame (int x, int y)
{
Point superViewBoundsOffset = SuperView?.GetBoundsOffset () ?? Point.Empty;
// BUGBUG: Hack. Move into Adornment somehow.
if (this is Adornment adornment)
{
superViewBoundsOffset = adornment.Parent.SuperView?.GetBoundsOffset () ?? Point.Empty;
return adornment.Parent.ScreenToFrame (x, y);
}

var ret = new Point (x - Frame.X - superViewBoundsOffset.X, y - Frame.Y - superViewBoundsOffset.Y);

if (SuperView is { })
Expand Down Expand Up @@ -901,30 +908,30 @@ internal static View GetLocationThatFits (
out StatusBar statusBar
)
{
int maxWidth;
int maxDimension;
View superView;

if (top?.SuperView is null || top == Application.Top || top?.SuperView == Application.Top)
{
maxWidth = Driver.Cols;
maxDimension = Driver.Cols;
superView = Application.Top;
}
else
{
// Use the SuperView's Bounds, not Frame
maxWidth = top.SuperView.Bounds.Width;
maxDimension = top.SuperView.Bounds.Width;
superView = top.SuperView;
}

if (superView.Margin is { } && superView == top.SuperView)
{
maxWidth -= superView.GetAdornmentsThickness ().Left + superView.GetAdornmentsThickness ().Right;
maxDimension -= superView.GetAdornmentsThickness ().Left + superView.GetAdornmentsThickness ().Right;
}

if (top.Frame.Width <= maxWidth)
if (top.Frame.Width <= maxDimension)
{
nx = Math.Max (targetX, 0);
nx = nx + top.Frame.Width > maxWidth ? Math.Max (maxWidth - top.Frame.Width, 0) : nx;
nx = nx + top.Frame.Width > maxDimension ? Math.Max (maxDimension - top.Frame.Width, 0) : nx;

if (nx > top.Frame.X + top.Frame.Width)
{
Expand Down Expand Up @@ -959,14 +966,14 @@ out StatusBar statusBar

if (top?.SuperView is null || top == Application.Top || top?.SuperView == Application.Top)
{
maxWidth = menuVisible ? 1 : 0;
maxDimension = menuVisible ? 1 : 0;
}
else
{
maxWidth = 0;
maxDimension = 0;
}

ny = Math.Max (targetY, maxWidth);
ny = Math.Max (targetY, maxDimension);

if (top?.SuperView is null || top == Application.Top || top?.SuperView == Application.Top)
{
Expand All @@ -988,24 +995,24 @@ out StatusBar statusBar

if (top?.SuperView is null || top == Application.Top || top?.SuperView == Application.Top)
{
maxWidth = statusVisible ? Driver.Rows - 1 : Driver.Rows;
maxDimension = statusVisible ? Driver.Rows - 1 : Driver.Rows;
}
else
{
maxWidth = statusVisible ? top.SuperView.Frame.Height - 1 : top.SuperView.Frame.Height;
maxDimension = statusVisible ? top.SuperView.Frame.Height - 1 : top.SuperView.Frame.Height;
}

if (superView.Margin is { } && superView == top.SuperView)
{
maxWidth -= superView.GetAdornmentsThickness ().Top + superView.GetAdornmentsThickness ().Bottom;
maxDimension -= superView.GetAdornmentsThickness ().Top + superView.GetAdornmentsThickness ().Bottom;
}

ny = Math.Min (ny, maxWidth);
ny = Math.Min (ny, maxDimension);

if (top.Frame.Height <= maxWidth)
if (top.Frame.Height <= maxDimension)
{
ny = ny + top.Frame.Height > maxWidth
? Math.Max (maxWidth - top.Frame.Height, menuVisible ? 1 : 0)
ny = ny + top.Frame.Height > maxDimension
? Math.Max (maxDimension - top.Frame.Height, menuVisible ? 1 : 0)
: ny;

if (ny > top.Frame.Y + top.Frame.Height)
Expand Down
9 changes: 4 additions & 5 deletions Terminal.Gui/Views/Menu/MenuBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1795,16 +1795,15 @@ internal bool HandleGrabView (MouseEvent me, View current)
X = newxy.X,
Y = newxy.Y,
Flags = me.Flags,
OfX = me.X - newxy.X,
OfY = me.Y - newxy.Y,
ScreenX = me.X - newxy.X,
ScreenY = me.Y - newxy.Y,
View = v
};
}
else
{
nme = new MouseEvent { X = me.X + current.Frame.X, Y = 0, Flags = me.Flags, View = v };
}

v.OnMouseEvent (nme);

return false;
Expand Down Expand Up @@ -1842,11 +1841,11 @@ internal bool HandleGrabView (MouseEvent me, View current)
MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
)))
{
Application.GrabMouse (current);
Application.GrabMouse (current);
}
else if (IsMenuOpen && (me.View is MenuBar || me.View is Menu))
{
Application.GrabMouse (me.View);
Application.GrabMouse (me.View);
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion Terminal.Gui/Views/TextField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1773,7 +1773,7 @@ private int PositionCursor (MouseEvent ev)

if (_text.Count == 0)
{
x = pX - ev.OfX;
x = pX - ev.ScreenX;
}
else
{
Expand Down
1 change: 1 addition & 0 deletions Terminal.Gui/Views/Toplevel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ public override void PositionCursor ()
/// <param name="top">The Toplevel to adjust.</param>
public virtual void PositionToplevel (Toplevel top)
{
return;
View superView = GetLocationThatFits (

Check warning on line 383 in Terminal.Gui/Views/Toplevel.cs

View workflow job for this annotation

GitHub Actions / build_and_test

Unreachable code detected
top,
top.Frame.X,
Expand Down
5 changes: 3 additions & 2 deletions UICatalog/Scenarios/Adornments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public override void Init ()
ConfigurationManager.Themes.Theme = Theme;
ConfigurationManager.Apply ();
Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];

var view = new Window { Title = "The _Window" };
var tf1 = new TextField { Width = 10, Text = "TextField" };
var color = new ColorPicker { Title = "BG", BoxHeight = 1, BoxWidth = 1, X = Pos.AnchorEnd (11) };
Expand Down Expand Up @@ -75,7 +75,8 @@ public override void Init ()
var editor = new AdornmentsEditor
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]
ColorScheme = Colors.ColorSchemes [TopLevelColorScheme],
//BorderStyle = LineStyle.None,
};
view.X = 36;
view.Y = 0;
Expand Down

0 comments on commit 3a834ac

Please sign in to comment.