Ver Fonte

Merge pull request #3267 from dodexahedron/v2_3256_cleanup_2

Stage 3 of #3256 - Miscellaneous cleanup before more removals
Tig há 1 ano atrás
pai
commit
0e34edf977

+ 51 - 0
Terminal.Gui/Application.MainLoopSyncContext.cs

@@ -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);
+                }
+            }
+        }
+    }
+}

+ 131 - 185
Terminal.Gui/Application.cs

@@ -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);
+            return;
+        }
 
-                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);
+        // Work inside-out (Padding, Border, Margin)
+        // TODO: Debate whether inside-out or outside-in is the right strategy
+        if (AdornmentHandledMouseEvent (view.Padding, a))
+        {
+            return;
+        }
 
-                return true;
+        if (AdornmentHandledMouseEvent (view.Border, a))
+        {
+            if (view is not Toplevel)
+            {
+                return;
             }
 
-            return false;
-        }
+            // TODO: This is a temporary hack to work around the fact that 
+            // drag handling is handled in Toplevel (See Issue #2537)
 
-        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))
+            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;
             }
 
-            if (AdornmentHandledMouseEvent (view?.Border))
+            WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
+
+            if (view.OnMouseEvent (me))
             {
-                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)
+                // Should we bubble up the event, if it is not handled?
+                //return;
+            }
 
-                    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;
-                    }
+            BringOverlappedTopToFront ();
 
-                    if (!view.WantMousePositionReports && a.MouseEvent.Flags == MouseFlags.ReportMousePosition)
-                    {
-                        return;
-                    }
+            return;
+        }
 
-                    WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
+        if (AdornmentHandledMouseEvent (view?.Margin, a))
+        {
+            return;
+        }
 
-                    if (view.OnMouseEvent (me))
-                    {
-                        // Should we bubble up the event, if it is not handled?
-                        //return;
-                    }
+        Rectangle bounds = view.BoundsToScreen (view.Bounds);
 
-                    BringOverlappedTopToFront ();
-                }
+        if (bounds.Contains (a.MouseEvent.X, a.MouseEvent.Y))
+        {
+            Point boundsPoint = view.ScreenToBounds (a.MouseEvent.X, a.MouseEvent.Y);
 
-                return;
+            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 (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);
+                // Should we bubble up the event, if it is not handled?
+                //return;
+            }
 
-                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;
-                }
+            BringOverlappedTopToFront ();
+        }
 
-                if (!view.WantMousePositionReports && a.MouseEvent.Flags == MouseFlags.ReportMousePosition)
-                {
-                    return;
-                }
+        return;
 
-                WantContinuousButtonPressedView = view.WantContinuousButtonPressed ? view : null;
+        static bool AdornmentHandledMouseEvent (Adornment? frame, MouseEventEventArgs args)
+        {
+            if (frame?.Thickness.Contains (frame.FrameToScreen (), args.MouseEvent.X, args.MouseEvent.Y) is not true)
+            {
+                return false;
+            }
 
-                if (view.OnMouseEvent (me))
-                {
-                    // Should we bubble up the event, if it is not handled?
-                    //return;
-                }
+            Point boundsPoint = frame.ScreenToBounds (args.MouseEvent.X, args.MouseEvent.Y);
 
-                BringOverlappedTopToFront ();
-            }
+            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 - 0
Terminal.Gui/IterationEventArgs.cs

@@ -0,0 +1,5 @@
+namespace Terminal.Gui;
+
+/// <summary>Event arguments for the <see cref="Application.Iteration"/> event.</summary>
+public class IterationEventArgs : EventArgs
+{ }

+ 1 - 1
Terminal.Gui/Terminal.Gui.csproj

@@ -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 - 0
Terminal.Gui/Types/Point.TemporaryOperators.cs

@@ -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);
+}

+ 1 - 1
Terminal.Gui/Types/Point.cs

@@ -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]

+ 0 - 294
Terminal.Gui/Types/Rectangle.cs

@@ -1,294 +0,0 @@
-//
-// Derived from System.Drawing.Rectangle.cs
-//
-// Author:
-//   Mike Kestner ([email protected])
-//
-// 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 - 0
Terminal.Gui/Types/Size.TemporaryOperators.cs

@@ -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);
+}

+ 1 - 1
Terminal.Gui/Types/Size.cs

@@ -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;
 

+ 21 - 29
Terminal.Gui/View/Layout/ViewLayout.cs

@@ -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];
-
-                    if (v.Visible && v.Frame.Contains (rx, ry))
-                    {
-                        View deep = FindDeepestView (v, rx, ry, out resx, out resy);
+                    View? deep = FindDeepestView (v, rx, ry, out resultX, out resultY);
 
-                        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>

+ 3 - 1
Terminal.Gui/Views/ToplevelOverlapped.cs

@@ -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 ()

+ 1 - 0
Terminal.sln.DotSettings

@@ -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">(?&lt;=\W|^)(?&lt;TAG&gt;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>

+ 0 - 1
UICatalog/Scenarios/Animation.cs

@@ -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;
 

+ 0 - 1
UICatalog/Scenarios/Images.cs

@@ -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;
 

+ 3 - 0
UICatalog/UICatalog.csproj

@@ -37,4 +37,7 @@
   <ItemGroup>
     <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />
   </ItemGroup>
+  <ItemGroup>
+    <Using Include="System.Drawing.Rectangle" Alias="Rectangle" />
+  </ItemGroup>
 </Project>

+ 9 - 9
UnitTests/Dialogs/DialogTests.cs

@@ -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]

+ 7 - 7
UnitTests/Dialogs/MessageBoxTests.cs

@@ -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 (
                                                                                        @$"

+ 0 - 519
UnitTests/Types/RectangleTests.cs

@@ -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 ());
-    }
-}

+ 1 - 0
UnitTests/UnitTests.csproj

@@ -46,6 +46,7 @@
     </None>
   </ItemGroup>
   <ItemGroup>
+    <Using Include="System.Drawing.Rectangle" Alias="Rectangle" />
     <Using Include="Terminal.Gui" />
     <Using Include="Xunit" />
   </ItemGroup>

+ 4 - 4
UnitTests/View/Text/AutoSizeTrueTests.cs

@@ -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]