mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-31 10:17:55 +01:00
Merge pull request #3267 from dodexahedron/v2_3256_cleanup_2
Stage 3 of #3256 - Miscellaneous cleanup before more removals
This commit is contained in:
51
Terminal.Gui/Application.MainLoopSyncContext.cs
Normal file
51
Terminal.Gui/Application.MainLoopSyncContext.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
namespace Terminal.Gui;
|
||||
|
||||
public static partial class Application
|
||||
{
|
||||
/// <summary>
|
||||
/// provides the sync context set while executing code in Terminal.Gui, to let
|
||||
/// users use async/await on their code
|
||||
/// </summary>
|
||||
private sealed class MainLoopSyncContext : SynchronizationContext
|
||||
{
|
||||
public override SynchronizationContext CreateCopy () { return new MainLoopSyncContext (); }
|
||||
|
||||
public override void Post (SendOrPostCallback d, object state)
|
||||
{
|
||||
MainLoop.AddIdle (
|
||||
() =>
|
||||
{
|
||||
d (state);
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
//_mainLoop.Driver.Wakeup ();
|
||||
public override void Send (SendOrPostCallback d, object state)
|
||||
{
|
||||
if (Thread.CurrentThread.ManagedThreadId == _mainThreadId)
|
||||
{
|
||||
d (state);
|
||||
}
|
||||
else
|
||||
{
|
||||
var wasExecuted = false;
|
||||
|
||||
Invoke (
|
||||
() =>
|
||||
{
|
||||
d (state);
|
||||
wasExecuted = true;
|
||||
}
|
||||
);
|
||||
|
||||
while (!wasExecuted)
|
||||
{
|
||||
Thread.Sleep (15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -716,53 +716,6 @@ public static partial class Application
|
||||
/// </summary>
|
||||
public static bool EndAfterFirstIteration { get; set; }
|
||||
|
||||
//
|
||||
// provides the sync context set while executing code in Terminal.Gui, to let
|
||||
// users use async/await on their code
|
||||
//
|
||||
private class MainLoopSyncContext : SynchronizationContext
|
||||
{
|
||||
public override SynchronizationContext CreateCopy () { return new MainLoopSyncContext (); }
|
||||
|
||||
public override void Post (SendOrPostCallback d, object state)
|
||||
{
|
||||
MainLoop.AddIdle (
|
||||
() =>
|
||||
{
|
||||
d (state);
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
//_mainLoop.Driver.Wakeup ();
|
||||
public override void Send (SendOrPostCallback d, object state)
|
||||
{
|
||||
if (Thread.CurrentThread.ManagedThreadId == _mainThreadId)
|
||||
{
|
||||
d (state);
|
||||
}
|
||||
else
|
||||
{
|
||||
var wasExecuted = false;
|
||||
|
||||
Invoke (
|
||||
() =>
|
||||
{
|
||||
d (state);
|
||||
wasExecuted = true;
|
||||
}
|
||||
);
|
||||
|
||||
while (!wasExecuted)
|
||||
{
|
||||
Thread.Sleep (15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Building block API: Runs the main loop for the created <see cref="Toplevel"/>.</summary>
|
||||
/// <param name="state">The state returned by the <see cref="Begin(Toplevel)"/> method.</param>
|
||||
public static void RunLoop (RunState state)
|
||||
@@ -1125,47 +1078,36 @@ public static partial class Application
|
||||
}
|
||||
}
|
||||
|
||||
private static View FindDeepestTop (Toplevel start, int x, int y, out int resx, out int resy)
|
||||
#nullable enable
|
||||
private static Toplevel? FindDeepestTop (Toplevel start, int x, int y)
|
||||
{
|
||||
Rectangle startFrame = start.Frame;
|
||||
|
||||
if (!startFrame.Contains (x, y))
|
||||
if (!start.Frame.Contains (x, y))
|
||||
{
|
||||
resx = 0;
|
||||
resy = 0;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_topLevels is { })
|
||||
if (_topLevels is { Count: > 0 })
|
||||
{
|
||||
int count = _topLevels.Count;
|
||||
int rx = x - start.Frame.X;
|
||||
int ry = y - start.Frame.Y;
|
||||
|
||||
if (count > 0)
|
||||
foreach (Toplevel t in _topLevels)
|
||||
{
|
||||
int rx = x - startFrame.X;
|
||||
int ry = y - startFrame.Y;
|
||||
|
||||
foreach (Toplevel t in _topLevels)
|
||||
if (t != Current)
|
||||
{
|
||||
if (t != Current)
|
||||
if (t != start && t.Visible && t.Frame.Contains (rx, ry))
|
||||
{
|
||||
if (t != start && t.Visible && t.Frame.Contains (rx, ry))
|
||||
{
|
||||
start = t;
|
||||
start = t;
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resx = x - startFrame.X;
|
||||
resy = y - startFrame.Y;
|
||||
|
||||
return start;
|
||||
}
|
||||
#nullable restore
|
||||
|
||||
private static View FindTopFromView (View view)
|
||||
{
|
||||
@@ -1181,12 +1123,13 @@ public static partial class Application
|
||||
return top;
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
// Only return true if the Current has changed.
|
||||
private static bool MoveCurrent (Toplevel top)
|
||||
private static bool MoveCurrent (Toplevel? top)
|
||||
{
|
||||
// The Current is modal and the top is not modal Toplevel then
|
||||
// the Current must be moved above the first not modal Toplevel.
|
||||
if (OverlappedTop != null
|
||||
if (OverlappedTop is { }
|
||||
&& top != OverlappedTop
|
||||
&& top != Current
|
||||
&& Current?.Modal == true
|
||||
@@ -1218,7 +1161,7 @@ public static partial class Application
|
||||
|
||||
// The Current and the top are both not running Toplevel then
|
||||
// the top must be moved above the first not running Toplevel.
|
||||
if (OverlappedTop != null
|
||||
if (OverlappedTop is { }
|
||||
&& top != OverlappedTop
|
||||
&& top != Current
|
||||
&& Current?.Running == false
|
||||
@@ -1261,6 +1204,7 @@ public static partial class Application
|
||||
|
||||
return true;
|
||||
}
|
||||
#nullable restore
|
||||
|
||||
/// <summary>Invoked when the terminal's size changed. The new size of the terminal is provided.</summary>
|
||||
/// <remarks>
|
||||
@@ -1406,8 +1350,9 @@ public static partial class Application
|
||||
UnGrabbedMouse?.Invoke (view, new ViewEventArgs (view));
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
// Used by OnMouseEvent to track the last view that was clicked on.
|
||||
internal static View _mouseEnteredView;
|
||||
internal static View? _mouseEnteredView;
|
||||
|
||||
/// <summary>Event fired when a mouse move or click occurs. Coordinates are screen relative.</summary>
|
||||
/// <remarks>
|
||||
@@ -1419,13 +1364,11 @@ public static partial class Application
|
||||
/// </remarks>
|
||||
public static event EventHandler<MouseEventEventArgs> MouseEvent;
|
||||
|
||||
/// <summary>Called when a mouse event occurs. Fires the <see cref="MouseEvent"/> event.</summary>
|
||||
/// <summary>Called when a mouse event occurs. Raises the <see cref="MouseEvent"/> event.</summary>
|
||||
/// <remarks>This method can be used to simulate a mouse event, e.g. in unit tests.</remarks>
|
||||
/// <param name="a">The mouse event with coordinates relative to the screen.</param>
|
||||
public static void OnMouseEvent (MouseEventEventArgs a)
|
||||
internal static void OnMouseEvent (MouseEventEventArgs a)
|
||||
{
|
||||
static bool OutsideRect (Point p, Rectangle r) { return p.X < 0 || p.X > r.Right || p.Y < 0 || p.Y > r.Bottom; }
|
||||
|
||||
if (IsMouseDisabled)
|
||||
{
|
||||
return;
|
||||
@@ -1433,7 +1376,7 @@ public static partial class Application
|
||||
|
||||
var view = View.FindDeepestView (Current, a.MouseEvent.X, a.MouseEvent.Y, out int screenX, out int screenY);
|
||||
|
||||
if (view is { } && view.WantContinuousButtonPressed)
|
||||
if (view is { WantContinuousButtonPressed: true })
|
||||
{
|
||||
WantContinuousButtonPressedView = view;
|
||||
}
|
||||
@@ -1470,9 +1413,9 @@ public static partial class Application
|
||||
View = view
|
||||
};
|
||||
|
||||
if (OutsideRect (new Point (nme.X, nme.Y), MouseGrabView.Bounds))
|
||||
if (MouseGrabView.Bounds.Contains (nme.X, nme.Y) is false)
|
||||
{
|
||||
// The mouse has moved outside the bounds of the the view that
|
||||
// The mouse has moved outside the bounds of the view that
|
||||
// grabbed the mouse, so we tell the view that last got
|
||||
// OnMouseEnter the mouse is leaving
|
||||
// BUGBUG: That sentence makes no sense. Either I'm missing something
|
||||
@@ -1493,7 +1436,7 @@ public static partial class Application
|
||||
&& a.MouseEvent.Flags != MouseFlags.ReportMousePosition
|
||||
&& a.MouseEvent.Flags != 0)
|
||||
{
|
||||
View top = FindDeepestTop (Top, a.MouseEvent.X, a.MouseEvent.Y, out _, out _);
|
||||
View? top = FindDeepestTop (Top, a.MouseEvent.X, a.MouseEvent.Y);
|
||||
view = View.FindDeepestView (top, a.MouseEvent.X, a.MouseEvent.Y, out screenX, out screenY);
|
||||
|
||||
if (view is { } && view != OverlappedTop && top != Current)
|
||||
@@ -1502,136 +1445,143 @@ public static partial class Application
|
||||
}
|
||||
}
|
||||
|
||||
bool AdornmentHandledMouseEvent (Adornment frame)
|
||||
if (view is null)
|
||||
{
|
||||
if (frame?.Thickness.Contains (frame.FrameToScreen (), a.MouseEvent.X, a.MouseEvent.Y) ?? false)
|
||||
{
|
||||
Point boundsPoint = frame.ScreenToBounds (a.MouseEvent.X, a.MouseEvent.Y);
|
||||
|
||||
var me = new MouseEvent
|
||||
{
|
||||
X = boundsPoint.X,
|
||||
Y = boundsPoint.Y,
|
||||
Flags = a.MouseEvent.Flags,
|
||||
OfX = boundsPoint.X,
|
||||
OfY = boundsPoint.Y,
|
||||
View = frame
|
||||
};
|
||||
frame.OnMouseEvent (me);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (view is { })
|
||||
// Work inside-out (Padding, Border, Margin)
|
||||
// TODO: Debate whether inside-out or outside-in is the right strategy
|
||||
if (AdornmentHandledMouseEvent (view.Padding, a))
|
||||
{
|
||||
// Work inside-out (Padding, Border, Margin)
|
||||
// TODO: Debate whether inside-out or outside-in is the right strategy
|
||||
if (AdornmentHandledMouseEvent (view?.Padding))
|
||||
return;
|
||||
}
|
||||
|
||||
if (AdornmentHandledMouseEvent (view.Border, a))
|
||||
{
|
||||
if (view is not Toplevel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AdornmentHandledMouseEvent (view?.Border))
|
||||
// TODO: This is a temporary hack to work around the fact that
|
||||
// drag handling is handled in Toplevel (See Issue #2537)
|
||||
|
||||
var me = new MouseEvent
|
||||
{
|
||||
if (view is Toplevel)
|
||||
{
|
||||
// TODO: This is a temporary hack to work around the fact that
|
||||
// drag handling is handled in Toplevel (See Issue #2537)
|
||||
X = screenX,
|
||||
Y = screenY,
|
||||
Flags = a.MouseEvent.Flags,
|
||||
OfX = screenX,
|
||||
OfY = screenY,
|
||||
View = view
|
||||
};
|
||||
|
||||
var me = new MouseEvent
|
||||
{
|
||||
X = screenX,
|
||||
Y = screenY,
|
||||
Flags = a.MouseEvent.Flags,
|
||||
OfX = screenX,
|
||||
OfY = screenY,
|
||||
View = view
|
||||
};
|
||||
|
||||
if (_mouseEnteredView is null)
|
||||
{
|
||||
_mouseEnteredView = view;
|
||||
view.OnMouseEnter (me);
|
||||
}
|
||||
else if (_mouseEnteredView != view)
|
||||
{
|
||||
_mouseEnteredView.OnMouseLeave (me);
|
||||
view.OnMouseEnter (me);
|
||||
_mouseEnteredView = view;
|
||||
}
|
||||
|
||||
if (!view.WantMousePositionReports && a.MouseEvent.Flags == MouseFlags.ReportMousePosition)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
|
||||
|
||||
if (view.OnMouseEvent (me))
|
||||
{
|
||||
// Should we bubble up the event, if it is not handled?
|
||||
//return;
|
||||
}
|
||||
|
||||
BringOverlappedTopToFront ();
|
||||
}
|
||||
|
||||
return;
|
||||
if (_mouseEnteredView is null)
|
||||
{
|
||||
_mouseEnteredView = view;
|
||||
view.OnMouseEnter (me);
|
||||
}
|
||||
else if (_mouseEnteredView != view)
|
||||
{
|
||||
_mouseEnteredView.OnMouseLeave (me);
|
||||
view.OnMouseEnter (me);
|
||||
_mouseEnteredView = view;
|
||||
}
|
||||
|
||||
if (AdornmentHandledMouseEvent (view?.Margin))
|
||||
if (!view.WantMousePositionReports && a.MouseEvent.Flags == MouseFlags.ReportMousePosition)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Rectangle bounds = view.BoundsToScreen (view.Bounds);
|
||||
WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
|
||||
|
||||
if (bounds.Contains (a.MouseEvent.X, a.MouseEvent.Y))
|
||||
if (view.OnMouseEvent (me))
|
||||
{
|
||||
Point boundsPoint = view.ScreenToBounds (a.MouseEvent.X, a.MouseEvent.Y);
|
||||
|
||||
var me = new MouseEvent
|
||||
{
|
||||
X = boundsPoint.X,
|
||||
Y = boundsPoint.Y,
|
||||
Flags = a.MouseEvent.Flags,
|
||||
OfX = boundsPoint.X,
|
||||
OfY = boundsPoint.Y,
|
||||
View = view
|
||||
};
|
||||
|
||||
if (_mouseEnteredView is null)
|
||||
{
|
||||
_mouseEnteredView = view;
|
||||
view.OnMouseEnter (me);
|
||||
}
|
||||
else if (_mouseEnteredView != view)
|
||||
{
|
||||
_mouseEnteredView.OnMouseLeave (me);
|
||||
view.OnMouseEnter (me);
|
||||
_mouseEnteredView = view;
|
||||
}
|
||||
|
||||
if (!view.WantMousePositionReports && a.MouseEvent.Flags == MouseFlags.ReportMousePosition)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
|
||||
|
||||
if (view.OnMouseEvent (me))
|
||||
{
|
||||
// Should we bubble up the event, if it is not handled?
|
||||
//return;
|
||||
}
|
||||
|
||||
BringOverlappedTopToFront ();
|
||||
// Should we bubble up the event, if it is not handled?
|
||||
//return;
|
||||
}
|
||||
|
||||
BringOverlappedTopToFront ();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (AdornmentHandledMouseEvent (view?.Margin, a))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Rectangle bounds = view.BoundsToScreen (view.Bounds);
|
||||
|
||||
if (bounds.Contains (a.MouseEvent.X, a.MouseEvent.Y))
|
||||
{
|
||||
Point boundsPoint = view.ScreenToBounds (a.MouseEvent.X, a.MouseEvent.Y);
|
||||
|
||||
var me = new MouseEvent
|
||||
{
|
||||
X = boundsPoint.X,
|
||||
Y = boundsPoint.Y,
|
||||
Flags = a.MouseEvent.Flags,
|
||||
OfX = boundsPoint.X,
|
||||
OfY = boundsPoint.Y,
|
||||
View = view
|
||||
};
|
||||
|
||||
if (_mouseEnteredView is null)
|
||||
{
|
||||
_mouseEnteredView = view;
|
||||
view.OnMouseEnter (me);
|
||||
}
|
||||
else if (_mouseEnteredView != view)
|
||||
{
|
||||
_mouseEnteredView.OnMouseLeave (me);
|
||||
view.OnMouseEnter (me);
|
||||
_mouseEnteredView = view;
|
||||
}
|
||||
|
||||
if (!view.WantMousePositionReports && a.MouseEvent.Flags == MouseFlags.ReportMousePosition)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
|
||||
|
||||
if (view.OnMouseEvent (me))
|
||||
{
|
||||
// Should we bubble up the event, if it is not handled?
|
||||
//return;
|
||||
}
|
||||
|
||||
BringOverlappedTopToFront ();
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
static bool AdornmentHandledMouseEvent (Adornment? frame, MouseEventEventArgs args)
|
||||
{
|
||||
if (frame?.Thickness.Contains (frame.FrameToScreen (), args.MouseEvent.X, args.MouseEvent.Y) is not true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Point boundsPoint = frame.ScreenToBounds (args.MouseEvent.X, args.MouseEvent.Y);
|
||||
|
||||
var me = new MouseEvent
|
||||
{
|
||||
X = boundsPoint.X,
|
||||
Y = boundsPoint.Y,
|
||||
Flags = args.MouseEvent.Flags,
|
||||
OfX = boundsPoint.X,
|
||||
OfY = boundsPoint.Y,
|
||||
View = frame
|
||||
};
|
||||
frame.OnMouseEvent (me);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#nullable restore
|
||||
|
||||
#endregion Mouse handling
|
||||
|
||||
@@ -1846,8 +1796,4 @@ public static partial class Application
|
||||
}
|
||||
|
||||
#endregion Keyboard handling
|
||||
}
|
||||
|
||||
/// <summary>Event arguments for the <see cref="Application.Iteration"/> event.</summary>
|
||||
public class IterationEventArgs
|
||||
{ }
|
||||
}
|
||||
5
Terminal.Gui/IterationEventArgs.cs
Normal file
5
Terminal.Gui/IterationEventArgs.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Terminal.Gui;
|
||||
|
||||
/// <summary>Event arguments for the <see cref="Application.Iteration"/> event.</summary>
|
||||
public class IterationEventArgs : EventArgs
|
||||
{ }
|
||||
@@ -90,9 +90,9 @@
|
||||
<ItemGroup>
|
||||
<Using Include="JetBrains.Annotations" />
|
||||
<Using Include="System.Diagnostics.Contracts.PureAttribute" Alias="PureAttribute" />
|
||||
<Using Include="System.Drawing.Rectangle" Alias="Rectangle" />
|
||||
<Using Include="System.Text" />
|
||||
<Using Include="JetBrains.Annotations" />
|
||||
<Using Include="System.Diagnostics.Contracts.PureAttribute" Alias="PureAttribute" />
|
||||
</ItemGroup>
|
||||
<!-- =================================================================== -->
|
||||
<!-- Nuget -->
|
||||
|
||||
11
Terminal.Gui/Types/Point.TemporaryOperators.cs
Normal file
11
Terminal.Gui/Types/Point.TemporaryOperators.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Terminal.Gui;
|
||||
|
||||
public partial struct Point
|
||||
{
|
||||
public static implicit operator System.Drawing.Point (Terminal.Gui.Point tgp) => new (tgp.X, tgp.Y);
|
||||
public static implicit operator Point (System.Drawing.Point sdp) => new (sdp.X, sdp.Y);
|
||||
public static bool operator != (Point left, System.Drawing.Point right) => new System.Drawing.Point (left.X,left.Y) != right;
|
||||
public static bool operator == (Point left, System.Drawing.Point right) => new System.Drawing.Point (left.X,left.Y) == right;
|
||||
public static bool operator != (System.Drawing.Point left, Point right) => left != new System.Drawing.Point(right.X,right.Y);
|
||||
public static bool operator == (System.Drawing.Point left, Point right) => left == new System.Drawing.Point(right.X,right.Y);
|
||||
}
|
||||
@@ -14,7 +14,7 @@ using System.Text.Json.Serialization;
|
||||
namespace Terminal.Gui;
|
||||
|
||||
/// <summary>Represents an ordered pair of integer x- and y-coordinates that defines a point in a two-dimensional plane.</summary>
|
||||
public struct Point
|
||||
public partial struct Point
|
||||
{
|
||||
/// <summary>Gets or sets the x-coordinate of this Point.</summary>
|
||||
[JsonInclude]
|
||||
|
||||
@@ -1,294 +0,0 @@
|
||||
//
|
||||
// Derived from System.Drawing.Rectangle.cs
|
||||
//
|
||||
// Author:
|
||||
// Mike Kestner (mkestner@speakeasy.net)
|
||||
//
|
||||
// Copyright (C) 2001 Mike Kestner
|
||||
// Copyright (C) 2004 Novell, Inc. http://www.novell.com
|
||||
//
|
||||
|
||||
namespace Terminal.Gui;
|
||||
|
||||
/// <summary>Stores a set of four integers that represent the location and size of a rectangle</summary>
|
||||
public struct Rectangle
|
||||
{
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
/// <summary>Gets or sets the x-coordinate of the upper-left corner of this Rectangle structure.</summary>
|
||||
public int X;
|
||||
|
||||
/// <summary>Gets or sets the y-coordinate of the upper-left corner of this Rectangle structure.</summary>
|
||||
public int Y;
|
||||
|
||||
/// <summary>Gets or sets the width of this Rect structure.</summary>
|
||||
public int Width
|
||||
{
|
||||
get => width;
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
throw new ArgumentException ("Width must be greater or equal to 0.");
|
||||
}
|
||||
|
||||
width = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the height of this Rectangle structure.</summary>
|
||||
public int Height
|
||||
{
|
||||
get => height;
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
throw new ArgumentException ("Height must be greater or equal to 0.");
|
||||
}
|
||||
|
||||
height = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Empty Shared Field</summary>
|
||||
/// <remarks>An uninitialized Rectangle Structure.</remarks>
|
||||
public static readonly Rectangle Empty;
|
||||
|
||||
/// <summary>FromLTRB Shared Method</summary>
|
||||
/// <remarks>Produces a Rectangle structure from left, top, right and bottom coordinates.</remarks>
|
||||
public static Rectangle FromLTRB (
|
||||
int left,
|
||||
int top,
|
||||
int right,
|
||||
int bottom
|
||||
)
|
||||
{
|
||||
return new Rectangle (
|
||||
left,
|
||||
top,
|
||||
right - left,
|
||||
bottom - top
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Produces a new Rect by inflating an existing Rect by the specified coordinate values.</summary>
|
||||
/// <remarks>
|
||||
/// Produces a new Rect by inflating an existing Rect by the specified coordinate values. The rectangle is
|
||||
/// enlarged in both directions along an axis.
|
||||
/// </remarks>
|
||||
public static Rectangle Inflate (Rectangle rect, int x, int y)
|
||||
{
|
||||
var r = new Rectangle (rect.Location, rect.Size);
|
||||
r.Inflate (x, y);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>Inflates an existing Rect by the specified coordinate values.</summary>
|
||||
/// <remarks>
|
||||
/// This method enlarges this rectangle, not a copy of it. The rectangle is enlarged in both directions along an
|
||||
/// axis.
|
||||
/// </remarks>
|
||||
public void Inflate (int width, int height)
|
||||
{
|
||||
// Set dims first so we don't lose the original values on exception
|
||||
Width += width * 2;
|
||||
Height += height * 2;
|
||||
|
||||
X -= width;
|
||||
Y -= height;
|
||||
}
|
||||
|
||||
/// <summary>Inflates an existing Rect by the specified Sizwe.</summary>
|
||||
/// <remarks>
|
||||
/// This method enlarges this rectangle, not a copy of it. The rectangle is enlarged in both directions along an
|
||||
/// axis.
|
||||
/// </remarks>
|
||||
public void Inflate (Size size) { Inflate (size.Width, size.Height); }
|
||||
|
||||
/// <summary>Intersect Shared Method</summary>
|
||||
/// <remarks>Produces a new Rectangle by intersecting 2 existing Rectangles. Returns Empty if there is no intersection.</remarks>
|
||||
public static Rectangle Intersect (Rectangle a, Rectangle b)
|
||||
{
|
||||
// MS.NET returns a non-empty rectangle if the two rectangles
|
||||
// touch each other
|
||||
if (!a.IntersectsWithInclusive (b))
|
||||
{
|
||||
return Empty;
|
||||
}
|
||||
|
||||
return FromLTRB (
|
||||
Math.Max (a.Left, b.Left),
|
||||
Math.Max (a.Top, b.Top),
|
||||
Math.Min (a.Right, b.Right),
|
||||
Math.Min (a.Bottom, b.Bottom)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Intersect Method</summary>
|
||||
/// <remarks>Replaces the Rectangle with the intersection of itself and another Rectangle.</remarks>
|
||||
public void Intersect (Rectangle rect) { this = Intersect (this, rect); }
|
||||
|
||||
/// <summary>Produces the uninion of two rectangles.</summary>
|
||||
/// <remarks>Produces a new Rectangle from the union of 2 existing Rectangles.</remarks>
|
||||
public static Rectangle Union (Rectangle a, Rectangle b)
|
||||
{
|
||||
//int x1 = Math.Min (a.X, b.X);
|
||||
//int x2 = Math.Max (a.X + a.Width, b.X + b.Width);
|
||||
//int y1 = Math.Min (a.Y, b.Y);oS
|
||||
//int y2 = Math.Max (a.Y + a.Height, b.Y + b.Height);
|
||||
//return new Rect (x1, y1, x2 - x1, y2 - y1);
|
||||
|
||||
int x1 = Math.Min (a.X, b.X);
|
||||
int x2 = Math.Max (a.X + Math.Abs (a.Width), b.X + Math.Abs (b.Width));
|
||||
int y1 = Math.Min (a.Y, b.Y);
|
||||
int y2 = Math.Max (a.Y + Math.Abs (a.Height), b.Y + Math.Abs (b.Height));
|
||||
|
||||
return new Rectangle (x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
/// <summary>Equality Operator</summary>
|
||||
/// <remarks>
|
||||
/// Compares two Rectangle objects. The return value is based on the equivalence of the Location and Size
|
||||
/// properties of the two Rectangles.
|
||||
/// </remarks>
|
||||
public static bool operator == (Rectangle left, Rectangle right) { return left.Location == right.Location && left.Size == right.Size; }
|
||||
|
||||
/// <summary>Inequality Operator</summary>
|
||||
/// <remarks>
|
||||
/// Compares two Rectangle objects. The return value is based on the equivalence of the Location and Size
|
||||
/// properties of the two Rectangles.
|
||||
/// </remarks>
|
||||
public static bool operator != (Rectangle left, Rectangle right) { return left.Location != right.Location || left.Size != right.Size; }
|
||||
|
||||
// -----------------------
|
||||
// Public Constructors
|
||||
// -----------------------
|
||||
|
||||
/// <summary>Rectangle Constructor</summary>
|
||||
/// <remarks>Creates a Rectangle from Point and Size values.</remarks>
|
||||
public Rectangle (Point location, Size size)
|
||||
{
|
||||
X = location.X;
|
||||
Y = location.Y;
|
||||
width = size.Width;
|
||||
height = size.Height;
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
/// <summary>Rectangle Constructor</summary>
|
||||
/// <remarks>Creates a Rectangle from a specified x,y location and width and height values.</remarks>
|
||||
public Rectangle (int x, int y, int width, int height)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
Width = this.width;
|
||||
Height = this.height;
|
||||
}
|
||||
|
||||
/// <summary>Bottom Property</summary>
|
||||
/// <remarks>The Y coordinate of the bottom edge of the Rectangle. Read only.</remarks>
|
||||
public int Bottom => Y + Height;
|
||||
|
||||
/// <summary>IsEmpty Property</summary>
|
||||
/// <remarks>Indicates if the width or height are zero. Read only.</remarks>
|
||||
public bool IsEmpty => X == 0 && Y == 0 && Width == 0 && Height == 0;
|
||||
|
||||
/// <summary>Left Property</summary>
|
||||
/// <remarks>The X coordinate of the left edge of the Rectangle. Read only.</remarks>
|
||||
public int Left => X;
|
||||
|
||||
/// <summary>Location Property</summary>
|
||||
/// <remarks>The Location of the top-left corner of the Rectangle.</remarks>
|
||||
public Point Location
|
||||
{
|
||||
get => new (X, Y);
|
||||
set
|
||||
{
|
||||
X = value.X;
|
||||
Y = value.Y;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Right Property</summary>
|
||||
/// <remarks>The X coordinate of the right edge of the Rectangle. Read only.</remarks>
|
||||
public int Right => X + Width;
|
||||
|
||||
/// <summary>Size Property</summary>
|
||||
/// <remarks>The Size of the Rectangle.</remarks>
|
||||
public Size Size
|
||||
{
|
||||
get => new (Width, Height);
|
||||
set
|
||||
{
|
||||
Width = value.Width;
|
||||
Height = value.Height;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Top Property</summary>
|
||||
/// <remarks>The Y coordinate of the top edge of the Rectangle. Read only.</remarks>
|
||||
public int Top => Y;
|
||||
|
||||
/// <summary>Contains Method</summary>
|
||||
/// <remarks>Checks if an x,y coordinate lies within this Rectangle.</remarks>
|
||||
public bool Contains (int x, int y) { return x >= Left && x < Right && y >= Top && y < Bottom; }
|
||||
|
||||
/// <summary>Contains Method</summary>
|
||||
/// <remarks>Checks if a Point lies within this Rectangle.</remarks>
|
||||
public bool Contains (Point pt) { return Contains (pt.X, pt.Y); }
|
||||
|
||||
/// <summary>Contains Method</summary>
|
||||
/// <remarks>Checks if a Rectangle lies entirely within this Rectangle.</remarks>
|
||||
public bool Contains (Rectangle rect) { return rect == Intersect (this, rect); }
|
||||
|
||||
/// <summary>Equals Method</summary>
|
||||
/// <remarks>Checks equivalence of this Rectangle and another object.</remarks>
|
||||
public override bool Equals (object obj)
|
||||
{
|
||||
if (!(obj is Rectangle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return this == (Rectangle)obj;
|
||||
}
|
||||
|
||||
/// <summary>GetHashCode Method</summary>
|
||||
/// <remarks>Calculates a hashing value.</remarks>
|
||||
public override int GetHashCode () { return X ^
|
||||
((Y << 13) | (Y >>> 19)) ^
|
||||
((Width << 26) | (Width >>> 6)) ^
|
||||
((Height << 7) | (Height >>> 25)); }
|
||||
|
||||
/// <summary>IntersectsWith Method</summary>
|
||||
/// <remarks>Checks if a Rectangle intersects with this one.</remarks>
|
||||
public bool IntersectsWith (Rectangle rect) { return !(Left >= rect.Right || Right <= rect.Left || Top >= rect.Bottom || Bottom <= rect.Top); }
|
||||
|
||||
private bool IntersectsWithInclusive (Rectangle r) { return !(Left > r.Right || Right < r.Left || Top > r.Bottom || Bottom < r.Top); }
|
||||
|
||||
/// <summary>Offset Method</summary>
|
||||
/// <remarks>Moves the Rectangle a specified distance.</remarks>
|
||||
public void Offset (int x, int y)
|
||||
{
|
||||
X += x;
|
||||
Y += y;
|
||||
}
|
||||
|
||||
/// <summary>Offset Method</summary>
|
||||
/// <remarks>Moves the Rectangle a specified distance.</remarks>
|
||||
public void Offset (Point pos)
|
||||
{
|
||||
X += pos.X;
|
||||
Y += pos.Y;
|
||||
}
|
||||
|
||||
/// <summary>ToString Method</summary>
|
||||
/// <remarks>Formats the Rectangle as a string in (x,y,w,h) notation.</remarks>
|
||||
public override string ToString () { return $"{{X={X},Y={Y},Width={Width},Height={Height}}}"; }
|
||||
}
|
||||
11
Terminal.Gui/Types/Size.TemporaryOperators.cs
Normal file
11
Terminal.Gui/Types/Size.TemporaryOperators.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Terminal.Gui;
|
||||
|
||||
public partial struct Size
|
||||
{
|
||||
public static implicit operator Size (System.Drawing.Size sds) => new (sds.Width, sds.Height);
|
||||
public static implicit operator System.Drawing.Size (Size tgs) => new (tgs.Width, tgs.Height);
|
||||
public static bool operator != (Size left, System.Drawing.Size right) => new System.Drawing.Size (left.Width,left.Height) != right;
|
||||
public static bool operator == (Size left, System.Drawing.Size right) => new System.Drawing.Size (left.Width,left.Height) == right;
|
||||
public static bool operator != (System.Drawing.Size left, Size right) => left != new System.Drawing.Size(right.Width,right.Height);
|
||||
public static bool operator == (System.Drawing.Size left, Size right) => left == new System.Drawing.Size(right.Width,right.Height);
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace Terminal.Gui;
|
||||
|
||||
/// <summary>Stores an ordered pair of integers, which specify a Height and Width.</summary>
|
||||
public struct Size
|
||||
public partial struct Size
|
||||
{
|
||||
private int width, height;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Terminal.Gui;
|
||||
@@ -542,61 +542,53 @@ public partial class View
|
||||
}
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
/// <summary>Finds which view that belong to the <paramref name="start"/> superview at the provided location.</summary>
|
||||
/// <param name="start">The superview where to look for.</param>
|
||||
/// <param name="x">The column location in the superview.</param>
|
||||
/// <param name="y">The row location in the superview.</param>
|
||||
/// <param name="resx">The found view screen relative column location.</param>
|
||||
/// <param name="resy">The found view screen relative row location.</param>
|
||||
/// <param name="resultX">The found view screen relative column location.</param>
|
||||
/// <param name="resultY">The found view screen relative row location.</param>
|
||||
/// <returns>
|
||||
/// The view that was found at the <praramref name="x"/> and <praramref name="y"/> coordinates.
|
||||
/// The view that was found at the <paramref name="x"/> and <paramref name="y"/> coordinates.
|
||||
/// <see langword="null"/> if no view was found.
|
||||
/// </returns>
|
||||
public static View FindDeepestView (View start, int x, int y, out int resx, out int resy)
|
||||
// CONCURRENCY: This method is not thread-safe.
|
||||
// Undefined behavior and likely program crashes are exposed by unsynchronized access to InternalSubviews.
|
||||
public static View? FindDeepestView (View? start, int x, int y, out int resultX, out int resultY)
|
||||
{
|
||||
resy = resx = 0;
|
||||
resultY = resultX = 0;
|
||||
|
||||
if (start is null || !start.Frame.Contains (x, y))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Rectangle startFrame = start.Frame;
|
||||
|
||||
if (start.InternalSubviews is { })
|
||||
if (start.InternalSubviews is { Count: > 0 })
|
||||
{
|
||||
int count = start.InternalSubviews.Count;
|
||||
Point boundsOffset = start.GetBoundsOffset ();
|
||||
int rx = x - (start.Frame.X + boundsOffset.X);
|
||||
int ry = y - (start.Frame.Y + boundsOffset.Y);
|
||||
|
||||
if (count > 0)
|
||||
for (int i = start.InternalSubviews.Count - 1; i >= 0; i--)
|
||||
{
|
||||
Point boundsOffset = start.GetBoundsOffset ();
|
||||
int rx = x - (startFrame.X + boundsOffset.X);
|
||||
int ry = y - (startFrame.Y + boundsOffset.Y);
|
||||
View v = start.InternalSubviews [i];
|
||||
|
||||
for (int i = count - 1; i >= 0; i--)
|
||||
if (v.Visible && v.Frame.Contains (rx, ry))
|
||||
{
|
||||
View v = start.InternalSubviews [i];
|
||||
View? deep = FindDeepestView (v, rx, ry, out resultX, out resultY);
|
||||
|
||||
if (v.Visible && v.Frame.Contains (rx, ry))
|
||||
{
|
||||
View deep = FindDeepestView (v, rx, ry, out resx, out resy);
|
||||
|
||||
if (deep is null)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
return deep;
|
||||
}
|
||||
return deep ?? v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resx = x - startFrame.X;
|
||||
resy = y - startFrame.Y;
|
||||
resultX = x - start.Frame.X;
|
||||
resultY = y - start.Frame.Y;
|
||||
|
||||
return start;
|
||||
}
|
||||
#nullable restore
|
||||
|
||||
/// <summary>Gets the <see cref="Frame"/> with a screen-relative location.</summary>
|
||||
/// <returns>The location and size of the view in screen-relative coordinates.</returns>
|
||||
|
||||
@@ -38,11 +38,12 @@ public static partial class Application
|
||||
}
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
/// <summary>
|
||||
/// The <see cref="Toplevel"/> object used for the application on startup which
|
||||
/// <see cref="Toplevel.IsOverlappedContainer"/> is true.
|
||||
/// </summary>
|
||||
public static Toplevel OverlappedTop
|
||||
public static Toplevel? OverlappedTop
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -54,6 +55,7 @@ public static partial class Application
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#nullable restore
|
||||
|
||||
/// <summary>Brings the superview of the most focused overlapped view is on front.</summary>
|
||||
public static void BringOverlappedTopToFront ()
|
||||
|
||||
@@ -425,4 +425,5 @@
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=B0C2F2A1AF61DA42BBF270980E3DCEF7/Name/@EntryValue">Concurrency Issue</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=B0C2F2A1AF61DA42BBF270980E3DCEF7/Pattern/@EntryValue">(?<=\W|^)(?<TAG>CONCURRENCY)(\W|$)(.*)</s:String>
|
||||
<s:String x:Key="/Default/PatternsAndTemplates/Todo/TodoPatterns/=B0C2F2A1AF61DA42BBF270980E3DCEF7/TodoIconStyle/@EntryValue">Warning</s:String>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsynchronized/@EntryIndexedValue">True</s:Boolean>
|
||||
</wpf:ResourceDictionary>
|
||||
|
||||
@@ -7,7 +7,6 @@ using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using Terminal.Gui;
|
||||
using Rectangle = Terminal.Gui.Rectangle;
|
||||
|
||||
namespace UICatalog.Scenarios;
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using Terminal.Gui;
|
||||
using Color = Terminal.Gui.Color;
|
||||
using Rectangle = Terminal.Gui.Rectangle;
|
||||
|
||||
namespace UICatalog.Scenarios;
|
||||
|
||||
|
||||
@@ -37,4 +37,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Using Include="System.Drawing.Rectangle" Alias="Rectangle" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -246,7 +246,7 @@ public class DialogTests
|
||||
new Button { Text = btn3Text },
|
||||
new Button { Text = btn4Text }
|
||||
);
|
||||
Assert.Equal (new Size (width, 1), dlg.Frame.Size);
|
||||
Assert.Equal (new Size (width, 1), (Size)dlg.Frame.Size);
|
||||
TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
|
||||
End (runstate);
|
||||
|
||||
@@ -1114,7 +1114,7 @@ public class DialogTests
|
||||
|
||||
// Default location is centered, so 100 / 2 - 85 / 2 = 7
|
||||
var expected = 7;
|
||||
Assert.Equal (new Point (expected, expected), d.Frame.Location);
|
||||
Assert.Equal (new Point (expected, expected), (Point)d.Frame.Location);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1127,7 +1127,7 @@ public class DialogTests
|
||||
|
||||
// Default location is centered, so 100 / 2 - 85 / 2 = 7
|
||||
var expected = 1;
|
||||
Assert.Equal (new Point (expected, expected), d.Frame.Location);
|
||||
Assert.Equal (new Point (expected, expected), (Point)d.Frame.Location);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1140,7 +1140,7 @@ public class DialogTests
|
||||
((FakeDriver)Driver).SetBufferSize (20, 10);
|
||||
|
||||
// Default location is centered, so 100 / 2 - 85 / 2 = 7
|
||||
Assert.Equal (new Point (expected, expected), d.Frame.Location);
|
||||
Assert.Equal (new Point (expected, expected), (Point)d.Frame.Location);
|
||||
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
@@ -1170,7 +1170,7 @@ public class DialogTests
|
||||
var d = new Dialog { X = 5, Y = 5, Height = 3, Width = 5 };
|
||||
Begin (d);
|
||||
|
||||
Assert.Equal (new Point (5, 5), d.Frame.Location);
|
||||
Assert.Equal (new Point (5, 5), (Point)d.Frame.Location);
|
||||
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
@@ -1191,7 +1191,7 @@ public class DialogTests
|
||||
Begin (d);
|
||||
|
||||
// This is because of PostionTopLevels and EnsureVisibleBounds
|
||||
Assert.Equal (new Point (3, 2), d.Frame.Location);
|
||||
Assert.Equal (new Point (3, 2), (Point)d.Frame.Location);
|
||||
|
||||
// #3127: Before
|
||||
// Assert.Equal (new Size (17, 8), d.Frame.Size);
|
||||
@@ -1208,7 +1208,7 @@ public class DialogTests
|
||||
//╚══└───────────────┘", _output);
|
||||
|
||||
// #3127: After: Because Toplevel is now Width/Height = Dim.Filll
|
||||
Assert.Equal (new Size (15, 6), d.Frame.Size);
|
||||
Assert.Equal (new Size (15, 6), (Size)d.Frame.Size);
|
||||
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (
|
||||
@"
|
||||
@@ -1272,7 +1272,7 @@ public class DialogTests
|
||||
((FakeDriver)Driver).SetBufferSize (100, 100);
|
||||
|
||||
// Default size is Percent(85)
|
||||
Assert.Equal (new Size ((int)(100 * .85), (int)(100 * .85)), d.Frame.Size);
|
||||
Assert.Equal (new Size ((int)(100 * .85), (int)(100 * .85)), (Size)d.Frame.Size);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1285,7 +1285,7 @@ public class DialogTests
|
||||
((FakeDriver)Driver).SetBufferSize (100, 100);
|
||||
|
||||
// Default size is Percent(85)
|
||||
Assert.Equal (new Size (50, 50), d.Frame.Size);
|
||||
Assert.Equal (new Size (50, 50), (Size)d.Frame.Size);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -153,7 +153,7 @@ public class MessageBoxTests
|
||||
// Default location is centered, so
|
||||
// X = (100 / 2) - (60 / 2) = 20
|
||||
// Y = (100 / 2) - (5 / 2) = 47
|
||||
Assert.Equal (new Point (20, 47), Application.Current.Frame.Location);
|
||||
Assert.Equal (new Point (20, 47), (Point)Application.Current.Frame.Location);
|
||||
|
||||
Application.RequestStop ();
|
||||
}
|
||||
@@ -295,7 +295,7 @@ public class MessageBoxTests
|
||||
╚══════════════════╝",
|
||||
_output
|
||||
);
|
||||
Assert.Equal (new Size (20 - 2, 10 - 2), Application.Current.Frame.Size);
|
||||
Assert.Equal (new Size (20 - 2, 10 - 2), (Size)Application.Current.Frame.Size);
|
||||
Application.RequestStop ();
|
||||
|
||||
// Really long text
|
||||
@@ -607,7 +607,7 @@ ffffffffffffffffffff
|
||||
Assert.IsType<Dialog> (Application.Current);
|
||||
|
||||
// Default size is Percent(60)
|
||||
Assert.Equal (new Size ((int)(100 * .60), 5), Application.Current.Frame.Size);
|
||||
Assert.Equal (new Size ((int)(100 * .60), 5), (Size)Application.Current.Frame.Size);
|
||||
|
||||
Application.RequestStop ();
|
||||
}
|
||||
@@ -801,7 +801,7 @@ ffffffffffffffffffff
|
||||
Application.Refresh ();
|
||||
|
||||
Assert.IsType<Dialog> (Application.Current);
|
||||
Assert.Equal (new Size (height, width), Application.Current.Frame.Size);
|
||||
Assert.Equal (new Size (height, width), (Size)Application.Current.Frame.Size);
|
||||
|
||||
Application.RequestStop ();
|
||||
}
|
||||
@@ -839,7 +839,7 @@ ffffffffffffffffffff
|
||||
Application.Refresh ();
|
||||
|
||||
Assert.IsType<Dialog> (Application.Current);
|
||||
Assert.Equal (new Size (height, width), Application.Current.Frame.Size);
|
||||
Assert.Equal (new Size (height, width), (Size)Application.Current.Frame.Size);
|
||||
|
||||
Application.RequestStop ();
|
||||
}
|
||||
@@ -873,7 +873,7 @@ ffffffffffffffffffff
|
||||
Application.Refresh ();
|
||||
|
||||
Assert.IsType<Dialog> (Application.Current);
|
||||
Assert.Equal (new Size (height, width), Application.Current.Frame.Size);
|
||||
Assert.Equal (new Size (height, width), (Size)Application.Current.Frame.Size);
|
||||
|
||||
Application.RequestStop ();
|
||||
}
|
||||
@@ -901,7 +901,7 @@ ffffffffffffffffffff
|
||||
{
|
||||
Application.Refresh ();
|
||||
|
||||
Assert.Equal (new Size (7, 5), Application.Current.Frame.Size);
|
||||
Assert.Equal (new Size (7, 5), (Size)Application.Current.Frame.Size);
|
||||
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (
|
||||
@$"
|
||||
|
||||
@@ -1,519 +0,0 @@
|
||||
namespace Terminal.Gui.TypeTests;
|
||||
|
||||
public class RectangleTests
|
||||
{
|
||||
[Theory]
|
||||
|
||||
// Empty
|
||||
[InlineData (
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)]
|
||||
[InlineData (
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
-1,
|
||||
0,
|
||||
2,
|
||||
0
|
||||
)]
|
||||
[InlineData (
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
-1,
|
||||
0,
|
||||
2
|
||||
)]
|
||||
[InlineData (
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
-1,
|
||||
-1,
|
||||
2,
|
||||
2
|
||||
)]
|
||||
[InlineData (
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
-1,
|
||||
-1, // Throws
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)]
|
||||
|
||||
// Zero location, Size of 1
|
||||
[InlineData (
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1
|
||||
)]
|
||||
[InlineData (
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
-1,
|
||||
0,
|
||||
3,
|
||||
1
|
||||
)]
|
||||
[InlineData (
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
-1,
|
||||
1,
|
||||
3
|
||||
)]
|
||||
[InlineData (
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
-1,
|
||||
-1,
|
||||
3,
|
||||
3
|
||||
)]
|
||||
|
||||
// Positive location, Size of 1
|
||||
[InlineData (
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
)]
|
||||
[InlineData (
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
1
|
||||
)]
|
||||
[InlineData (
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
3
|
||||
)]
|
||||
[InlineData (
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
3
|
||||
)]
|
||||
public void Inflate (
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
int inflateWidth,
|
||||
int inflateHeight,
|
||||
int expectedX,
|
||||
int exptectedY,
|
||||
int expectedWidth,
|
||||
int expectedHeight
|
||||
)
|
||||
{
|
||||
var rect = new Rectangle (x, y, width, height);
|
||||
|
||||
if (rect.Width + inflateWidth < 0 || rect.Height + inflateHeight < 0)
|
||||
{
|
||||
Assert.Throws<ArgumentException> (() => rect.Inflate (inflateWidth, inflateHeight));
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.Inflate (inflateWidth, inflateHeight);
|
||||
}
|
||||
|
||||
Assert.Equal (expectedWidth, rect.Width);
|
||||
Assert.Equal (expectedHeight, rect.Height);
|
||||
Assert.Equal (expectedX, rect.X);
|
||||
Assert.Equal (exptectedY, rect.Y);
|
||||
|
||||
// Use the other overload (Size)
|
||||
rect = new Rectangle (x, y, width, height);
|
||||
|
||||
if (rect.Width + inflateWidth < 0 || rect.Height + inflateHeight < 0)
|
||||
{
|
||||
Assert.Throws<ArgumentException> (() => rect.Inflate (new Size (inflateWidth, inflateHeight)));
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.Inflate (new Size (inflateWidth, inflateHeight));
|
||||
}
|
||||
|
||||
Assert.Equal (expectedWidth, rect.Width);
|
||||
Assert.Equal (expectedHeight, rect.Height);
|
||||
Assert.Equal (expectedX, rect.X);
|
||||
Assert.Equal (exptectedY, rect.Y);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Negative_X_Y_Positions ()
|
||||
{
|
||||
var rect = new Rectangle (-10, -5, 100, 50);
|
||||
int yCount = 0, xCount = 0, yxCount = 0;
|
||||
|
||||
for (int line = rect.Y; line < rect.Y + rect.Height; line++)
|
||||
{
|
||||
yCount++;
|
||||
xCount = 0;
|
||||
|
||||
for (int col = rect.X; col < rect.X + rect.Width; col++)
|
||||
{
|
||||
xCount++;
|
||||
yxCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Equal (yCount, rect.Height);
|
||||
Assert.Equal (xCount, rect.Width);
|
||||
Assert.Equal (yxCount, rect.Height * rect.Width);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Positive_X_Y_Positions ()
|
||||
{
|
||||
var rect = new Rectangle (10, 5, 100, 50);
|
||||
int yCount = 0, xCount = 0, yxCount = 0;
|
||||
|
||||
for (int line = rect.Y; line < rect.Y + rect.Height; line++)
|
||||
{
|
||||
yCount++;
|
||||
xCount = 0;
|
||||
|
||||
for (int col = rect.X; col < rect.X + rect.Width; col++)
|
||||
{
|
||||
xCount++;
|
||||
yxCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Equal (yCount, rect.Height);
|
||||
Assert.Equal (xCount, rect.Width);
|
||||
Assert.Equal (yxCount, rect.Height * rect.Width);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rect_Contains ()
|
||||
{
|
||||
var rect = new Rectangle (0, 0, 3, 3);
|
||||
Assert.True (rect.Contains (new Point (1, 1)));
|
||||
Assert.True (rect.Contains (new Point (1, 2)));
|
||||
Assert.True (rect.Contains (new Point (2, 1)));
|
||||
Assert.True (rect.Contains (new Point (2, 2)));
|
||||
|
||||
Assert.False (rect.Contains (new Point (-1, 1)));
|
||||
Assert.False (rect.Contains (new Point (1, -1)));
|
||||
Assert.False (rect.Contains (new Point (3, 2)));
|
||||
Assert.False (rect.Contains (new Point (2, 3)));
|
||||
Assert.False (rect.Contains (new Point (3, 3)));
|
||||
|
||||
Assert.True (rect.Contains (new Rectangle (1, 1, 2, 2)));
|
||||
Assert.True (rect.Contains (new Rectangle (1, 2, 2, 1)));
|
||||
Assert.True (rect.Contains (new Rectangle (2, 1, 1, 2)));
|
||||
Assert.True (rect.Contains (new Rectangle (2, 2, 1, 1)));
|
||||
Assert.True (rect.Contains (new Rectangle (0, 0, 3, 3)));
|
||||
|
||||
Assert.False (rect.Contains (new Rectangle (-1, 1, 3, 3)));
|
||||
Assert.False (rect.Contains (new Rectangle (1, -1, 3, 3)));
|
||||
Assert.False (rect.Contains (new Rectangle (3, 2, 3, 3)));
|
||||
Assert.False (rect.Contains (new Rectangle (2, 3, 3, 3)));
|
||||
Assert.False (rect.Contains (new Rectangle (3, 3, 3, 3)));
|
||||
|
||||
Assert.True (rect.Contains (1, 1));
|
||||
Assert.True (rect.Contains (1, 2));
|
||||
Assert.True (rect.Contains (2, 1));
|
||||
Assert.True (rect.Contains (2, 2));
|
||||
|
||||
Assert.False (rect.Contains (-1, 1));
|
||||
Assert.False (rect.Contains (1, -1));
|
||||
Assert.False (rect.Contains (3, 2));
|
||||
Assert.False (rect.Contains (2, 3));
|
||||
Assert.False (rect.Contains (3, 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rect_Equals ()
|
||||
{
|
||||
var rect1 = new Rectangle ();
|
||||
var rect2 = new Rectangle ();
|
||||
Assert.Equal (rect1, rect2);
|
||||
|
||||
rect1 = new Rectangle (1, 2, 3, 4);
|
||||
rect2 = new Rectangle (1, 2, 3, 4);
|
||||
Assert.Equal (rect1, rect2);
|
||||
|
||||
rect1 = new Rectangle (1, 2, 3, 4);
|
||||
rect2 = new Rectangle (-1, 2, 3, 4);
|
||||
Assert.NotEqual (rect1, rect2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rect_New ()
|
||||
{
|
||||
var rect = new Rectangle ();
|
||||
Assert.True (rect.IsEmpty);
|
||||
|
||||
rect = new Rectangle (new Point (), new Size ());
|
||||
Assert.True (rect.IsEmpty);
|
||||
|
||||
rect = new Rectangle (1, 2, 3, 4);
|
||||
Assert.False (rect.IsEmpty);
|
||||
|
||||
rect = new Rectangle (-1, -2, 3, 4);
|
||||
Assert.False (rect.IsEmpty);
|
||||
|
||||
Action action = () => new Rectangle (1, 2, -3, 4);
|
||||
var ex = Assert.Throws<ArgumentException> (action);
|
||||
Assert.Equal ("Width must be greater or equal to 0.", ex.Message);
|
||||
|
||||
action = () => new Rectangle (1, 2, 3, -4);
|
||||
ex = Assert.Throws<ArgumentException> (action);
|
||||
Assert.Equal ("Height must be greater or equal to 0.", ex.Message);
|
||||
|
||||
action = () => new Rectangle (1, 2, -3, -4);
|
||||
ex = Assert.Throws<ArgumentException> (action);
|
||||
Assert.Equal ("Width must be greater or equal to 0.", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rect_SetsValue ()
|
||||
{
|
||||
var rect = new Rectangle { X = 0, Y = 0 };
|
||||
Assert.True (rect.IsEmpty);
|
||||
|
||||
rect = new Rectangle { X = -1, Y = -2 };
|
||||
Assert.False (rect.IsEmpty);
|
||||
|
||||
rect = new Rectangle { Width = 3, Height = 4 };
|
||||
Assert.False (rect.IsEmpty);
|
||||
|
||||
rect = new Rectangle { X = -1, Y = -2, Width = 3, Height = 4 };
|
||||
Assert.False (rect.IsEmpty);
|
||||
|
||||
Action action = () => { rect = new Rectangle { X = -1, Y = -2, Width = -3, Height = 4 }; };
|
||||
var ex = Assert.Throws<ArgumentException> (action);
|
||||
Assert.Equal ("Width must be greater or equal to 0.", ex.Message);
|
||||
|
||||
action = () => { rect = new Rectangle { X = -1, Y = -2, Width = 3, Height = -4 }; };
|
||||
ex = Assert.Throws<ArgumentException> (action);
|
||||
Assert.Equal ("Height must be greater or equal to 0.", ex.Message);
|
||||
|
||||
action = () => { rect = new Rectangle { X = -1, Y = -2, Width = -3, Height = -4 }; };
|
||||
ex = Assert.Throws<ArgumentException> (action);
|
||||
Assert.Equal ("Width must be greater or equal to 0.", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_EmptyRectangles ()
|
||||
{
|
||||
var r1 = new Rectangle (0, 0, 0, 0);
|
||||
var r2 = new Rectangle (1, 1, 0, 0);
|
||||
Rectangle result = Rectangle.Union (r1, r2);
|
||||
Assert.Equal (new Rectangle (0, 0, 1, 1), result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_NegativeCoords ()
|
||||
{
|
||||
// arrange
|
||||
var rect1 = new Rectangle (-2, -2, 4, 4);
|
||||
var rect2 = new Rectangle (-1, -1, 5, 5);
|
||||
|
||||
// act
|
||||
Rectangle result = Rectangle.Union (rect1, rect2);
|
||||
|
||||
// assert
|
||||
Assert.Equal (new Rectangle (-2, -2, 6, 6), result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_PositiveCoords ()
|
||||
{
|
||||
var r1 = new Rectangle (0, 0, 2, 2);
|
||||
var r2 = new Rectangle (1, 1, 2, 2);
|
||||
Rectangle result = Rectangle.Union (r1, r2);
|
||||
Assert.Equal (new Rectangle (0, 0, 3, 3), result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_RectangleAHasNegativeCoordinates_ReturnsCombinedRectangle ()
|
||||
{
|
||||
// arrange
|
||||
var rect1 = new Rectangle (-2, -2, 5, 5);
|
||||
var rect2 = new Rectangle (3, 3, 4, 4);
|
||||
|
||||
// act
|
||||
Rectangle result = Rectangle.Union (rect1, rect2);
|
||||
|
||||
// assert
|
||||
Assert.Equal (new Rectangle (-2, -2, 9, 9), result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_RectangleAIsLarger_ReturnsA ()
|
||||
{
|
||||
// arrange
|
||||
var rect1 = new Rectangle (1, 1, 6, 6);
|
||||
var rect2 = new Rectangle (2, 2, 3, 3);
|
||||
|
||||
// act
|
||||
Rectangle result = Rectangle.Union (rect1, rect2);
|
||||
|
||||
// assert
|
||||
Assert.Equal (new Rectangle (1, 1, 6, 6), result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_RectangleBIsLarger_ReturnsB ()
|
||||
{
|
||||
// arrange
|
||||
var rect1 = new Rectangle (1, 1, 3, 3);
|
||||
var rect2 = new Rectangle (2, 2, 6, 6);
|
||||
|
||||
// act
|
||||
Rectangle result = Rectangle.Union (rect1, rect2);
|
||||
|
||||
// assert
|
||||
Assert.Equal (new Rectangle (1, 1, 7, 7), result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_RectanglesDoNotOverlap_ReturnsCombinedRectangle ()
|
||||
{
|
||||
// arrange
|
||||
var rect1 = new Rectangle (1, 1, 3, 3);
|
||||
var rect2 = new Rectangle (5, 5, 3, 3);
|
||||
|
||||
// act
|
||||
Rectangle result = Rectangle.Union (rect1, rect2);
|
||||
|
||||
// assert
|
||||
Assert.Equal (new Rectangle (1, 1, 7, 7), result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_RectanglesOverlap_ReturnsCombinedRectangle ()
|
||||
{
|
||||
// arrange
|
||||
var rect1 = new Rectangle (1, 1, 3, 3);
|
||||
var rect2 = new Rectangle (2, 2, 3, 3);
|
||||
|
||||
// act
|
||||
Rectangle result = Rectangle.Union (rect1, rect2);
|
||||
|
||||
// assert
|
||||
Assert.Equal (new Rectangle (1, 1, 4, 4), result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_RectanglesTouchHorizontally_ReturnsCombinedRectangle ()
|
||||
{
|
||||
// arrange
|
||||
var rect1 = new Rectangle (1, 1, 3, 3);
|
||||
var rect2 = new Rectangle (4, 2, 3, 3);
|
||||
|
||||
// act
|
||||
Rectangle result = Rectangle.Union (rect1, rect2);
|
||||
|
||||
// assert
|
||||
Assert.Equal (new Rectangle (1, 1, 6, 4), result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_RectanglesTouchVertically_ReturnsCombinedRectangle ()
|
||||
{
|
||||
// arrange
|
||||
var rect1 = new Rectangle (1, 1, 3, 3);
|
||||
var rect2 = new Rectangle (2, 4, 3, 3);
|
||||
|
||||
// act
|
||||
Rectangle result = Rectangle.Union (rect1, rect2);
|
||||
|
||||
// assert
|
||||
Assert.Equal (new Rectangle (1, 1, 4, 6), result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_SameRectangle ()
|
||||
{
|
||||
var r1 = new Rectangle (0, 0, 2, 2);
|
||||
var r2 = new Rectangle (0, 0, 2, 2);
|
||||
Rectangle result = Rectangle.Union (r1, r2);
|
||||
Assert.Equal (new Rectangle (0, 0, 2, 2), result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[CombinatorialData]
|
||||
public void ToString_ReturnsExpectedString ([CombinatorialValues(-1,0,1)]int x, [CombinatorialValues(-1,0,1)]int y, [CombinatorialValues(1,10)]int width, [CombinatorialValues(1,10)]int height)
|
||||
{
|
||||
Rectangle r = new (x, y, width, height);
|
||||
string expectedString = $"{{X={r.X},Y={r.Y},Width={r.Width},Height={r.Height}}}";
|
||||
Assert.Equal (expectedString, r.ToString ());
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Using Include="System.Drawing.Rectangle" Alias="Rectangle" />
|
||||
<Using Include="Terminal.Gui" />
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -2692,12 +2692,12 @@ Y
|
||||
Assert.True (horizontalView.AutoSize);
|
||||
Assert.Equal (new Rectangle (0, 0, 12, 1), horizontalView.Frame);
|
||||
Assert.Equal (new Size (12, 1), horizontalView.GetSizeNeededForTextWithoutHotKey ());
|
||||
Assert.Equal (horizontalView.Frame.Size, horizontalView.GetSizeNeededForTextWithoutHotKey ());
|
||||
Assert.Equal ((Size)horizontalView.Frame.Size, horizontalView.GetSizeNeededForTextWithoutHotKey ());
|
||||
|
||||
Assert.True (verticalView.AutoSize);
|
||||
Assert.Equal (new Rectangle (0, 0, 2, 11), verticalView.Frame);
|
||||
Assert.Equal (new Size (2, 11), verticalView.GetSizeNeededForTextWithoutHotKey ());
|
||||
Assert.Equal (verticalView.Frame.Size, verticalView.GetSizeNeededForTextWithoutHotKey ());
|
||||
Assert.Equal ((Size)verticalView.Frame.Size, verticalView.GetSizeNeededForTextWithoutHotKey ());
|
||||
|
||||
text = "Say He_llo 你";
|
||||
horizontalView.Text = text;
|
||||
@@ -2706,12 +2706,12 @@ Y
|
||||
Assert.True (horizontalView.AutoSize);
|
||||
Assert.Equal (new Rectangle (0, 0, 12, 1), horizontalView.Frame);
|
||||
Assert.Equal (new Size (12, 1), horizontalView.GetSizeNeededForTextWithoutHotKey ());
|
||||
Assert.Equal (horizontalView.Frame.Size, horizontalView.GetSizeNeededForTextWithoutHotKey ());
|
||||
Assert.Equal ((Size)horizontalView.Frame.Size, horizontalView.GetSizeNeededForTextWithoutHotKey ());
|
||||
|
||||
Assert.True (verticalView.AutoSize);
|
||||
Assert.Equal (new Rectangle (0, 0, 2, 11), verticalView.Frame);
|
||||
Assert.Equal (new Size (2, 11), verticalView.GetSizeNeededForTextWithoutHotKey ());
|
||||
Assert.Equal (verticalView.Frame.Size, verticalView.GetSizeNeededForTextWithoutHotKey ());
|
||||
Assert.Equal ((Size)verticalView.Frame.Size, verticalView.GetSizeNeededForTextWithoutHotKey ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user