Browse Source

Simplified and made FindDeepestView more efficient

Tig 1 year ago
parent
commit
064262bed6

+ 4 - 0
Terminal.Gui/Application.cs

@@ -490,6 +490,8 @@ public static partial class Application
         Toplevel.SetRelativeLayout (Driver.Bounds);
         Toplevel.SetRelativeLayout (Driver.Bounds);
 
 
         //}
         //}
+
+        // BUGBUG: This call is likley not needed.
         Toplevel.LayoutSubviews ();
         Toplevel.LayoutSubviews ();
         Toplevel.PositionToplevels ();
         Toplevel.PositionToplevels ();
         Toplevel.FocusFirst ();
         Toplevel.FocusFirst ();
@@ -1029,6 +1031,8 @@ public static partial class Application
         runState.Toplevel?.Dispose ();
         runState.Toplevel?.Dispose ();
         runState.Toplevel = null;
         runState.Toplevel = null;
         runState.Dispose ();
         runState.Dispose ();
+
+        // BUGBUG: Application.Top is now invalid?!?!
     }
     }
 
 
     #endregion Run (Begin, Run, End)
     #endregion Run (Begin, Run, End)

+ 15 - 0
Terminal.Gui/View/Adornment/Adornment.cs

@@ -191,6 +191,21 @@ public class Adornment : View
 
 
     #region Mouse Support
     #region Mouse Support
 
 
+
+    /// <summary>
+    /// Indicates whether the specified Parent's SuperView-relative coordinates are within the Adornment's Thickness.
+    /// </summary>
+    /// <param name="x"></param>
+    /// <param name="y"></param>
+    /// <returns><see langword="true"/> if the specified Parent's SuperView-relative coordinates are within the Adornment's Thickness. </returns>
+    public override bool Contains (int x, int y)
+    {
+        Rectangle frame = Frame;
+        frame.Offset (Parent.Frame.Location);
+
+        return Thickness.Contains (frame, x, y);
+    }
+
     private Point? _dragPosition;
     private Point? _dragPosition;
     private Point _startGrabPoint;
     private Point _startGrabPoint;
 
 

+ 51 - 276
Terminal.Gui/View/Layout/ViewLayout.cs

@@ -92,16 +92,16 @@ public partial class View
 
 
         while (super is { })
         while (super is { })
         {
         {
-            Point boundsOffset = super.GetBoundsOffset ();
             if (super is Adornment adornment)
             if (super is Adornment adornment)
             {
             {
                 // Adornments don't have SuperViews; use Adornment.FrameToScreen override
                 // Adornments don't have SuperViews; use Adornment.FrameToScreen override
                 ret = adornment.FrameToScreen ();
                 ret = adornment.FrameToScreen ();
                 ret.Offset (Frame.X, Frame.Y);
                 ret.Offset (Frame.X, Frame.Y);
-                boundsOffset = GetBoundsOffset ();
-                //ret.Offset(-boundsOffset.X, -boundsOffset.Y);
+
                 return ret;
                 return ret;
             }
             }
+
+            Point boundsOffset = super.GetBoundsOffset ();
             ret.X += super.Frame.X + boundsOffset.X;
             ret.X += super.Frame.X + boundsOffset.X;
             ret.Y += super.Frame.Y + boundsOffset.Y;
             ret.Y += super.Frame.Y + boundsOffset.Y;
             super = super.SuperView;
             super = super.SuperView;
@@ -286,7 +286,7 @@ public partial class View
     #region Bounds
     #region Bounds
 
 
     /// <summary>
     /// <summary>
-    ///     The bounds represent the View-relative rectangle used for this view; the area inside of the view where
+    ///     The bounds represent the View-relative rectangle used for this view; the area inside the view where
     ///     subviews and content are presented.
     ///     subviews and content are presented.
     /// </summary>
     /// </summary>
     /// <value>The rectangle describing the location and size of the area where the views' subviews and content are drawn.</value>
     /// <value>The rectangle describing the location and size of the area where the views' subviews and content are drawn.</value>
@@ -325,14 +325,18 @@ public partial class View
 
 
             if (Margin is null || Border is null || Padding is null)
             if (Margin is null || Border is null || Padding is null)
             {
             {
+                // CreateAdornments has not been called yet.
                 return Rectangle.Empty with { Size = Frame.Size };
                 return Rectangle.Empty with { Size = Frame.Size };
             }
             }
 
 
             Thickness totalThickness = GetAdornmentsThickness ();
             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) };
+            return Rectangle.Empty with
+            {
+                Size = new (
+                            Math.Max (0, Frame.Size.Width - totalThickness.Horizontal),
+                            Math.Max (0, Frame.Size.Height - totalThickness.Vertical))
+            };
         }
         }
         set
         set
         {
         {
@@ -350,7 +354,9 @@ public partial class View
 
 
             Frame = Frame with
             Frame = Frame with
             {
             {
-                Size = new (value.Size.Width + totalThickness.Horizontal, value.Size.Height + totalThickness.Vertical)
+                Size = new (
+                            value.Size.Width + totalThickness.Horizontal,
+                            value.Size.Height + totalThickness.Vertical)
             };
             };
         }
         }
     }
     }
@@ -359,9 +365,8 @@ public partial class View
     public Rectangle BoundsToScreen (in Rectangle bounds)
     public Rectangle BoundsToScreen (in Rectangle bounds)
     {
     {
         // Translate bounds to Frame (our SuperView's Bounds-relative coordinates)
         // Translate bounds to Frame (our SuperView's Bounds-relative coordinates)
-        Point boundsOffset = GetBoundsOffset ();
-
         Rectangle screen = FrameToScreen ();
         Rectangle screen = FrameToScreen ();
+        Point boundsOffset = GetBoundsOffset ();
         screen.Offset (boundsOffset.X + bounds.X, boundsOffset.Y + bounds.Y);
         screen.Offset (boundsOffset.X + bounds.X, boundsOffset.Y + bounds.Y);
 
 
         return new (screen.Location, bounds.Size);
         return new (screen.Location, bounds.Size);
@@ -373,249 +378,21 @@ public partial class View
     /// <param name="y">Screen-relative row.</param>
     /// <param name="y">Screen-relative row.</param>
     public Point ScreenToBounds (int x, int y)
     public Point ScreenToBounds (int x, int y)
     {
     {
-        Point screen = ScreenToFrame (x, y);
         Point boundsOffset = GetBoundsOffset ();
         Point boundsOffset = GetBoundsOffset ();
+        Point screen = ScreenToFrame (x, y);
+        screen.Offset (-boundsOffset.X, -boundsOffset.Y);
 
 
-        return new (screen.X - boundsOffset.X, screen.Y - boundsOffset.Y);
+        return screen;
     }
     }
 
 
     /// <summary>
     /// <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
     ///     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"/>.
     ///     of <see cref="Margin"/>, <see cref="Border"/> and <see cref="Padding"/>.
     /// </summary>
     /// </summary>
-    public virtual Point GetBoundsOffset ()
-    {
-        if (Padding is null)
-        {
-            return Point.Empty;
-        }
-
-        return Padding.Thickness.GetInside (Padding.Frame).Location;
-    }
+    public Point GetBoundsOffset () { return Padding is null ? Point.Empty : Padding.Thickness.GetInside (Padding.Frame).Location; }
 
 
     #endregion Bounds
     #endregion Bounds
 
 
-    #region Adornments
-
-    private void CreateAdornments ()
-    {
-        Margin = CreateAdornment (typeof (Margin)) as Margin;
-        Border = CreateAdornment (typeof (Border)) as Border;
-        Padding = CreateAdornment (typeof (Padding)) as Padding;
-    }
-
-    // TODO: Move this to Adornment as a static factory method
-    /// <summary>
-    ///     This internal method is overridden by Adornment to do nothing to prevent recursion during View construction.
-    ///     And, because Adornments don't have Adornments. It's internal to support unit tests.
-    /// </summary>
-    /// <param name="adornmentType"></param>
-    /// <exception cref="ArgumentNullException"></exception>
-    /// <exception cref="ArgumentException"></exception>
-    internal virtual Adornment CreateAdornment (Type adornmentType)
-    {
-        void ThicknessChangedHandler (object sender, EventArgs e)
-        {
-            if (IsInitialized)
-            {
-                LayoutAdornments ();
-            }
-
-            SetNeedsLayout ();
-            SetNeedsDisplay ();
-        }
-
-        Adornment adornment;
-
-        adornment = Activator.CreateInstance (adornmentType, this) as Adornment;
-        adornment.ThicknessChanged += ThicknessChangedHandler;
-
-        return adornment;
-    }
-
-    private void BeginInitAdornments ()
-    {
-        Margin?.BeginInit ();
-        Border?.BeginInit ();
-        Padding?.BeginInit ();
-    }
-
-    private void EndInitAdornments ()
-    {
-        Margin?.EndInit ();
-        Border?.EndInit ();
-        Padding?.EndInit ();
-    }
-
-    private void DisposeAdornments ()
-    {
-        Margin?.Dispose ();
-        Margin = null;
-        Border?.Dispose ();
-        Border = null;
-        Padding?.Dispose ();
-        Padding = null;
-    }
-
-    /// <summary>
-    ///     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>
-    ///     <para>
-    ///         The adornments (<see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/>) are not part of the
-    ///         View's content and are not clipped by the View's Clip Area.
-    ///     </para>
-    ///     <para>
-    ///         Changing the size of an adornment (<see cref="Margin"/>, <see cref="Border"/>, or <see cref="Padding"/>) will
-    ///         change the size of <see cref="Frame"/> and trigger <see cref="LayoutSubviews"/> to update the layout of the
-    ///         <see cref="SuperView"/> and its <see cref="Subviews"/>.
-    ///     </para>
-    /// </remarks>
-    public Margin Margin { get; private set; }
-
-    /// <summary>
-    ///     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>
-    /// <remarks>
-    ///     <para><see cref="BorderStyle"/> provides a simple helper for turning a simple border frame on or off.</para>
-    ///     <para>
-    ///         The adornments (<see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/>) are not part of the
-    ///         View's content and are not clipped by the View's Clip Area.
-    ///     </para>
-    ///     <para>
-    ///         Changing the size of a frame (<see cref="Margin"/>, <see cref="Border"/>, or <see cref="Padding"/>) will
-    ///         change the size of the <see cref="Frame"/> and trigger <see cref="LayoutSubviews"/> to update the layout of the
-    ///         <see cref="SuperView"/> and its <see cref="Subviews"/>.
-    ///     </para>
-    /// </remarks>
-    public Border Border { get; private set; }
-
-    /// <summary>Gets or sets whether the view has a one row/col thick border.</summary>
-    /// <remarks>
-    ///     <para>
-    ///         This is a helper for manipulating the view's <see cref="Border"/>. Setting this property to any value other
-    ///         than <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
-    ///         <see cref="Adornment.Thickness"/> to `1` and <see cref="BorderStyle"/> to the value.
-    ///     </para>
-    ///     <para>
-    ///         Setting this property to <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
-    ///         <see cref="Adornment.Thickness"/> to `0` and <see cref="BorderStyle"/> to <see cref="LineStyle.None"/>.
-    ///     </para>
-    ///     <para>For more advanced customization of the view's border, manipulate see <see cref="Border"/> directly.</para>
-    /// </remarks>
-    public LineStyle BorderStyle
-    {
-        get => Border?.LineStyle ?? LineStyle.Single;
-        set
-        {
-            if (Border is null)
-            {
-                return;
-            }
-
-            if (value != LineStyle.None)
-            {
-                Border.Thickness = new (1);
-            }
-            else
-            {
-                Border.Thickness = new (0);
-            }
-
-            Border.LineStyle = value;
-            LayoutAdornments ();
-            SetNeedsLayout ();
-        }
-    }
-
-    /// <summary>
-    ///     The <see cref="Adornment"/> inside of the view that offsets the <see cref="Bounds"/>
-    ///     from the <see cref="Border"/>.
-    /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         The adornments (<see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/>) are not part of the
-    ///         View's content and are not clipped by the View's Clip Area.
-    ///     </para>
-    ///     <para>
-    ///         Changing the size of a frame (<see cref="Margin"/>, <see cref="Border"/>, or <see cref="Padding"/>) will
-    ///         change the size of the <see cref="Frame"/> and trigger <see cref="LayoutSubviews"/> to update the layout of the
-    ///         <see cref="SuperView"/> and its <see cref="Subviews"/>.
-    ///     </para>
-    /// </remarks>
-    public Padding Padding { get; private set; }
-
-    /// <summary>
-    ///     <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 () { return Margin.Thickness + Border.Thickness + Padding.Thickness; }
-
-    /// <summary>Overriden by <see cref="Adornment"/> to do nothing, as the <see cref="Adornment"/> does not have adornments.</summary>
-    internal virtual void LayoutAdornments ()
-    {
-        if (Margin is null)
-        {
-            return; // CreateAdornments () has not been called yet
-        }
-
-        if (Margin.Frame.Size != Frame.Size)
-        {
-            Margin._frame = Rectangle.Empty with { Size = Frame.Size };
-            Margin.X = 0;
-            Margin.Y = 0;
-            Margin.Width = Frame.Size.Width;
-            Margin.Height = Frame.Size.Height;
-        }
-        Margin.SetNeedsLayout ();
-        Margin.SetNeedsDisplay ();
-
-        if (IsInitialized)
-        {
-            Margin.LayoutSubviews ();
-        }
-
-        Rectangle border = Margin.Thickness.GetInside (Margin.Frame);
-
-        if (border != Border.Frame)
-        {
-            Border._frame = border;
-            Border.X = border.Location.X;
-            Border.Y = border.Location.Y;
-            Border.Width = border.Size.Width;
-            Border.Height = border.Size.Height;
-        }
-        Border.SetNeedsLayout ();
-        Border.SetNeedsDisplay ();
-        if (IsInitialized)
-        {
-            Border.LayoutSubviews ();
-        }
-
-        Rectangle padding = Border.Thickness.GetInside (Border.Frame);
-
-        if (padding != Padding.Frame)
-        {
-            Padding._frame = padding;
-            Padding.X = padding.Location.X;
-            Padding.Y = padding.Location.Y;
-            Padding.Width = padding.Size.Width;
-            Padding.Height = padding.Size.Height;
-        }
-        Padding.SetNeedsLayout ();
-        Padding.SetNeedsDisplay ();
-        if (IsInitialized)
-        {
-            Padding.LayoutSubviews ();
-        }
-    }
-
-    #endregion Adornments
-
     #region AutoSize
     #region AutoSize
 
 
     private bool _autoSize;
     private bool _autoSize;
@@ -871,52 +648,51 @@ public partial class View
 
 
     internal bool LayoutNeeded { get; private set; } = true;
     internal bool LayoutNeeded { get; private set; } = true;
 
 
+    /// <summary>
+    /// Indicates whether the specified SuperView-relative coordinates are within the View's <see cref="Frame"/>.
+    /// </summary>
+    /// <param name="x">SuperView-relative X coordinate.</param>
+    /// <param name="y">SuperView-relative Y coordinate.</param>
+    /// <returns><see langword="true"/> if the specified SuperView-relative coordinates are within the View.</returns>
+    public virtual bool Contains (int x, int y)
+    {
+        return Frame.Contains (x, y);
+    }
+
 #nullable enable
 #nullable enable
-    /// <summary>Finds which view that belong to the <paramref name="start"/> superview at the provided location.</summary>
-    /// <param name="start">The superview where to look for.</param>
-    /// <param name="x">The column location in the superview.</param>
-    /// <param name="y">The row location in the superview.</param>
+    /// <summary>Finds the first Subview of <paramref name="start"/> that is visible at the provided location.</summary>
+    /// <remarks>
+    /// <para>
+    ///     Used to determine what view the mouse is over. 
+    /// </para>
+    /// </remarks>
+    /// <param name="start">The view to scope the search by.</param>
+    /// <param name="x"><paramref name="start"/>.SuperView-relative X coordinate.</param>
+    /// <param name="y"><paramref name="start"/>.SuperView-relative Y coordinate..</param>
     /// <returns>
     /// <returns>
     ///     The view that was found at the <paramref name="x"/> and <paramref name="y"/> coordinates.
     ///     The view that was found at the <paramref name="x"/> and <paramref name="y"/> coordinates.
     ///     <see langword="null"/> if no view was found.
     ///     <see langword="null"/> if no view was found.
     /// </returns>
     /// </returns>
+
     // CONCURRENCY: This method is not thread-safe. Undefined behavior and likely program crashes are exposed by unsynchronized access to InternalSubviews.
     // CONCURRENCY: This method is not thread-safe. Undefined behavior and likely program crashes are exposed by unsynchronized access to InternalSubviews.
     internal static View? FindDeepestView (View? start, int x, int y)
     internal static View? FindDeepestView (View? start, int x, int y)
     {
     {
-        if (start is null || !start.Visible)
-        {
-            return null;
-        }
-
-        if (!start.Frame.Contains (x, y))
+        if (start is null || !start.Visible || !start.Contains (x, y))
         {
         {
             return null;
             return null;
         }
         }
 
 
         Adornment? found = null;
         Adornment? found = null;
-        if (start.Margin.Thickness.Contains (start.Frame, x, y))
+
+        if (start.Margin.Contains (x, y))
         {
         {
             found = start.Margin;
             found = start.Margin;
         }
         }
-        else if (start.Border.Thickness.Contains (
-                                                  start.Border.Frame with
-                                                  {
-                                                      X = start.Frame.X + start.Border.Frame.X,
-                                                      Y = start.Frame.Y + start.Border.Frame.Y
-                                                  },
-                                                  x,
-                                                  y))
+        else if (start.Border.Contains (x, y))
         {
         {
             found = start.Border;
             found = start.Border;
         }
         }
-        else if (start.Padding.Thickness.Contains (
-                                                   start.Padding.Frame with
-                                                   {
-                                                       X = start.Frame.X + start.Padding.Frame.X,
-                                                       Y = start.Frame.Y + start.Padding.Frame.Y
-                                                   },
-                                                   x,
-                                                   y))
+        else if (start.Padding.Contains (x, y))
         {
         {
             found = start.Padding;
             found = start.Padding;
         }
         }
@@ -931,18 +707,17 @@ public partial class View
 
 
         if (start.InternalSubviews is { Count: > 0 })
         if (start.InternalSubviews is { Count: > 0 })
         {
         {
-            int rx = x - (start.Frame.X + boundsOffset.X);
-            int ry = y - (start.Frame.Y + boundsOffset.Y);
+            int startOffsetX = x - (start.Frame.X + boundsOffset.X);
+            int startOffsetY = y - (start.Frame.Y + boundsOffset.Y);
 
 
             for (int i = start.InternalSubviews.Count - 1; i >= 0; i--)
             for (int i = start.InternalSubviews.Count - 1; i >= 0; i--)
             {
             {
-                View v = start.InternalSubviews [i];
+                View nextStart = start.InternalSubviews [i];
 
 
-                if (v.Visible && v.Frame.Contains (rx, ry))
+                if (nextStart.Visible && nextStart.Contains (startOffsetX, startOffsetY))
                 {
                 {
-                    View? deep = FindDeepestView (v, rx, ry);
-
-                    return deep ?? v;
+                    // TODO: Remove recursion
+                    return FindDeepestView (nextStart, startOffsetX, startOffsetY) ?? nextStart;
                 }
                 }
             }
             }
         }
         }

+ 225 - 0
Terminal.Gui/View/ViewAdornments.cs

@@ -0,0 +1,225 @@
+namespace Terminal.Gui;
+public partial class View
+{
+    private void CreateAdornments ()
+    {
+        Margin = CreateAdornment (typeof (Margin)) as Margin;
+        Border = CreateAdornment (typeof (Border)) as Border;
+        Padding = CreateAdornment (typeof (Padding)) as Padding;
+    }
+
+    // TODO: Move this to Adornment as a static factory method
+    /// <summary>
+    ///     This internal method is overridden by Adornment to do nothing to prevent recursion during View construction.
+    ///     And, because Adornments don't have Adornments. It's internal to support unit tests.
+    /// </summary>
+    /// <param name="adornmentType"></param>
+    /// <exception cref="ArgumentNullException"></exception>
+    /// <exception cref="ArgumentException"></exception>
+    internal virtual Adornment CreateAdornment (Type adornmentType)
+    {
+        void ThicknessChangedHandler (object sender, EventArgs e)
+        {
+            if (IsInitialized)
+            {
+                LayoutAdornments ();
+            }
+
+            SetNeedsLayout ();
+            SetNeedsDisplay ();
+        }
+
+        Adornment adornment;
+
+        adornment = Activator.CreateInstance (adornmentType, this) as Adornment;
+        adornment.ThicknessChanged += ThicknessChangedHandler;
+
+        return adornment;
+    }
+
+    private void BeginInitAdornments ()
+    {
+        Margin?.BeginInit ();
+        Border?.BeginInit ();
+        Padding?.BeginInit ();
+    }
+
+    private void EndInitAdornments ()
+    {
+        Margin?.EndInit ();
+        Border?.EndInit ();
+        Padding?.EndInit ();
+    }
+
+    private void DisposeAdornments ()
+    {
+        Margin?.Dispose ();
+        Margin = null;
+        Border?.Dispose ();
+        Border = null;
+        Padding?.Dispose ();
+        Padding = null;
+    }
+
+    /// <summary>
+    ///     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>
+    ///     <para>
+    ///         The adornments (<see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/>) are not part of the
+    ///         View's content and are not clipped by the View's Clip Area.
+    ///     </para>
+    ///     <para>
+    ///         Changing the size of an adornment (<see cref="Margin"/>, <see cref="Border"/>, or <see cref="Padding"/>) will
+    ///         change the size of <see cref="Frame"/> and trigger <see cref="LayoutSubviews"/> to update the layout of the
+    ///         <see cref="SuperView"/> and its <see cref="Subviews"/>.
+    ///     </para>
+    /// </remarks>
+    public Margin Margin { get; private set; }
+
+    /// <summary>
+    ///     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>
+    /// <remarks>
+    ///     <para><see cref="BorderStyle"/> provides a simple helper for turning a simple border frame on or off.</para>
+    ///     <para>
+    ///         The adornments (<see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/>) are not part of the
+    ///         View's content and are not clipped by the View's Clip Area.
+    ///     </para>
+    ///     <para>
+    ///         Changing the size of a frame (<see cref="Margin"/>, <see cref="Border"/>, or <see cref="Padding"/>) will
+    ///         change the size of the <see cref="Frame"/> and trigger <see cref="LayoutSubviews"/> to update the layout of the
+    ///         <see cref="SuperView"/> and its <see cref="Subviews"/>.
+    ///     </para>
+    /// </remarks>
+    public Border Border { get; private set; }
+
+    /// <summary>Gets or sets whether the view has a one row/col thick border.</summary>
+    /// <remarks>
+    ///     <para>
+    ///         This is a helper for manipulating the view's <see cref="Border"/>. Setting this property to any value other
+    ///         than <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
+    ///         <see cref="Adornment.Thickness"/> to `1` and <see cref="BorderStyle"/> to the value.
+    ///     </para>
+    ///     <para>
+    ///         Setting this property to <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
+    ///         <see cref="Adornment.Thickness"/> to `0` and <see cref="BorderStyle"/> to <see cref="LineStyle.None"/>.
+    ///     </para>
+    ///     <para>For more advanced customization of the view's border, manipulate see <see cref="Border"/> directly.</para>
+    /// </remarks>
+    public LineStyle BorderStyle
+    {
+        get => Border?.LineStyle ?? LineStyle.Single;
+        set
+        {
+            if (Border is null)
+            {
+                return;
+            }
+
+            if (value != LineStyle.None)
+            {
+                Border.Thickness = new (1);
+            }
+            else
+            {
+                Border.Thickness = new (0);
+            }
+
+            Border.LineStyle = value;
+            LayoutAdornments ();
+            SetNeedsLayout ();
+        }
+    }
+
+    /// <summary>
+    ///     The <see cref="Adornment"/> inside of the view that offsets the <see cref="Bounds"/>
+    ///     from the <see cref="Border"/>.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         The adornments (<see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/>) are not part of the
+    ///         View's content and are not clipped by the View's Clip Area.
+    ///     </para>
+    ///     <para>
+    ///         Changing the size of a frame (<see cref="Margin"/>, <see cref="Border"/>, or <see cref="Padding"/>) will
+    ///         change the size of the <see cref="Frame"/> and trigger <see cref="LayoutSubviews"/> to update the layout of the
+    ///         <see cref="SuperView"/> and its <see cref="Subviews"/>.
+    ///     </para>
+    /// </remarks>
+    public Padding Padding { get; private set; }
+
+    /// <summary>
+    ///     <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 () { return Margin.Thickness + Border.Thickness + Padding.Thickness; }
+
+    /// <summary>Overriden by <see cref="Adornment"/> to do nothing, as the <see cref="Adornment"/> does not have adornments.</summary>
+    internal virtual void LayoutAdornments ()
+    {
+        if (Margin is null)
+        {
+            return; // CreateAdornments () has not been called yet
+        }
+
+        if (Margin.Frame.Size != Frame.Size)
+        {
+            Margin._frame = Rectangle.Empty with { Size = Frame.Size };
+            Margin.X = 0;
+            Margin.Y = 0;
+            Margin.Width = Frame.Size.Width;
+            Margin.Height = Frame.Size.Height;
+        }
+
+        Margin.SetNeedsLayout ();
+        Margin.SetNeedsDisplay ();
+
+        if (IsInitialized)
+        {
+            Margin.LayoutSubviews ();
+        }
+
+        Rectangle border = Margin.Thickness.GetInside (Margin.Frame);
+
+        if (border != Border.Frame)
+        {
+            Border._frame = border;
+            Border.X = border.Location.X;
+            Border.Y = border.Location.Y;
+            Border.Width = border.Size.Width;
+            Border.Height = border.Size.Height;
+        }
+
+        Border.SetNeedsLayout ();
+        Border.SetNeedsDisplay ();
+
+        if (IsInitialized)
+        {
+            Border.LayoutSubviews ();
+        }
+
+        Rectangle padding = Border.Thickness.GetInside (Border.Frame);
+
+        if (padding != Padding.Frame)
+        {
+            Padding._frame = padding;
+            Padding.X = padding.Location.X;
+            Padding.Y = padding.Location.Y;
+            Padding.Width = padding.Size.Width;
+            Padding.Height = padding.Size.Height;
+        }
+
+        Padding.SetNeedsLayout ();
+        Padding.SetNeedsDisplay ();
+
+        if (IsInitialized)
+        {
+            Padding.LayoutSubviews ();
+        }
+    }
+}