Browse Source

Merge pull request #3300 from tig/v2_3298_REDO-xxxToScreen-improvements

Fixes #3298 (redo).  `BoundsToScreen` to `FrameToScreen` improvements
Tig 1 year ago
parent
commit
e026520b6d

+ 1 - 1
Terminal.Gui/Application.cs

@@ -807,7 +807,7 @@ public static partial class Application
                 || state.Toplevel.SubViewNeedsDisplay
                 || state.Toplevel.LayoutNeeded))
         {
-            state.Toplevel.Clear (Driver.Bounds);
+            Driver.ClearContents ();
         }
 
         if (state.Toplevel.NeedsDisplay || state.Toplevel.SubViewNeedsDisplay || state.Toplevel.LayoutNeeded || OverlappedChildNeedsDisplay ())

+ 18 - 0
Terminal.Gui/Drawing/Thickness.cs

@@ -100,6 +100,24 @@ public class Thickness : IEquatable<Thickness>
         return outside.Contains (x, y) && !inside.Contains (x, y);
     }
 
+    /// <summary>
+    /// Adds the thickness widths of another <see cref="Thickness"/> to the current <see cref="Thickness"/>, returning a new <see cref="Thickness"/>.
+    /// </summary>
+    /// <param name="other"></param>
+    /// <returns></returns>
+    public Thickness Add (Thickness other)
+    {
+        return new Thickness (Left + other.Left, Top + other.Top, Right + other.Right, Bottom + other.Bottom);
+    }
+
+    /// <summary>
+    /// Adds the thickness widths of another <see cref="Thickness"/> to another <see cref="Thickness"/>.
+    /// </summary>
+    /// <param name="a"></param>
+    /// <param name="b"></param>
+    /// <returns></returns>
+    public static Thickness operator + (Thickness a, Thickness b) { return a.Add (b); }
+
     /// <summary>Draws the <see cref="Thickness"/> rectangle with an optional diagnostics label.</summary>
     /// <remarks>
     ///     If <see cref="ConsoleDriver.DiagnosticFlags"/> is set to

+ 11 - 27
Terminal.Gui/View/Adornment/Adornment.cs

@@ -30,11 +30,13 @@ public class Adornment : View
     /// <param name="parent"></param>
     public Adornment (View parent) { Parent = parent; }
 
-    /// <summary>Gets the rectangle that describes the inner area of the Adornment. The Location is always (0,0).</summary>
+    /// <summary>
+    ///     Gets the rectangle that describes the area of the Adornment. The Location is always (0,0).
+    ///     The size is the size of the Frame
+    /// </summary>
     public override Rectangle Bounds
     {
-        get => new Rectangle (Point.Empty, Thickness?.GetInside (new (Point.Empty, Frame.Size)).Size ?? Frame.Size);
-        // QUESTION: So why even have a setter then?
+        get => Frame with { Location = Point.Empty };
         set => throw new InvalidOperationException ("It makes no sense to set Bounds of a Thickness.");
     }
 
@@ -82,21 +84,6 @@ public class Adornment : View
         }
     }
 
-    /// <inheritdoc/>
-    public override void BoundsToScreen (int col, int row, out int rcol, out int rrow, bool clipped = true)
-    {
-        // Adornments are *Children* of a View, not SubViews. Thus View.BoundsToScreen will not work.
-        // To get the screen-relative coordinates of a Adornment, we need to know who
-        // the Parent is
-        Rectangle parentFrame = Parent?.Frame ?? Frame;
-        rrow = row + parentFrame.Y;
-        rcol = col + parentFrame.X;
-
-        // We now have rcol/rrow in coordinates relative to our View's SuperView. If our View's SuperView has
-        // a SuperView, keep going...
-        Parent?.SuperView?.BoundsToScreen (rcol, rrow, out rcol, out rrow, clipped);
-    }
-
     /// <inheritdoc/>
     public override Rectangle FrameToScreen ()
     {
@@ -106,12 +93,10 @@ public class Adornment : View
         }
 
         // Adornments are *Children* of a View, not SubViews. Thus View.FrameToScreen will not work.
-        // To get the screen-relative coordinates of a Adornment, we need to know who
-        // the Parent is
+        // To get the screen-relative coordinates of an Adornment, we need get the parent's Frame
+        // in screen coords, and add our Frame location to it.
         Rectangle parent = Parent.FrameToScreen ();
 
-        // We now have coordinates relative to our View. If our View's SuperView has
-        // a SuperView, keep going...
         return new (new (parent.X + Frame.X, parent.Y + Frame.Y), Frame.Size);
     }
 
@@ -127,13 +112,12 @@ public class Adornment : View
             return;
         }
 
-        Rectangle screenBounds = BoundsToScreen (Frame);
-
+        Rectangle screenBounds = BoundsToScreen (contentArea);
         Attribute normalAttr = GetNormalColor ();
+        Driver.SetAttribute (normalAttr);
 
         // This just draws/clears the thickness, not the insides.
-        Driver.SetAttribute (normalAttr);
-        Thickness.Draw (screenBounds, (string)(Data ?? string.Empty));
+        Thickness.Draw (screenBounds, ToString ());
 
         if (!string.IsNullOrEmpty (TextFormatter.Text))
         {
@@ -158,7 +142,7 @@ public class Adornment : View
     {
         ThicknessChanged?.Invoke (
                                   this,
-                                  new ThicknessEventArgs { Thickness = Thickness, PreviousThickness = previousThickness }
+                                  new() { Thickness = Thickness, PreviousThickness = previousThickness }
                                  );
     }
 

+ 38 - 129
Terminal.Gui/View/Adornment/Border.cs

@@ -99,100 +99,6 @@ public class Border : Adornment
         set => _lineStyle = value;
     }
 
-    /// <summary>Draws a frame in the current view, clipped by the boundary of this view</summary>
-    /// <param name="region">View-relative region for the frame to be drawn.</param>
-    /// <param name="clear">If set to <see langword="true"/> it clear the region.</param>
-    [Obsolete ("This method is obsolete in v2. Use use LineCanvas or Frame instead.", false)]
-    public void DrawFrame (Rectangle region, bool clear)
-    {
-        Rectangle savedClip = ClipToBounds ();
-        Rectangle screenBounds = BoundsToScreen (region);
-
-        if (clear)
-        {
-            Driver.FillRect (region);
-        }
-
-        var lc = new LineCanvas ();
-        bool drawTop = region.Width > 1 && region.Height > 1;
-        bool drawLeft = region.Width > 1 && region.Height > 1;
-        bool drawBottom = region.Width > 1 && region.Height > 1;
-        bool drawRight = region.Width > 1 && region.Height > 1;
-
-        if (drawTop)
-        {
-            lc.AddLine (screenBounds.Location, screenBounds.Width, Orientation.Horizontal, LineStyle);
-        }
-
-        if (drawLeft)
-        {
-            lc.AddLine (screenBounds.Location, screenBounds.Height, Orientation.Vertical, LineStyle);
-        }
-
-        if (drawBottom)
-        {
-            lc.AddLine (
-                        new Point (screenBounds.X, screenBounds.Y + screenBounds.Height - 1),
-                        screenBounds.Width,
-                        Orientation.Horizontal,
-                        LineStyle
-                       );
-        }
-
-        if (drawRight)
-        {
-            lc.AddLine (
-                        new Point (screenBounds.X + screenBounds.Width - 1, screenBounds.Y),
-                        screenBounds.Height,
-                        Orientation.Vertical,
-                        LineStyle
-                       );
-        }
-
-        foreach (KeyValuePair<Point, Rune> p in lc.GetMap ())
-        {
-            Driver.Move (p.Key.X, p.Key.Y);
-            Driver.AddRune (p.Value);
-        }
-
-        lc.Clear ();
-
-        // TODO: This should be moved to LineCanvas as a new BorderStyle.Ruler
-        if ((ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler)
-            == ConsoleDriver.DiagnosticFlags.FrameRuler)
-        {
-            // Top
-            var hruler = new Ruler { Length = screenBounds.Width, Orientation = Orientation.Horizontal };
-
-            if (drawTop)
-            {
-                hruler.Draw (new Point (screenBounds.X, screenBounds.Y));
-            }
-
-            //Left
-            var vruler = new Ruler { Length = screenBounds.Height - 2, Orientation = Orientation.Vertical };
-
-            if (drawLeft)
-            {
-                vruler.Draw (new Point (screenBounds.X, screenBounds.Y + 1), 1);
-            }
-
-            // Bottom
-            if (drawBottom)
-            {
-                hruler.Draw (new Point (screenBounds.X, screenBounds.Y + screenBounds.Height - 1));
-            }
-
-            // Right
-            if (drawRight)
-            {
-                vruler.Draw (new Point (screenBounds.X + screenBounds.Width - 1, screenBounds.Y + 1), 1);
-            }
-        }
-
-        Driver.Clip = savedClip;
-    }
-    
     /// <inheritdoc/>
     public override void OnDrawContent (Rectangle contentArea)
     {
@@ -204,7 +110,7 @@ public class Border : Adornment
         }
 
         //Driver.SetAttribute (Colors.ColorSchemes ["Error"].Normal);
-        Rectangle screenBounds = BoundsToScreen (Frame);
+        Rectangle screenBounds = BoundsToScreen (contentArea);
 
         //OnDrawSubviews (bounds); 
 
@@ -241,12 +147,13 @@ public class Border : Adornment
         int titleY = borderBounds.Y;
         var titleBarsLength = 0; // the little vertical thingies
 
-        int maxTitleWidth = Math.Max (0,
+        int maxTitleWidth = Math.Max (
+                                      0,
                                       Math.Min (
-                                          Parent.TitleTextFormatter.FormatAndGetSize ().Width,
-                                          Math.Min (screenBounds.Width - 4, borderBounds.Width - 4)
-                                          )
-                                      );
+                                                Parent.TitleTextFormatter.FormatAndGetSize ().Width,
+                                                Math.Min (screenBounds.Width - 4, borderBounds.Width - 4)
+                                               )
+                                     );
         Parent.TitleTextFormatter.Size = new (maxTitleWidth, 1);
 
         int sideLineLength = borderBounds.Height;
@@ -286,7 +193,8 @@ public class Border : Adornment
 
         if (canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && !string.IsNullOrEmpty (Parent?.Title))
         {
-            Parent.TitleTextFormatter.Draw (new (borderBounds.X + 2, titleY, maxTitleWidth, 1),
+            Parent.TitleTextFormatter.Draw (
+                                            new (borderBounds.X + 2, titleY, maxTitleWidth, 1),
                                             Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor (),
                                             Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetHotNormalColor ());
         }
@@ -319,7 +227,7 @@ public class Border : Adornment
                 {
                     // ╔╡╞╗ should be ╔══╗
                     lc.AddLine (
-                                new Point (borderBounds.Location.X, titleY),
+                                new (borderBounds.Location.X, titleY),
                                 borderBounds.Width,
                                 Orientation.Horizontal,
                                 LineStyle,
@@ -334,7 +242,7 @@ public class Border : Adornment
                     if (Thickness.Top == 2)
                     {
                         lc.AddLine (
-                                    new Point (borderBounds.X + 1, topTitleLineY),
+                                    new (borderBounds.X + 1, topTitleLineY),
                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
                                     Orientation.Horizontal,
                                     LineStyle,
@@ -348,7 +256,7 @@ public class Border : Adornment
                     if (borderBounds.Width >= 4 && Thickness.Top > 2)
                     {
                         lc.AddLine (
-                                    new Point (borderBounds.X + 1, topTitleLineY),
+                                    new (borderBounds.X + 1, topTitleLineY),
                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
                                     Orientation.Horizontal,
                                     LineStyle,
@@ -356,7 +264,7 @@ public class Border : Adornment
                                    );
 
                         lc.AddLine (
-                                    new Point (borderBounds.X + 1, topTitleLineY + 2),
+                                    new (borderBounds.X + 1, topTitleLineY + 2),
                                     Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
                                     Orientation.Horizontal,
                                     LineStyle,
@@ -367,7 +275,7 @@ public class Border : Adornment
                     // ╔╡Title╞═════╗
                     // Add a short horiz line for ╔╡
                     lc.AddLine (
-                                new Point (borderBounds.Location.X, titleY),
+                                new (borderBounds.Location.X, titleY),
                                 2,
                                 Orientation.Horizontal,
                                 LineStyle,
@@ -376,7 +284,7 @@ public class Border : Adornment
 
                     // Add a vert line for ╔╡
                     lc.AddLine (
-                                new Point (borderBounds.X + 1, topTitleLineY),
+                                new (borderBounds.X + 1, topTitleLineY),
                                 titleBarsLength,
                                 Orientation.Vertical,
                                 LineStyle.Single,
@@ -385,13 +293,13 @@ public class Border : Adornment
 
                     // Add a vert line for ╞
                     lc.AddLine (
-                                new Point (
-                                           borderBounds.X
-                                           + 1
-                                           + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2)
-                                           - 1,
-                                           topTitleLineY
-                                          ),
+                                new (
+                                     borderBounds.X
+                                     + 1
+                                     + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2)
+                                     - 1,
+                                     topTitleLineY
+                                    ),
                                 titleBarsLength,
                                 Orientation.Vertical,
                                 LineStyle.Single,
@@ -400,13 +308,13 @@ public class Border : Adornment
 
                     // Add the right hand line for ╞═════╗
                     lc.AddLine (
-                                new Point (
-                                           borderBounds.X
-                                           + 1
-                                           + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2)
-                                           - 1,
-                                           titleY
-                                          ),
+                                new (
+                                     borderBounds.X
+                                     + 1
+                                     + Math.Min (borderBounds.Width - 2, maxTitleWidth + 2)
+                                     - 1,
+                                     titleY
+                                    ),
                                 borderBounds.Width - Math.Min (borderBounds.Width - 2, maxTitleWidth + 2),
                                 Orientation.Horizontal,
                                 LineStyle,
@@ -418,7 +326,7 @@ public class Border : Adornment
             if (drawLeft)
             {
                 lc.AddLine (
-                            new Point (borderBounds.Location.X, titleY),
+                            new (borderBounds.Location.X, titleY),
                             sideLineLength,
                             Orientation.Vertical,
                             LineStyle,
@@ -429,7 +337,7 @@ public class Border : Adornment
             if (drawBottom)
             {
                 lc.AddLine (
-                            new Point (borderBounds.X, borderBounds.Y + borderBounds.Height - 1),
+                            new (borderBounds.X, borderBounds.Y + borderBounds.Height - 1),
                             borderBounds.Width,
                             Orientation.Horizontal,
                             LineStyle,
@@ -440,7 +348,7 @@ public class Border : Adornment
             if (drawRight)
             {
                 lc.AddLine (
-                            new Point (borderBounds.X + borderBounds.Width - 1, titleY),
+                            new (borderBounds.X + borderBounds.Width - 1, titleY),
                             sideLineLength,
                             Orientation.Vertical,
                             LineStyle,
@@ -459,13 +367,14 @@ public class Border : Adornment
 
                 if (drawTop)
                 {
-                    hruler.Draw (new Point (screenBounds.X, screenBounds.Y));
+                    hruler.Draw (new (screenBounds.X, screenBounds.Y));
                 }
 
                 // Redraw title 
                 if (drawTop && maxTitleWidth > 0 && !string.IsNullOrEmpty (Parent?.Title))
                 {
-                    Parent.TitleTextFormatter.Draw (new (borderBounds.X + 2, titleY, maxTitleWidth, 1),
+                    Parent.TitleTextFormatter.Draw (
+                                                    new (borderBounds.X + 2, titleY, maxTitleWidth, 1),
                                                     Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor (),
                                                     Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor ());
                 }
@@ -475,19 +384,19 @@ public class Border : Adornment
 
                 if (drawLeft)
                 {
-                    vruler.Draw (new Point (screenBounds.X, screenBounds.Y + 1), 1);
+                    vruler.Draw (new (screenBounds.X, screenBounds.Y + 1), 1);
                 }
 
                 // Bottom
                 if (drawBottom)
                 {
-                    hruler.Draw (new Point (screenBounds.X, screenBounds.Y + screenBounds.Height - 1));
+                    hruler.Draw (new (screenBounds.X, screenBounds.Y + screenBounds.Height - 1));
                 }
 
                 // Right
                 if (drawRight)
                 {
-                    vruler.Draw (new Point (screenBounds.X + screenBounds.Width - 1, screenBounds.Y + 1), 1);
+                    vruler.Draw (new (screenBounds.X + screenBounds.Width - 1, screenBounds.Y + 1), 1);
                 }
             }
         }

+ 42 - 95
Terminal.Gui/View/Layout/ViewLayout.cs

@@ -86,8 +86,8 @@ public partial class View
     }
 
     /// <summary>
-    ///     The adornment (specified as a <see cref="Thickness"/>) inside of the view that offsets the
-    ///     <see cref="Bounds"/> from the <see cref="Margin"/>. The Border provides the space for a visual border (drawn using
+    ///     The <see cref="Adornment"/> that offsets the <see cref="Bounds"/> from the <see cref="Margin"/>.
+    ///     The Border provides the space for a visual border (drawn using
     ///     line-drawing glyphs) and the Title. The Border expands inward; in other words if `Border.Thickness.Top == 2` the
     ///     border and title will take up the first row and the second row will be filled with spaces.
     /// </summary>
@@ -125,11 +125,11 @@ public partial class View
         {
             if (value != LineStyle.None)
             {
-                Border.Thickness = new Thickness (1);
+                Border.Thickness = new (1);
             }
             else
             {
-                Border.Thickness = new Thickness (0);
+                Border.Thickness = new (0);
             }
 
             Border.LineStyle = value;
@@ -176,24 +176,14 @@ public partial class View
             }
 #endif // DEBUG
 
-            // BUGBUG: I think there's a bug here. This should be && not ||
             if (Margin is null || Border is null || Padding is null)
             {
                 return Rectangle.Empty with { Size = Frame.Size };
             }
 
-            int width = Math.Max (
-                                  0,
-                                  Frame.Size.Width
-                                  - Margin.Thickness.Horizontal
-                                  - Border.Thickness.Horizontal
-                                  - Padding.Thickness.Horizontal
-                                 );
-
-            int height = Math.Max (
-                                   0,
-                                   Frame.Size.Height - Margin.Thickness.Vertical - Border.Thickness.Vertical - Padding.Thickness.Vertical
-                                  );
+            Thickness totalThickness = GetAdornmentsThickness ();
+            int width = Math.Max (0, Frame.Size.Width - totalThickness.Horizontal);
+            int height = Math.Max (0, Frame.Size.Height - totalThickness.Vertical);
 
             return Rectangle.Empty with { Size = new (width, height) };
         }
@@ -209,19 +199,11 @@ public partial class View
                                 );
             }
 #endif // DEBUG
+            Thickness totalThickness = GetAdornmentsThickness ();
+
             Frame = Frame with
             {
-                Size =
-                new (
-                     value.Size.Width
-                     + Margin.Thickness.Horizontal
-                     + Border.Thickness.Horizontal
-                     + Padding.Thickness.Horizontal,
-                     value.Size.Height
-                     + Margin.Thickness.Vertical
-                     + Border.Thickness.Vertical
-                     + Padding.Thickness.Vertical
-                    )
+                Size = new (value.Size.Width + totalThickness.Horizontal, value.Size.Height + totalThickness.Vertical)
             };
         }
     }
@@ -240,7 +222,8 @@ public partial class View
     ///     <para>This causes <see cref="LayoutStyle"/> to be <see cref="LayoutStyle.Absolute"/>.</para>
     ///     <para>
     ///         Altering the Frame will eventually (when the view hierarchy is next laid out via  see
-    ///         cref="LayoutSubviews"/>) cause <see cref="LayoutSubview(View, Rectangle)"/> and <see cref="OnDrawContent(Rectangle)"/>
+    ///         cref="LayoutSubviews"/>) cause <see cref="LayoutSubview(View, Rectangle)"/> and
+    ///         <see cref="OnDrawContent(Rectangle)"/>
     ///         methods to be called.
     ///     </para>
     /// </remarks>
@@ -352,7 +335,7 @@ public partial class View
     }
 
     /// <summary>
-    ///     The frame (specified as a <see cref="Thickness"/>) that separates a View from other SubViews of the same
+    ///     The <see cref="Adornment"/> that enables separation of a View from other SubViews of the same
     ///     SuperView. The margin offsets the <see cref="Bounds"/> from the <see cref="Frame"/>.
     /// </summary>
     /// <remarks>
@@ -369,7 +352,7 @@ public partial class View
     public Margin Margin { get; private set; }
 
     /// <summary>
-    ///     The frame (specified as a <see cref="Thickness"/>) inside of the view that offsets the <see cref="Bounds"/>
+    ///     The <see cref="Adornment"/> inside of the view that offsets the <see cref="Bounds"/>
     ///     from the <see cref="Border"/>.
     /// </summary>
     /// <remarks>
@@ -440,7 +423,8 @@ public partial class View
     /// <remarks>
     ///     <para>
     ///         If set to a relative value (e.g. <see cref="Pos.Center"/>) the value is indeterminate until the view has been
-    ///         initialized ( <see cref="IsInitialized"/> is true) and <see cref="SetRelativeLayout(Rectangle)"/> has been called.
+    ///         initialized ( <see cref="IsInitialized"/> is true) and <see cref="SetRelativeLayout(Rectangle)"/> has been
+    ///         called.
     ///     </para>
     ///     <para>
     ///         Changing this property will eventually (when the view is next drawn) cause the
@@ -467,7 +451,8 @@ public partial class View
     /// <remarks>
     ///     <para>
     ///         If set to a relative value (e.g. <see cref="Pos.Center"/>) the value is indeterminate until the view has been
-    ///         initialized ( <see cref="IsInitialized"/> is true) and <see cref="SetRelativeLayout(Rectangle)"/> has been called.
+    ///         initialized ( <see cref="IsInitialized"/> is true) and <see cref="SetRelativeLayout(Rectangle)"/> has been
+    ///         called.
     ///     </para>
     ///     <para>
     ///         Changing this property will eventually (when the view is next drawn) cause the
@@ -498,51 +483,16 @@ public partial class View
     /// </summary>
     public event EventHandler Initialized;
 
-    /// <summary>Converts a <see cref="Bounds"/>-relative region to a screen-relative region.</summary>
-    public Rectangle BoundsToScreen (Rectangle region)
+    /// <summary>Converts a <see cref="Bounds"/>-relative rectangle to a screen-relative rectangle.</summary>
+    public Rectangle BoundsToScreen (in Rectangle bounds)
     {
-        BoundsToScreen (region.X, region.Y, out int screenX, out int screenY, false);
-
-        return region with { X = screenX, Y = screenY };
-    }
-
-    /// <summary>
-    ///     Converts a <see cref="Bounds"/>-relative coordinate to a screen-relative coordinate. The output is optionally
-    ///     clamped to the screen dimensions.
-    /// </summary>
-    /// <param name="x"><see cref="Bounds"/>-relative column.</param>
-    /// <param name="y"><see cref="Bounds"/>-relative row.</param>
-    /// <param name="rx">Absolute column; screen-relative.</param>
-    /// <param name="ry">Absolute row; screen-relative.</param>
-    /// <param name="clamped">
-    ///     If <see langword="true"/>, <paramref name="rx"/> and <paramref name="ry"/> will be clamped to the
-    ///     screen dimensions (will never be negative and will always be less than <see cref="ConsoleDriver.Cols"/> and
-    ///     <see cref="ConsoleDriver.Rows"/>, respectively.
-    /// </param>
-    public virtual void BoundsToScreen (int x, int y, out int rx, out int ry, bool clamped = true)
-    {
-        // PERF: Use Point.Offset
-        // Already dealing with Point here.
+        // Translate bounds to Frame (our SuperView's Bounds-relative coordinates)
         Point boundsOffset = GetBoundsOffset ();
-        rx = x + Frame.X + boundsOffset.X;
-        ry = y + Frame.Y + boundsOffset.Y;
 
-        View super = SuperView;
-
-        while (super is { })
-        {
-            boundsOffset = super.GetBoundsOffset ();
-            rx += super.Frame.X + boundsOffset.X;
-            ry += super.Frame.Y + boundsOffset.Y;
-            super = super.SuperView;
-        }
+        Rectangle screen = FrameToScreen ();
+        screen.Offset (boundsOffset.X + bounds.X, boundsOffset.Y + bounds.Y);
 
-        // The following ensures that the cursor is always in the screen boundaries.
-        if (clamped)
-        {
-            ry = Math.Min (ry, Driver.Rows - 1);
-            rx = Math.Min (rx, Driver.Cols - 1);
-        }
+        return new (screen.Location, bounds.Size);
     }
 
 #nullable enable
@@ -577,15 +527,16 @@ public partial class View
             {
                 return start.Margin;
             }
+
             if (start.Border.Thickness.Contains (start.Border.Frame, x, y))
             {
                 return start.Border;
             }
+
             if (start.Padding.Thickness.Contains (start.Padding.Frame, x, y))
             {
                 return start.Padding;
             }
-
         }
 
         if (start.InternalSubviews is { Count: > 0 })
@@ -601,10 +552,12 @@ public partial class View
                 if (v.Visible && v.Frame.Contains (rx, ry))
                 {
                     View? deep = FindDeepestView (v, rx, ry, findAdornments);
+
                     return deep ?? v;
                 }
             }
         }
+
         return start;
     }
 #nullable restore
@@ -631,26 +584,20 @@ public partial class View
     ///     <para>Gets the thickness describing the sum of the Adornments' thicknesses.</para>
     /// </summary>
     /// <returns>A thickness that describes the sum of the Adornments' thicknesses.</returns>
-    public Thickness GetAdornmentsThickness ()
-    {
-        int left = Margin.Thickness.Left + Border.Thickness.Left + Padding.Thickness.Left;
-        int top = Margin.Thickness.Top + Border.Thickness.Top + Padding.Thickness.Top;
-        int right = Margin.Thickness.Right + Border.Thickness.Right + Padding.Thickness.Right;
-        int bottom = Margin.Thickness.Bottom + Border.Thickness.Bottom + Padding.Thickness.Bottom;
-
-        return new Thickness (left, top, right, bottom);
-    }
+    public Thickness GetAdornmentsThickness () { return Margin.Thickness + Border.Thickness + Padding.Thickness; }
 
     /// <summary>
     ///     Helper to get the X and Y offset of the Bounds from the Frame. This is the sum of the Left and Top properties
     ///     of <see cref="Margin"/>, <see cref="Border"/> and <see cref="Padding"/>.
     /// </summary>
-    public Point GetBoundsOffset ()
+    public virtual Point GetBoundsOffset ()
     {
-        return new Point (
-                          Padding?.Thickness.GetInside (Padding.Frame).X ?? 0,
-                          Padding?.Thickness.GetInside (Padding.Frame).Y ?? 0
-                         );
+        if (Padding is null)
+        {
+            return Point.Empty;
+        }
+
+        return Padding.Thickness.GetInside (Padding.Frame).Location;
     }
 
     /// <summary>Fired after the View's <see cref="LayoutSubviews"/> method has completed.</summary>
@@ -695,7 +642,7 @@ public partial class View
         LayoutAdornments ();
 
         Rectangle oldBounds = Bounds;
-        OnLayoutStarted (new LayoutEventArgs { OldBounds = oldBounds });
+        OnLayoutStarted (new() { OldBounds = oldBounds });
 
         SetTextFormatterSize ();
 
@@ -722,7 +669,7 @@ public partial class View
 
         LayoutNeeded = false;
 
-        OnLayoutComplete (new LayoutEventArgs { OldBounds = oldBounds });
+        OnLayoutComplete (new() { OldBounds = oldBounds });
     }
 
     /// <summary>Converts a screen-relative coordinate to a bounds-relative coordinate.</summary>
@@ -734,7 +681,7 @@ public partial class View
         Point screen = ScreenToFrame (x, y);
         Point boundsOffset = GetBoundsOffset ();
 
-        return new Point (screen.X - boundsOffset.X, screen.Y - boundsOffset.Y);
+        return new (screen.X - boundsOffset.X, screen.Y - boundsOffset.Y);
     }
 
     /// <summary>
@@ -752,7 +699,7 @@ public partial class View
         if (SuperView is { })
         {
             Point superFrame = SuperView.ScreenToFrame (x - superViewBoundsOffset.X, y - superViewBoundsOffset.Y);
-            ret = new Point (superFrame.X - Frame.X, superFrame.Y - Frame.Y);
+            ret = new (superFrame.X - Frame.X, superFrame.Y - Frame.Y);
         }
 
         return ret;
@@ -929,8 +876,8 @@ public partial class View
         // First try SuperView.Bounds, then Application.Top, then Driver.Bounds.
         // Finally, if none of those are valid, use int.MaxValue (for Unit tests).
         Rectangle relativeBounds = SuperView is { IsInitialized: true } ? SuperView.Bounds :
-                              Application.Top is { } && Application.Top.IsInitialized ? Application.Top.Bounds :
-                              Application.Driver?.Bounds ?? new Rectangle (0, 0, int.MaxValue, int.MaxValue);
+                                   Application.Top is { } && Application.Top.IsInitialized ? Application.Top.Bounds :
+                                   Application.Driver?.Bounds ?? new Rectangle (0, 0, int.MaxValue, int.MaxValue);
         SetRelativeLayout (relativeBounds);
 
         // TODO: Determine what, if any of the below is actually needed here.

+ 17 - 25
Terminal.Gui/View/ViewDrawing.cs

@@ -80,25 +80,14 @@ public partial class View
         Driver.AddRune (ch);
     }
 
-    /// <summary>Clears the <see cref="Bounds"/> with the normal background color.</summary>
-    /// <remarks>
-    ///     <para>This clears the Bounds used by this view.</para>
-    /// </remarks>
-    public void Clear ()
-    {
-        if (IsInitialized)
-        {
-            Clear (BoundsToScreen (Bounds));
-        }
-    }
+    /// <summary>Clears <see cref="Bounds"/> with the normal background.</summary>
+    /// <remarks></remarks>
+    public void Clear () { Clear (Bounds); }
 
-    // BUGBUG: This version of the Clear API should be removed. We should have a tenet that says 
-    // "View APIs only deal with View-relative coords". This is only used by ComboBox which can
-    // be refactored to use the View-relative version.
-    /// <summary>Clears the specified screen-relative rectangle with the normal background.</summary>
+    /// <summary>Clears the specified <see cref="Bounds"/>-relative rectangle with the normal background.</summary>
     /// <remarks></remarks>
-    /// <param name="regionScreen">The screen-relative rectangle to clear.</param>
-    public void Clear (Rectangle regionScreen)
+    /// <param name="contentArea">The Bounds-relative rectangle to clear.</param>
+    public void Clear (Rectangle contentArea)
     {
         if (Driver is null)
         {
@@ -106,7 +95,10 @@ public partial class View
         }
 
         Attribute prev = Driver.SetAttribute (GetNormalColor ());
-        Driver.FillRect (regionScreen);
+
+        // Clamp the region to the bounds of the view
+        contentArea = Rectangle.Intersect (contentArea, Bounds);
+        Driver.FillRect (BoundsToScreen (contentArea));
         Driver.SetAttribute (prev);
     }
 
@@ -281,7 +273,7 @@ public partial class View
 
         if (ColorScheme is null)
         {
-            cs = new ColorScheme ();
+            cs = new ();
         }
 
         return Enabled ? cs.Focus : cs.Disabled;
@@ -299,7 +291,7 @@ public partial class View
 
         if (ColorScheme is null)
         {
-            cs = new ColorScheme ();
+            cs = new ();
         }
 
         return Enabled ? cs.HotNormal : cs.Disabled;
@@ -317,7 +309,7 @@ public partial class View
 
         if (ColorScheme is null)
         {
-            cs = new ColorScheme ();
+            cs = new ();
         }
 
         return Enabled ? cs.Normal : cs.Disabled;
@@ -334,8 +326,8 @@ public partial class View
             return;
         }
 
-        BoundsToScreen (col, row, out int rCol, out int rRow, false);
-        Driver?.Move (rCol, rRow);
+        Rectangle screen = BoundsToScreen (new (col, row, 0, 0));
+        Driver?.Move (screen.X, screen.Y);
     }
 
     // TODO: Make this cancelable
@@ -373,7 +365,7 @@ public partial class View
         {
             if (SuperView is { })
             {
-                Clear (BoundsToScreen (contentArea));
+                Clear (contentArea);
             }
 
             if (!string.IsNullOrEmpty (TextFormatter.Text))
@@ -434,7 +426,7 @@ public partial class View
     ///     This method will be called after any subviews removed with <see cref="Remove(View)"/> have been completed
     ///     drawing.
     /// </remarks>
-    public virtual void OnDrawContentComplete (Rectangle contentArea) { DrawContentComplete?.Invoke (this, new DrawEventArgs (contentArea)); }
+    public virtual void OnDrawContentComplete (Rectangle contentArea) { DrawContentComplete?.Invoke (this, new (contentArea)); }
 
     // TODO: Make this cancelable
     /// <summary>

+ 3 - 3
Terminal.Gui/Views/ComboBox.cs

@@ -490,11 +490,11 @@ public class ComboBox : View
             OnOpenSelectedItem ();
         }
 
-        Rectangle rect = _listview.BoundsToScreen (_listview.IsInitialized ? _listview.Bounds : Rectangle.Empty);
         Reset (true);
-        _listview.Clear (rect);
+        _listview.Clear (_listview.IsInitialized ? _listview.Bounds : Rectangle.Empty);
         _listview.TabStop = false;
         SuperView?.SendSubviewToBack (this);
+        Rectangle rect = _listview.BoundsToScreen (_listview.IsInitialized ? _listview.Bounds : Rectangle.Empty);
         SuperView?.SetNeedsDisplay (rect);
         OnCollapsed ();
     }
@@ -761,7 +761,7 @@ public class ComboBox : View
     private void ShowList ()
     {
         _listview.SetSource (_searchset);
-        _listview.Clear (); // Ensure list shrinks in Dialog as you type
+        _listview.Clear (Bounds); // Ensure list shrinks in Dialog as you type
         _listview.Height = CalculatetHeight ();
         SuperView?.BringSubviewToFront (this);
     }

+ 2 - 4
Terminal.Gui/Views/Menu/ContextMenu.cs

@@ -147,8 +147,7 @@ public sealed class ContextMenu : IDisposable
 
         if (Host is { })
         {
-            Host.BoundsToScreen (frame.X, frame.Y, out int x, out int y);
-            var pos = new Point (x, y);
+            Point pos = Host.BoundsToScreen (frame).Location;
             pos.Y += Host.Frame.Height - 1;
 
             if (position != pos)
@@ -185,8 +184,7 @@ public sealed class ContextMenu : IDisposable
                 }
                 else
                 {
-                    Host.BoundsToScreen (frame.X, frame.Y, out int x, out int y);
-                    var pos = new Point (x, y);
+                    Point pos = Host.BoundsToScreen (frame).Location;
                     position.Y = pos.Y - rect.Height - 1;
                 }
             }

+ 7 - 8
Terminal.Gui/Views/Menu/Menu.cs

@@ -884,11 +884,10 @@ internal sealed class Menu : View
                 textToDraw = item.Title;
             }
 
-            BoundsToScreen (0, i, out int vtsCol, out int vtsRow, false);
-
-            if (vtsCol < Driver.Cols)
+            Rectangle screen = BoundsToScreen (new (new (0 , i), Size.Empty));
+            if (screen.X < Driver.Cols)
             {
-                Driver.Move (vtsCol + 1, vtsRow);
+                Driver.Move (screen.X + 1, screen.Y);
 
                 if (!item.IsEnabled ())
                 {
@@ -923,17 +922,17 @@ internal sealed class Menu : View
                             ? item.Help.GetColumns ()
                             : item.Help.GetColumns () + item.ShortcutTag.GetColumns () + 2;
                 int col = Frame.Width - l - 3;
-                BoundsToScreen (col, i, out vtsCol, out vtsRow, false);
+                screen = BoundsToScreen (new (new (col, i), Size.Empty));
 
-                if (vtsCol < Driver.Cols)
+                if (screen.X < Driver.Cols)
                 {
-                    Driver.Move (vtsCol, vtsRow);
+                    Driver.Move (screen.X, screen.Y);
                     Driver.AddStr (item.Help);
 
                     // The shortcut tag string
                     if (!string.IsNullOrEmpty (item.ShortcutTag))
                     {
-                        Driver.Move (vtsCol + l - item.ShortcutTag.GetColumns (), vtsRow);
+                        Driver.Move (screen.X + l - item.ShortcutTag.GetColumns (), screen.Y);
                         Driver.AddStr (item.ShortcutTag);
                     }
                 }

+ 4 - 4
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -1320,8 +1320,8 @@ public class MenuBar : View
 
         if (mi.IsTopLevel)
         {
-            BoundsToScreen (i, 0, out int rx, out int ry);
-            var menu = new Menu { Host = this, X = rx, Y = ry, BarItems = mi };
+            Rectangle screen = BoundsToScreen (new (new (0, i), Size.Empty));
+            var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = mi };
             menu.Run (mi.Action);
             menu.Dispose ();
         }
@@ -1682,8 +1682,8 @@ public class MenuBar : View
                     {
                         if (Menus [i].IsTopLevel)
                         {
-                            BoundsToScreen (i, 0, out int rx, out int ry);
-                            var menu = new Menu { Host = this, X = rx, Y = ry, BarItems = Menus [i] };
+                            Rectangle screen = BoundsToScreen (new (new (0, i), Size.Empty));
+                            var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = Menus [i] };
                             menu.Run (Menus [i].Action);
                             menu.Dispose ();
                         }

+ 1 - 1
Terminal.Gui/Views/ScrollView.cs

@@ -342,7 +342,7 @@ public class ScrollView : View
         Rectangle savedClip = ClipToBounds ();
 
         // TODO: It's bad practice for views to always clear a view. It negates clipping.
-        Clear ();
+        Clear (contentArea);
 
         if (!string.IsNullOrEmpty (_contentView.Text) || _contentView.Subviews.Count > 0)
         {

+ 1 - 1
Terminal.Gui/Views/Slider.cs

@@ -1073,7 +1073,7 @@ public class Slider<T> : View
     private void DrawSlider ()
     {
         // TODO: be more surgical on clear
-        Clear ();
+        Clear (Bounds);
 
         // Attributes
 

+ 1 - 1
Terminal.Gui/Views/TabView.cs

@@ -662,7 +662,7 @@ public class TabView : View
             _host._tabLocations = _host.CalculateViewport (Bounds).ToArray ();
 
             // clear any old text
-            Clear ();
+            Clear (contentArea);
 
             RenderTabLine ();
 

+ 5 - 7
Terminal.Gui/Views/TileView.cs

@@ -182,7 +182,7 @@ public class TileView : View
     public override void OnDrawContent (Rectangle contentArea)
     {
         Driver.SetAttribute (ColorScheme.Normal);
-        Clear ();
+        Clear (contentArea);
 
         base.OnDrawContent (contentArea);
 
@@ -217,8 +217,8 @@ public class TileView : View
             {
                 bool isRoot = _splitterLines.Contains (line);
 
-                line.BoundsToScreen (0, 0, out int x1, out int y1);
-                Point origin = ScreenToFrame (x1, y1);
+                Rectangle screen = line.BoundsToScreen (Rectangle.Empty);
+                Point origin = ScreenToFrame (screen.X, screen.Y);
                 int length = line.Orientation == Orientation.Horizontal ? line.Frame.Width : line.Frame.Height;
 
                 if (!isRoot)
@@ -837,10 +837,8 @@ public class TileView : View
         /// </summary>
         public Point GetLocalCoordinateForTitle (TileView intoCoordinateSpace)
         {
-            Tile.ContentView.BoundsToScreen (0, 0, out int screenCol, out int screenRow);
-            screenRow--;
-
-            return intoCoordinateSpace.ScreenToFrame (screenCol, screenRow);
+            Rectangle screen = Tile.ContentView.BoundsToScreen (Rectangle.Empty);
+            return intoCoordinateSpace.ScreenToFrame (screen.X, screen.Y - 1);
         }
 
         internal string GetTrimmedTitle ()

+ 2 - 0
Terminal.Gui/Views/Toplevel.cs

@@ -1,3 +1,5 @@
+using System.Net.Mime;
+
 namespace Terminal.Gui;
 
 /// <summary>

+ 2 - 2
UICatalog/Scenarios/Notepad.cs

@@ -350,9 +350,9 @@ public class Notepad : Scenario
                                     );
         }
 
-        ((View)sender).BoundsToScreen (e.MouseEvent.X, e.MouseEvent.Y, out int screenX, out int screenY);
+        Rectangle screen = ((View)sender).BoundsToScreen (new (e.MouseEvent.X, e.MouseEvent.Y, 0, 0));
 
-        var contextMenu = new ContextMenu { Position = new Point (screenX, screenY), MenuItems = items };
+        var contextMenu = new ContextMenu { Position = screen.Location, MenuItems = items };
 
         contextMenu.Show ();
         e.MouseEvent.Handled = true;

+ 1 - 1
UICatalog/Scenarios/Snake.cs

@@ -314,7 +314,7 @@ public class Snake : Scenario
             base.OnDrawContent (contentArea);
 
             Driver.SetAttribute (white);
-            Clear ();
+            Clear (contentArea);
 
             var canvas = new LineCanvas ();
 

+ 1 - 1
UnitTests/Drawing/LineCanvasTests.cs

@@ -1312,7 +1312,7 @@ public class LineCanvasTests
 
         v.DrawContentComplete += (s, e) =>
                                  {
-                                     v.Clear ();
+                                     v.Clear (v.Bounds);
 
                                      foreach (KeyValuePair<Point, Rune> p in canvasCopy.GetMap ())
                                      {

+ 107 - 0
UnitTests/Drawing/ThicknessTests.cs

@@ -340,6 +340,22 @@ public class ThicknessTests
         Assert.Equal (t.GetHashCode (), t.GetHashCode ());
     }
 
+    // Test Thickness.GetInside(Rectangle)
+    [Theory]
+    [InlineData (0, 0, 10, 10, 1, 1, 8, 8)]
+    [InlineData (1, 0, 10, 10, 2, 1, 8, 8)]
+    [InlineData (0, 1, 10, 10, 1, 2, 8, 8)]
+    public void GetInside_Uniform (int x, int y, int width, int height, int expectedX, int expectedY, int expectedWidth, int expectedHeight)
+    {
+        var t = new Thickness (1, 1, 1, 1); // Uniform thickness for simplicity
+        var r = new Rectangle (x, y, width, height);
+        Rectangle inside = t.GetInside (r);
+        Assert.Equal (expectedX, inside.X);
+        Assert.Equal (expectedY, inside.Y);
+        Assert.Equal (expectedWidth, inside.Width);
+        Assert.Equal (expectedHeight, inside.Height);
+    }
+
     [Fact]
     public void GetInsideTests_Mixed_Pos_Neg_Thickness_Non_Empty_Size ()
     {
@@ -769,4 +785,95 @@ public class ThicknessTests
         Assert.Equal (0, t.Bottom);
         Assert.Equal (0, t.Horizontal);
     }
+
+    // Test Thickness.Add
+    [Theory]
+    [InlineData (
+                    1,
+                    2,
+                    3,
+                    4,
+                    1,
+                    2,
+                    3,
+                    4,
+                    2,
+                    4,
+                    6,
+                    8)]
+    [InlineData (
+                    1,
+                    2,
+                    3,
+                    4,
+                    0,
+                    0,
+                    0,
+                    0,
+                    1,
+                    2,
+                    3,
+                    4)]
+    [InlineData (
+                    1,
+                    2,
+                    3,
+                    4,
+                    -1,
+                    -2,
+                    -3,
+                    -4,
+                    0,
+                    0,
+                    0,
+                    0)]
+    [InlineData (
+                    1,
+                    2,
+                    3,
+                    4,
+                    1,
+                    1,
+                    1,
+                    1,
+                    2,
+                    3,
+                    4,
+                    5)]
+    [InlineData (
+                    1,
+                    2,
+                    3,
+                    4,
+                    1,
+                    1,
+                    1,
+                    1,
+                    2,
+                    3,
+                    4,
+                    5)]
+    public void AddTest (
+        int left,
+        int top,
+        int right,
+        int bottom,
+        int left2,
+        int top2,
+        int right2,
+        int bottom2,
+        int expectedLeft,
+        int expectedTop,
+        int expectedRight,
+        int expectedBottom
+    )
+    {
+        var t = new Thickness (left, top, right, bottom);
+        var t2 = new Thickness (left2, top2, right2, bottom2);
+        var result = t.Add (t2);
+        Assert.Equal (expectedLeft, result.Left);
+        Assert.Equal (expectedTop, result.Top);
+        Assert.Equal (expectedRight, result.Right);
+        Assert.Equal (expectedBottom, result.Bottom);
+    }
 }

+ 1 - 1
UnitTests/UICatalog/ScenarioTests.cs

@@ -254,7 +254,7 @@ public class ScenarioTests
                                                       _hostPane.Remove (_curView);
                                                       _curView.Dispose ();
                                                       _curView = null;
-                                                      _hostPane.Clear ();
+                                                      _hostPane.Clear (_hostPane.Bounds);
                                                   }
 
                                                   _curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);

+ 61 - 16
UnitTests/View/Adornment/AdornmentTests.cs

@@ -15,35 +15,80 @@ public class AdornmentTests
             X = 1,
             Y = 2,
             Width = 20,
-            Height = 31
+            Height = 20
         };
 
+        view.BeginInit ();
+        view.EndInit ();
+
+        Assert.Equal (new (1, 2, 20, 20), view.Frame);
+        Assert.Equal (new (0, 0, 20, 20), view.Bounds);
+
         var marginThickness = 1;
-        view.Margin.Thickness = new Thickness (marginThickness);
+        view.Margin.Thickness = new  (marginThickness);
+        Assert.Equal (new (0, 0, 18, 18), view.Bounds);
 
         var borderThickness = 2;
-        view.Border.Thickness = new Thickness (borderThickness);
+        view.Border.Thickness = new (borderThickness);
+        Assert.Equal (new (0, 0, 14, 14), view.Bounds);
 
         var paddingThickness = 3;
         view.Padding.Thickness = new Thickness (paddingThickness);
+        Assert.Equal (new (0, 0, 8, 8), view.Bounds);
 
-        view.BeginInit ();
-        view.EndInit ();
+        Assert.Equal (new (0, 0, view.Margin.Frame.Width, view.Margin.Frame.Height), view.Margin.Bounds);
 
-        Assert.Equal (new Rectangle (1, 2, 20, 31), view.Frame);
-        Assert.Equal (new Rectangle (0, 0, 8, 19), view.Bounds);
+        Assert.Equal (new (0, 0, view.Border.Frame.Width, view.Border.Frame.Height), view.Border.Bounds);
 
-        Assert.Equal (new Rectangle (0, 0, view.Margin.Frame.Width - marginThickness * 2, view.Margin.Frame.Height - marginThickness * 2), view.Margin.Bounds);
+        Assert.Equal (new (0, 0, view.Padding.Frame.Width , view.Padding.Frame.Height), view.Padding.Bounds);
+    }
 
-        Assert.Equal (new Rectangle (0, 0, view.Border.Frame.Width - borderThickness * 2, view.Border.Frame.Height - borderThickness * 2), view.Border.Bounds);
+    // Test that Adornment.Bounds_get override returns Frame.Size minus Thickness
+    [Theory]
+    [InlineData (0, 0, 0, 0, 0)]
+    [InlineData (0, 0, 0, 1, 1)]
+    [InlineData (0, 0, 0, 1, 0)]
+    [InlineData (0, 0, 0, 0, 1)]
+    [InlineData (1, 0, 0, 0, 0)]
+    [InlineData (1, 0, 0, 1, 1)]
+    [InlineData (1, 0, 0, 1, 0)]
+    [InlineData (1, 0, 0, 0, 1)]
+    [InlineData (1, 0, 0, 4, 4)]
+    [InlineData (1, 0, 0, 4, 0)]
+    [InlineData (1, 0, 0, 0, 4)]
+
+    [InlineData (0, 1, 0, 0, 0)]
+    [InlineData (0, 1, 0, 1, 1)]
+    [InlineData (0, 1, 0, 1, 0)]
+    [InlineData (0, 1, 0, 0, 1)]
+    [InlineData (1, 1, 0, 0, 0)]
+    [InlineData (1, 1, 0, 1, 1)]
+    [InlineData (1, 1, 0, 1, 0)]
+    [InlineData (1, 1, 0, 0, 1)]
+    [InlineData (1, 1, 0, 4, 4)]
+    [InlineData (1, 1, 0, 4, 0)]
+    [InlineData (1, 1, 0, 0, 4)]
+
+    [InlineData (0, 1, 1, 0, 0)]
+    [InlineData (0, 1, 1, 1, 1)]
+    [InlineData (0, 1, 1, 1, 0)]
+    [InlineData (0, 1, 1, 0, 1)]
+    [InlineData (1, 1, 1, 0, 0)]
+    [InlineData (1, 1, 1, 1, 1)]
+    [InlineData (1, 1, 1, 1, 0)]
+    [InlineData (1, 1, 1, 0, 1)]
+    [InlineData (1, 1, 1, 4, 4)]
+    [InlineData (1, 1, 1, 4, 0)]
+    [InlineData (1, 1, 1, 0, 4)]
+    public void Bounds_Width_Is_Frame_Width (int thickness, int x, int y, int w, int h)
+    {
+        var adornment = new Adornment (null);
+        adornment.Thickness = new Thickness (thickness);
+        adornment.Frame = new Rectangle (x, y, w, h);
+        Assert.Equal (new Rectangle (x, y, w, h), adornment.Frame);
 
-        Assert.Equal (
-                      new Rectangle (
-                                     0,
-                                     0,
-                                     view.Padding.Frame.Width - (marginThickness + borderThickness) * 2,
-                                     view.Padding.Frame.Height - (marginThickness + borderThickness) * 2),
-                      view.Padding.Bounds);
+        var expectedBounds = new Rectangle (0, 0, w, h);
+        Assert.Equal (expectedBounds, adornment.Bounds);
     }
 
     // Test that Adornment.Bounds_get override uses Parent not SuperView

+ 3 - 3
UnitTests/View/Adornment/MarginTests.cs

@@ -36,9 +36,9 @@ public class MarginTests
 
         TestHelpers.AssertDriverContentsAre (
                                              @"
-LTR
-L R
-BBB",
+MMM
+M M
+MMM",
                                              _output
                                             );
         TestHelpers.AssertDriverAttributesAre ("0", null, superView.GetNormalColor ());

+ 3 - 3
UnitTests/View/Adornment/PaddingTests.cs

@@ -31,9 +31,9 @@ public class PaddingTests
 
         TestHelpers.AssertDriverContentsAre (
                                              @"
-LTR
-L R
-BBB",
+PPP
+P P
+PPP",
                                              _output
                                             );
         TestHelpers.AssertDriverAttributesAre ("0", null, view.GetNormalColor ());

+ 515 - 0
UnitTests/View/Adornment/ToScreenTests.cs

@@ -0,0 +1,515 @@
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewTests;
+
+/// <summary>
+/// Test the <see cref="Adornment.FrameToScreen"/> and <see cref="Adornment.BoundsToScreen"/> methods.
+/// DOES NOT TEST View.xxxToScreen methods. Those are in ./View/Layout/ToScreenTests.cs
+/// </summary>
+/// <param name="output"></param>
+public class AdornmentToScreenTests (ITestOutputHelper output)
+{
+    private readonly ITestOutputHelper _output = output;
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (1, 1)]
+    [InlineData (-1, -1)]
+    [InlineData (11, 11)]
+    public void FrameToScreen_NoSuperView_WithoutAdornments (int x, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var view = new View ();
+        view.Frame = frame;
+
+        // Act
+        var marginScreen = view.Margin.FrameToScreen();
+        var borderScreen = view.Border.FrameToScreen ();
+        var paddingScreen = view.Padding.FrameToScreen ();
+
+        // Assert
+        Assert.Equal(expectedX, marginScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left, borderScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left + view.Border.Thickness.Left, paddingScreen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (1, 1)]
+    [InlineData (-1, -1)]
+    [InlineData (11, 11)]
+    public void FrameToScreen_NoSuperView_WithAdornments (int x, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var view = new View ();
+        view.Border.Thickness = new (1);
+        view.Frame = frame;
+
+        // Act
+        var marginScreen = view.Margin.FrameToScreen ();
+        var borderScreen = view.Border.FrameToScreen ();
+        var paddingScreen = view.Padding.FrameToScreen ();
+
+        // Assert
+        Assert.Equal (expectedX, marginScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left, borderScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left + view.Border.Thickness.Left, paddingScreen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (1, 1)]
+    [InlineData (-1, -1)]
+    [InlineData (11, 11)]
+    public void FrameToScreen_SuperView_WithoutAdornments (int x, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var marginScreen = view.Margin.FrameToScreen ();
+        var borderScreen = view.Border.FrameToScreen ();
+        var paddingScreen = view.Padding.FrameToScreen ();
+
+        // Assert
+        Assert.Equal (expectedX, marginScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left, borderScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left + view.Border.Thickness.Left, paddingScreen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 3)]
+    [InlineData (1, 4)]
+    [InlineData (-1, 2)]
+    [InlineData (11, 14)]
+    public void FrameToScreen_SuperView_WithAdornments (int x, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        superView.Margin.Thickness = new (1);
+        superView.Border.Thickness = new (1);
+        superView.Padding.Thickness = new (1);
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var marginScreen = view.Margin.FrameToScreen ();
+        var borderScreen = view.Border.FrameToScreen ();
+        var paddingScreen = view.Padding.FrameToScreen ();
+
+        // Assert
+        Assert.Equal (expectedX, marginScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left, borderScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left + view.Border.Thickness.Left, paddingScreen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (1, 1)]
+    [InlineData (-1, -1)]
+    [InlineData (11, 11)]
+    public void FrameToScreen_NestedSuperView_WithoutAdornments (int x, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var superSuperView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        superSuperView.Add (superView);
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var marginScreen = view.Margin.FrameToScreen ();
+        var borderScreen = view.Border.FrameToScreen ();
+        var paddingScreen = view.Padding.FrameToScreen ();
+
+        // Assert
+        Assert.Equal (expectedX, marginScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left, borderScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left + view.Border.Thickness.Left, paddingScreen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 6)]
+    [InlineData (1, 7)]
+    [InlineData (-1, 5)]
+    [InlineData (11, 17)]
+    public void FrameToScreen_NestedSuperView_WithAdornments (int x, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var superSuperView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        superSuperView.Margin.Thickness = new (1);
+        superSuperView.Border.Thickness = new (1);
+        superSuperView.Padding.Thickness = new (1);
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        superView.Margin.Thickness = new (1);
+        superView.Border.Thickness = new (1);
+        superView.Padding.Thickness = new (1);
+        superSuperView.Add (superView);
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var marginScreen = view.Margin.FrameToScreen ();
+        var borderScreen = view.Border.FrameToScreen ();
+        var paddingScreen = view.Padding.FrameToScreen ();
+
+        // Assert
+        Assert.Equal (expectedX, marginScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left, borderScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left + view.Border.Thickness.Left, paddingScreen.X);
+    }
+
+
+
+    [Theory]
+    [InlineData (0, 0, 0)]
+    [InlineData (1, 0, 1)]
+    [InlineData (-1, 0, -1)]
+    [InlineData (11, 0, 11)]
+    public void BoundsToScreen_NoSuperView_WithoutAdornments (int frameX, int boundsX, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (frameX, 0, 10, 10);
+
+        var view = new View ();
+        view.Frame = frame;
+
+        // Act
+        var marginScreen = view.Margin.BoundsToScreen (new (boundsX, 0, 0, 0));
+        var borderScreen = view.Border.BoundsToScreen (new (boundsX, 0, 0, 0));
+        var paddingScreen = view.Padding.BoundsToScreen (new (boundsX, 0, 0, 0));
+
+        // Assert
+        Assert.Equal (expectedX, marginScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left, borderScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left + view.Border.Thickness.Left, paddingScreen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0, 0)]
+    [InlineData (1, 0, 1)]
+    [InlineData (-1, 0, -1)]
+    [InlineData (11, 0, 11)]
+
+    [InlineData (0, 1, 1)]
+    [InlineData (1, 1, 2)]
+    [InlineData (-1, 1, 0)]
+    [InlineData (11, 1, 12)]
+
+    [InlineData (0, -1, -1)]
+    [InlineData (1, -1, 0)]
+    [InlineData (-1, -1, -2)]
+    [InlineData (11, -1, 10)]
+    public void BoundsToScreen_NoSuperView_WithAdornments (int frameX, int boundsX, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (frameX, 0, 10, 10);
+
+        var view = new View ();
+        view.Margin.Thickness = new (1);
+        view.Border.Thickness = new (1);
+        view.Padding.Thickness = new (1);
+        // Total thickness is 3 (view.Bounds will be Frame.Width - 6)
+        view.Frame = frame;
+
+        Assert.Equal(4, view.Bounds.Width);
+
+        // Act
+        var marginScreen = view.Margin.BoundsToScreen (new (boundsX, 0, 0, 0));
+        var borderScreen = view.Border.BoundsToScreen (new (boundsX, 0, 0, 0));
+        var paddingScreen = view.Padding.BoundsToScreen (new (boundsX, 0, 0, 0));
+
+        // Assert
+        Assert.Equal (expectedX, marginScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left, borderScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left + view.Border.Thickness.Left, paddingScreen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0, 0)]
+    [InlineData (1, 0, 1)]
+    [InlineData (-1, 0, -1)]
+    [InlineData (11, 0, 11)]
+
+    [InlineData (0, 1, 1)]
+    [InlineData (1, 1, 2)]
+    [InlineData (-1, 1, 0)]
+    [InlineData (11, 1, 12)]
+
+    [InlineData (0, -1, -1)]
+    [InlineData (1, -1, 0)]
+    [InlineData (-1, -1, -2)]
+    [InlineData (11, -1, 10)]
+    public void BoundsToScreen_SuperView_WithoutAdornments (int frameX, int boundsX, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (frameX, 0, 10, 10);
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var marginScreen = view.Margin.BoundsToScreen (new (boundsX, 0, 0, 0));
+        var borderScreen = view.Border.BoundsToScreen (new (boundsX, 0, 0, 0));
+        var paddingScreen = view.Padding.BoundsToScreen (new (boundsX, 0, 0, 0));
+
+        // Assert
+        Assert.Equal (expectedX, marginScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left, borderScreen.X);
+        Assert.Equal (expectedX + view.Border.Thickness.Left, paddingScreen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0, 1)]
+    [InlineData (1, 0, 2)]
+    [InlineData (-1, 0, 0)]
+    [InlineData (11, 0, 12)]
+
+    [InlineData (0, 1, 2)]
+    [InlineData (1, 1, 3)]
+    [InlineData (-1, 1, 1)]
+    [InlineData (11, 1, 13)]
+
+    [InlineData (0, -1, 0)]
+    [InlineData (1, -1, 1)]
+    [InlineData (-1, -1, -1)]
+    [InlineData (11, -1, 11)]
+    public void BoundsToScreen_SuperView_WithAdornments (int frameX, int boundsX, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (frameX, 0, 10, 10);
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        superView.Border.Thickness = new (1);
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var marginScreen = view.Margin.BoundsToScreen (new (boundsX, 0, 0, 0));
+        var borderScreen = view.Border.BoundsToScreen (new (boundsX, 0, 0, 0));
+        var paddingScreen = view.Padding.BoundsToScreen (new (boundsX, 0, 0, 0));
+
+        // Assert
+        Assert.Equal (expectedX, marginScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left, borderScreen.X);
+        Assert.Equal (expectedX + view.Border.Thickness.Left, paddingScreen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0, 0)]
+    [InlineData (1, 0, 1)]
+    [InlineData (-1, 0, -1)]
+    [InlineData (11, 0, 11)]
+
+    [InlineData (0, 1, 1)]
+    [InlineData (1, 1, 2)]
+    [InlineData (-1, 1, 0)]
+    [InlineData (11, 1, 12)]
+
+    [InlineData (0, -1, -1)]
+    [InlineData (1, -1, 0)]
+    [InlineData (-1, -1, -2)]
+    [InlineData (11, -1, 10)]
+    public void BoundsToScreen_NestedSuperView_WithoutAdornments (int frameX, int boundsX, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (frameX, 0, 10, 10);
+
+        var superSuperView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        superSuperView.Add (superView);
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var marginScreen = view.Margin.BoundsToScreen (new (boundsX, 0, 0, 0));
+        var borderScreen = view.Border.BoundsToScreen (new (boundsX, 0, 0, 0));
+        var paddingScreen = view.Padding.BoundsToScreen (new (boundsX, 0, 0, 0));
+
+        // Assert
+        Assert.Equal (expectedX, marginScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left, borderScreen.X);
+        Assert.Equal (expectedX + view.Border.Thickness.Left, paddingScreen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0, 6)]
+    [InlineData (1, 0, 7)]
+    [InlineData (-1, 0, 5)]
+    [InlineData (11, 0, 17)]
+
+    [InlineData (0, 1, 7)]
+    [InlineData (1, 1, 8)]
+    [InlineData (-1, 1, 6)]
+    [InlineData (11, 1, 18)]
+
+    [InlineData (0, -1, 5)]
+    [InlineData (1, -1, 6)]
+    [InlineData (-1, -1, 4)]
+    [InlineData (11, -1, 16)]
+    public void BoundsToScreen_NestedSuperView_WithAdornments (int frameX, int boundsX, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (frameX, 0, 10, 10);
+
+        var superSuperView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        superSuperView.Margin.Thickness = new (1);
+        superSuperView.Border.Thickness = new (1);
+        superSuperView.Padding.Thickness = new (1);
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        superView.Margin.Thickness = new (1);
+        superView.Border.Thickness = new (1);
+        superView.Padding.Thickness = new (1);
+        superSuperView.Add (superView);
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var marginScreen = view.Margin.BoundsToScreen (new (boundsX, 0, 0, 0));
+        var borderScreen = view.Border.BoundsToScreen (new (boundsX, 0, 0, 0));
+        var paddingScreen = view.Padding.BoundsToScreen (new (boundsX, 0, 0, 0));
+
+        // Assert
+        Assert.Equal (expectedX, marginScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left, borderScreen.X);
+        Assert.Equal (expectedX + view.Margin.Thickness.Left + view.Border.Thickness.Left, paddingScreen.X);
+    }
+
+}

+ 42 - 1
UnitTests/View/DrawTests.cs

@@ -2,7 +2,7 @@
 using System.Text;
 using Xunit.Abstractions;
 
-namespace Terminal.Gui.ViewsTests;
+namespace Terminal.Gui.ViewTests;
 
 [Trait("Category","Output")]
 public class DrawTests
@@ -10,6 +10,47 @@ public class DrawTests
     private readonly ITestOutputHelper _output;
     public DrawTests (ITestOutputHelper output) { _output = output; }
 
+    [Theory]
+    [InlineData (0, 0, 1, 1)]
+    [InlineData (0, 0, 2, 2)]
+    [InlineData (-1, -1, 2, 2)]
+    [SetupFakeDriver]
+    public void Clear_Bounds_Clears_Only_Bounds (int x, int y, int width, int height)
+    {
+        var superView = new View { Width = Dim.Fill (), Height = Dim.Fill () };
+
+        var view = new View
+        {
+            Text = "X",
+            X = 1, Y = 1,
+            Width = 3, Height = 3,
+            BorderStyle = LineStyle.Single
+        };
+        superView.Add (view);
+        superView.BeginInit ();
+        superView.EndInit ();
+        superView.LayoutSubviews ();
+
+        superView.Draw ();
+
+        TestHelpers.AssertDriverContentsWithFrameAre (
+                                                      @"
+ ┌─┐
+ │X│
+ └─┘",
+                                                      _output);
+
+        Rectangle boundsToClear = new (x, y, width, height);
+        view.Clear (boundsToClear);
+        TestHelpers.AssertDriverContentsWithFrameAre (
+                                                      @"
+ ┌─┐
+ │ │
+ └─┘",
+                                                      _output);
+
+    }
+
     [Fact]
     [AutoInitShutdown]
     [Trait("Category","Unicode")]

+ 152 - 0
UnitTests/View/Layout/BoundsTests.cs

@@ -0,0 +1,152 @@
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewTests;
+
+/// <summary>
+/// Test the <see cref="View.Bounds"/>.
+/// DOES NOT TEST Adornment.Bounds methods. Those are in ./Adornment/BoundsTests.cs
+/// </summary>
+/// <param name="output"></param>
+public class BoundsTests (ITestOutputHelper output)
+{
+    private readonly ITestOutputHelper _output = output;
+
+    [Theory]
+    [InlineData (0, 10)]
+    [InlineData (1, 10)]
+    [InlineData (-1, 10)]
+    [InlineData (11, 10)]
+    public void Get_Bounds_NoSuperView_WithoutAdornments (int x, int expectedW)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var view = new View ();
+        view.Frame = frame;
+        view.BeginInit();
+        view.EndInit();
+
+        // Act
+        var bounds = view.Bounds;
+
+        // Assert
+        Assert.Equal(expectedW, bounds.Width);
+    }
+    
+    [Theory]
+    [InlineData (0, 0, 10)]
+    [InlineData (1, 0, 9)]
+    [InlineData (-1, 0, 11)]
+    [InlineData (10, 0, 0)]
+    [InlineData (11, 0, 0)]
+
+    [InlineData (0, 1, 6)]
+    [InlineData (1, 1, 5)]
+    [InlineData (-1, 1, 7)]
+    [InlineData (10, 1, 0)]
+    [InlineData (11, 1, 0)]
+
+    public void Get_Bounds_NestedSuperView_WithAdornments (int frameX, int borderThickness, int expectedW)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var superSuperView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = 10,
+            Width = 10,
+        };
+        superSuperView.Border.Thickness = new Thickness (borderThickness);
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        superView.Border.Thickness = new Thickness (borderThickness);
+
+        superSuperView.Add (superView);
+
+        var view = new View ()
+        {
+            X = frameX,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        superView.Add (view);
+        superSuperView.BeginInit ();
+        superSuperView.EndInit ();
+        superSuperView.LayoutSubviews ();
+
+        // Act
+        var bounds = view.Bounds;
+
+        // Assert
+        Assert.Equal (expectedW, bounds.Width);
+    }
+
+
+
+    [Theory]
+    [InlineData (0, 0, 10)]
+    [InlineData (1, 0, 9)]
+    [InlineData (-1, 0, 11)]
+    [InlineData (10, 0, 0)]
+    [InlineData (11, 0, 0)]
+
+    [InlineData (0, 1, 4)]
+    [InlineData (1, 1, 3)]
+    [InlineData (-1, 1, 5)]
+    [InlineData (10, 1, 0)]
+    [InlineData (11, 1, 0)]
+    public void Get_Bounds_NestedSuperView_WithAdornments_WithBorder (int frameX, int borderThickness, int expectedW)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var superSuperView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = 10,
+            Width = 10,
+        };
+        superSuperView.Border.Thickness = new Thickness (borderThickness);
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        superView.Border.Thickness = new Thickness (borderThickness);
+
+        superSuperView.Add (superView);
+
+        var view = new View ()
+        {
+            X = frameX,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        view.Border.Thickness = new Thickness (borderThickness);
+
+        superView.Add (view);
+        superSuperView.BeginInit ();
+        superSuperView.EndInit ();
+        superSuperView.LayoutSubviews ();
+
+        // Act
+        var bounds = view.Bounds;
+
+        // Assert
+        Assert.Equal (expectedW, bounds.Width);
+    }
+}

+ 18 - 18
UnitTests/View/Layout/CoordinateTests.cs → UnitTests/View/Layout/ScreenToTests.cs

@@ -3,13 +3,13 @@
 namespace Terminal.Gui.ViewTests;
 
 /// <summary>Tests for view coordinate mapping (e.g. <see cref="View.ScreenToFrame"/> etc...).</summary>
-public class CoordinateTests
+public class ScreenToTests
 {
     private readonly ITestOutputHelper _output;
-    public CoordinateTests (ITestOutputHelper output) { _output = output; }
+    public ScreenToTests (ITestOutputHelper output) { _output = output; }
 
     /// <summary>
-    ///     Tests that screen to bounds mapping works correctly when the view has no superview and there ARE Frames on the
+    ///     Tests that screen to bounds mapping works correctly when the view has no superview and there ARE Adornments on the
     ///     view.
     /// </summary>
     [Theory]
@@ -21,7 +21,7 @@ public class CoordinateTests
     [InlineData (1, 1, 1, 1, -1, -1)]
     [InlineData (1, 1, 9, 9, 7, 7)]
     [InlineData (1, 1, 11, 11, 9, 9)]
-    public void ScreenToBounds_NoSuper_HasFrames (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
+    public void ScreenToBounds_NoSuper_HasAdornments (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
     {
         var view = new View
         {
@@ -38,7 +38,7 @@ public class CoordinateTests
     }
 
     /// <summary>
-    ///     Tests that screen to bounds mapping works correctly when the view has no superview and there are no Frames on
+    ///     Tests that screen to bounds mapping works correctly when the view has no superview and there are no Adornments on
     ///     the view.
     /// </summary>
     [Theory]
@@ -50,7 +50,7 @@ public class CoordinateTests
     [InlineData (1, 1, 1, 1, 0, 0)]
     [InlineData (1, 1, 9, 9, 8, 8)]
     [InlineData (1, 1, 11, 11, 10, 10)] // it's ok for the view to return coordinates outside of its bounds
-    public void ScreenToBounds_NoSuper_NoFrames (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
+    public void ScreenToBounds_NoSuper_NoAdornments (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
     {
         var view = new View { X = viewX, Y = viewY, Width = 10, Height = 10 };
 
@@ -59,7 +59,7 @@ public class CoordinateTests
         Assert.Equal (expectedY, actual.Y);
     }
 
-    /// <summary>Tests that screen to bounds mapping works correctly when the view has as superview it DOES have Frames</summary>
+    /// <summary>Tests that screen to bounds mapping works correctly when the view has as superview it DOES have Adornments</summary>
     [Theory]
     [InlineData (0, 0, 0, 0, -1, -1)] // it's ok for the view to return coordinates outside of its bounds
     [InlineData (0, 0, 1, 1, 0, 0)]
@@ -69,7 +69,7 @@ public class CoordinateTests
     [InlineData (1, 1, 1, 1, -1, -1)]
     [InlineData (1, 1, 9, 9, 7, 7)]
     [InlineData (1, 1, 11, 11, 9, 9)] // it's ok for the view to return coordinates outside of its bounds
-    public void ScreenToBounds_SuperHasFrames (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
+    public void ScreenToBounds_SuperHasAdornments (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
     {
         var super = new View
         {
@@ -87,7 +87,7 @@ public class CoordinateTests
         Assert.Equal (expectedY, actual.Y);
     }
 
-    /// <summary>Tests that screen to bounds mapping works correctly when the view has as superview it does not have Frames</summary>
+    /// <summary>Tests that screen to bounds mapping works correctly when the view has as superview it does not have Adornments</summary>
     [Theory]
     [InlineData (0, 0, 0, 0, 0, 0)]
     [InlineData (0, 0, 1, 1, 1, 1)]
@@ -97,7 +97,7 @@ public class CoordinateTests
     [InlineData (1, 1, 1, 1, 0, 0)]
     [InlineData (1, 1, 9, 9, 8, 8)]
     [InlineData (1, 1, 11, 11, 10, 10)] // it's ok for the view to return coordinates outside of its bounds
-    public void ScreenToBounds_SuperHasNoFrames (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
+    public void ScreenToBounds_SuperHasNoAdornments (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
     {
         var super = new View { X = 0, Y = 0, Width = 10, Height = 10 };
         var view = new View { X = viewX, Y = viewY, Width = 5, Height = 5 };
@@ -109,7 +109,7 @@ public class CoordinateTests
     }
 
     /// <summary>
-    ///     Tests that screen to view mapping works correctly when the view has no superview and there ARE Frames on the
+    ///     Tests that screen to view mapping works correctly when the view has no superview and there ARE Adornments on the
     ///     view.
     /// </summary>
     [Theory]
@@ -121,7 +121,7 @@ public class CoordinateTests
     [InlineData (1, 1, 1, 1, 0, 0)]
     [InlineData (1, 1, 9, 9, 8, 8)]
     [InlineData (1, 1, 11, 11, 10, 10)] // it's ok for the view to return coordinates outside of its bounds
-    public void ScreenToView_NoSuper_HasFrames (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
+    public void ScreenToView_NoSuper_HasAdornments (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
     {
         var view = new View
         {
@@ -138,7 +138,7 @@ public class CoordinateTests
     }
 
     /// <summary>
-    ///     Tests that screen to view mapping works correctly when the view has no superview and there are no Frames on
+    ///     Tests that screen to view mapping works correctly when the view has no superview and there are no Adornments on
     ///     the view.
     /// </summary>
     [Theory]
@@ -150,7 +150,7 @@ public class CoordinateTests
     [InlineData (1, 1, 1, 1, 0, 0)]
     [InlineData (1, 1, 9, 9, 8, 8)]
     [InlineData (1, 1, 11, 11, 10, 10)] // it's ok for the view to return coordinates outside of its bounds
-    public void ScreenToView_NoSuper_NoFrames (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
+    public void ScreenToView_NoSuper_NoAdornments (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
     {
         var view = new View { X = viewX, Y = viewY, Width = 10, Height = 10 };
 
@@ -159,7 +159,7 @@ public class CoordinateTests
         Assert.Equal (expectedY, actual.Y);
     }
 
-    /// <summary>Tests that screen to view mapping works correctly when the view has as superview it DOES have Frames</summary>
+    /// <summary>Tests that screen to view mapping works correctly when the view has as superview it DOES have Adornments</summary>
     [Theory]
     [InlineData (0, 0, 0, 0, -1, -1)] // it's ok for the view to return coordinates outside of its bounds
     [InlineData (0, 0, 1, 1, 0, 0)]
@@ -169,7 +169,7 @@ public class CoordinateTests
     [InlineData (1, 1, 1, 1, -1, -1)]
     [InlineData (1, 1, 9, 9, 7, 7)]
     [InlineData (1, 1, 11, 11, 9, 9)] // it's ok for the view to return coordinates outside of its bounds
-    public void ScreenToView_SuperHasFrames (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
+    public void ScreenToView_SuperHasAdornments (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
     {
         var super = new View
         {
@@ -187,7 +187,7 @@ public class CoordinateTests
         Assert.Equal (expectedY, actual.Y);
     }
 
-    /// <summary>Tests that screen to view mapping works correctly when the view has as superview it does not have Frames</summary>
+    /// <summary>Tests that screen to view mapping works correctly when the view has as superview it does not have Adornments</summary>
     [Theory]
     [InlineData (0, 0, 0, 0, 0, 0)]
     [InlineData (0, 0, 1, 1, 1, 1)]
@@ -197,7 +197,7 @@ public class CoordinateTests
     [InlineData (1, 1, 1, 1, 0, 0)]
     [InlineData (1, 1, 9, 9, 8, 8)]
     [InlineData (1, 1, 11, 11, 10, 10)] // it's ok for the view to return coordinates outside of its bounds
-    public void ScreenToView_SuperHasNoFrames (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
+    public void ScreenToView_SuperHasNoAdornments (int viewX, int viewY, int x, int y, int expectedX, int expectedY)
     {
         var super = new View { X = 0, Y = 0, Width = 10, Height = 10 };
         var view = new View { X = viewX, Y = viewY, Width = 5, Height = 5 };

+ 454 - 0
UnitTests/View/Layout/ToScreenTests.cs

@@ -0,0 +1,454 @@
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewTests;
+
+/// <summary>
+/// Test the <see cref="View.FrameToScreen"/> and <see cref="View.BoundsToScreen"/> methods.
+/// DOES NOT TEST Adornment.xxxToScreen methods. Those are in ./Adornment/ToScreenTests.cs
+/// </summary>
+/// <param name="output"></param>
+public class ToScreenTests (ITestOutputHelper output)
+{
+    private readonly ITestOutputHelper _output = output;
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (1, 1)]
+    [InlineData (-1, -1)]
+    [InlineData (11, 11)]
+    public void FrameToScreen_NoSuperView_WithoutAdornments (int x, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var view = new View ();
+        view.Frame = frame;
+
+        // Act
+        var screen = view.FrameToScreen();
+
+        // Assert
+        Assert.Equal(expectedX, screen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (1, 1)]
+    [InlineData (-1, -1)]
+    [InlineData (11, 11)]
+    public void FrameToScreen_NoSuperView_WithAdornments (int x, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var view = new View ();
+        view.BorderStyle = LineStyle.Single;
+        view.Frame = frame;
+
+        // Act
+        var screen = view.FrameToScreen ();
+
+        // Assert
+        Assert.Equal (expectedX, screen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (1, 1)]
+    [InlineData (-1, -1)]
+    [InlineData (11, 11)]
+    public void FrameToScreen_SuperView_WithoutAdornments (int x, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var screen = view.FrameToScreen ();
+
+        // Assert
+        Assert.Equal (expectedX, screen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 1)]
+    [InlineData (1, 2)]
+    [InlineData (-1, 0)]
+    [InlineData (11, 12)]
+    public void FrameToScreen_SuperView_WithAdornments (int x, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        superView.BorderStyle = LineStyle.Single;
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var screen = view.FrameToScreen ();
+
+        // Assert
+        Assert.Equal (expectedX, screen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0)]
+    [InlineData (1, 1)]
+    [InlineData (-1, -1)]
+    [InlineData (11, 11)]
+    public void FrameToScreen_NestedSuperView_WithoutAdornments (int x, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var superSuperView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        superSuperView.Add (superView);
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var screen = view.FrameToScreen ();
+
+        // Assert
+        Assert.Equal (expectedX, screen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 2)]
+    [InlineData (1, 3)]
+    [InlineData (-1, 1)]
+    [InlineData (11, 13)]
+    public void FrameToScreen_NestedSuperView_WithAdornments (int x, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (x, 0, 10, 10);
+
+        var superSuperView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        superSuperView.BorderStyle = LineStyle.Single;
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        superSuperView.Add (superView);
+        superView.BorderStyle = LineStyle.Single;
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var screen = view.FrameToScreen ();
+
+        // Assert
+        Assert.Equal (expectedX, screen.X);
+    }
+
+
+
+    [Theory]
+    [InlineData (0, 0, 0)]
+    [InlineData (1, 0, 1)]
+    [InlineData (-1, 0, -1)]
+    [InlineData (11, 0, 11)]
+    public void BoundsToScreen_NoSuperView_WithoutAdornments (int frameX, int boundsX, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (frameX, 0, 10, 10);
+
+        var view = new View ();
+        view.Frame = frame;
+
+        // Act
+        var screen = view.BoundsToScreen (new (boundsX, 0, 0, 0));
+
+        // Assert
+        Assert.Equal (expectedX, screen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0, 1)]
+    [InlineData (1, 0, 2)]
+    [InlineData (-1, 0, 0)]
+    [InlineData (11, 0, 12)]
+
+    [InlineData (0, 1, 2)]
+    [InlineData (1, 1, 3)]
+    [InlineData (-1, 1, 1)]
+    [InlineData (11, 1, 13)]
+
+    [InlineData (0, -1, 0)]
+    [InlineData (1, -1, 1)]
+    [InlineData (-1, -1, -1)]
+    [InlineData (11, -1, 11)]
+    public void BoundsToScreen_NoSuperView_WithAdornments (int frameX, int boundsX, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (frameX, 0, 10, 10);
+
+        var view = new View ();
+        view.BorderStyle = LineStyle.Single;
+        view.Frame = frame;
+
+        // Act
+        var screen = view.BoundsToScreen (new (boundsX, 0, 0, 0));
+
+        // Assert
+        Assert.Equal (expectedX, screen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0, 0)]
+    [InlineData (1, 0, 1)]
+    [InlineData (-1, 0, -1)]
+    [InlineData (11, 0, 11)]
+
+    [InlineData (0, 1, 1)]
+    [InlineData (1, 1, 2)]
+    [InlineData (-1, 1, 0)]
+    [InlineData (11, 1, 12)]
+
+    [InlineData (0, -1, -1)]
+    [InlineData (1, -1, 0)]
+    [InlineData (-1, -1, -2)]
+    [InlineData (11, -1, 10)]
+    public void BoundsToScreen_SuperView_WithoutAdornments (int frameX, int boundsX, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (frameX, 0, 10, 10);
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var screen = view.BoundsToScreen (new (boundsX, 0, 0, 0));
+
+        // Assert
+        Assert.Equal (expectedX, screen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0, 1)]
+    [InlineData (1, 0, 2)]
+    [InlineData (-1, 0, 0)]
+    [InlineData (11, 0, 12)]
+
+    [InlineData (0, 1, 2)]
+    [InlineData (1, 1, 3)]
+    [InlineData (-1, 1, 1)]
+    [InlineData (11, 1, 13)]
+
+    [InlineData (0, -1, 0)]
+    [InlineData (1, -1, 1)]
+    [InlineData (-1, -1, -1)]
+    [InlineData (11, -1, 11)]
+    public void BoundsToScreen_SuperView_WithAdornments (int frameX, int boundsX, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (frameX, 0, 10, 10);
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        superView.BorderStyle = LineStyle.Single;
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var screen = view.BoundsToScreen (new (boundsX, 0, 0, 0));
+
+        // Assert
+        Assert.Equal (expectedX, screen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0, 0)]
+    [InlineData (1, 0, 1)]
+    [InlineData (-1, 0, -1)]
+    [InlineData (11, 0, 11)]
+
+    [InlineData (0, 1, 1)]
+    [InlineData (1, 1, 2)]
+    [InlineData (-1, 1, 0)]
+    [InlineData (11, 1, 12)]
+
+    [InlineData (0, -1, -1)]
+    [InlineData (1, -1, 0)]
+    [InlineData (-1, -1, -2)]
+    [InlineData (11, -1, 10)]
+    public void BoundsToScreen_NestedSuperView_WithoutAdornments (int frameX, int boundsX, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (frameX, 0, 10, 10);
+
+        var superSuperView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        superSuperView.Add (superView);
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var screen = view.BoundsToScreen (new (boundsX, 0, 0, 0));
+
+        // Assert
+        Assert.Equal (expectedX, screen.X);
+    }
+
+    [Theory]
+    [InlineData (0, 0, 2)]
+    [InlineData (1, 0, 3)]
+    [InlineData (-1, 0, 1)]
+    [InlineData (11, 0, 13)]
+
+    [InlineData (0, 1, 3)]
+    [InlineData (1, 1, 4)]
+    [InlineData (-1, 1, 2)]
+    [InlineData (11, 1, 14)]
+
+    [InlineData (0, -1, 1)]
+    [InlineData (1, -1, 2)]
+    [InlineData (-1, -1, 0)]
+    [InlineData (11, -1, 12)]
+    public void BoundsToScreen_NestedSuperView_WithAdornments (int frameX, int boundsX, int expectedX)
+    {
+        // We test with only X because Y is equivalent. Height/Width are irrelevant.
+        // Arrange
+        var frame = new Rectangle (frameX, 0, 10, 10);
+
+        var superSuperView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+        superSuperView.BorderStyle = LineStyle.Single;
+
+        var superView = new View ()
+        {
+            X = 0,
+            Y = 0,
+            Height = Dim.Fill (),
+            Width = Dim.Fill ()
+        };
+
+        superSuperView.Add (superView);
+        superView.BorderStyle = LineStyle.Single;
+
+        var view = new View ();
+        view.Frame = frame;
+
+        superView.Add (view);
+        superView.LayoutSubviews ();
+
+        // Act
+        var screen = view.BoundsToScreen (new (boundsX, 0, 0, 0));
+
+        // Assert
+        Assert.Equal (expectedX, screen.X);
+    }
+
+}

+ 104 - 104
UnitTests/View/NavigationTests.cs

@@ -841,31 +841,31 @@ public class NavigationTests
 
         // top
         Assert.Equal (Point.Empty, top.ScreenToFrame (0, 0));
-        top.Margin.BoundsToScreen (0, 0, out int col, out int row);
-        Assert.Equal (0, col);
-        Assert.Equal (0, row);
-        top.Border.BoundsToScreen (0, 0, out col, out row);
-        Assert.Equal (0, col);
-        Assert.Equal (0, row);
-        top.Padding.BoundsToScreen (0, 0, out col, out row);
-        Assert.Equal (0, col);
-        Assert.Equal (0, row);
-        top.BoundsToScreen (0, 0, out col, out row);
-        Assert.Equal (1, col);
-        Assert.Equal (1, row);
-        top.BoundsToScreen (-1, -1, out col, out row);
-        Assert.Equal (0, col);
-        Assert.Equal (0, row);
+        Rectangle screen = top.Margin.BoundsToScreen (new (0, 0, 0, 0));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
+        screen = top.Border.BoundsToScreen (new (0, 0, 0, 0));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
+        screen = top.Padding.BoundsToScreen (new (0, 0, 0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = top.BoundsToScreen (new (0, 0, 0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = top.BoundsToScreen (new (-1, -1, 0, 0));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
         var found = View.FindDeepestView (top, 0, 0);
         Assert.Equal (top, found);
  
         Assert.Equal (0, found.Frame.X);
         Assert.Equal (0, found.Frame.Y);
         Assert.Equal (new Point (3, 2), top.ScreenToFrame (3, 2));
-        top.BoundsToScreen (3, 2, out col, out row);
-        Assert.Equal (4, col);
-        Assert.Equal (3, row);
-        found = View.FindDeepestView (top, col, row);
+        screen = top.BoundsToScreen (new (3, 2, 0, 0));
+        Assert.Equal (4, screen.X);
+        Assert.Equal (3, screen.Y);
+        found = View.FindDeepestView (top, screen.X, screen.Y);
         Assert.Equal (view, found);
         //Assert.Equal (0, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
@@ -874,24 +874,24 @@ public class NavigationTests
         //Assert.Equal (3, found.FrameToScreen ().X);
         //Assert.Equal (2, found.FrameToScreen ().Y);
         Assert.Equal (new Point (13, 2), top.ScreenToFrame (13, 2));
-        top.BoundsToScreen (12, 2, out col, out row);
-        Assert.Equal (13, col);
-        Assert.Equal (3, row);
-        found = View.FindDeepestView (top, col, row);
+        screen = top.BoundsToScreen (new (12, 2, 0, 0));
+        Assert.Equal (13, screen.X);
+        Assert.Equal (3, screen.Y);
+        found = View.FindDeepestView (top, screen.X, screen.Y);
         Assert.Equal (view, found);
         //Assert.Equal (9, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
-        top.BoundsToScreen (13, 2, out col, out row);
-        Assert.Equal (14, col);
-        Assert.Equal (3, row);
+        screen = top.BoundsToScreen (new (13, 2, 0, 0));
+        Assert.Equal (14, screen.X);
+        Assert.Equal (3, screen.Y);
         found = View.FindDeepestView (top, 13, 2);
         Assert.Equal (top, found);
         //Assert.Equal (13, found.FrameToScreen ().X);
         //Assert.Equal (2, found.FrameToScreen ().Y);
         Assert.Equal (new Point (14, 3), top.ScreenToFrame (14, 3));
-        top.BoundsToScreen (14, 3, out col, out row);
-        Assert.Equal (15, col);
-        Assert.Equal (4, row);
+        screen = top.BoundsToScreen (new (14, 3, 0, 0));
+        Assert.Equal (15, screen.X);
+        Assert.Equal (4, screen.Y);
         found = View.FindDeepestView (top, 14, 3);
         Assert.Equal (top, found);
         //Assert.Equal (14, found.FrameToScreen ().X);
@@ -899,45 +899,45 @@ public class NavigationTests
 
         // view
         Assert.Equal (new Point (-4, -3), view.ScreenToFrame (0, 0));
-        view.Margin.BoundsToScreen (-3, -2, out col, out row);
-        Assert.Equal (1, col);
-        Assert.Equal (1, row);
-        view.Border.BoundsToScreen (-3, -2, out col, out row);
-        Assert.Equal (1, col);
-        Assert.Equal (1, row);
-        view.Padding.BoundsToScreen (-3, -2, out col, out row);
-        Assert.Equal (1, col);
-        Assert.Equal (1, row);
-        view.BoundsToScreen (-3, -2, out col, out row);
-        Assert.Equal (1, col);
-        Assert.Equal (1, row);
-        view.BoundsToScreen (-4, -3, out col, out row);
-        Assert.Equal (0, col);
-        Assert.Equal (0, row);
+        screen = view.Margin.BoundsToScreen (new (-3, -2, 0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.Border.BoundsToScreen (new (-3, -2, 0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.Padding.BoundsToScreen (new (-3, -2, 0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.BoundsToScreen (new (-3, -2, 0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.BoundsToScreen (new (-4, -3, 0, 0));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
         found = View.FindDeepestView (top, 0, 0);
         Assert.Equal (top, found);
         //Assert.Equal (0, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
         Assert.Equal (new Point (-1, -1), view.ScreenToFrame (3, 2));
-        view.BoundsToScreen (0, 0, out col, out row);
-        Assert.Equal (4, col);
-        Assert.Equal (3, row);
+        screen = view.BoundsToScreen (new (0, 0, 0, 0));
+        Assert.Equal (4, screen.X);
+        Assert.Equal (3, screen.Y);
         found = View.FindDeepestView (top, 4, 3);
         Assert.Equal (view, found);
         //Assert.Equal (0, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
         Assert.Equal (new Point (9, -1), view.ScreenToFrame (13, 2));
-        view.BoundsToScreen (10, 0, out col, out row);
-        Assert.Equal (14, col);
-        Assert.Equal (3, row);
+        screen = view.BoundsToScreen (new (10, 0, 0, 0));
+        Assert.Equal (14, screen.X);
+        Assert.Equal (3, screen.Y);
         found = View.FindDeepestView (top, 14, 3);
         Assert.Equal (top, found);
         //Assert.Equal (14, found.FrameToScreen ().X);
         //Assert.Equal (3, found.FrameToScreen ().Y);
         Assert.Equal (new Point (10, 0), view.ScreenToFrame (14, 3));
-        view.BoundsToScreen (11, 1, out col, out row);
-        Assert.Equal (15, col);
-        Assert.Equal (4, row);
+        screen = view.BoundsToScreen (new (11, 1, 0, 0));
+        Assert.Equal (15, screen.X);
+        Assert.Equal (4, screen.Y);
         found = View.FindDeepestView (top, 15, 4);
         Assert.Equal (top, found);
         //Assert.Equal (15, found.FrameToScreen ().X);
@@ -1000,96 +1000,96 @@ public class NavigationTests
 
         // top
         Assert.Equal (new Point (-3, -2), top.ScreenToFrame (0, 0));
-        top.Margin.BoundsToScreen (-3, -2, out int col, out int row);
-        Assert.Equal (0, col);
-        Assert.Equal (0, row);
-        top.Border.BoundsToScreen (-3, -2, out col, out row);
-        Assert.Equal (0, col);
-        Assert.Equal (0, row);
-        top.Padding.BoundsToScreen (-3, -2, out col, out row);
-        Assert.Equal (0, col);
-        Assert.Equal (0, row);
-        top.BoundsToScreen (-3, -2, out col, out row);
-        Assert.Equal (1, col);
-        Assert.Equal (1, row);
-        top.BoundsToScreen (-4, -3, out col, out row);
-        Assert.Equal (0, col);
-        Assert.Equal (0, row);
+        Rectangle screen = top.Margin.BoundsToScreen (new (-3, -2, 0, 0));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
+        screen = top.Border.BoundsToScreen (new (-3, -2, 0, 0));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
+        screen = top.Padding.BoundsToScreen (new (-3, -2, 0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = top.BoundsToScreen (new (-3, -2, 0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = top.BoundsToScreen (new (-4, -3, 0, 0));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
         var found = View.FindDeepestView (top, -4, -3);
         Assert.Null (found);
         //Assert.Equal (0, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
         Assert.Equal (Point.Empty, top.ScreenToFrame (3, 2));
-        top.BoundsToScreen (0, 0, out col, out row);
-        Assert.Equal (4, col);
-        Assert.Equal (3, row);
+        screen = top.BoundsToScreen (new (0, 0, 0, 0));
+        Assert.Equal (4, screen.X);
+        Assert.Equal (3, screen.Y);
         Assert.Equal (top, View.FindDeepestView (top, 3, 2));
         //Assert.Equal (0, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
         Assert.Equal (new Point (10, 0), top.ScreenToFrame (13, 2));
-        top.BoundsToScreen (10, 0, out col, out row);
-        Assert.Equal (14, col);
-        Assert.Equal (3, row);
+        screen = top.BoundsToScreen (new (10, 0, 0, 0));
+        Assert.Equal (14, screen.X);
+        Assert.Equal (3, screen.Y);
         Assert.Equal (top, View.FindDeepestView (top, 13, 2));
         //Assert.Equal (10, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
         Assert.Equal (new Point (11, 1), top.ScreenToFrame (14, 3));
-        top.BoundsToScreen (11, 1, out col, out row);
-        Assert.Equal (15, col);
-        Assert.Equal (4, row);
+        screen = top.BoundsToScreen (new (11, 1, 0, 0));
+        Assert.Equal (15, screen.X);
+        Assert.Equal (4, screen.Y);
         Assert.Equal (top, View.FindDeepestView (top, 14, 3));
         //Assert.Equal (11, found.FrameToScreen ().X);
         //Assert.Equal (1, found.FrameToScreen ().Y);
 
         // view
         Assert.Equal (new Point (-7, -5), view.ScreenToFrame (0, 0));
-        view.Margin.BoundsToScreen (-6, -4, out col, out row);
-        Assert.Equal (1, col);
-        Assert.Equal (1, row);
-        view.Border.BoundsToScreen (-6, -4, out col, out row);
-        Assert.Equal (1, col);
-        Assert.Equal (1, row);
-        view.Padding.BoundsToScreen (-6, -4, out col, out row);
-        Assert.Equal (1, col);
-        Assert.Equal (1, row);
-        view.BoundsToScreen (-6, -4, out col, out row);
-        Assert.Equal (1, col);
-        Assert.Equal (1, row);
+        screen = view.Margin.BoundsToScreen (new (-6, -4, 0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.Border.BoundsToScreen (new (-6, -4, 0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.Padding.BoundsToScreen (new (-6, -4, 0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.BoundsToScreen (new (-6, -4, 0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
         Assert.Null (View.FindDeepestView (top, 1, 1));
         //Assert.Equal (0, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
         Assert.Equal (new Point (-4, -3), view.ScreenToFrame (3, 2));
-        view.BoundsToScreen (-3, -2, out col, out row);
-        Assert.Equal (4, col);
-        Assert.Equal (3, row);
+        screen = view.BoundsToScreen (new (-3, -2, 0, 0));
+        Assert.Equal (4, screen.X);
+        Assert.Equal (3, screen.Y);
         Assert.Equal (top, View.FindDeepestView (top, 4, 3));
         //Assert.Equal (1, found.FrameToScreen ().X);
         //Assert.Equal (1, found.FrameToScreen ().Y);
         Assert.Equal (new Point (-1, -1), view.ScreenToFrame (6, 4));
-        view.BoundsToScreen (0, 0, out col, out row);
-        Assert.Equal (7, col);
-        Assert.Equal (5, row);
+        screen = view.BoundsToScreen (new (0, 0, 0, 0));
+        Assert.Equal (7, screen.X);
+        Assert.Equal (5, screen.Y);
         Assert.Equal (view, View.FindDeepestView (top, 7, 5));
         //Assert.Equal (0, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
         Assert.Equal (new Point (6, -1), view.ScreenToFrame (13, 4));
-        view.BoundsToScreen (7, 0, out col, out row);
-        Assert.Equal (14, col);
-        Assert.Equal (5, row);
+        screen = view.BoundsToScreen (new (7, 0, 0, 0));
+        Assert.Equal (14, screen.X);
+        Assert.Equal (5, screen.Y);
         Assert.Equal (view, View.FindDeepestView (top, 14, 5));
         //Assert.Equal (7, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);
         Assert.Equal (new Point (7, -2), view.ScreenToFrame (14, 3));
-        view.BoundsToScreen (8, -1, out col, out row);
-        Assert.Equal (15, col);
-        Assert.Equal (4, row);
+        screen = view.BoundsToScreen (new (8, -1, 0, 0));
+        Assert.Equal (15, screen.X);
+        Assert.Equal (4, screen.Y);
         Assert.Equal (top, View.FindDeepestView (top, 15, 4));
         //Assert.Equal (12, found.FrameToScreen ().X);
         //Assert.Equal (2, found.FrameToScreen ().Y);
         Assert.Equal (new Point (16, -2), view.ScreenToFrame (23, 3));
-        view.BoundsToScreen (17, -1, out col, out row);
-        Assert.Equal (24, col);
-        Assert.Equal (4, row);
+        screen = view.BoundsToScreen (new (17, -1, 0, 0));
+        Assert.Equal (24, screen.X);
+        Assert.Equal (4, screen.Y);
         Assert.Null (View.FindDeepestView (top, 24, 4));
         //Assert.Equal (0, found.FrameToScreen ().X);
         //Assert.Equal (0, found.FrameToScreen ().Y);

+ 22 - 4
UnitTests/View/ViewTests.cs

@@ -53,13 +53,22 @@ public class ViewTests
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
         Assert.Equal (new Rectangle (0, 0, 20, 10), pos);
 
-        view.Clear (view.Frame);
+        view.Clear (view.Bounds);
 
         expected = @"
+┌──────────────────┐
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+└──────────────────┘
 ";
 
         pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
-        Assert.Equal (Rectangle.Empty, pos);
     }
 
     [Fact]
@@ -106,13 +115,22 @@ public class ViewTests
         Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
         Assert.Equal (new Rectangle (0, 0, 20, 10), pos);
 
-        view.Clear (view.Frame);
+        view.Clear (view.Bounds);
 
         expected = @"
+┌──────────────────┐
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+└──────────────────┘
 ";
 
         pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
-        Assert.Equal (Rectangle.Empty, pos);
     }
 
     [Theory]