Explorar el Código

#nullable enable

Tig hace 9 meses
padre
commit
7656602245

+ 19 - 20
Terminal.Gui/Drawing/Thickness.cs

@@ -1,4 +1,5 @@
-using System.Numerics;
+#nullable enable
+using System.Numerics;
 using System.Text.Json.Serialization;
 
 namespace Terminal.Gui;
@@ -15,10 +16,7 @@ namespace Terminal.Gui;
 ///         with the thickness widths subtracted.
 ///     </para>
 ///     <para>
-///         Use the helper API (<see cref="Draw(Rectangle, string)"/> to draw the frame with the specified thickness.
-///     </para>
-///     <para>
-///         Thickness uses <see langword="float"/> intenrally. As a result, there is a potential precision loss for very
+///         Thickness uses <see langword="float"/> internally. As a result, there is a potential precision loss for very
 ///         large numbers. This is typically not an issue for UI dimensions but could be relevant in other contexts.
 ///     </para>
 /// </remarks>
@@ -91,7 +89,7 @@ public record struct Thickness
     /// <param name="diagnosticFlags"></param>
     /// <param name="label">The diagnostics label to draw on the bottom of the <see cref="Bottom"/>.</param>
     /// <returns>The inner rectangle remaining to be drawn.</returns>
-    public Rectangle Draw (Rectangle rect, ViewDiagnosticFlags diagnosticFlags = ViewDiagnosticFlags.Off, string label = null)
+    public Rectangle Draw (Rectangle rect, ViewDiagnosticFlags diagnosticFlags = ViewDiagnosticFlags.Off, string? label = null)
     {
         if (rect.Size.Width < 1 || rect.Size.Height < 1)
         {
@@ -134,26 +132,26 @@ public record struct Thickness
         if (Right > 0)
         {
             Application.Driver?.FillRect (
-                                         rect with
-                                         {
-                                             X = Math.Max (0, rect.X + rect.Width - Right),
-                                             Width = Math.Min (rect.Width, Right)
-                                         },
-                                         rightChar
-                                        );
+                                          rect with
+                                          {
+                                              X = Math.Max (0, rect.X + rect.Width - Right),
+                                              Width = Math.Min (rect.Width, Right)
+                                          },
+                                          rightChar
+                                         );
         }
 
         // Draw the Bottom side
         if (Bottom > 0)
         {
             Application.Driver?.FillRect (
-                                         rect with
-                                         {
-                                             Y = rect.Y + Math.Max (0, rect.Height - Bottom),
-                                             Height = Bottom
-                                         },
-                                         bottomChar
-                                        );
+                                          rect with
+                                          {
+                                              Y = rect.Y + Math.Max (0, rect.Height - Bottom),
+                                              Height = Bottom
+                                          },
+                                          bottomChar
+                                         );
         }
 
         if (diagnosticFlags.HasFlag (ViewDiagnosticFlags.Ruler))
@@ -192,6 +190,7 @@ public record struct Thickness
         {
             // Draw the diagnostics label on the bottom
             string text = label is null ? string.Empty : $"{label} {this}";
+
             var tf = new TextFormatter
             {
                 Text = text,

+ 6 - 6
Terminal.Gui/View/Adornment/Border.cs

@@ -483,8 +483,8 @@ public class Border : Adornment
                 Point parentLoc = Parent.SuperView?.ScreenToViewport (new (mouseEvent.ScreenPosition.X, mouseEvent.ScreenPosition.Y))
                                   ?? mouseEvent.ScreenPosition;
 
-                int minHeight = Thickness.Vertical + Parent!.Margin.Thickness.Bottom;
-                int minWidth = Thickness.Horizontal + Parent!.Margin.Thickness.Right;
+                int minHeight = Thickness.Vertical + Parent!.Margin!.Thickness.Bottom;
+                int minWidth = Thickness.Horizontal + Parent!.Margin!.Thickness.Right;
 
                 // TODO: This code can be refactored to be more readable and maintainable.
                 switch (_arranging)
@@ -1072,7 +1072,7 @@ public class Border : Adornment
                 NoPadding = true,
                 ShadowStyle = ShadowStyle.None,
                 Text = $"{Glyphs.SizeVertical}",
-                X = Pos.Center () + Parent!.Margin.Thickness.Horizontal,
+                X = Pos.Center () + Parent!.Margin!.Thickness.Horizontal,
                 Y = 0,
                 Visible = false,
                 Data = ViewArrangement.TopResizable
@@ -1095,7 +1095,7 @@ public class Border : Adornment
                 ShadowStyle = ShadowStyle.None,
                 Text = $"{Glyphs.SizeHorizontal}",
                 X = Pos.AnchorEnd (),
-                Y = Pos.Center () + Parent!.Margin.Thickness.Vertical / 2,
+                Y = Pos.Center () + Parent!.Margin!.Thickness.Vertical / 2,
                 Visible = false,
                 Data = ViewArrangement.RightResizable
             };
@@ -1117,7 +1117,7 @@ public class Border : Adornment
                 ShadowStyle = ShadowStyle.None,
                 Text = $"{Glyphs.SizeHorizontal}",
                 X = 0,
-                Y = Pos.Center () + Parent!.Margin.Thickness.Vertical / 2,
+                Y = Pos.Center () + Parent!.Margin!.Thickness.Vertical / 2,
                 Visible = false,
                 Data = ViewArrangement.LeftResizable
             };
@@ -1138,7 +1138,7 @@ public class Border : Adornment
                 NoPadding = true,
                 ShadowStyle = ShadowStyle.None,
                 Text = $"{Glyphs.SizeVertical}",
-                X = Pos.Center () + Parent!.Margin.Thickness.Horizontal / 2,
+                X = Pos.Center () + Parent!.Margin!.Thickness.Horizontal / 2,
                 Y = Pos.AnchorEnd (),
                 Visible = false,
                 Data = ViewArrangement.BottomResizable

+ 2 - 2
Terminal.Gui/View/Adornment/Margin.cs

@@ -275,13 +275,13 @@ public class Margin : Adornment
             {
                 case ShadowStyle.Transparent:
                     // BUGBUG: This doesn't work right for all Border.Top sizes - Need an API on Border that gives top-right location of line corner.
-                    _rightShadow.Y = Parent!.Border.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0;
+                    _rightShadow.Y = Parent!.Border!.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0;
 
                     break;
 
                 case ShadowStyle.Opaque:
                     // BUGBUG: This doesn't work right for all Border.Top sizes - Need an API on Border that gives top-right location of line corner.
-                    _rightShadow.Y = Parent!.Border.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0;
+                    _rightShadow.Y = Parent!.Border!.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0;
                     _bottomShadow.X = Parent.Border.Thickness.Left > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).X + 1 : 0;
 
                     break;

+ 0 - 3
Terminal.Gui/View/DrawEventArgs.cs

@@ -20,9 +20,6 @@ public class DrawEventArgs : CancelEventArgs
         OldViewport = oldViewport;
     }
 
-    /// <summary>If set to true, the draw operation will be canceled, if applicable.</summary>
-    public bool Cancel { get; set; }
-
     /// <summary>Gets the Content-relative rectangle describing the old visible viewport into the <see cref="View"/>.</summary>
     public Rectangle OldViewport { get; }
 

+ 53 - 35
Terminal.Gui/View/View.Adornments.cs

@@ -1,4 +1,5 @@
-namespace Terminal.Gui;
+#nullable enable
+namespace Terminal.Gui;
 
 public partial class View // Adornments
 {
@@ -7,9 +8,10 @@ public partial class View // Adornments
     /// </summary>
     private void SetupAdornments ()
     {
-        //// TODO: Move this to Adornment as a static factory method
+        // TODO: Move this to Adornment as a static factory method
         if (this is not Adornment)
         {
+            // TODO: Make the Adornments Lazy and only create them when needed
             Margin = new (this);
             Border = new (this);
             Padding = new (this);
@@ -61,7 +63,7 @@ public partial class View // Adornments
     ///         <see cref="SuperView"/> and its <see cref="Subviews"/>.
     ///     </para>
     /// </remarks>
-    public Margin Margin { get; private set; }
+    public Margin? Margin { get; private set; }
 
     private ShadowStyle _shadowStyle;
 
@@ -117,7 +119,7 @@ public partial class View // Adornments
     ///         <see cref="SuperView"/> and its <see cref="Subviews"/>.
     ///     </para>
     /// </remarks>
-    public Border Border { get; private set; }
+    public Border? Border { get; private set; }
 
     /// <summary>Gets or sets whether the view has a one row/col thick border.</summary>
     /// <remarks>
@@ -130,6 +132,10 @@ public partial class View // Adornments
     ///         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>
+    ///         Calls <see cref="OnBorderStyleChanging"/> and raises <see cref="BorderStyleChanging"/>, which allows change
+    ///         to be cancelled.
+    ///     </para>
     ///     <para>For more advanced customization of the view's border, manipulate see <see cref="Border"/> directly.</para>
     /// </remarks>
     public LineStyle BorderStyle
@@ -150,32 +156,32 @@ public partial class View // Adornments
                 return;
             }
 
+            BorderStyleChanging?.Invoke (this, e);
+
+            if (e.Cancel)
+            {
+                return;
+            }
+
             SetBorderStyle (e.NewValue);
             SetAdornmentFrames ();
             SetNeedsLayout ();
-
         }
     }
 
     /// <summary>
-    ///     Called when the <see cref="BorderStyle"/> is changing. Invokes <see cref="BorderStyleChanging"/>, which allows the
-    ///     event to be cancelled.
+    ///     Called when the <see cref="BorderStyle"/> is changing.
     /// </summary>
     /// <remarks>
-    ///     Override <see cref="SetBorderStyle"/> to prevent the <see cref="BorderStyle"/> from changing.
+    ///     Set e.Cancel to true to prevent the <see cref="BorderStyle"/> from changing.
     /// </remarks>
     /// <param name="e"></param>
-    protected virtual bool OnBorderStyleChanging (CancelEventArgs<LineStyle> e)
-    {
-        if (Border is null)
-        {
-            return false;
-        }
-
-        BorderStyleChanging?.Invoke (this, e);
+    protected virtual bool OnBorderStyleChanging (CancelEventArgs<LineStyle> e) { return false; }
 
-        return e.Cancel;
-    }
+    /// <summary>
+    ///     Fired when the <see cref="BorderStyle"/> is changing. Allows the event to be cancelled.
+    /// </summary>
+    public event EventHandler<CancelEventArgs<LineStyle>>? BorderStyleChanging;
 
     /// <summary>
     ///     Sets the <see cref="BorderStyle"/> of the view to the specified value.
@@ -198,25 +204,19 @@ public partial class View // Adornments
     {
         if (value != LineStyle.None)
         {
-            if (Border.Thickness == Thickness.Empty)
+            if (Border!.Thickness == Thickness.Empty)
             {
                 Border.Thickness = new (1);
             }
         }
         else
         {
-            Border.Thickness = new (0);
+            Border!.Thickness = new (0);
         }
 
         Border.LineStyle = value;
     }
 
-    /// <summary>
-    ///     Fired when the <see cref="BorderStyle"/> is changing. Allows the event to be cancelled.
-    /// </summary>
-    [CanBeNull]
-    public event EventHandler<CancelEventArgs<LineStyle>> BorderStyleChanging;
-
     /// <summary>
     ///     The <see cref="Adornment"/> inside of the view that offsets the <see cref="Viewport"/>
     ///     from the <see cref="Border"/>.
@@ -232,7 +232,7 @@ public partial class View // Adornments
     ///         <see cref="SuperView"/> and its <see cref="Subviews"/>.
     ///     </para>
     /// </remarks>
-    public Padding Padding { get; private set; }
+    public Padding? Padding { get; private set; }
 
     /// <summary>
     ///     <para>Gets the thickness describing the sum of the Adornments' thicknesses.</para>
@@ -245,12 +245,24 @@ public partial class View // Adornments
     /// <returns>A thickness that describes the sum of the Adornments' thicknesses.</returns>
     public Thickness GetAdornmentsThickness ()
     {
-        if (Margin is null)
+        Thickness result = Thickness.Empty;
+
+        if (Margin is { })
+        {
+            result += Margin.Thickness;
+        }
+
+        if (Border is { })
         {
-            return Thickness.Empty;
+            result += Border.Thickness;
         }
 
-        return Margin.Thickness + Border.Thickness + Padding.Thickness;
+        if (Padding is { })
+        {
+            result += Padding.Thickness;
+        }
+
+        return result;
     }
 
     /// <summary>Sets the Frame's of the Margin, Border, and Padding.</summary>
@@ -262,13 +274,19 @@ public partial class View // Adornments
             return;
         }
 
-        if (Margin is null)
+        if (Margin is { })
+        {
+            Margin!.Frame = Rectangle.Empty with { Size = Frame.Size };
+        }
+
+        if (Border is { } && Margin is { })
         {
-            return; // CreateAdornments () has not been called yet
+            Border!.Frame = Margin!.Thickness.GetInside (Margin!.Frame);
         }
 
-        Margin.Frame = Rectangle.Empty with { Size = Frame.Size };
-        Border.Frame = Margin.Thickness.GetInside (Margin.Frame);
-        Padding.Frame = Border.Thickness.GetInside (Border.Frame);
+        if (Padding is { } && Border is { })
+        {
+            Padding!.Frame = Border!.Thickness.GetInside (Border!.Frame);
+        }
     }
 }

+ 2 - 1
Terminal.Gui/View/View.Arrangement.cs

@@ -1,4 +1,5 @@
-namespace Terminal.Gui;
+#nullable enable
+namespace Terminal.Gui;
 
 public partial class View
 {

+ 21 - 22
Terminal.Gui/View/View.Drawing.Clipping.cs

@@ -1,6 +1,4 @@
 #nullable enable
-using static Unix.Terminal.Curses;
-
 namespace Terminal.Gui;
 
 public partial class View
@@ -13,21 +11,20 @@ public partial class View
     ///         There is a single clip region for the entire application.
     ///     </para>
     ///     <para>
-    ///         This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is recommended to clone it first.
+    ///         This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is
+    ///         recommended to clone it first.
     ///     </para>
     /// </remarks>
     /// <returns>The current Clip.</returns>
-    public static Region? GetClip ()
-    {
-        return Application.Driver?.Clip;
-    }
+    public static Region? GetClip () { return Application.Driver?.Clip; }
 
     /// <summary>
     ///     Sets the Clip to the specified region.
     /// </summary>
     /// <remarks>
     ///     <para>
-    ///         There is a single clip region for the entire application. This method sets the clip region to the specified region.
+    ///         There is a single clip region for the entire application. This method sets the clip region to the specified
+    ///         region.
     ///     </para>
     /// </remarks>
     /// <param name="region"></param>
@@ -47,7 +44,8 @@ public partial class View
     ///         There is a single clip region for the entire application. This method sets the clip region to the screen.
     ///     </para>
     ///     <para>
-    ///         This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is recommended to clone it first.
+    ///         This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is
+    ///         recommended to clone it first.
     ///     </para>
     /// </remarks>
     /// <returns>
@@ -56,6 +54,7 @@ public partial class View
     public static Region? SetClipToScreen ()
     {
         Region? previous = GetClip ();
+
         if (Driver is { })
         {
             Driver.Clip = new (Application.Screen);
@@ -65,7 +64,7 @@ public partial class View
     }
 
     /// <summary>
-    ///      Removes the specified rectangle from the Clip.
+    ///     Removes the specified rectangle from the Clip.
     /// </summary>
     /// <remarks>
     ///     <para>
@@ -73,17 +72,15 @@ public partial class View
     ///     </para>
     /// </remarks>
     /// <param name="rectangle"></param>
-    public static void ExcludeFromClip (Rectangle rectangle)
-    {
-        Driver?.Clip?.Exclude (rectangle);
-    }
+    public static void ExcludeFromClip (Rectangle rectangle) { Driver?.Clip?.Exclude (rectangle); }
 
     /// <summary>
     ///     Changes the Clip to the intersection of the current Clip and the <see cref="Frame"/> of this View.
     /// </summary>
     /// <remarks>
     ///     <para>
-    ///         This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is recommended to clone it first.
+    ///         This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is
+    ///         recommended to clone it first.
     ///     </para>
     /// </remarks>
     /// <returns>
@@ -99,6 +96,7 @@ public partial class View
         Region previous = GetClip () ?? new (Application.Screen);
 
         Region frameRegion = previous.Clone ();
+
         // Translate viewportRegion to screen-relative coords
         Rectangle screenRect = FrameToScreen ();
         frameRegion.Intersect (screenRect);
@@ -109,7 +107,7 @@ public partial class View
             frameRegion.Exclude (adornment.Thickness.GetInside (Frame));
         }
 
-        View.SetClip (frameRegion);
+        SetClip (frameRegion);
 
         return previous;
     }
@@ -125,11 +123,12 @@ public partial class View
     ///         If <see cref="ViewportSettings"/> has <see cref="Gui.ViewportSettings.ClipContentOnly"/> set, clipping will be
     ///         applied to just the visible content area.
     ///     </para>
-    /// <remarks>
-    ///     <para>
-    ///         This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is recommended to clone it first.
-    ///     </para>
-    /// </remarks>
+    ///     <remarks>
+    ///         <para>
+    ///             This method returns the current clip region, not a clone. If there is a need to modify the clip region, it
+    ///             is recommended to clone it first.
+    ///         </para>
+    ///     </remarks>
     /// </remarks>
     /// <returns>
     ///     The current Clip, which can be then re-applied <see cref="View.SetClip"/>
@@ -161,7 +160,7 @@ public partial class View
             viewportRegion?.Exclude (adornment.Thickness.GetInside (viewport));
         }
 
-        View.SetClip (viewportRegion);
+        SetClip (viewportRegion);
 
         return previous;
     }

+ 1 - 5
Terminal.Gui/View/View.Drawing.Primitives.cs

@@ -2,8 +2,6 @@
 
 public partial class View
 {
-    #region Drawing Primitives
-
     /// <summary>Moves the drawing cursor to the specified <see cref="Viewport"/>-relative location in the view.</summary>
     /// <remarks>
     ///     <para>
@@ -121,8 +119,6 @@ public partial class View
         Attribute prev = SetAttribute (new (color ?? GetNormalColor ().Background));
         Driver.FillRect (toClear);
         SetAttribute (prev);
-        View.SetClip (prevClip);
+        SetClip (prevClip);
     }
-
-    #endregion Drawing Primitives
 }

+ 10 - 13
Terminal.Gui/View/View.Drawing.cs

@@ -1,13 +1,10 @@
 #nullable enable
-//#define HACK_DRAW_OVERLAPPED
 using System.ComponentModel;
-using System.Diagnostics;
 
 namespace Terminal.Gui;
 
 public partial class View // Drawing APIs
 {
-
     internal static void Draw (IEnumerable<View> views, bool force)
     {
         IEnumerable<View> viewsArray = views as View [] ?? views.ToArray ();
@@ -25,7 +22,6 @@ public partial class View // Drawing APIs
         Margin.DrawMargins (viewsArray);
     }
 
-
     /// <summary>
     ///     Draws the view if it needs to be drawn.
     /// </summary>
@@ -47,11 +43,12 @@ public partial class View // Drawing APIs
         }
 
         Region? saved = GetClip ();
+
         if (NeedsDraw || SubViewNeedsDraw)
         {
             saved = ClipFrame ();
             DoDrawBorderAndPadding ();
-            View.SetClip (saved);
+            SetClip (saved);
 
             // By default, we clip to the viewport preventing drawing outside the viewport
             // We also clip to the content, but if a developer wants to draw outside the viewport, they can do
@@ -108,7 +105,7 @@ public partial class View // Drawing APIs
         DoDrawComplete ();
 
         // QUESTION: Should this go before DoDrawComplete? What is more correct?
-        View.SetClip (saved);
+        SetClip (saved);
 
         // Exclude this view (not including Margin) from the Clip
         if (this is not Adornment && GetClip () is { })
@@ -221,7 +218,6 @@ public partial class View // Drawing APIs
         SetNormalAttribute ();
     }
 
-
     /// <summary>
     ///     Called when the normal attribute for the View is to be set. This is called before the View is drawn.
     /// </summary>
@@ -245,13 +241,12 @@ public partial class View // Drawing APIs
         }
     }
 
-
     #endregion
+
     #region ClearViewport
 
     private void DoClearViewport ()
     {
-
         if (OnClearingViewport ())
         {
             return;
@@ -329,7 +324,6 @@ public partial class View // Drawing APIs
 
     private void DoDrawText ()
     {
-
         if (OnDrawingText ())
         {
             return;
@@ -513,14 +507,15 @@ public partial class View // Drawing APIs
     /// <summary>
     ///     Gets or sets whether this View will use it's SuperView's <see cref="LineCanvas"/> for rendering any
     ///     lines. If <see langword="true"/> the rendering of any borders drawn by this Frame will be done by its parent's
-    ///     SuperView. If <see langword="false"/> (the default) this View's <see cref="OnDrawingBorderAndPadding"/> method will be
+    ///     SuperView. If <see langword="false"/> (the default) this View's <see cref="OnDrawingBorderAndPadding"/> method will
+    ///     be
     ///     called to render the borders.
     /// </summary>
     public virtual bool SuperViewRendersLineCanvas { get; set; } = false;
 
     /// <summary>
-    ///     Causes the contents of <see cref="LineCanvas"/> to be drawn. 
-    ///      If <see cref="SuperViewRendersLineCanvas"/> is true, only the
+    ///     Causes the contents of <see cref="LineCanvas"/> to be drawn.
+    ///     If <see cref="SuperViewRendersLineCanvas"/> is true, only the
     ///     <see cref="LineCanvas"/> of this view's subviews will be rendered. If <see cref="SuperViewRendersLineCanvas"/> is
     ///     false (the default), this method will cause the <see cref="LineCanvas"/> to be rendered.
     /// </summary>
@@ -577,6 +572,7 @@ public partial class View // Drawing APIs
             LineCanvas.Clear ();
         }
     }
+
     #endregion DrawLineCanvas
 
     #region DrawComplete
@@ -757,6 +753,7 @@ public partial class View // Drawing APIs
         {
             Padding?.ClearNeedsDraw ();
         }
+
         foreach (View subview in Subviews)
         {
             subview.ClearNeedsDraw ();

+ 70 - 56
Terminal.Gui/View/View.Layout.cs

@@ -1,7 +1,5 @@
 #nullable enable
 using System.Diagnostics;
-using Microsoft.CodeAnalysis;
-using static Unix.Terminal.Curses;
 
 namespace Terminal.Gui;
 
@@ -33,10 +31,12 @@ public partial class View // Layout APIs
     ///         .
     ///     </para>
     ///     <para>
-    ///         Setting Frame will set <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, and <see cref="Height"/> to absoulte values.
+    ///         Setting Frame will set <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, and <see cref="Height"/> to
+    ///         absoulte values.
     ///     </para>
     ///     <para>
-    ///         Changing this property will result in <see cref="NeedsLayout"/> and <see cref="NeedsDraw"/> to be set, resulting in the
+    ///         Changing this property will result in <see cref="NeedsLayout"/> and <see cref="NeedsDraw"/> to be set,
+    ///         resulting in the
     ///         view being laid out and redrawn as appropriate in the next iteration of the <see cref="MainLoop"/>.
     ///     </para>
     /// </remarks>
@@ -44,10 +44,11 @@ public partial class View // Layout APIs
     {
         get
         {
-            if (_needsLayout)
+            if (NeedsLayout)
             {
                 //Debug.WriteLine("Frame_get with _layoutNeeded");
             }
+
             return _frame ?? Rectangle.Empty;
         }
         set
@@ -193,7 +194,8 @@ public partial class View // Layout APIs
     ///         laid out (e.g. <see cref="Layout(System.Drawing.Size)"/> has been called).
     ///     </para>
     ///     <para>
-    ///         Changing this property will result in <see cref="NeedsLayout"/> and <see cref="NeedsDraw"/> to be set, resulting in the
+    ///         Changing this property will result in <see cref="NeedsLayout"/> and <see cref="NeedsDraw"/> to be set,
+    ///         resulting in the
     ///         view being laid out and redrawn as appropriate in the next iteration of the <see cref="MainLoop"/>.
     ///     </para>
     ///     <para>
@@ -219,7 +221,6 @@ public partial class View // Layout APIs
 
     private Pos _y = Pos.Absolute (0);
 
-
     /// <summary>Gets or sets the Y position for the view (the row).</summary>
     /// <value>The <see cref="Pos"/> object representing the Y position.</value>
     /// <remarks>
@@ -236,7 +237,8 @@ public partial class View // Layout APIs
     ///         laid out (e.g. <see cref="Layout(System.Drawing.Size)"/> has been called).
     ///     </para>
     ///     <para>
-    ///         Changing this property will result in <see cref="NeedsLayout"/> and <see cref="NeedsDraw"/> to be set, resulting in the
+    ///         Changing this property will result in <see cref="NeedsLayout"/> and <see cref="NeedsDraw"/> to be set,
+    ///         resulting in the
     ///         view being laid out and redrawn as appropriate in the next iteration of the <see cref="MainLoop"/>.
     ///     </para>
     ///     <para>
@@ -277,7 +279,8 @@ public partial class View // Layout APIs
     ///         laid out (e.g. <see cref="Layout(System.Drawing.Size)"/> has been called).
     ///     </para>
     ///     <para>
-    ///         Changing this property will result in <see cref="NeedsLayout"/> and <see cref="NeedsDraw"/> to be set, resulting in the
+    ///         Changing this property will result in <see cref="NeedsLayout"/> and <see cref="NeedsDraw"/> to be set,
+    ///         resulting in the
     ///         view being laid out and redrawn as appropriate in the next iteration of the <see cref="MainLoop"/>.
     ///     </para>
     ///     <para>
@@ -323,7 +326,8 @@ public partial class View // Layout APIs
     ///         laid out (e.g. <see cref="Layout(System.Drawing.Size)"/> has been called).
     ///     </para>
     ///     <para>
-    ///         Changing this property will result in <see cref="NeedsLayout"/> and <see cref="NeedsDraw"/> to be set, resulting in the
+    ///         Changing this property will result in <see cref="NeedsLayout"/> and <see cref="NeedsDraw"/> to be set,
+    ///         resulting in the
     ///         view being laid out and redrawn as appropriate in the next iteration of the <see cref="MainLoop"/>.
     ///     </para>
     ///     <para>
@@ -354,14 +358,15 @@ public partial class View // Layout APIs
     #region Core Layout API
 
     /// <summary>
-    ///     INTERNAL API - Performs layout of the specified views within the specified content size. Called by the Application main loop.
+    ///     INTERNAL API - Performs layout of the specified views within the specified content size. Called by the Application
+    ///     main loop.
     /// </summary>
     /// <param name="views">The views to layout.</param>
     /// <param name="contentSize">The size to bound the views by.</param>
     /// <returns><see langword="true"/>If any of the views needed to be laid out.</returns>
     internal static bool Layout (IEnumerable<View> views, Size contentSize)
     {
-        bool neededLayout = false;
+        var neededLayout = false;
 
         foreach (View v in views)
         {
@@ -404,7 +409,8 @@ public partial class View // Layout APIs
     }
 
     /// <summary>
-    ///     Performs layout of the view and its subviews using the content size of either the <see cref="SuperView"/> or <see cref="Application.Screen"/>.
+    ///     Performs layout of the view and its subviews using the content size of either the <see cref="SuperView"/> or
+    ///     <see cref="Application.Screen"/>.
     /// </summary>
     /// <remarks>
     ///     <para>
@@ -417,14 +423,12 @@ public partial class View // Layout APIs
     ///     </para>
     /// </remarks>
     /// <returns><see langword="false"/>If the view could not be laid out (typically because dependency was not ready). </returns>
-    public bool Layout ()
-    {
-        return Layout (GetContainerSize ());
-    }
+    public bool Layout () { return Layout (GetContainerSize ()); }
 
     /// <summary>
     ///     Sets the position and size of this view, relative to the SuperView's ContentSize (nominally the same as
-    ///     <c>this.SuperView.GetContentSize ()</c>) based on the values of <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>,
+    ///     <c>this.SuperView.GetContentSize ()</c>) based on the values of <see cref="X"/>, <see cref="Y"/>,
+    ///     <see cref="Width"/>,
     ///     and <see cref="Height"/>.
     /// </summary>
     /// <remarks>
@@ -467,6 +471,7 @@ public partial class View // Layout APIs
             {
                 newW = _width.Calculate (0, superviewContentSize.Width, this, Dimension.Width);
                 newX = _x.Calculate (superviewContentSize.Width, newW, this, Dimension.Width);
+
                 if (newW != Frame.Width)
                 {
                     // Pos.Calculate gave us a new position. We need to redo dimension
@@ -483,6 +488,7 @@ public partial class View // Layout APIs
             {
                 newH = _height.Calculate (0, superviewContentSize.Height, this, Dimension.Height);
                 newY = _y.Calculate (superviewContentSize.Height, newH, this, Dimension.Height);
+
                 if (newH != Frame.Height)
                 {
                     // Pos.Calculate gave us a new position. We need to redo dimension
@@ -494,7 +500,6 @@ public partial class View // Layout APIs
                 newY = _y.Calculate (superviewContentSize.Height, _height, this, Dimension.Height);
                 newH = _height.Calculate (newY, superviewContentSize.Height, this, Dimension.Height);
             }
-
         }
         catch (LayoutException le)
         {
@@ -552,7 +557,8 @@ public partial class View // Layout APIs
     }
 
     /// <summary>
-    ///    INTERNAL API - Causes the view's subviews and adornments to be laid out within the view's content areas. Assumes the view's relative layout has been set via <see cref="SetRelativeLayout"/>.
+    ///     INTERNAL API - Causes the view's subviews and adornments to be laid out within the view's content areas. Assumes
+    ///     the view's relative layout has been set via <see cref="SetRelativeLayout"/>.
     /// </summary>
     /// <remarks>
     ///     <para>
@@ -584,10 +590,12 @@ public partial class View // Layout APIs
         {
             Margin.LayoutSubviews ();
         }
+
         if (Border is { Subviews.Count: > 0 })
         {
             Border.LayoutSubviews ();
         }
+
         if (Padding is { Subviews.Count: > 0 })
         {
             Padding.LayoutSubviews ();
@@ -600,6 +608,7 @@ public partial class View // Layout APIs
         List<View> ordered = TopologicalSort (SuperView!, nodes, edges);
 
         List<View> redo = new ();
+
         foreach (View v in ordered)
         {
             if (!v.Layout (contentSize))
@@ -608,7 +617,7 @@ public partial class View // Layout APIs
             }
         }
 
-        bool layoutStillNeeded = false;
+        var layoutStillNeeded = false;
 
         if (redo.Count > 0)
         {
@@ -633,7 +642,7 @@ public partial class View // Layout APIs
             }
         }
 
-        _needsLayout = layoutStillNeeded;
+        NeedsLayout = layoutStillNeeded;
 
         OnSubviewsLaidOut (new (contentSize));
         SubviewsLaidOut?.Invoke (this, new (contentSize));
@@ -648,8 +657,10 @@ public partial class View // Layout APIs
     /// </remarks>
     protected virtual void OnSubviewLayout (LayoutEventArgs args) { }
 
-    /// <summary>Raised by <see cref="LayoutSubviews"/> before any subviews
-    ///     have been laid out.</summary>
+    /// <summary>
+    ///     Raised by <see cref="LayoutSubviews"/> before any subviews
+    ///     have been laid out.
+    /// </summary>
     /// <remarks>
     ///     Subscribe to this event to perform tasks when the layout is changing.
     /// </remarks>
@@ -677,65 +688,72 @@ public partial class View // Layout APIs
     #region NeedsLayout
 
     // We expose no setter for this to ensure that the ONLY place it's changed is in SetNeedsLayout
-    private bool _needsLayout = true;
 
     /// <summary>
     ///     Indicates the View's Frame or the layout of the View's subviews (including Adornments) have
-    ///     changed since the last time the View was laid out. 
+    ///     changed since the last time the View was laid out.
     /// </summary>
     /// <remarks>
-    /// <para>Used to prevent <see cref="Layout()"/> from needlessly computing
-    ///     layout.
-    /// </para>
+    ///     <para>
+    ///         Used to prevent <see cref="Layout()"/> from needlessly computing
+    ///         layout.
+    ///     </para>
     /// </remarks>
     /// <value>
     ///     <see langword="true"/> if layout is needed.
     /// </value>
-    public bool NeedsLayout => _needsLayout;
+    public bool NeedsLayout { get; private set; } = true;
 
     /// <summary>
-    ///     Sets <see cref="NeedsLayout"/> to return <see langword="true"/>, indicating this View and all of it's subviews (including adornments) need to be laid out in the next Application iteration.
+    ///     Sets <see cref="NeedsLayout"/> to return <see langword="true"/>, indicating this View and all of it's subviews
+    ///     (including adornments) need to be laid out in the next Application iteration.
     /// </summary>
     /// <remarks>
     ///     <para>
-    ///         The <see cref="MainLoop"/> will cause <see cref="Layout()"/> to be called on the next <see cref="Application.Iteration"/> so there is normally no reason to call see <see cref="Layout()"/>.
+    ///         The <see cref="MainLoop"/> will cause <see cref="Layout()"/> to be called on the next
+    ///         <see cref="Application.Iteration"/> so there is normally no reason to call see <see cref="Layout()"/>.
     ///     </para>
     /// </remarks>
-
     public void SetNeedsLayout ()
     {
-        _needsLayout = true;
+        NeedsLayout = true;
 
         if (Margin is { Subviews.Count: > 0 })
         {
             Margin.SetNeedsLayout ();
         }
+
         if (Border is { Subviews.Count: > 0 })
         {
             Border.SetNeedsLayout ();
         }
+
         if (Padding is { Subviews.Count: > 0 })
         {
             Padding.SetNeedsLayout ();
         }
 
         // Use a stack to avoid recursion
-        Stack<View> stack = new Stack<View> (Subviews);
+        Stack<View> stack = new (Subviews);
 
         while (stack.Count > 0)
         {
             View current = stack.Pop ();
+
             if (!current.NeedsLayout)
             {
-                current._needsLayout = true;
+                current.NeedsLayout = true;
+
                 if (current.Margin is { Subviews.Count: > 0 })
                 {
                     current.Margin.SetNeedsLayout ();
                 }
+
                 if (current.Border is { Subviews.Count: > 0 })
                 {
                     current.Border.SetNeedsLayout ();
                 }
+
                 if (current.Padding is { Subviews.Count: > 0 })
                 {
                     current.Padding.SetNeedsLayout ();
@@ -757,7 +775,7 @@ public partial class View // Layout APIs
 
         if (SuperView is null)
         {
-            foreach (var tl in Application.TopLevels)
+            foreach (Toplevel tl in Application.TopLevels)
             {
                 tl.SetNeedsDraw ();
             }
@@ -778,7 +796,6 @@ public partial class View // Layout APIs
 
     #region Topological Sort
 
-
     /// <summary>
     ///     INTERNAL API - Collects all views and their dependencies from a given starting view for layout purposes. Used by
     ///     <see cref="TopologicalSort"/> to create an ordered list of views to layout.
@@ -802,7 +819,7 @@ public partial class View // Layout APIs
     }
 
     /// <summary>
-    ///      INTERNAL API - Collects dimension (where Width or Height is `DimView`) dependencies for a given view.
+    ///     INTERNAL API - Collects dimension (where Width or Height is `DimView`) dependencies for a given view.
     /// </summary>
     /// <param name="dim">The dimension (width or height) to collect dependencies for.</param>
     /// <param name="from">The view for which to collect dimension dependencies.</param>
@@ -813,7 +830,7 @@ public partial class View // Layout APIs
     /// </param>
     internal void CollectDim (Dim? dim, View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
     {
-        if (dim!.Has<DimView> (out DimView dv))
+        if (dim!.Has (out DimView dv))
         {
             if (dv.Target != this)
             {
@@ -821,7 +838,7 @@ public partial class View // Layout APIs
             }
         }
 
-        if (dim!.Has<DimCombine> (out DimCombine dc))
+        if (dim!.Has (out DimCombine dc))
         {
             CollectDim (dc.Left, from, ref nNodes, ref nEdges);
             CollectDim (dc.Right, from, ref nNodes, ref nEdges);
@@ -845,6 +862,7 @@ public partial class View // Layout APIs
         {
             case PosView pv:
                 Debug.Assert (pv.Target is { });
+
                 if (pv.Target != this)
                 {
                     nEdges.Add ((pv.Target!, from));
@@ -960,22 +978,21 @@ public partial class View // Layout APIs
     #region Utilities
 
     /// <summary>
-    ///    INTERNAL API - Gets the size of the SuperView's content (nominally the same as
-    ///    the SuperView's <see cref="GetContentSize ()"/>) or the screen size if there's no SuperView.
+    ///     INTERNAL API - Gets the size of the SuperView's content (nominally the same as
+    ///     the SuperView's <see cref="GetContentSize ()"/>) or the screen size if there's no SuperView.
     /// </summary>
     /// <returns></returns>
     private Size GetContainerSize ()
     {
         // TODO: Get rid of refs to Top
-        Size superViewContentSize = SuperView?.GetContentSize () ??
-                                    (Application.Top is { } && Application.Top != this && Application.Top.IsInitialized
-                                         ? Application.Top.GetContentSize ()
-                                         : Application.Screen.Size);
+        Size superViewContentSize = SuperView?.GetContentSize ()
+                                    ?? (Application.Top is { } && Application.Top != this && Application.Top.IsInitialized
+                                            ? Application.Top.GetContentSize ()
+                                            : Application.Screen.Size);
 
         return superViewContentSize;
     }
 
-
     // BUGBUG: This method interferes with Dialog/MessageBox default min/max size.
     // TODO: Get rid of MenuBar coupling as part of https://github.com/gui-cs/Terminal.Gui/issues/2975
     /// <summary>
@@ -1107,11 +1124,8 @@ public partial class View // Layout APIs
 
     #endregion Utilities
 
-
     #region Diagnostics and Verification
 
-
-
     // Diagnostics to highlight when X or Y is read before the view has been initialized
     private Pos VerifyIsInitialized (Pos pos, string member)
     {
@@ -1228,12 +1242,12 @@ public partial class View // Layout APIs
             if (bad != null)
             {
                 throw new LayoutException (
-                                                     $"{view.GetType ().Name}.{name} = {bad.GetType ().Name} "
-                                                     + $"which depends on the SuperView's dimensions and the SuperView uses Dim.Auto."
-                                                    );
+                                           $"{view.GetType ().Name}.{name} = {bad.GetType ().Name} "
+                                           + $"which depends on the SuperView's dimensions and the SuperView uses Dim.Auto."
+                                          );
             }
         }
     }
-    #endregion Diagnostics and Verification
 
-}
+    #endregion Diagnostics and Verification
+}

+ 3 - 3
Terminal.Gui/View/View.Mouse.cs

@@ -666,15 +666,15 @@ public partial class View // Mouse APIs
 
             if (start is not Adornment)
             {
-                if (start.Margin.Contains (currentLocation))
+                if (start.Margin is {} && start.Margin.Contains (currentLocation))
                 {
                     found = start.Margin;
                 }
-                else if (start.Border.Contains (currentLocation))
+                else if (start.Border is {} && start.Border.Contains (currentLocation))
                 {
                     found = start.Border;
                 }
-                else if (start.Padding.Contains (currentLocation))
+                else if (start.Padding is { } && start.Padding.Contains(currentLocation))
                 {
                     found = start.Padding;
                 }

+ 5 - 2
Terminal.Gui/Views/Bar.cs

@@ -87,8 +87,11 @@ public class Bar : View, IOrientation, IDesignable
     /// <inheritdoc/>
     public override void SetBorderStyle (LineStyle value)
     {
-        // The default changes the thickness. We don't want that. We just set the style.
-        Border.LineStyle = value;
+        if (Border is { })
+        {
+            // The default changes the thickness. We don't want that. We just set the style.
+            Border.LineStyle = value;
+        }
     }
 
     #region IOrientation members

+ 1 - 0
Terminal.Gui/Views/Menuv2.cs

@@ -46,6 +46,7 @@ public class Menuv2 : Bar
     // Menuv2 arranges the items horizontally.
     // The first item has no left border, the last item has no right border.
     // The Shortcuts are configured with the command, help, and key views aligned in reverse order (EndToStart).
+    /// <inheritdoc />
     protected override void OnSubviewLayout (LayoutEventArgs args)
     {
         for (int index = 0; index < Subviews.Count; index++)

+ 43 - 28
Terminal.Gui/Views/Shortcut.cs

@@ -98,7 +98,11 @@ public class Shortcut : View, IOrientation, IDesignable
         CanFocus = true;
 
         SuperViewRendersLineCanvas = true;
-        Border.Settings &= ~BorderSettings.Title;
+
+        if (Border is { })
+        {
+            Border.Settings &= ~BorderSettings.Title;
+        }
 
         Width = GetWidthDimAuto ();
         Height = Dim.Auto (DimAutoStyle.Content, 1);
@@ -237,40 +241,40 @@ public class Shortcut : View, IOrientation, IDesignable
         ShowHide ();
         ForceCalculateNaturalWidth ();
 
-        if (Width is DimAuto widthAuto)
+        if (Width is DimAuto widthAuto || HelpView!.Margin is null)
         {
+            return;
+        }
 
-        } 
-        else
+        // Frame.Width is smaller than the natural width. Reduce width of HelpView.
+        _maxHelpWidth = int.Max (0, GetContentSize ().Width - CommandView.Frame.Width - KeyView.Frame.Width);
+
+        if (_maxHelpWidth < 3)
         {
-            // Frame.Width is smaller than the natural width. Reduce width of HelpView.
-            _maxHelpWidth = int.Max (0, GetContentSize ().Width - CommandView.Frame.Width - KeyView.Frame.Width);
-            if (_maxHelpWidth < 3)
+            Thickness t = GetMarginThickness ();
+
+            switch (_maxHelpWidth)
             {
-                Thickness t = GetMarginThickness ();
-                switch (_maxHelpWidth)
-                {
-                    case 0:
-                    case 1:
-                        // Scrunch it by removing both margins
-                        HelpView.Margin.Thickness = new (t.Right - 1, t.Top, t.Left - 1, t.Bottom);
+                case 0:
+                case 1:
+                    // Scrunch it by removing both margins
+                    HelpView.Margin.Thickness = new (t.Right - 1, t.Top, t.Left - 1, t.Bottom);
 
-                        break;
+                    break;
 
-                    case 2:
+                case 2:
 
-                        // Scrunch just the right margin
-                        HelpView.Margin.Thickness = new (t.Right, t.Top, t.Left - 1, t.Bottom);
+                    // Scrunch just the right margin
+                    HelpView.Margin.Thickness = new (t.Right, t.Top, t.Left - 1, t.Bottom);
 
-                        break;
-                }
-            }
-            else
-            {
-                // Reset to default
-                HelpView.Margin.Thickness = GetMarginThickness ();
+                    break;
             }
         }
+        else
+        {
+            // Reset to default
+            HelpView.Margin.Thickness = GetMarginThickness ();
+        }
     }
 
 
@@ -522,7 +526,11 @@ public class Shortcut : View, IOrientation, IDesignable
 
     private void SetHelpViewDefaultLayout ()
     {
-        HelpView.Margin.Thickness = GetMarginThickness ();
+        if (HelpView.Margin is { })
+        {
+            HelpView.Margin.Thickness = GetMarginThickness ();
+        }
+
         HelpView.X = Pos.Align (Alignment.End, AlignmentModes);
         _maxHelpWidth = HelpView.Text.GetColumns ();
         HelpView.Width = Dim.Auto (DimAutoStyle.Text, maximumContentDim: Dim.Func ((() => _maxHelpWidth)));
@@ -654,7 +662,11 @@ public class Shortcut : View, IOrientation, IDesignable
 
     private void SetKeyViewDefaultLayout ()
     {
-        KeyView.Margin.Thickness = GetMarginThickness ();
+        if (KeyView.Margin is { })
+        {
+            KeyView.Margin.Thickness = GetMarginThickness ();
+        }
+
         KeyView.X = Pos.Align (Alignment.End, AlignmentModes);
         KeyView.Width = Dim.Auto (DimAutoStyle.Text, minimumContentDim: Dim.Func (() => MinimumKeyTextSize));
         KeyView.Height = Dim.Fill ();
@@ -763,7 +775,10 @@ public class Shortcut : View, IOrientation, IDesignable
             KeyView.ColorScheme = cs;
         }
 
-        CommandView.Margin.ColorScheme = base.ColorScheme;
+        if (CommandView.Margin is { })
+        {
+            CommandView.Margin.ColorScheme = base.ColorScheme;
+        }
     }
 
     /// <inheritdoc/>

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

@@ -216,7 +216,7 @@ public class TabView : View
     /// <summary>
     ///     Updates the control to use the latest state settings in <see cref="Style"/>. This can change the size of the
     ///     client area of the tab (for rendering the selected tab's content).  This method includes a call to
-    ///     <see cref="View.SetNeedsDraw"/>.
+    ///     <see cref="View.SetNeedsDraw()"/>.
     /// </summary>
     public void ApplyStyleChanges ()
     {
@@ -288,7 +288,7 @@ public class TabView : View
 
     /// <summary>Updates <see cref="TabScrollOffset"/> to be a valid index of <see cref="Tabs"/>.</summary>
     /// <param name="value">The value to validate.</param>
-    /// <remarks>Changes will not be immediately visible in the display until you call <see cref="View.SetNeedsDraw"/>.</remarks>
+    /// <remarks>Changes will not be immediately visible in the display until you call <see cref="View.SetNeedsDraw()"/>.</remarks>
     /// <returns>The valid <see cref="TabScrollOffset"/> for the given value.</returns>
     public int EnsureValidScrollOffsets (int value) { return Math.Max (Math.Min (value, Tabs.Count - 1), 0); }
 

+ 4 - 4
Terminal.Gui/Views/TableView/TableView.cs

@@ -583,7 +583,7 @@ public class TableView : View, IDesignable
     ///     not been set.
     /// </summary>
     /// <remarks>
-    ///     Changes will not be immediately visible in the display until you call <see cref="View.SetNeedsDraw"/>
+    ///     Changes will not be immediately visible in the display until you call <see cref="View.SetNeedsDraw()"/>
     /// </remarks>
     public void EnsureSelectedCellIsVisible ()
     {
@@ -644,7 +644,7 @@ public class TableView : View, IDesignable
     ///     (by adjusting them to the nearest existing cell).  Has no effect if <see cref="Table"/> has not been set.
     /// </summary>
     /// <remarks>
-    ///     Changes will not be immediately visible in the display until you call <see cref="View.SetNeedsDraw"/>
+    ///     Changes will not be immediately visible in the display until you call <see cref="View.SetNeedsDraw()"/>
     /// </remarks>
     public void EnsureValidScrollOffsets ()
     {
@@ -663,7 +663,7 @@ public class TableView : View, IDesignable
     ///     <see cref="Table"/> has not been set.
     /// </summary>
     /// <remarks>
-    ///     Changes will not be immediately visible in the display until you call <see cref="View.SetNeedsDraw"/>
+    ///     Changes will not be immediately visible in the display until you call <see cref="View.SetNeedsDraw()"/>
     /// </remarks>
     public void EnsureValidSelection ()
     {
@@ -1227,7 +1227,7 @@ public class TableView : View, IDesignable
     ///     Updates the view to reflect changes to <see cref="Table"/> and to (<see cref="ColumnOffset"/> /
     ///     <see cref="RowOffset"/>) etc
     /// </summary>
-    /// <remarks>This always calls <see cref="View.SetNeedsDraw"/></remarks>
+    /// <remarks>This always calls <see cref="View.SetNeedsDraw()"/></remarks>
     public void Update ()
     {
         if (!IsInitialized || TableIsNullOrInvisible ())