浏览代码

Tons of stuff

Tig 9 月之前
父节点
当前提交
b31339da02
共有 91 个文件被更改,包括 1652 次插入1023 次删除
  1. 1 1
      Terminal.Gui/Application/Application.Screen.cs
  2. 3 3
      Terminal.Gui/Drawing/Thickness.cs
  3. 34 72
      Terminal.Gui/View/Adornment/Adornment.cs
  4. 1 1
      Terminal.Gui/View/Adornment/Margin.cs
  5. 2 2
      Terminal.Gui/View/Layout/Dim.cs
  6. 23 13
      Terminal.Gui/View/Layout/DimAuto.cs
  7. 1 1
      Terminal.Gui/View/Layout/DimFunc.cs
  8. 1 1
      Terminal.Gui/View/Layout/LayoutEventArgs.cs
  9. 30 0
      Terminal.Gui/View/Layout/LayoutException.cs
  10. 2 2
      Terminal.Gui/View/Layout/Pos.cs
  11. 1 1
      Terminal.Gui/View/Layout/PosAlign.cs
  12. 1 1
      Terminal.Gui/View/Layout/PosAnchorEnd.cs
  13. 1 1
      Terminal.Gui/View/Layout/PosFunc.cs
  14. 4 4
      Terminal.Gui/View/View.Adornments.cs
  15. 2 2
      Terminal.Gui/View/View.Content.cs
  16. 13 5
      Terminal.Gui/View/View.Diagnostics.cs
  17. 2 2
      Terminal.Gui/View/View.Hierarchy.cs
  18. 263 248
      Terminal.Gui/View/View.Layout.cs
  19. 4 4
      Terminal.Gui/View/View.Text.cs
  20. 5 16
      Terminal.Gui/View/View.cs
  21. 4 6
      Terminal.Gui/Views/Bar.cs
  22. 1 1
      Terminal.Gui/Views/Button.cs
  23. 1 1
      Terminal.Gui/Views/CheckBox.cs
  24. 2 2
      Terminal.Gui/Views/ColorPicker.16.cs
  25. 2 2
      Terminal.Gui/Views/ColorPicker.cs
  26. 2 2
      Terminal.Gui/Views/ComboBox.cs
  27. 2 2
      Terminal.Gui/Views/FileDialog.cs
  28. 3 3
      Terminal.Gui/Views/HexView.cs
  29. 3 3
      Terminal.Gui/Views/ListView.cs
  30. 1 1
      Terminal.Gui/Views/MenuBarv2.cs
  31. 2 2
      Terminal.Gui/Views/Menuv2.cs
  32. 1 1
      Terminal.Gui/Views/RadioGroup.cs
  33. 2 2
      Terminal.Gui/Views/ScrollBarView.cs
  34. 3 3
      Terminal.Gui/Views/ScrollView.cs
  35. 5 5
      Terminal.Gui/Views/Shortcut.cs
  36. 1 1
      Terminal.Gui/Views/Slider.cs
  37. 1 1
      Terminal.Gui/Views/StatusBar.cs
  38. 8 8
      Terminal.Gui/Views/TabView.cs
  39. 1 1
      Terminal.Gui/Views/TextView.cs
  40. 8 8
      Terminal.Gui/Views/TileView.cs
  41. 2 2
      Terminal.Gui/Views/Wizard/Wizard.cs
  42. 4 2
      UICatalog/Scenarios/Adornments.cs
  43. 36 311
      UICatalog/Scenarios/AllViewsTester.cs
  44. 1 1
      UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs
  45. 0 0
      UICatalog/Scenarios/AnimationScenario/Spinning_globe_dark_small.gif
  46. 0 0
      UICatalog/Scenarios/AnimationScenario/spinning-globe-attribution.txt
  47. 1 0
      UICatalog/Scenarios/Arrangement.cs
  48. 1 1
      UICatalog/Scenarios/ComputedLayout.cs
  49. 8 7
      UICatalog/Scenarios/ContentScrolling.cs
  50. 73 32
      UICatalog/Scenarios/Editors/AdornmentEditor.cs
  51. 68 75
      UICatalog/Scenarios/Editors/AdornmentsEditor.cs
  52. 34 32
      UICatalog/Scenarios/Editors/ArrangementEditor.cs
  53. 22 24
      UICatalog/Scenarios/Editors/BorderEditor.cs
  54. 300 0
      UICatalog/Scenarios/Editors/DimEditor.cs
  55. 14 9
      UICatalog/Scenarios/Editors/ExpanderButton.cs
  56. 228 0
      UICatalog/Scenarios/Editors/LayoutEditor.cs
  57. 7 8
      UICatalog/Scenarios/Editors/MarginEditor.cs
  58. 2 1
      UICatalog/Scenarios/Editors/PaddingEditor.cs
  59. 292 0
      UICatalog/Scenarios/Editors/PosEditor.cs
  60. 2 2
      UICatalog/Scenarios/GraphViewExample.cs
  61. 1 1
      UICatalog/Scenarios/LineDrawing.cs
  62. 1 1
      UICatalog/Scenarios/MenuBarScenario.cs
  63. 1 0
      UICatalog/Scenarios/Navigation.cs
  64. 1 0
      UICatalog/Scenarios/NumericUpDownDemo.cs
  65. 3 1
      UICatalog/Scenarios/ProgressBarStyles.cs
  66. 2 0
      UICatalog/Scenarios/ShadowStyles.cs
  67. 1 1
      UICatalog/Scenarios/Sliders.cs
  68. 1 1
      UICatalog/Scenarios/TextViewAutocompletePopup.cs
  69. 3 1
      UICatalog/Scenarios/ViewExperiments.cs
  70. 1 1
      UICatalog/Scenarios/VkeyPacketSimulator.cs
  71. 12 12
      UICatalog/UICatalog.cs
  72. 1 1
      UICatalog/UICatalog.csproj
  73. 2 2
      UnitTests/Dialogs/DialogTests.cs
  74. 4 4
      UnitTests/Drawing/ThicknessTests.cs
  75. 2 2
      UnitTests/UICatalog/ScenarioTests.cs
  76. 1 1
      UnitTests/View/Adornment/AdornmentSubViewTests.cs
  77. 2 2
      UnitTests/View/Adornment/AdornmentTests.cs
  78. 1 1
      UnitTests/View/Adornment/MarginTests.cs
  79. 1 1
      UnitTests/View/Adornment/PaddingTests.cs
  80. 2 2
      UnitTests/View/DiagnosticsTests.cs
  81. 3 3
      UnitTests/View/Draw/AllViewsDrawTests.cs
  82. 2 2
      UnitTests/View/Draw/DrawTests.cs
  83. 2 2
      UnitTests/View/Draw/NeedsDisplayTests.cs
  84. 3 3
      UnitTests/View/Layout/LayoutTests.cs
  85. 1 1
      UnitTests/View/Layout/Pos.CombineTests.cs
  86. 45 24
      UnitTests/View/Layout/SetLayoutTests.cs
  87. 4 4
      UnitTests/View/ViewTests.cs
  88. 1 1
      UnitTests/Views/ScrollBarViewTests.cs
  89. 1 1
      UnitTests/Views/ScrollViewTests.cs
  90. 1 1
      UnitTests/Views/StatusBarTests.cs
  91. 4 4
      UnitTests/Views/ToplevelTests.cs

+ 1 - 1
Terminal.Gui/Application/Application.Screen.cs

@@ -36,7 +36,7 @@ public static partial class Application // Screen related stuff
         foreach (Toplevel t in TopLevels)
         {
             t.OnSizeChanging (new (args.Size));
-            t.SetLayoutNeeded ();
+            t.SetNeedsLayout ();
         }
 
         Refresh ();

+ 3 - 3
Terminal.Gui/Drawing/Thickness.cs

@@ -82,7 +82,7 @@ public record struct Thickness
     /// <summary>Draws the <see cref="Thickness"/> rectangle with an optional diagnostics label.</summary>
     /// <remarks>
     ///     If <see cref="ViewDiagnosticFlags"/> is set to
-    ///     <see cref="ViewDiagnosticFlags.Padding"/> then 'T', 'L', 'R', and 'B' glyphs will be used instead of
+    ///     <see cref="ViewDiagnosticFlags.Thickness"/> then 'T', 'L', 'R', and 'B' glyphs will be used instead of
     ///     space. If <see cref="ViewDiagnosticFlags"/> is set to
     ///     <see cref="ViewDiagnosticFlags.Ruler"/> then a ruler will be drawn on the outer edge of the
     ///     Thickness.
@@ -104,7 +104,7 @@ public record struct Thickness
         Rune topChar = clearChar;
         Rune bottomChar = clearChar;
 
-        if (diagnosticFlags.HasFlag (ViewDiagnosticFlags.Padding))
+        if (diagnosticFlags.HasFlag (ViewDiagnosticFlags.Thickness))
         {
             leftChar = (Rune)'L';
             rightChar = (Rune)'R';
@@ -188,7 +188,7 @@ public record struct Thickness
             }
         }
 
-        if (diagnosticFlags.HasFlag (ViewDiagnosticFlags.Padding))
+        if (diagnosticFlags.HasFlag (ViewDiagnosticFlags.Thickness))
         {
             // Draw the diagnostics label on the bottom
             string text = label is null ? string.Empty : $"{label} {this}";

+ 34 - 72
Terminal.Gui/View/Adornment/Adornment.cs

@@ -1,7 +1,5 @@
 #nullable enable
-using System.ComponentModel;
 using Terminal.Gui;
-using static Terminal.Gui.SpinnerStyle;
 using Attribute = Terminal.Gui.Attribute;
 
 /// <summary>
@@ -28,7 +26,7 @@ public class Adornment : View, IDesignable
     /// <param name="parent"></param>
     public Adornment (View parent)
     {
-        // By default Adornments can't get focus; has to be enabled specifically.
+        // By default, Adornments can't get focus; has to be enabled specifically.
         CanFocus = false;
         TabStop = TabBehavior.NoStop;
         Parent = parent;
@@ -44,9 +42,12 @@ public class Adornment : View, IDesignable
     #region Thickness
 
     /// <summary>
-    /// 
+    ///     Gets or sets whether the Adornment will draw diagnostic information. This is a bit-field of <see cref="ViewDiagnosticFlags"/>.
     /// </summary>
-    public ViewDiagnosticFlags Diagnostics { get; set; } = View.Diagnostics;
+    /// <remarks>
+    ///     The <see cref="View.Diagnostics"/> static property is used as the default value for this property.
+    /// </remarks>
+    public new ViewDiagnosticFlags Diagnostics { get; set; } = View.Diagnostics;
 
     private Thickness _thickness = Thickness.Empty;
 
@@ -63,9 +64,8 @@ public class Adornment : View, IDesignable
             if (current != _thickness)
             {
                 Parent?.SetAdornmentFrames ();
-                SetLayoutNeeded ();
+                SetNeedsLayout ();
                 SetNeedsDisplay ();
-                //Parent?.SetLayoutNeeded ();
 
                 OnThicknessChanged ();
             }
@@ -73,14 +73,10 @@ public class Adornment : View, IDesignable
     }
 
     /// <summary>Fired whenever the <see cref="Thickness"/> property changes.</summary>
-    [CanBeNull]
     public event EventHandler? ThicknessChanged;
 
     /// <summary>Called whenever the <see cref="Thickness"/> property changes.</summary>
-    public void OnThicknessChanged ()
-    {
-        ThicknessChanged?.Invoke (this, EventArgs.Empty);
-    }
+    public void OnThicknessChanged () { ThicknessChanged?.Invoke (this, EventArgs.Empty); }
 
     #endregion Thickness
 
@@ -91,7 +87,8 @@ public class Adornment : View, IDesignable
     ///     <see cref="InvalidOperationException"/>.
     /// </summary>
     /// <remarks>
-    ///     While there are no real use cases for an Adornment being a subview, it is not explicitly dis-allowed to support testing. E.g. in AllViewsTester.
+    ///     While there are no real use cases for an Adornment being a subview, it is not explicitly dis-allowed to support
+    ///     testing. E.g. in AllViewsTester.
     /// </remarks>
     public override View? SuperView
     {
@@ -99,17 +96,6 @@ public class Adornment : View, IDesignable
         set => throw new InvalidOperationException (@"Adornments can not be Subviews or have SuperViews. Use Parent instead.");
     }
 
-    //internal override Adornment CreateAdornment (Type adornmentType)
-    //{
-    //    /* Do nothing - Adornments do not have Adornments */
-    //    return null;
-    //}
-
-    //internal override void LayoutAdornments ()
-    //{
-    //    /* Do nothing - Adornments do not have Adornments */
-    //}
-
     /// <summary>
     ///     Gets the rectangle that describes the area of the Adornment. The Location is always (0,0).
     ///     The size is the size of the <see cref="View.Frame"/>.
@@ -137,6 +123,7 @@ public class Adornment : View, IDesignable
 
                 return new (super, Frame.Size);
             }
+
             return Frame;
         }
 
@@ -150,16 +137,13 @@ public class Adornment : View, IDesignable
     }
 
     /// <inheritdoc/>
-    public override Point ScreenToFrame (in Point location)
-    {
-        return Parent!.ScreenToFrame (new (location.X - Frame.X, location.Y - Frame.Y));
-    }
+    public override Point ScreenToFrame (in Point location) { return Parent!.ScreenToFrame (new (location.X - Frame.X, location.Y - Frame.Y)); }
 
     /// <summary>Does nothing for Adornment</summary>
     /// <returns></returns>
     protected override bool OnDrawAdornments () { return false; }
 
-    /// <inheritdoc />
+    /// <inheritdoc/>
     protected override bool OnClearViewport (Rectangle viewport)
     {
         Attribute normalAttr = GetNormalColor ();
@@ -170,7 +154,7 @@ public class Adornment : View, IDesignable
 
         return true;
     }
-    
+
     /// <summary>Does nothing for Adornment</summary>
     /// <returns></returns>
     protected override bool OnRenderLineCanvas () { return false; }
@@ -185,70 +169,48 @@ public class Adornment : View, IDesignable
         set => throw new InvalidOperationException (@"Adornment can only render to their Parent or Parent's Superview.");
     }
 
-    #endregion View Overrides
-
-    #region Mouse Support
-
-
     /// <summary>
-    /// Indicates whether the specified Parent's SuperView-relative coordinates are within the Adornment's Thickness.
+    ///     Indicates whether the specified Parent's SuperView-relative coordinates are within the Adornment's Thickness.
     /// </summary>
     /// <remarks>
     ///     The <paramref name="location"/> is relative to the PARENT's SuperView.
     /// </remarks>
     /// <param name="location"></param>
-    /// <returns><see langword="true"/> if the specified Parent's SuperView-relative coordinates are within the Adornment's Thickness. </returns>
+    /// <returns>
+    ///     <see langword="true"/> if the specified Parent's SuperView-relative coordinates are within the Adornment's
+    ///     Thickness.
+    /// </returns>
     public override bool Contains (in Point location)
     {
-        if (Parent is null)
+        View? parentOrSuperView = Parent;
+
+        if (parentOrSuperView is null)
         {
-            return false;
+            // While there are no real use cases for an Adornment being a subview, we support it for
+            // testing. E.g. in AllViewsTester.
+            parentOrSuperView = SuperView;
+
+            if (parentOrSuperView is null)
+            {
+                return false;
+            }
         }
 
         Rectangle outside = Frame;
-        outside.Offset (Parent.Frame.Location);
+        outside.Offset (parentOrSuperView.Frame.Location);
 
         return Thickness.Contains (outside, location);
     }
 
-    ///// <inheritdoc/>
-    //protected override bool OnMouseEnter (CancelEventArgs mouseEvent)
-    //{
-    //    // Invert Normal
-    //    if (Diagnostics.HasFlag (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null)
-    //    {
-    //        var cs = new ColorScheme (ColorScheme)
-    //        {
-    //            Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground)
-    //        };
-    //        ColorScheme = cs;
-    //    }
-
-    //    return false;
-    //}
-
-    ///// <inheritdoc/>   
-    //protected override void OnMouseLeave ()
-    //{
-    //    // Invert Normal
-    //    if (Diagnostics.FastHasFlags (ViewDiagnosticFlags.MouseEnter) && ColorScheme != null)
-    //    {
-    //        var cs = new ColorScheme (ColorScheme)
-    //        {
-    //            Normal = new (ColorScheme.Normal.Background, ColorScheme.Normal.Foreground)
-    //        };
-    //        ColorScheme = cs;
-    //    }
-    //}
-    #endregion Mouse Support
-
+    #endregion View Overrides
 
     /// <inheritdoc/>
     bool IDesignable.EnableForDesign ()
     {
+        // This enables AllViewsTester to show something useful.
         Thickness = new (3);
         Frame = new (0, 0, 10, 10);
-        Diagnostics = ViewDiagnosticFlags.Padding;
+        Diagnostics = ViewDiagnosticFlags.Thickness;
 
         return true;
     }

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

@@ -21,7 +21,7 @@ public class Margin : Adornment
         // BUGBUG: We should not set HighlightStyle.Pressed here, but wherever it is actually needed
         // HighlightStyle |= HighlightStyle.Pressed;
         Highlight += Margin_Highlight;
-        LayoutStarted += Margin_LayoutStarted;
+        SubviewLayout += Margin_LayoutStarted;
 
         // Margin should not be focusable
         CanFocus = false;

+ 2 - 2
Terminal.Gui/View/Layout/Dim.cs

@@ -259,7 +259,7 @@ public abstract record Dim : IEqualityOperators<Dim, Dim, bool>
         }
 
         var newDim = new DimCombine (AddOrSubtract.Add, left, right);
-        (left as DimView)?.Target?.SetLayoutNeeded ();
+        (left as DimView)?.Target?.SetNeedsLayout ();
 
         return newDim;
     }
@@ -284,7 +284,7 @@ public abstract record Dim : IEqualityOperators<Dim, Dim, bool>
         }
 
         var newDim = new DimCombine (AddOrSubtract.Subtract, left, right);
-        (left as DimView)?.Target?.SetLayoutNeeded ();
+        (left as DimView)?.Target?.SetNeedsLayout ();
 
         return newDim;
     }

+ 23 - 13
Terminal.Gui/View/Layout/DimAuto.cs

@@ -70,15 +70,15 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
 
         if (Style.FastHasFlags (DimAutoStyle.Content))
         {
-            if (!us.ContentSizeTracksViewport)
+            maxCalculatedSize = textSize;
+
+            if (us is { ContentSizeTracksViewport: false, Subviews.Count: 0 })
             {
                 // ContentSize was explicitly set. Use `us.ContentSize` to determine size.
                 maxCalculatedSize = dimension == Dimension.Width ? us.GetContentSize ().Width : us.GetContentSize ().Height;
             }
             else
             {
-                maxCalculatedSize = textSize;
-
                 // TOOD: All the below is a naive implementation. It may be possible to optimize this.
 
                 List<View> includedSubviews = us.Subviews.ToList ();
@@ -130,7 +130,10 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
                 {
                     notDependentSubViews = includedSubviews.Where (
                                                                    v => v.Width is { }
-                                                                        && (v.X is PosAbsolute or PosFunc || v.Width is DimAuto or DimAbsolute or DimFunc) // BUGBUG: We should use v.X.Has and v.Width.Has?
+                                                                        && (v.X is PosAbsolute or PosFunc
+                                                                            || v.Width is DimAuto
+                                                                                          or DimAbsolute
+                                                                                          or DimFunc) // BUGBUG: We should use v.X.Has and v.Width.Has?
                                                                         && !v.X.Has<PosAnchorEnd> (out _)
                                                                         && !v.X.Has<PosAlign> (out _)
                                                                         && !v.X.Has<PosCenter> (out _)
@@ -143,7 +146,10 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
                 {
                     notDependentSubViews = includedSubviews.Where (
                                                                    v => v.Height is { }
-                                                                        && (v.Y is PosAbsolute or PosFunc || v.Height is DimAuto or DimAbsolute or DimFunc) // BUGBUG: We should use v.Y.Has and v.Height.Has?
+                                                                        && (v.Y is PosAbsolute or PosFunc
+                                                                            || v.Height is DimAuto
+                                                                                           or DimAbsolute
+                                                                                           or DimFunc) // BUGBUG: We should use v.Y.Has and v.Height.Has?
                                                                         && !v.Y.Has<PosAnchorEnd> (out _)
                                                                         && !v.Y.Has<PosAlign> (out _)
                                                                         && !v.Y.Has<PosCenter> (out _)
@@ -239,11 +245,13 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
                                                               v =>
                                                               {
                                                                   return dimension switch
-                                                                         {
-                                                                             Dimension.Width when v.X.Has<PosAlign> (out PosAlign posAlign) => ((PosAlign)posAlign).GroupId,
-                                                                             Dimension.Height when v.Y.Has<PosAlign> (out PosAlign posAlign) => ((PosAlign)posAlign).GroupId,
-                                                                             _ => -1
-                                                                         };
+                                                                  {
+                                                                      Dimension.Width when v.X.Has<PosAlign> (out PosAlign posAlign) =>
+                                                                              ((PosAlign)posAlign).GroupId,
+                                                                      Dimension.Height when v.Y.Has<PosAlign> (out PosAlign posAlign) =>
+                                                                              ((PosAlign)posAlign).GroupId,
+                                                                      _ => -1
+                                                                  };
                                                               })
                                                      .Distinct ()
                                                      .ToList ();
@@ -376,6 +384,7 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
                 #endregion DimView
 
                 #region DimAuto
+
                 // [ ] DimAuto      - Dimension is internally calculated
 
                 List<View> dimAutoSubViews;
@@ -393,9 +402,9 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
                 {
                     View v = dimAutoSubViews [i];
 
-                    int maxDimAuto = dimension == Dimension.Width 
-                                        ? v.Frame.X + v.Width!.Calculate (0, maxCalculatedSize, v, dimension) 
-                                        : v.Frame.Y + v.Height!.Calculate (0, maxCalculatedSize, v, dimension);
+                    int maxDimAuto = dimension == Dimension.Width
+                                         ? v.Frame.X + v.Width!.Calculate (0, maxCalculatedSize, v, dimension)
+                                         : v.Frame.Y + v.Height!.Calculate (0, maxCalculatedSize, v, dimension);
 
                     if (maxDimAuto > maxCalculatedSize)
                     {
@@ -407,6 +416,7 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
 
 
                 #region DimFill
+
                 //// [ ] DimFill      - Dimension is internally calculated
 
                 //List<View> DimFillSubViews;

+ 1 - 1
Terminal.Gui/View/Layout/DimFunc.cs

@@ -8,7 +8,7 @@ namespace Terminal.Gui;
 ///     This is a low-level API that is typically used internally by the layout system. Use the various static
 ///     methods on the <see cref="Gui.Dim"/> class to create <see cref="Gui.Dim"/> objects instead.
 /// </remarks>
-/// <param name="Fn">The function that computes the dimension. If this function throws <see cref="Exception"/> </param>
+/// <param name="Fn">The function that computes the dimension. If this function throws <see cref="LayoutException"/>... </param>
 public record DimFunc (Func<int> Fn) : Dim
 {
     /// <summary>

+ 1 - 1
Terminal.Gui/View/Layout/LayoutEventArgs.cs

@@ -1,6 +1,6 @@
 namespace Terminal.Gui;
 
-/// <summary>Event arguments for the <see cref="View.LayoutComplete"/> event.</summary>
+/// <summary>Event arguments for the <see cref="View.SubviewsLaidOut"/> event.</summary>
 public class LayoutEventArgs : EventArgs
 {
     /// <summary>Creates a new instance of the <see cref="Terminal.Gui.LayoutEventArgs"/> class.</summary>

+ 30 - 0
Terminal.Gui/View/Layout/LayoutException.cs

@@ -0,0 +1,30 @@
+#nullable enable
+namespace Terminal.Gui;
+
+/// <summary>
+///     Represents an exception that is thrown when a layout operation fails.
+/// </summary>
+[Serializable]
+public class LayoutException : Exception
+{
+
+    /// <summary>
+    ///     Creates a new instance of <see cref="LayoutException"/>.
+    /// </summary>
+    public LayoutException () { }
+
+    /// <summary>
+    ///     Creates a new instance of <see cref="LayoutException"/>.
+    /// </summary>
+    /// <param name="message"></param>
+    public LayoutException (string? message) : base (message) { }
+
+    /// <summary>
+    ///     Creates a new instance of <see cref="LayoutException"/>.
+    /// </summary>
+    /// <param name="message"></param>
+    /// <param name="innerException"></param>
+    public LayoutException (string? message, Exception? innerException)
+        : base (message, innerException)
+    { }
+}

+ 2 - 2
Terminal.Gui/View/Layout/Pos.cs

@@ -366,7 +366,7 @@ public abstract record Pos
 
         if (left is PosView view)
         {
-            view.Target?.SetLayoutNeeded ();
+            view.Target?.SetNeedsLayout ();
         }
 
         return newPos;
@@ -395,7 +395,7 @@ public abstract record Pos
 
         if (left is PosView view)
         {
-            view.Target?.SetLayoutNeeded ();
+            view.Target?.SetNeedsLayout ();
         }
 
         return newPos;

+ 1 - 1
Terminal.Gui/View/Layout/PosAlign.cs

@@ -29,7 +29,7 @@ public record PosAlign : Pos
     /// <summary>
     ///     The cached location. Used to store the calculated location to minimize recalculating it.
     /// </summary>
-    public int? _cachedLocation;
+    internal int? _cachedLocation;
 
     private readonly Aligner? _aligner;
 

+ 1 - 1
Terminal.Gui/View/Layout/PosAnchorEnd.cs

@@ -36,7 +36,7 @@ public record PosAnchorEnd : Pos
     public bool UseDimForOffset { get; }
 
     /// <inheritdoc/>
-    public override string ToString () { return UseDimForOffset ? "AnchorEnd()" : $"AnchorEnd({Offset})"; }
+    public override string ToString () { return UseDimForOffset ? "AnchorEnd" : $"AnchorEnd({Offset})"; }
 
     internal override int GetAnchor (int size)
     {

+ 1 - 1
Terminal.Gui/View/Layout/PosFunc.cs

@@ -4,7 +4,7 @@ namespace Terminal.Gui;
 /// <summary>
 ///     Represents a position that is computed by executing a function that returns an integer position.
 /// </summary>
-/// <param name="Fn">The function that computes the position.</param>
+/// <param name="Fn">The function that computes the dimension. If this function throws <see cref="LayoutException"/>... </param>
 public record PosFunc (Func<int> Fn) : Pos
 {
     /// <inheritdoc/>

+ 4 - 4
Terminal.Gui/View/View.Adornments.cs

@@ -54,7 +54,7 @@ public partial class View // Adornments
     ///     </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"/> which will call <see cref="SetLayoutNeeded"/> to update the layout of the
+    ///         change the size of <see cref="Frame"/> which will call <see cref="SetNeedsLayout"/> to update the layout of the
     ///         <see cref="SuperView"/> and its <see cref="Subviews"/>.
     ///     </para>
     /// </remarks>
@@ -110,7 +110,7 @@ public partial class View // Adornments
     ///     </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"/> which will call <see cref="SetLayoutNeeded"/> to update the layout of the
+    ///         change the size of <see cref="Frame"/> which will call <see cref="SetNeedsLayout"/> to update the layout of the
     ///         <see cref="SuperView"/> and its <see cref="Subviews"/>.
     ///     </para>
     /// </remarks>
@@ -149,7 +149,7 @@ public partial class View // Adornments
 
             SetBorderStyle (e.NewValue);
             SetAdornmentFrames ();
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
 
         }
     }
@@ -225,7 +225,7 @@ public partial class View // Adornments
     ///     </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"/> which will call <see cref="SetLayoutNeeded"/> to update the layout of the
+    ///         change the size of <see cref="Frame"/> which will call <see cref="SetNeedsLayout"/> to update the layout of the
     ///         <see cref="SuperView"/> and its <see cref="Subviews"/>.
     ///     </para>
     /// </remarks>

+ 2 - 2
Terminal.Gui/View/View.Content.cs

@@ -151,7 +151,7 @@ public partial class View
 
         if (e.Cancel != true)
         {
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
         }
 
         return e.Cancel;
@@ -308,7 +308,7 @@ public partial class View
             if (_viewportLocation != viewport.Location)
             {
                 _viewportLocation = viewport.Location;
-                SetLayoutNeeded ();
+                SetNeedsLayout ();
             }
 
             OnViewportChanged (new (IsInitialized ? Viewport : Rectangle.Empty, oldViewport));

+ 13 - 5
Terminal.Gui/View/View.Diagnostics.cs

@@ -9,15 +9,15 @@ public enum ViewDiagnosticFlags : uint
     Off = 0b_0000_0000,
 
     /// <summary>
-    ///     When enabled, <see cref="View.OnDrawAdornments"/> will draw a ruler in the Thickness.
+    ///     When enabled, <see cref="Adornment"/> will draw a ruler in the Thickness. See <see cref="Adornment.Diagnostics"/>.
     /// </summary>
     Ruler = 0b_0000_0001,
 
     /// <summary>
-    ///     When enabled, <see cref="View.OnDrawAdornments"/> will draw the first letter of the Adornment name ('M', 'B', or 'P')
-    ///     in the Thickness.
+    ///     When enabled, <see cref="Adornment"/> will draw the first letter of the Adornment name ('M', 'B', or 'P')
+    ///     in the Thickness. See <see cref="Adornment.Diagnostics"/>.
     /// </summary>
-    Padding = 0b_0000_0010,
+    Thickness = 0b_0000_0010,
 
     /// <summary>
     ///     When enabled the View's colors will be darker when the mouse is hovering over the View (See <see cref="View.MouseEnter"/> and <see cref="View.MouseLeave"/>.
@@ -27,6 +27,14 @@ public enum ViewDiagnosticFlags : uint
 
 public partial class View
 {
-    /// <summary>Flags to enable/disable <see cref="View"/> diagnostics.</summary>
+    /// <summary>Gets or sets whether diagnostic information will be drawn. This is a bit-field of <see cref="ViewDiagnosticFlags"/>.e <see cref="View"/> diagnostics.</summary>
+    /// <remarks>
+    /// <para>
+    ///     <see cref="Adornment.Diagnostics"/> gets set to this property by default, enabling <see cref="ViewDiagnosticFlags.Ruler"/> and <see cref="ViewDiagnosticFlags.Thickness"/>.
+    /// </para>
+    /// <para>
+    ///     <see cref="ViewDiagnosticFlags.Hover"/> is enabled for all Views independently of Adornments.
+    /// </para>
+    /// </remarks>
     public static ViewDiagnosticFlags Diagnostics { get; set; }
 }

+ 2 - 2
Terminal.Gui/View/View.Hierarchy.cs

@@ -86,7 +86,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
         }
 
         SetNeedsDisplay ();
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
 
         return view;
     }
@@ -176,7 +176,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
         }
         view._superView = null;
 
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
         SetNeedsDisplay ();
 
         foreach (View v in _subviews)

+ 263 - 248
Terminal.Gui/View/View.Layout.cs

@@ -6,6 +6,8 @@ namespace Terminal.Gui;
 
 public partial class View // Layout APIs
 {
+    #region Frame/Position/Dimension
+
     /// <summary>
     ///     Indicates whether the specified SuperView-relative coordinates are within the View's <see cref="Frame"/>.
     /// </summary>
@@ -13,160 +15,6 @@ public partial class View // Layout APIs
     /// <returns><see langword="true"/> if the specified SuperView-relative coordinates are within the View.</returns>
     public virtual bool Contains (in Point location) { return Frame.Contains (location); }
 
-    // BUGBUG: This method interferes with Dialog/MessageBox default min/max size.
-    /// <summary>
-    ///     Gets a new location of the <see cref="View"/> that is within the Viewport of the <paramref name="viewToMove"/>'s
-    ///     <see cref="View.SuperView"/> (e.g. for dragging a Window). The `out` parameters are the new X and Y coordinates.
-    /// </summary>
-    /// <remarks>
-    ///     If <paramref name="viewToMove"/> does not have a <see cref="View.SuperView"/> or it's SuperView is not
-    ///     <see cref="Application.Top"/> the position will be bound by  <see cref="Application.Screen"/>.
-    /// </remarks>
-    /// <param name="viewToMove">The View that is to be moved.</param>
-    /// <param name="targetX">The target x location.</param>
-    /// <param name="targetY">The target y location.</param>
-    /// <param name="nx">The new x location that will ensure <paramref name="viewToMove"/> will be fully visible.</param>
-    /// <param name="ny">The new y location that will ensure <paramref name="viewToMove"/> will be fully visible.</param>
-    /// <returns>
-    ///     Either <see cref="Application.Top"/> (if <paramref name="viewToMove"/> does not have a Super View) or
-    ///     <paramref name="viewToMove"/>'s SuperView. This can be used to ensure LayoutSubviews is called on the correct View.
-    /// </returns>
-    internal static View? GetLocationEnsuringFullVisibility (
-        View viewToMove,
-        int targetX,
-        int targetY,
-        out int nx,
-        out int ny
-    //,
-    // out StatusBar? statusBar
-    )
-    {
-        int maxDimension;
-        View? superView;
-        //statusBar = null!;
-
-        if (viewToMove is not Toplevel || viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
-        {
-            maxDimension = Application.Screen.Width;
-            superView = Application.Top;
-        }
-        else
-        {
-            // Use the SuperView's Viewport, not Frame
-            maxDimension = viewToMove!.SuperView.Viewport.Width;
-            superView = viewToMove.SuperView;
-        }
-
-        if (superView?.Margin is { } && superView == viewToMove!.SuperView)
-        {
-            maxDimension -= superView.GetAdornmentsThickness ().Left + superView.GetAdornmentsThickness ().Right;
-        }
-
-        if (viewToMove!.Frame.Width <= maxDimension)
-        {
-            nx = Math.Max (targetX, 0);
-            nx = nx + viewToMove.Frame.Width > maxDimension ? Math.Max (maxDimension - viewToMove.Frame.Width, 0) : nx;
-
-            if (nx > viewToMove.Frame.X + viewToMove.Frame.Width)
-            {
-                nx = Math.Max (viewToMove.Frame.Right, 0);
-            }
-        }
-        else
-        {
-            nx = targetX;
-        }
-
-        //System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
-        var menuVisible = false;
-        var statusVisible = false;
-
-        if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
-        {
-            menuVisible = Application.Top?.MenuBar?.Visible == true;
-        }
-        else
-        {
-            View? t = viewToMove!.SuperView;
-
-            while (t is { } and not Toplevel)
-            {
-                t = t.SuperView;
-            }
-
-            if (t is Toplevel topLevel)
-            {
-                menuVisible = topLevel.MenuBar?.Visible == true;
-            }
-        }
-
-        if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
-        {
-            maxDimension = menuVisible ? 1 : 0;
-        }
-        else
-        {
-            maxDimension = 0;
-        }
-
-        ny = Math.Max (targetY, maxDimension);
-
-        //if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
-        //{
-        //    statusVisible = Application.Top?.StatusBar?.Visible == true;
-        //    statusBar = Application.Top?.StatusBar!;
-        //}
-        //else
-        //{
-        //    View? t = viewToMove!.SuperView;
-
-        //    while (t is { } and not Toplevel)
-        //    {
-        //        t = t.SuperView;
-        //    }
-
-        //    if (t is Toplevel topLevel)
-        //    {
-        //        statusVisible = topLevel.StatusBar?.Visible == true;
-        //        statusBar = topLevel.StatusBar!;
-        //    }
-        //}
-
-        if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
-        {
-            maxDimension = statusVisible ? Application.Screen.Height - 1 : Application.Screen.Height;
-        }
-        else
-        {
-            maxDimension = statusVisible ? viewToMove!.SuperView.Viewport.Height - 1 : viewToMove!.SuperView.Viewport.Height;
-        }
-
-        if (superView?.Margin is { } && superView == viewToMove?.SuperView)
-        {
-            maxDimension -= superView.GetAdornmentsThickness ().Top + superView.GetAdornmentsThickness ().Bottom;
-        }
-
-        ny = Math.Min (ny, maxDimension);
-
-        if (viewToMove?.Frame.Height <= maxDimension)
-        {
-            ny = ny + viewToMove.Frame.Height > maxDimension
-                     ? Math.Max (maxDimension - viewToMove.Frame.Height, menuVisible ? 1 : 0)
-                     : ny;
-
-            if (ny > viewToMove.Frame.Y + viewToMove.Frame.Height)
-            {
-                ny = Math.Max (viewToMove.Frame.Bottom, 0);
-            }
-        }
-
-        //System.Diagnostics.Debug.WriteLine ($"ny:{ny}, rHeight:{rHeight}");
-
-        return superView!;
-    }
-
-    #region Frame
-
     private Rectangle? _frame;
 
     /// <summary>Gets or sets the absolute location and dimension of the view.</summary>
@@ -243,7 +91,7 @@ public partial class View // Layout APIs
         SetAdornmentFrames ();
 
         SetNeedsDisplay ();
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
 
         // BUGBUG: When SetFrame is called from Frame_set, this event gets raised BEFORE OnResizeNeeded. Is that OK?
         OnViewportChanged (new (IsInitialized ? Viewport : Rectangle.Empty, oldViewport));
@@ -311,7 +159,8 @@ public partial class View // Layout APIs
     // helper for X, Y, Width, Height setters to ensure consistency
     private void PosDimSet ()
     {
-        SetLayoutNeeded ();
+        _needsLayout = false;
+        SetNeedsLayout ();
 
         if (_x is PosAbsolute && _y is PosAbsolute && _width is DimAbsolute && _height is DimAbsolute)
         {
@@ -440,12 +289,6 @@ public partial class View // Layout APIs
                 return;
             }
 
-            if (_height is { } && _height.Has<DimAuto> (out _))
-            {
-                // Reset ContentSize to Viewport
-                _contentSize = null;
-            }
-
             _height = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (Height)} cannot be null");
 
             // Reset TextFormatter - Will be recalculated in SetTextFormatterSize
@@ -492,12 +335,6 @@ public partial class View // Layout APIs
                 return;
             }
 
-            if (_width is { } && _width.Has<DimAuto> (out _))
-            {
-                // Reset ContentSize to Viewport
-                _contentSize = null;
-            }
-
             _width = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (Width)} cannot be null");
 
             // Reset TextFormatter - Will be recalculated in SetTextFormatterSize
@@ -506,9 +343,9 @@ public partial class View // Layout APIs
         }
     }
 
-    #endregion Frame
+    #endregion Frame/Position/Dimension
 
-    #region Layout Engine
+    #region Core Layout API
 
     /// <summary>
     ///     Performs layout of the view and its subviews within the specified content size.
@@ -527,24 +364,13 @@ public partial class View // Layout APIs
     /// <returns><see langword="false"/>If the view could not be laid out (typically because a dependencies was not ready). </returns>
     public bool Layout (Size contentSize)
     {
-        int bailAfter = 100;
-        // Note, SetRelativeLayout calls SetTextFormatterSize
-        //while (NeedsLayout)
-        //{
-            if (SetRelativeLayout (contentSize))
-            {
-                LayoutSubviews ();
-
-               // Debug.Assert(!NeedsLayout);
-                return true;
-            }
+        if (SetRelativeLayout (contentSize))
+        {
+            LayoutSubviews ();
 
-        //    if (--bailAfter == 0)
-        //    {
-        //        Debug.Write ($"Layout: After {100} tries, SetRelativeLayout was unable to complete.");
-        //        return false;
-        //    }
-        //}
+            // Debug.Assert(!NeedsLayout);
+            return true;
+        }
 
         return false;
     }
@@ -565,23 +391,9 @@ public partial class View // Layout APIs
     /// <returns><see langword="false"/>If the view could not be laid out (typically because dependency was not ready). </returns>
     public bool Layout ()
     {
-        return Layout (GetBestGuessSuperViewContentSize ());
+        return Layout (GetContainerSize ());
     }
 
-    /// <summary>Fired after the View's <see cref="LayoutSubviews"/> method has completed.</summary>
-    /// <remarks>
-    ///     Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has
-    ///     otherwise changed.
-    /// </remarks>
-    public event EventHandler<LayoutEventArgs>? LayoutComplete;
-
-    /// <summary>Fired after the View's <see cref="LayoutSubviews"/> method has completed.</summary>
-    /// <remarks>
-    ///     Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has
-    ///     otherwise changed.
-    /// </remarks>
-    public event EventHandler<LayoutEventArgs>? LayoutStarted;
-
     /// <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"/>,
@@ -613,7 +425,10 @@ public partial class View // Layout APIs
         Debug.Assert (_height is { });
 
         CheckDimAuto ();
+
+        // TODO: Should move to View.LayoutSubviews?
         SetTextFormatterSize ();
+
         int newX, newW, newY, newH;
 
         try
@@ -653,9 +468,9 @@ public partial class View // Layout APIs
             }
 
         }
-        catch (Exception _)
+        catch (LayoutException le)
         {
-            // A Dim/PosFunc threw indicating it could not calculate (typically because a dependent View was not laid out).
+            Debug.WriteLine ($"A Dim/PosFunc function threw (typically this is because a dependent View was not laid out)\n{le}.");
             return false;
         }
 
@@ -720,7 +535,7 @@ public partial class View // Layout APIs
     ///         The position and dimensions of the view are indeterminate until the view has been initialized. Therefore, the
     ///         behavior of this method is indeterminate if <see cref="IsInitialized"/> is <see langword="false"/>.
     ///     </para>
-    ///     <para>Raises the <see cref="LayoutComplete"/> event before it returns.</para>
+    ///     <para>Raises the <see cref="SubviewsLaidOut"/> event before it returns.</para>
     /// </remarks>
     internal void LayoutSubviews ()
     {
@@ -732,12 +547,23 @@ public partial class View // Layout APIs
         CheckDimAuto ();
 
         Size contentSize = GetContentSize ();
-        OnLayoutStarted (new (contentSize));
+
+        OnSubviewLayout (new (contentSize));
+        SubviewLayout?.Invoke(this, new (contentSize));
 
         // The Adornments already have their Frame's set by SetRelativeLayout so we call LayoutSubViews vs. Layout here.
-        Margin?.LayoutSubviews ();
-        Border?.LayoutSubviews ();
-        Padding?.LayoutSubviews ();
+        if (Margin is { Subviews.Count: > 0 })
+        {
+            Margin.LayoutSubviews ();
+        }
+        if (Border is { Subviews.Count: > 0 })
+        {
+            Border.LayoutSubviews ();
+        }
+        if (Padding is { Subviews.Count: > 0 })
+        {
+            Padding.LayoutSubviews ();
+        }
 
         // Sort out the dependencies of the X, Y, Width, Height properties
         HashSet<View> nodes = new ();
@@ -777,22 +603,46 @@ public partial class View // Layout APIs
 
         _needsLayout = layoutStillNeeded;
 
-        OnLayoutComplete (new (contentSize));
+        OnSubviewsLaidOut (new (contentSize));
+        SubviewsLaidOut?.Invoke (this, new (contentSize));
     }
 
-
     /// <summary>
-    ///     Raises the <see cref="LayoutComplete"/> event. Called from  <see cref="LayoutSubviews"/> before all sub-views
+    ///     Called from <see cref="LayoutSubviews"/> before any subviews
     ///     have been laid out.
     /// </summary>
-    internal virtual void OnLayoutComplete (LayoutEventArgs args) { LayoutComplete?.Invoke (this, args); }
+    /// <remarks>
+    ///     Override to perform tasks when the layout is changing.
+    /// </remarks>
+    protected virtual void OnSubviewLayout (LayoutEventArgs args) {  }
+
+    /// <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>
+    public event EventHandler<LayoutEventArgs>? SubviewLayout;
 
     /// <summary>
-    ///     Raises the <see cref="LayoutStarted"/> event. Called from  <see cref="LayoutSubviews"/> before any subviews
+    ///     Called from <see cref="LayoutSubviews"/> after all sub-views
     ///     have been laid out.
     /// </summary>
-    internal virtual void OnLayoutStarted (LayoutEventArgs args) { LayoutStarted?.Invoke (this, args); }
+    /// <remarks>
+    ///     Override to perform tasks after the <see cref="View"/> has been resized or the layout has
+    ///     otherwise changed.
+    /// </remarks>
+    protected virtual void OnSubviewsLaidOut (LayoutEventArgs args) { }
+
+    /// <summary>Raised after all sub-views have been laid out.</summary>
+    /// <remarks>
+    ///     Subscribe to this event to perform tasks after the <see cref="View"/> has been resized or the layout has
+    ///     otherwise changed.
+    /// </remarks>
+    public event EventHandler<LayoutEventArgs>? SubviewsLaidOut;
 
+    #endregion Core Layout API
+
+    #region NeedsLayout
 
     // We expose no setter for this to ensure that the ONLY place it's changed is in SetNeedsLayout
     private bool _needsLayout = false;
@@ -820,7 +670,7 @@ public partial class View // Layout APIs
     ///     </para>
     /// </remarks>
 
-    public void SetLayoutNeeded ()
+    public void SetNeedsLayout ()
     {
         if (NeedsLayout)
         {
@@ -830,9 +680,18 @@ public partial class View // Layout APIs
 
         _needsLayout = true;
 
-        Margin?.SetLayoutNeeded ();
-        Border?.SetLayoutNeeded ();
-        Padding?.SetLayoutNeeded ();
+        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);
@@ -843,9 +702,18 @@ public partial class View // Layout APIs
             if (!current.NeedsLayout)
             {
                 current._needsLayout = true;
-                current.Margin?.SetLayoutNeeded ();
-                current.Border?.SetLayoutNeeded ();
-                current.Padding?.SetLayoutNeeded ();
+                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 ();
+                }
 
                 foreach (View subview in current.Subviews)
                 {
@@ -856,28 +724,18 @@ public partial class View // Layout APIs
 
         TextFormatter.NeedsFormat = true;
 
-        SuperView?.SetLayoutNeeded ();
+        SuperView?.SetNeedsLayout ();
 
         if (this is Adornment adornment)
         {
-            adornment.Parent?.SetLayoutNeeded ();
+            adornment.Parent?.SetNeedsLayout ();
         }
     }
 
-    /// <summary>
-    ///    INTERNAL API FOR UNIT TESTS - Gets the size of the SuperView's content (nominally the same as
-    ///    the SuperView's <see cref="GetContentSize ()"/>).
-    /// </summary>
-    /// <returns></returns>
-    private Size GetBestGuessSuperViewContentSize ()
-    {
-        Size superViewContentSize = SuperView?.GetContentSize () ??
-                                    (Application.Top is { } && Application.Top != this && Application.Top.IsInitialized
-                                         ? Application.Top.GetContentSize ()
-                                         : Application.Screen.Size);
+    #endregion NeedsLayout
+
+    #region Topological Sort
 
-        return superViewContentSize;
-    }
 
     /// <summary>
     ///     INTERNAL API - Collects all views and their dependencies from a given starting view for layout purposes. Used by
@@ -1038,16 +896,16 @@ public partial class View // Layout APIs
             {
                 if (ReferenceEquals (from.SuperView, to))
                 {
-                    throw new InvalidOperationException (
-                                                         $"ComputedLayout for \"{superView}\": \"{to}\" "
-                                                         + $"references a SubView (\"{from}\")."
-                                                        );
+                    throw new LayoutException (
+                                               $"ComputedLayout for \"{superView}\": \"{to}\" "
+                                               + $"references a SubView (\"{from}\")."
+                                              );
                 }
 
-                throw new InvalidOperationException (
-                                                     $"ComputedLayout for \"{superView}\": \"{from}\" "
-                                                     + $"linked with \"{to}\" was not found. Did you forget to add it to {superView}?"
-                                                    );
+                throw new LayoutException (
+                                           $"ComputedLayout for \"{superView}\": \"{from}\" "
+                                           + $"linked with \"{to}\" was not found. Did you forget to add it to {superView}?"
+                                          );
             }
         }
 
@@ -1055,6 +913,163 @@ public partial class View // Layout APIs
         return result;
     } // TopologicalSort
 
+    #endregion Topological Sort
+
+    #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.
+    /// </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);
+
+        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>
+    ///     Gets a new location of the <see cref="View"/> that is within the Viewport of the <paramref name="viewToMove"/>'s
+    ///     <see cref="View.SuperView"/> (e.g. for dragging a Window). The `out` parameters are the new X and Y coordinates.
+    /// </summary>
+    /// <remarks>
+    ///     If <paramref name="viewToMove"/> does not have a <see cref="View.SuperView"/> or it's SuperView is not
+    ///     <see cref="Application.Top"/> the position will be bound by  <see cref="Application.Screen"/>.
+    /// </remarks>
+    /// <param name="viewToMove">The View that is to be moved.</param>
+    /// <param name="targetX">The target x location.</param>
+    /// <param name="targetY">The target y location.</param>
+    /// <param name="nx">The new x location that will ensure <paramref name="viewToMove"/> will be fully visible.</param>
+    /// <param name="ny">The new y location that will ensure <paramref name="viewToMove"/> will be fully visible.</param>
+    /// <returns>
+    ///     Either <see cref="Application.Top"/> (if <paramref name="viewToMove"/> does not have a Super View) or
+    ///     <paramref name="viewToMove"/>'s SuperView. This can be used to ensure LayoutSubviews is called on the correct View.
+    /// </returns>
+    internal static View? GetLocationEnsuringFullVisibility (
+        View viewToMove,
+        int targetX,
+        int targetY,
+        out int nx,
+        out int ny
+    )
+    {
+        int maxDimension;
+        View? superView;
+
+        if (viewToMove is not Toplevel || viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
+        {
+            maxDimension = Application.Screen.Width;
+            superView = Application.Top;
+        }
+        else
+        {
+            // Use the SuperView's Viewport, not Frame
+            maxDimension = viewToMove!.SuperView.Viewport.Width;
+            superView = viewToMove.SuperView;
+        }
+
+        if (superView?.Margin is { } && superView == viewToMove!.SuperView)
+        {
+            maxDimension -= superView.GetAdornmentsThickness ().Left + superView.GetAdornmentsThickness ().Right;
+        }
+
+        if (viewToMove!.Frame.Width <= maxDimension)
+        {
+            nx = Math.Max (targetX, 0);
+            nx = nx + viewToMove.Frame.Width > maxDimension ? Math.Max (maxDimension - viewToMove.Frame.Width, 0) : nx;
+
+            if (nx > viewToMove.Frame.X + viewToMove.Frame.Width)
+            {
+                nx = Math.Max (viewToMove.Frame.Right, 0);
+            }
+        }
+        else
+        {
+            nx = targetX;
+        }
+
+        //System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
+        var menuVisible = false;
+        var statusVisible = false;
+
+        if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
+        {
+            menuVisible = Application.Top?.MenuBar?.Visible == true;
+        }
+        else
+        {
+            View? t = viewToMove!.SuperView;
+
+            while (t is { } and not Toplevel)
+            {
+                t = t.SuperView;
+            }
+
+            if (t is Toplevel topLevel)
+            {
+                menuVisible = topLevel.MenuBar?.Visible == true;
+            }
+        }
+
+        if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
+        {
+            maxDimension = menuVisible ? 1 : 0;
+        }
+        else
+        {
+            maxDimension = 0;
+        }
+
+        ny = Math.Max (targetY, maxDimension);
+        
+        if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
+        {
+            maxDimension = statusVisible ? Application.Screen.Height - 1 : Application.Screen.Height;
+        }
+        else
+        {
+            maxDimension = statusVisible ? viewToMove!.SuperView.Viewport.Height - 1 : viewToMove!.SuperView.Viewport.Height;
+        }
+
+        if (superView?.Margin is { } && superView == viewToMove?.SuperView)
+        {
+            maxDimension -= superView.GetAdornmentsThickness ().Top + superView.GetAdornmentsThickness ().Bottom;
+        }
+
+        ny = Math.Min (ny, maxDimension);
+
+        if (viewToMove?.Frame.Height <= maxDimension)
+        {
+            ny = ny + viewToMove.Frame.Height > maxDimension
+                     ? Math.Max (maxDimension - viewToMove.Frame.Height, menuVisible ? 1 : 0)
+                     : ny;
+
+            if (ny > viewToMove.Frame.Y + viewToMove.Frame.Height)
+            {
+                ny = Math.Max (viewToMove.Frame.Bottom, 0);
+            }
+        }
+
+        //System.Diagnostics.Debug.WriteLine ($"ny:{ny}, rHeight:{rHeight}");
+
+        return superView!;
+    }
+
+    #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)
     {
@@ -1170,13 +1185,13 @@ public partial class View // Layout APIs
 
             if (bad != null)
             {
-                throw new InvalidOperationException (
+                throw new LayoutException (
                                                      $"{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 Layout Engine
-}
+}

+ 4 - 4
Terminal.Gui/View/View.Text.cs

@@ -26,7 +26,7 @@ public partial class View // Text Property APIs
             {
                 TextFormatter.PreserveTrailingSpaces = value;
                 TextFormatter.NeedsFormat = true;
-                SetLayoutNeeded ();
+                SetNeedsLayout ();
             }
         }
     }
@@ -67,7 +67,7 @@ public partial class View // Text Property APIs
             _text = value;
 
             UpdateTextFormatterText ();
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
 #if DEBUG
             if (_text is { } && string.IsNullOrEmpty (Id))
             {
@@ -97,7 +97,7 @@ public partial class View // Text Property APIs
         {
             TextFormatter.Alignment = value;
             UpdateTextFormatterText ();
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
         }
     }
 
@@ -234,7 +234,7 @@ public partial class View // Text Property APIs
         {
             TextFormatter.ConstrainToWidth = null;
             TextFormatter.ConstrainToHeight = null;
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
         }
 
         SetNeedsDisplay ();

+ 5 - 16
Terminal.Gui/View/View.cs

@@ -74,7 +74,7 @@ namespace Terminal.Gui;
 ///         To flag the entire view for redraw call <see cref="SetNeedsDisplay()"/>.
 ///     </para>
 ///     <para>
-///         The <see cref="SetLayoutNeeded"/> method is called when the size or layout of a view has changed. The <see cref="MainLoop"/> will
+///         The <see cref="SetNeedsLayout"/> method is called when the size or layout of a view has changed. 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 direclty call
 ///         see <see cref="Layout()"/>.
 ///     </para>
@@ -231,7 +231,7 @@ public partial class View : Responder, ISupportInitializeNotification
         UpdateTextDirection (TextDirection);
         UpdateTextFormatterText ();
 
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
 
         if (_subviews is { })
         {
@@ -296,18 +296,7 @@ public partial class View : Responder, ISupportInitializeNotification
 
             foreach (View view in _subviews)
             {
-                if (!_enabled)
-                {
-                    view._oldEnabled = view.Enabled;
-                    view.Enabled = _enabled;
-                }
-                else
-                {
-                    view.Enabled = view._oldEnabled;
-#if AUTO_CANFOCUS
-                    view._addingViewSoCanFocusAlsoUpdatesSuperView = _enabled;
-#endif
-                }
+                view.Enabled = Enabled;
             }
         }
     }
@@ -368,8 +357,8 @@ public partial class View : Responder, ISupportInitializeNotification
             OnVisibleChanged ();
             VisibleChanged?.Invoke (this, EventArgs.Empty);
 
-            SetLayoutNeeded ();
-            SuperView?.SetLayoutNeeded();
+            SetNeedsLayout ();
+            SuperView?.SetNeedsLayout();
             SetNeedsDisplay ();
             SuperView?.SetNeedsDisplay();
         }

+ 4 - 6
Terminal.Gui/Views/Bar.cs

@@ -138,7 +138,7 @@ public class Bar : View, IOrientation, IDesignable
         {
             _alignmentModes = value;
             SetNeedsDisplay ();
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
         }
     }
 
@@ -166,7 +166,7 @@ public class Bar : View, IOrientation, IDesignable
         }
 
         SetNeedsDisplay ();
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
     }
 
     // TODO: Move this to View
@@ -190,17 +190,15 @@ public class Bar : View, IOrientation, IDesignable
         {
             Remove (toRemove);
             SetNeedsDisplay ();
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
         }
 
         return toRemove as Shortcut;
     }
 
     /// <inheritdoc />
-    internal override void OnLayoutStarted (LayoutEventArgs args)
+    protected override void OnSubviewLayout (LayoutEventArgs args)
     {
-        base.OnLayoutStarted (args);
-
         LayoutBarItems (args.OldContentSize);
     }
 

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

@@ -191,7 +191,7 @@ public class Button : View, IDesignable
             _isDefault = value;
 
             UpdateTextFormatterText ();
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
         }
     }
 

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

@@ -153,7 +153,7 @@ public class CheckBox : View
 
         _checkedState = value;
         UpdateTextFormatterText ();
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
 
         EventArgs<CheckState> args = new (in _checkedState);
         OnCheckedStateChanged (args);

+ 2 - 2
Terminal.Gui/Views/ColorPicker.16.cs

@@ -28,7 +28,7 @@ public class ColorPicker16 : View
                 Width = Dim.Auto (minimumContentDim: _boxWidth * _cols);
                 Height = Dim.Auto (minimumContentDim: _boxHeight * _rows);
                 SetContentSize (new (_boxWidth * _cols, _boxHeight * _rows));
-                SetLayoutNeeded ();
+                SetNeedsLayout ();
             }
         }
     }
@@ -45,7 +45,7 @@ public class ColorPicker16 : View
                 Width = Dim.Auto (minimumContentDim: _boxWidth * _cols);
                 Height = Dim.Auto (minimumContentDim: _boxHeight * _rows);
                 SetContentSize (new (_boxWidth * _cols, _boxHeight * _rows));
-                SetLayoutNeeded ();
+                SetNeedsLayout ();
             }
         }
     }

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

@@ -90,7 +90,7 @@ public partial class ColorPicker : View
         CreateTextField ();
         SelectedColor = oldValue;
 
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
     }
 
     /// <summary>
@@ -274,7 +274,7 @@ public partial class ColorPicker : View
             _tfHex.Text = colorHex;
         }
 
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
     }
 
     private void UpdateSingleBarValueFromTextField (object? sender, HasFocusEventArgs e)

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

@@ -57,7 +57,7 @@ public class ComboBox : View, IDesignable
         Initialized += (s, e) => ProcessLayout ();
 
         // On resize
-        LayoutComplete += (sender, a) => ProcessLayout ();
+        SubviewsLaidOut += (sender, a) => ProcessLayout ();
 
         Added += (s, e) =>
                  {
@@ -72,7 +72,7 @@ public class ComboBox : View, IDesignable
                          }
                      }
 
-                     SetLayoutNeeded ();
+                     SetNeedsLayout ();
                      SetNeedsDisplay ();
                      ShowHideList (Text);
                  };

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

@@ -189,7 +189,7 @@ public class FileDialog : Dialog
                                                   bool newState = !tile.ContentView.Visible;
                                                   tile.ContentView.Visible = newState;
                                                   _btnToggleSplitterCollapse.Text = GetToggleSplitterText (newState);
-                                                  SetLayoutNeeded();
+                                                  SetNeedsLayout();
                                               };
 
         _tbFind = new TextField
@@ -494,7 +494,7 @@ public class FileDialog : Dialog
             MoveSubviewTowardsStart (_btnCancel);
         }
 
-        SetLayoutNeeded();
+        SetNeedsLayout();
     }
 
     /// <inheritdoc/>

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

@@ -104,7 +104,7 @@ public class HexView : View, IDesignable
         KeyBindings.Remove (Key.Space);
         KeyBindings.Remove (Key.Enter);
 
-        LayoutComplete += HexView_LayoutComplete;
+        SubviewsLaidOut += HexView_LayoutComplete;
     }
 
     /// <summary>Initializes a <see cref="HexView"/> class.</summary>
@@ -198,7 +198,7 @@ public class HexView : View, IDesignable
                 Address = 0;
             }
 
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
             SetNeedsDisplay ();
         }
     }
@@ -272,7 +272,7 @@ public class HexView : View, IDesignable
 
             _addressWidth = value;
             SetNeedsDisplay ();
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
         }
     }
 

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

@@ -214,7 +214,7 @@ public class ListView : View, IDesignable
         KeyBindings.Add (Key.A.WithCtrl, new KeyBinding ([Command.SelectAll], KeyBindingScope.Focused, true));
         KeyBindings.Add (Key.U.WithCtrl, new KeyBinding ([Command.SelectAll], KeyBindingScope.Focused, false));
 
-        LayoutComplete += ListView_LayoutComplete;
+        SubviewsLaidOut += ListView_LayoutComplete;
     }
 
     private void ListView_LayoutComplete (object sender, LayoutEventArgs e)
@@ -454,11 +454,11 @@ public class ListView : View, IDesignable
                 Viewport = Viewport with { Y = _selected - Viewport.Height + 1 };
             }
 
-            LayoutStarted -= ListView_LayoutStarted;
+            SubviewLayout -= ListView_LayoutStarted;
         }
         else
         {
-            LayoutStarted += ListView_LayoutStarted;
+            SubviewLayout += ListView_LayoutStarted;
         }
     }
 

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

@@ -22,7 +22,7 @@ public class MenuBarv2 : Bar
         ColorScheme = Colors.ColorSchemes ["Menu"];
         Orientation = Orientation.Horizontal;
 
-        LayoutStarted += MenuBarv2_LayoutStarted;
+        SubviewLayout += MenuBarv2_LayoutStarted;
     }
 
     // MenuBarv2 arranges the items horizontally.

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

@@ -46,7 +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).
-    internal override void OnLayoutStarted (LayoutEventArgs args)
+    protected override void OnSubviewLayout (LayoutEventArgs args)
     {
         for (int index = 0; index < Subviews.Count; index++)
         {
@@ -58,7 +58,7 @@ public class Menuv2 : Bar
             }
 
         }
-        base.OnLayoutStarted (args);
+        base.OnSubviewLayout (args);
     }
 
     /// <inheritdoc/>

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

@@ -175,7 +175,7 @@ public class RadioGroup : View, IDesignable, IOrientation
 
         SetupKeyBindings ();
 
-        LayoutStarted += RadioGroup_LayoutStarted;
+        SubviewLayout += RadioGroup_LayoutStarted;
 
         HighlightStyle = HighlightStyle.PressedOutside | HighlightStyle.Pressed;
 

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

@@ -227,7 +227,7 @@ public class ScrollBarView : View
 
             if (IsInitialized)
             {
-                SetLayoutNeeded ();
+                SetNeedsLayout ();
 
                 if (value)
                 {
@@ -261,7 +261,7 @@ public class ScrollBarView : View
             {
                 SetRelativeLayout (SuperView?.Frame.Size ?? Host.Frame.Size);
                 ShowHideScrollBars (false);
-                SetLayoutNeeded ();
+                SetNeedsLayout ();
             }
         }
     }

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

@@ -287,7 +287,7 @@ public class ScrollView : View
             if (value != _showHorizontalScrollIndicator)
             {
                 _showHorizontalScrollIndicator = value;
-                SetLayoutNeeded ();
+                SetNeedsLayout ();
 
                 if (value)
                 {
@@ -322,7 +322,7 @@ public class ScrollView : View
             if (value != _showVerticalScrollIndicator)
             {
                 _showVerticalScrollIndicator = value;
-                SetLayoutNeeded ();
+                SetNeedsLayout ();
 
                 if (value)
                 {
@@ -367,7 +367,7 @@ public class ScrollView : View
             _contentView.Add (view);
         }
 
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
         return view;
     }
 

+ 5 - 5
Terminal.Gui/Views/Shortcut.cs

@@ -130,7 +130,7 @@ public class Shortcut : View, IOrientation, IDesignable
 
         Action = action;
 
-        LayoutStarted += OnLayoutStarted;
+        SubviewLayout += OnLayoutStarted;
 
         ShowHide ();
     }
@@ -371,7 +371,7 @@ public class Shortcut : View, IOrientation, IDesignable
     public void OnOrientationChanged (Orientation newOrientation)
     {
         // TODO: Determine what, if anything, is opinionated about the orientation.
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
     }
 
     #endregion
@@ -644,9 +644,9 @@ public class Shortcut : View, IOrientation, IDesignable
             SetKeyViewDefaultLayout ();
 
             // TODO: Prob not needed
-            CommandView.SetLayoutNeeded ();
-            HelpView.SetLayoutNeeded ();
-            KeyView.SetLayoutNeeded ();
+            CommandView.SetNeedsLayout ();
+            HelpView.SetNeedsLayout ();
+            KeyView.SetNeedsLayout ();
             SetSubViewNeedsDisplay ();
         }
     }

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

@@ -59,7 +59,7 @@ public class Slider<T> : View, IOrientation
         // BUGBUG: This should not be needed - Need to ensure SetRelativeLayout gets called during EndInit
         Initialized += (s, e) => { SetContentSize (); };
 
-        LayoutStarted += (s, e) => { SetContentSize (); };
+        SubviewLayout += (s, e) => { SetContentSize (); };
     }
 
     // TODO: Make configurable via ConfigurationManager

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

@@ -26,7 +26,7 @@ public class StatusBar : Bar, IDesignable
         BorderStyle = LineStyle.Dashed;
         ColorScheme = Colors.ColorSchemes ["Menu"];
 
-        LayoutStarted += StatusBar_LayoutStarted;
+        SubviewLayout += StatusBar_LayoutStarted;
     }
 
     // StatusBar arranges the items horizontally.

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

@@ -147,7 +147,7 @@ public class TabView : View
 
                 OnSelectedTabChanged (old, value);
             }
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
         }
     }
 
@@ -165,7 +165,7 @@ public class TabView : View
                 return;
             }
             _style = value;
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
         }
     }
 
@@ -181,7 +181,7 @@ public class TabView : View
         set
         {
             _tabScrollOffset = EnsureValidScrollOffsets (value);
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
         }
     }
 
@@ -207,7 +207,7 @@ public class TabView : View
             tab.View?.SetFocus ();
         }
 
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
     }
 
     /// <summary>
@@ -264,7 +264,7 @@ public class TabView : View
             // Should be able to just use 0 but switching between top/bottom tabs repeatedly breaks in ValidatePosDim if just using the absolute value 0
         }
 
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
     }
 
     /// <summary>Updates <see cref="TabScrollOffset"/> to ensure that <see cref="SelectedTab"/> is visible.</summary>
@@ -342,7 +342,7 @@ public class TabView : View
         }
 
         EnsureSelectedTabIsVisible ();
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
     }
 
     /// <summary>Event for when <see cref="SelectedTab"/> changes.</summary>
@@ -622,7 +622,7 @@ public class TabView : View
                 {
                     _host.SwitchTabBy (scrollIndicatorHit);
 
-                    SetLayoutNeeded ();
+                    SetNeedsLayout ();
 
                     return true;
                 }
@@ -630,7 +630,7 @@ public class TabView : View
                 if (hit is { })
                 {
                     _host.SelectedTab = hit;
-                    SetLayoutNeeded ();
+                    SetNeedsLayout ();
 
                     return true;
                 }

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

@@ -1901,7 +1901,7 @@ public class TextView : View
 
         Added += TextView_Added!;
 
-        LayoutComplete += TextView_LayoutComplete;
+        SubviewsLaidOut += TextView_LayoutComplete;
 
         // Things this view knows how to do
 

+ 8 - 8
Terminal.Gui/Views/TileView.cs

@@ -22,7 +22,7 @@ public class TileView : View
         CanFocus = true;
         RebuildForTileCount (tiles);
 
-        LayoutStarted += (_, _) =>
+        SubviewLayout += (_, _) =>
                          {
                              Rectangle viewport = Viewport;
 
@@ -57,7 +57,7 @@ public class TileView : View
             _orientation = value;
 
             SetNeedsDisplay ();
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
 
         }
     }
@@ -151,7 +151,7 @@ public class TileView : View
         }
 
         SetNeedsDisplay ();
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
 
         return toReturn;
     }
@@ -359,10 +359,10 @@ public class TileView : View
             Add (tile.ContentView);
 
             // BUGBUG: This should not be needed:
-            tile.TitleChanged += (s, e) => SetLayoutNeeded ();
+            tile.TitleChanged += (s, e) => SetNeedsLayout ();
         }
 
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
     }
 
     /// <summary>
@@ -432,7 +432,7 @@ public class TileView : View
         _splitterDistances [idx] = value;
         OnSplitterMoved (idx);
         SetNeedsDisplay ();
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
 
         return true;
     }
@@ -949,7 +949,7 @@ public class TileView : View
                     moveRuneRenderLocation = new Point (0, Math.Max (1, Math.Min (Viewport.Height - 2, mouseEvent.Position.Y)));
                 }
 
-                Parent.SetLayoutNeeded ();
+                Parent.SetNeedsLayout ();
 
                 return true;
             }
@@ -1034,7 +1034,7 @@ public class TileView : View
         {
             SetNeedsDisplay ();
 
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
 
             if (oldValue is PosPercent)
             {

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

@@ -92,7 +92,7 @@ public class Wizard : Dialog
         Closing += Wizard_Closing;
         TitleChanged += Wizard_TitleChanged;
 
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
     }
 
     /// <summary>
@@ -545,7 +545,7 @@ public class Wizard : Dialog
 
         SizeStep (CurrentStep);
 
-        SetLayoutNeeded ();
+        SetNeedsLayout ();
     }
 
     private void Wizard_Closing (object sender, ToplevelClosingEventArgs obj)

+ 4 - 2
UICatalog/Scenarios/Adornments.cs

@@ -99,10 +99,12 @@ public class Adornments : Scenario
         longLabel.TextFormatter.WordWrap = true;
         window.Add (tf1, color, button, label, btnButtonInWindow, labelAnchorEnd, longLabel);
 
-        editor.Initialized += (s, e) => { editor.ViewToEdit = window; };
-
         window.Initialized += (s, e) =>
                               {
+                                  editor.ViewToEdit = window;
+
+                                  editor.ShowViewIdentifier = true;
+
                                   var labelInPadding = new Label { X = 1, Y = 0, Title = "_Text:" };
                                   window.Padding.Add (labelInPadding);
 

+ 36 - 311
UICatalog/Scenarios/AllViewsTester.cs

@@ -15,37 +15,20 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Adornments")]
 public class AllViewsTester : Scenario
 {
-    private readonly List<string> _dimNames = new () { "Auto", "Percent", "Fill", "Absolute" };
 
-    // TODO: This is missing some
-    private readonly List<string> _posNames = new () { "Percent", "AnchorEnd", "Center", "Absolute" };
+    private Dictionary<string, Type> _viewClasses;
     private ListView _classListView;
-    private View _curView;
-    private FrameView _hostPane;
     private AdornmentsEditor _adornmentsEditor;
-    private RadioGroup _hRadioGroup;
-    private TextField _hText;
-    private int _hVal;
-    private FrameView _leftPane;
-    private FrameView _locationFrame;
 
-    // Settings
+    private LayoutEditor _layoutEditor;
     private FrameView _settingsPane;
-    private FrameView _sizeFrame;
-    private Dictionary<string, Type> _viewClasses;
-    private RadioGroup _wRadioGroup;
-    private TextField _wText;
-    private int _wVal;
-    private RadioGroup _xRadioGroup;
-    private TextField _xText;
-    private int _xVal;
-    private RadioGroup _yRadioGroup;
-    private TextField _yText;
-    private int _yVal;
     private RadioGroup _orientation;
     private string _demoText = "This, that, and the other thing.";
     private TextView _demoTextView;
 
+    private FrameView _hostPane;
+    private View _curView;
+
     public override void Main ()
     {
         // Don't create a sub-win (Scenario.Win); just use Application.Top
@@ -54,7 +37,8 @@ public class AllViewsTester : Scenario
         var app = new Window
         {
             Title = GetQuitKeyAndName (),
-            ColorScheme = Colors.ColorSchemes ["TopLevel"]
+            ColorScheme = Colors.ColorSchemes ["TopLevel"],
+            BorderStyle = LineStyle.None
         };
 
         _viewClasses = GetAllViewClassesCollection ()
@@ -62,19 +46,9 @@ public class AllViewsTester : Scenario
                        .Select (t => new KeyValuePair<string, Type> (t.Name, t))
                        .ToDictionary (t => t.Key, t => t.Value);
 
-        _leftPane = new ()
-        {
-            X = 0,
-            Y = 0,
-            Width = Dim.Auto (DimAutoStyle.Content),
-            Height = Dim.Fill (),
-            CanFocus = true,
-            ColorScheme = Colors.ColorSchemes ["TopLevel"],
-            Title = "Classes"
-        };
-
         _classListView = new ()
         {
+            Title = "Classes [_1]",
             X = 0,
             Y = 0,
             Width = Dim.Auto (),
@@ -82,7 +56,8 @@ public class AllViewsTester : Scenario
             AllowsMarking = false,
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
             SelectedItem = 0,
-            Source = new ListWrapper<string> (new (_viewClasses.Keys.ToList ()))
+            Source = new ListWrapper<string> (new (_viewClasses.Keys.ToList ())),
+            BorderStyle = LineStyle.Rounded
         };
 
         _classListView.SelectedItemChanged += (s, args) =>
@@ -100,11 +75,11 @@ public class AllViewsTester : Scenario
                                                       _adornmentsEditor.ViewToEdit = _curView;
                                                   }
                                               };
-        _leftPane.Add (_classListView);
 
         _adornmentsEditor = new ()
         {
-            X = Pos.Right (_leftPane),
+            Title = "Adornments [_2]",
+            X = Pos.Right (_classListView),
             Y = 0,
             Width = Dim.Auto (),
             Height = Dim.Fill (),
@@ -121,150 +96,31 @@ public class AllViewsTester : Scenario
         };
         _adornmentsEditor.Border.Add (expandButton);
 
-        _settingsPane = new ()
+        _layoutEditor = new ()
         {
+            Title = "Layout [_3]",
             X = Pos.Right (_adornmentsEditor),
-            Y = 0, // for menu
+            Y = 0,
             Width = Dim.Fill (),
             Height = Dim.Auto (),
             CanFocus = true,
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
-            Title = "Settings"
+            BorderStyle = LineStyle.Rounded
         };
 
-        string [] radioItems = { "_Percent(x)", "_AnchorEnd", "_Center", "A_bsolute(x)" };
-
-        _locationFrame = new ()
-        {
-            X = 0,
-            Y = 0,
-            Height = Dim.Auto (),
-            Width = Dim.Auto (),
-            Title = "Location (Pos)",
-            TabStop = TabBehavior.TabStop,
-        };
-        _settingsPane.Add (_locationFrame);
-
-        var label = new Label { X = 0, Y = 0, Text = "X:" };
-        _locationFrame.Add (label);
-        _xRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
-        _xRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
-        _xText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_xVal}" };
-
-        _xText.Accepting += (s, args) =>
-                         {
-                             try
-                             {
-                                 _xVal = int.Parse (_xText.Text);
-                                 DimPosChanged (_curView);
-                             }
-                             catch
-                             { }
-                         };
-        _locationFrame.Add (_xText);
-
-        _locationFrame.Add (_xRadioGroup);
-
-        radioItems = new [] { "P_ercent(y)", "A_nchorEnd", "C_enter", "Absolute(_y)" };
-        label = new () { X = Pos.Right (_xRadioGroup) + 1, Y = 0, Text = "Y:" };
-        _locationFrame.Add (label);
-        _yText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_yVal}" };
-
-        _yText.Accepting += (s, args) =>
-                         {
-                             try
-                             {
-                                 _yVal = int.Parse (_yText.Text);
-                                 DimPosChanged (_curView);
-                             }
-                             catch
-                             { }
-                         };
-        _locationFrame.Add (_yText);
-        _yRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
-        _yRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
-        _locationFrame.Add (_yRadioGroup);
-
-        _sizeFrame = new ()
+        _settingsPane = new ()
         {
-            X = Pos.Right (_locationFrame),
-            Y = Pos.Y (_locationFrame),
+            Title = "Settings [_4]",
+            X = Pos.Right (_adornmentsEditor),
+            Y = Pos.Bottom (_layoutEditor),
+            Width = Dim.Fill (),
             Height = Dim.Auto (),
-            Width = Dim.Auto (),
-            Title = "Size (Dim)",
-            TabStop = TabBehavior.TabStop,
+            CanFocus = true,
+            ColorScheme = Colors.ColorSchemes ["TopLevel"],
+            BorderStyle = LineStyle.Rounded
         };
 
-        radioItems = new [] { "Auto", "_Percent(width)", "_Fill(width)", "A_bsolute(width)" };
-        label = new () { X = 0, Y = 0, Text = "Width:" };
-        _sizeFrame.Add (label);
-        _wRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
-        _wRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
-        _wText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_wVal}" };
-
-        _wText.Accepting += (s, args) =>
-                         {
-                             try
-                             {
-                                 switch (_wRadioGroup.SelectedItem)
-                                 {
-                                     case 1:
-                                         _wVal = Math.Min (int.Parse (_wText.Text), 100);
-
-                                         break;
-                                     case 0:
-                                     case 2:
-                                     case 3:
-                                         _wVal = int.Parse (_wText.Text);
-
-                                         break;
-                                 }
-
-                                 DimPosChanged (_curView);
-                             }
-                             catch
-                             { }
-                         };
-        _sizeFrame.Add (_wText);
-        _sizeFrame.Add (_wRadioGroup);
-
-        radioItems = new [] { "_Auto", "P_ercent(height)", "F_ill(height)", "Ab_solute(height)" };
-        label = new () { X = Pos.Right (_wRadioGroup) + 1, Y = 0, Text = "Height:" };
-        _sizeFrame.Add (label);
-        _hText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_hVal}" };
-
-        _hText.Accepting += (s, args) =>
-                         {
-                             try
-                             {
-                                 switch (_hRadioGroup.SelectedItem)
-                                 {
-                                     case 1:
-                                         _hVal = Math.Min (int.Parse (_hText.Text), 100);
-
-                                         break;
-                                     case 0:
-                                     case 2:
-                                     case 3:
-                                         _hVal = int.Parse (_hText.Text);
-
-                                         break;
-                                 }
-
-                                 DimPosChanged (_curView);
-                             }
-                             catch
-                             { }
-                         };
-        _sizeFrame.Add (_hText);
-
-        _hRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
-        _hRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
-        _sizeFrame.Add (_hRadioGroup);
-
-        _settingsPane.Add (_sizeFrame);
-
-        label = new () { X = 0, Y = Pos.Bottom (_sizeFrame), Text = "_Orientation:" };
+        Label label = new () { X = 0, Y = 0, Text = "_Orientation:" };
 
         _orientation = new ()
         {
@@ -320,27 +176,21 @@ public class AllViewsTester : Scenario
         _hostPane.Padding.Diagnostics = ViewDiagnosticFlags.Ruler;
         _hostPane.Padding.ColorScheme = Colors.ColorSchemes ["Error"];
 
-        _hostPane.LayoutStarted += (sender, args) =>
-                                   {
-
-                                   };
-
-        app.Add (_leftPane, _adornmentsEditor, _settingsPane, _hostPane);
+        app.Add (_classListView, _adornmentsEditor, _layoutEditor, _settingsPane, _hostPane);
 
         _classListView.SelectedItem = 0;
-        _leftPane.SetFocus ();
+        _classListView.SetFocus ();
 
         Application.Run (app);
         app.Dispose ();
         Application.Shutdown ();
     }
 
-    private void OnRadioGroupOnSelectedItemChanged (object s, SelectedItemChangedArgs selected) { DimPosChanged (_curView); }
 
     // TODO: Add Command.HotKey handler (pop a message box?)
     private void CreateCurrentView (Type type)
     {
-        Debug.Assert(_curView is null);
+        Debug.Assert (_curView is null);
 
         // If we are to create a generic Type
         if (type.IsGenericType)
@@ -381,11 +231,13 @@ public class AllViewsTester : Scenario
         }
 
         view.Initialized += CurrentView_Initialized;
-        view.LayoutComplete += CurrentView_LayoutComplete;
+        view.SubviewsLaidOut += CurrentView_LayoutComplete;
 
         view.Id = "_curView";
         _curView = view;
         _hostPane.Add (_curView);
+        _layoutEditor.ViewToEdit = _curView;
+        _curView.SetNeedsLayout ();
     }
 
     private void DisposeCurrentView ()
@@ -393,89 +245,16 @@ public class AllViewsTester : Scenario
         if (_curView != null)
         {
             _curView.Initialized -= CurrentView_Initialized;
-            _curView.LayoutComplete -= CurrentView_LayoutComplete;
+            _curView.SubviewsLaidOut -= CurrentView_LayoutComplete;
             _hostPane.Remove (_curView);
+            _layoutEditor.ViewToEdit = null;
+
             _curView.Dispose ();
             _curView = null;
         }
     }
 
-    private void DimPosChanged (View view)
-    {
-        if (view == null || _updatingSettings)
-        {
-            return;
-        }
-
-        try
-        {
-            view.X = _xRadioGroup.SelectedItem switch
-            {
-                0 => Pos.Percent (_xVal),
-                1 => Pos.AnchorEnd (),
-                2 => Pos.Center (),
-                3 => Pos.Absolute (_xVal),
-                _ => view.X
-            };
-
-            view.Y = _yRadioGroup.SelectedItem switch
-            {
-                0 => Pos.Percent (_yVal),
-                1 => Pos.AnchorEnd (),
-                2 => Pos.Center (),
-                3 => Pos.Absolute (_yVal),
-                _ => view.Y
-            };
-
-            view.Width = _wRadioGroup.SelectedItem switch
-            {
-                0 => Dim.Auto (),
-                1 => Dim.Percent (_wVal),
-                2 => Dim.Fill (_wVal),
-                3 => Dim.Absolute (_wVal),
-                _ => view.Width
-            };
-
-            view.Height = _hRadioGroup.SelectedItem switch
-            {
-                0 => Dim.Auto (),
-                1 => Dim.Percent (_hVal),
-                2 => Dim.Fill (_hVal),
-                3 => Dim.Absolute (_hVal),
-                _ => view.Height
-            };
-        }
-        catch (Exception e)
-        {
-            MessageBox.ErrorQuery ("Exception", e.Message, "Ok");
-        }
-
-        if (view.Width is DimAuto)
-        {
-            _wText.Text = "Auto";
-            _wText.Enabled = false;
-        }
-        else
-        {
-            _wText.Text = $"{_wVal}";
-            _wText.Enabled = true;
-        }
-
-        if (view.Height is DimAuto)
-        {
-            _hText.Text = "Auto";
-            _hText.Enabled = false;
-        }
-        else
-        {
-            _hText.Text = $"{_hVal}";
-            _hText.Enabled = true;
-        }
-
-        UpdateHostTitle (view);
-    }
-
-    private List<Type> GetAllViewClassesCollection ()
+    private static List<Type> GetAllViewClassesCollection ()
     {
         List<Type> types = new ();
 
@@ -495,62 +274,10 @@ public class AllViewsTester : Scenario
 
     private void CurrentView_LayoutComplete (object sender, LayoutEventArgs args)
     {
-        UpdateSettings (_curView);
         UpdateHostTitle (_curView);
     }
 
-    private bool _updatingSettings = false;
-    private void UpdateSettings (View view)
-    {
-        _updatingSettings = true;
-        var x = view.X.ToString ();
-        var y = view.Y.ToString ();
-
-        try
-        {
-            _xRadioGroup.SelectedItem = _posNames.IndexOf (_posNames.First (s => x.Contains (s)));
-            _yRadioGroup.SelectedItem = _posNames.IndexOf (_posNames.First (s => y.Contains (s)));
-        }
-        catch (InvalidOperationException e)
-        {
-            // This is a hack to work around the fact that the Pos enum doesn't have an "Align" value yet
-            Debug.WriteLine ($"{e}");
-        }
-
-        _xText.Text = $"{view.Frame.X}";
-        _yText.Text = $"{view.Frame.Y}";
-
-        var w = view.Width.ToString ();
-        var h = view.Height.ToString ();
-        _wRadioGroup.SelectedItem = _dimNames.IndexOf (_dimNames.First (s => w.Contains (s)));
-        _hRadioGroup.SelectedItem = _dimNames.IndexOf (_dimNames.First (s => h.Contains (s)));
-
-        if (view.Width.Has<DimAuto> (out _))
-        {
-            _wText.Text = "Auto";
-            _wText.Enabled = false;
-        }
-        else
-        {
-            _wText.Text = $"{view.Frame.Width}";
-            _wText.Enabled = true;
-        }
-
-        if (view.Height.Has<DimAuto> (out _))
-        {
-            _hText.Text = "Auto";
-            _hText.Enabled = false;
-        }
-        else
-        {
-            _hText.Text = $"{view.Frame.Height}";
-            _hText.Enabled = true;
-        }
-
-        _updatingSettings = false;
-    }
-
-    private void UpdateHostTitle (View view) { _hostPane.Title = $"_Demo of {view.GetType ().Name}"; }
+    private void UpdateHostTitle (View view) { _hostPane.Title = $"Demo of {view.GetType ().Name} [_0]"; }
 
     private void CurrentView_Initialized (object sender, EventArgs e)
     {
@@ -569,8 +296,6 @@ public class AllViewsTester : Scenario
             view.Height = Dim.Fill ();
         }
 
-        UpdateSettings (view);
-
         UpdateHostTitle (view);
     }
 }

+ 1 - 1
UICatalog/Scenarios/AnimationScenario.cs → UICatalog/Scenarios/AnimationScenario/AnimationScenario.cs

@@ -57,7 +57,7 @@ public class AnimationScenario : Scenario
         }
 
         var f = new FileInfo (
-                              Path.Combine (dir.FullName, "Scenarios", "Spinning_globe_dark_small.gif")
+                              Path.Combine (dir.FullName, "Scenarios\\AnimationScenario", "Spinning_globe_dark_small.gif")
                              );
 
         if (!f.Exists)

+ 0 - 0
UICatalog/Scenarios/Spinning_globe_dark_small.gif → UICatalog/Scenarios/AnimationScenario/Spinning_globe_dark_small.gif


+ 0 - 0
UICatalog/Scenarios/spinning-globe-attribution.txt → UICatalog/Scenarios/AnimationScenario/spinning-globe-attribution.txt


+ 1 - 0
UICatalog/Scenarios/Arrangement.cs

@@ -29,6 +29,7 @@ public class Arrangement : Scenario
             Y = 0,
             AutoSelectViewToEdit = true,
             TabStop = TabBehavior.NoStop,
+            ShowViewIdentifier = true
         };
 
         app.Add (adornmentsEditor);

+ 1 - 1
UICatalog/Scenarios/ComputedLayout.cs

@@ -50,7 +50,7 @@ public class ComputedLayout : Scenario
             Text = vrule
         };
 
-        app.LayoutComplete += (s, a) =>
+        app.SubviewsLaidOut += (s, a) =>
                                           {
                                               if (horizontalRuler.Viewport.Width == 0 || horizontalRuler.Viewport.Height == 0)
                                               {

+ 8 - 7
UICatalog/Scenarios/ContentScrolling.cs

@@ -108,7 +108,8 @@ public class ContentScrolling : Scenario
 
         var editor = new AdornmentsEditor
         {
-            AutoSelectViewToEdit = true
+            AutoSelectViewToEdit = true,
+            ShowViewIdentifier = true
         };
         app.Add (editor);
 
@@ -137,10 +138,10 @@ public class ContentScrolling : Scenario
         var cbAllowNegativeX = new CheckBox
         {
             Title = "Allow _X < 0",
-            Y = Pos.Bottom(frameLabel),
+            Y = Pos.Bottom (frameLabel),
             CanFocus = true
         };
-        cbAllowNegativeX.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.AllowNegativeX) ? CheckState.Checked : CheckState.UnChecked;
+        cbAllowNegativeX.CheckedState = view.ViewportSettings.HasFlag (ViewportSettings.AllowNegativeX) ? CheckState.Checked : CheckState.UnChecked;
         cbAllowNegativeX.CheckedStateChanging += AllowNegativeX_Toggle;
 
         void AllowNegativeX_Toggle (object sender, CancelEventArgs<CheckState> e)
@@ -164,7 +165,7 @@ public class ContentScrolling : Scenario
             Y = Pos.Bottom (frameLabel),
             CanFocus = true
         };
-        cbAllowNegativeY.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.AllowNegativeY) ? CheckState.Checked : CheckState.UnChecked;
+        cbAllowNegativeY.CheckedState = view.ViewportSettings.HasFlag (ViewportSettings.AllowNegativeY) ? CheckState.Checked : CheckState.UnChecked;
         cbAllowNegativeY.CheckedStateChanging += AllowNegativeY_Toggle;
 
         void AllowNegativeY_Toggle (object sender, CancelEventArgs<CheckState> e)
@@ -187,7 +188,7 @@ public class ContentScrolling : Scenario
             Y = Pos.Bottom (cbAllowNegativeX),
             CanFocus = true
         };
-        cbAllowXGreaterThanContentWidth.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.AllowXGreaterThanContentWidth) ? CheckState.Checked : CheckState.UnChecked;
+        cbAllowXGreaterThanContentWidth.CheckedState = view.ViewportSettings.HasFlag (ViewportSettings.AllowXGreaterThanContentWidth) ? CheckState.Checked : CheckState.UnChecked;
         cbAllowXGreaterThanContentWidth.CheckedStateChanging += AllowXGreaterThanContentWidth_Toggle;
 
         void AllowXGreaterThanContentWidth_Toggle (object sender, CancelEventArgs<CheckState> e)
@@ -211,7 +212,7 @@ public class ContentScrolling : Scenario
             Y = Pos.Bottom (cbAllowNegativeX),
             CanFocus = true
         };
-        cbAllowYGreaterThanContentHeight.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.AllowYGreaterThanContentHeight) ? CheckState.Checked : CheckState.UnChecked;
+        cbAllowYGreaterThanContentHeight.CheckedState = view.ViewportSettings.HasFlag (ViewportSettings.AllowYGreaterThanContentHeight) ? CheckState.Checked : CheckState.UnChecked;
         cbAllowYGreaterThanContentHeight.CheckedStateChanging += AllowYGreaterThanContentHeight_Toggle;
 
         void AllowYGreaterThanContentHeight_Toggle (object sender, CancelEventArgs<CheckState> e)
@@ -290,7 +291,7 @@ public class ContentScrolling : Scenario
             Y = Pos.Top (labelContentSize),
             CanFocus = true
         };
-        cbClearContentOnly.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.ClearContentOnly) ? CheckState.Checked : CheckState.UnChecked;
+        cbClearContentOnly.CheckedState = view.ViewportSettings.HasFlag (ViewportSettings.ClearContentOnly) ? CheckState.Checked : CheckState.UnChecked;
         cbClearContentOnly.CheckedStateChanging += ClearContentOnly_Toggle;
 
         void ClearContentOnly_Toggle (object sender, CancelEventArgs<CheckState> e)

+ 73 - 32
UICatalog/Scenarios/AdornmentEditor.cs → UICatalog/Scenarios/Editors/AdornmentEditor.cs

@@ -1,4 +1,5 @@
-using System;
+#nullable enable
+using System;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios;
@@ -15,7 +16,6 @@ public class AdornmentEditor : View
         BoxHeight = 1,
         BorderStyle = LineStyle.Single,
         SuperViewRendersLineCanvas = true,
-        Enabled = false
     };
 
     private readonly ColorPicker16 _foregroundColorPicker = new ()
@@ -25,11 +25,15 @@ public class AdornmentEditor : View
         BoxHeight = 1,
         BorderStyle = LineStyle.Single,
         SuperViewRendersLineCanvas = true,
-        Enabled = false
     };
 
-    private Adornment _adornment;
-    public Adornment AdornmentToEdit
+
+    private CheckBox? _diagThicknessCheckBox;
+    private CheckBox? _diagRulerCheckBox;
+
+
+    private Adornment? _adornment;
+    public Adornment? AdornmentToEdit
     {
         get => _adornment;
         set
@@ -41,10 +45,7 @@ public class AdornmentEditor : View
 
             _adornment = value;
 
-            foreach (var subview in Subviews)
-            {
-                subview.Enabled = _adornment is { };
-            }
+            Enabled = _adornment is { };
 
             if (_adornment is null)
             {
@@ -53,10 +54,10 @@ public class AdornmentEditor : View
 
             if (IsInitialized)
             {
-                _topEdit.Value = _adornment.Thickness.Top;
-                _leftEdit.Value = _adornment.Thickness.Left;
-                _bottomEdit.Value = _adornment.Thickness.Bottom;
-                _rightEdit.Value = _adornment.Thickness.Right;
+                _topEdit!.Value = _adornment.Thickness.Top;
+                _leftEdit!.Value = _adornment.Thickness.Left;
+                _bottomEdit!.Value = _adornment.Thickness.Bottom;
+                _rightEdit!.Value = _adornment.Thickness.Right;
 
                 _adornment.Initialized += (sender, args) =>
                                           {
@@ -71,17 +72,17 @@ public class AdornmentEditor : View
         }
     }
 
-    public event EventHandler<EventArgs> AdornmentChanged;
+    public event EventHandler<EventArgs>? AdornmentChanged;
 
     public void OnAdornmentChanged ()
     {
         AdornmentChanged?.Invoke (this, EventArgs.Empty);
     }
 
-    private NumericUpDown<int> _topEdit;
-    private NumericUpDown<int> _leftEdit;
-    private NumericUpDown<int> _bottomEdit;
-    private NumericUpDown<int> _rightEdit;
+    private NumericUpDown<int>? _topEdit;
+    private NumericUpDown<int>? _leftEdit;
+    private NumericUpDown<int>? _bottomEdit;
+    private NumericUpDown<int>? _rightEdit;
 
     public AdornmentEditor ()
     {
@@ -95,16 +96,15 @@ public class AdornmentEditor : View
         TabStop = TabBehavior.TabStop;
     }
 
-    private void AdornmentEditor_Initialized (object sender, EventArgs e)
+    private void AdornmentEditor_Initialized (object? sender, EventArgs e)
     {
-        ExpanderButton expandButton;
+        ExpanderButton? expandButton;
         Border.Add (expandButton = new ExpanderButton ());
 
         _topEdit = new ()
         {
             X = Pos.Center (), Y = 0,
             Format = "{0, 2}",
-            Enabled = false
         };
 
         _topEdit.ValueChanging += Top_ValueChanging;
@@ -114,7 +114,6 @@ public class AdornmentEditor : View
         {
             X = Pos.Left (_topEdit) - Pos.Func (() => _topEdit.Text.Length) - 2, Y = Pos.Bottom (_topEdit),
             Format = _topEdit.Format,
-            Enabled = false
         };
 
         _leftEdit.ValueChanging += Left_ValueChanging;
@@ -124,7 +123,6 @@ public class AdornmentEditor : View
         {
             X = Pos.Right (_leftEdit) + 5, Y = Pos.Bottom (_topEdit),
             Format = _topEdit.Format,
-            Enabled = false
         };
 
         _rightEdit.ValueChanging += Right_ValueChanging;
@@ -134,7 +132,6 @@ public class AdornmentEditor : View
         {
             X = Pos.Center (), Y = Pos.Bottom (_leftEdit),
             Format = _topEdit.Format,
-            Enabled = false
         };
 
         _bottomEdit.ValueChanging += Bottom_ValueChanging;
@@ -143,12 +140,11 @@ public class AdornmentEditor : View
         var copyTop = new Button
         {
             X = Pos.Center (), Y = Pos.Bottom (_bottomEdit), Text = "Cop_y Top",
-            Enabled = false
         };
 
         copyTop.Accepting += (s, e) =>
                           {
-                              AdornmentToEdit.Thickness = new (_topEdit.Value);
+                              AdornmentToEdit!.Thickness = new (_topEdit.Value);
                               _leftEdit.Value = _rightEdit.Value = _bottomEdit.Value = _topEdit.Value;
                           };
         Add (copyTop);
@@ -172,10 +168,55 @@ public class AdornmentEditor : View
         _rightEdit.Value = AdornmentToEdit?.Thickness.Right ?? 0;
         _bottomEdit.Value = AdornmentToEdit?.Thickness.Bottom ?? 0;
 
-        foreach (var subview in Subviews)
+        _diagThicknessCheckBox = new () { Text = "_Thickness Diag." };
+        if (AdornmentToEdit is { })
+        {
+            _diagThicknessCheckBox.CheckedState = AdornmentToEdit.Diagnostics.FastHasFlags (ViewDiagnosticFlags.Thickness) ? CheckState.Checked : CheckState.UnChecked;
+        }
+        else
         {
-            subview.Enabled = AdornmentToEdit is { };
+            _diagThicknessCheckBox.CheckedState = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Thickness) ? CheckState.Checked : CheckState.UnChecked;
         }
+
+        _diagThicknessCheckBox.CheckedStateChanging += (s, e) =>
+                                                     {
+                                                         if (e.NewValue == CheckState.Checked)
+                                                         {
+                                                             AdornmentToEdit!.Diagnostics |= ViewDiagnosticFlags.Thickness;
+                                                         }
+                                                         else
+                                                         {
+                                                             AdornmentToEdit!.Diagnostics &= ~ViewDiagnosticFlags.Thickness;
+                                                         }
+                                                     };
+
+        Add (_diagThicknessCheckBox);
+        _diagThicknessCheckBox.Y = Pos.Bottom (_backgroundColorPicker);
+
+        _diagRulerCheckBox = new () { Text = "_Ruler" };
+        if (AdornmentToEdit is { })
+        {
+            _diagRulerCheckBox.CheckedState = AdornmentToEdit.Diagnostics.FastHasFlags (ViewDiagnosticFlags.Ruler) ? CheckState.Checked : CheckState.UnChecked;
+        }
+        else
+        {
+            _diagRulerCheckBox.CheckedState = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Ruler) ? CheckState.Checked : CheckState.UnChecked;
+        }
+
+        _diagRulerCheckBox.CheckedStateChanging += (s, e) =>
+                                                   {
+                                                       if (e.NewValue == CheckState.Checked)
+                                                       {
+                                                           AdornmentToEdit!.Diagnostics |= ViewDiagnosticFlags.Ruler;
+                                                       }
+                                                       else
+                                                       {
+                                                           AdornmentToEdit!.Diagnostics &= ~ViewDiagnosticFlags.Ruler;
+                                                       }
+                                                   };
+
+        Add (_diagRulerCheckBox);
+        _diagRulerCheckBox.Y = Pos.Bottom (_diagThicknessCheckBox);
     }
 
     private EventHandler<ColorEventArgs> ColorPickerColorChanged ()
@@ -193,7 +234,7 @@ public class AdornmentEditor : View
                };
     }
 
-    private void Top_ValueChanging (object sender, CancelEventArgs<int> e)
+    private void Top_ValueChanging (object? sender, CancelEventArgs<int> e)
     {
         if (e.NewValue < 0 || AdornmentToEdit is null)
         {
@@ -205,7 +246,7 @@ public class AdornmentEditor : View
         AdornmentToEdit.Thickness = new (AdornmentToEdit.Thickness.Left, e.NewValue, AdornmentToEdit.Thickness.Right, AdornmentToEdit.Thickness.Bottom);
     }
 
-    private void Left_ValueChanging (object sender, CancelEventArgs<int> e)
+    private void Left_ValueChanging (object? sender, CancelEventArgs<int> e)
     {
         if (e.NewValue < 0 || AdornmentToEdit is null)
         {
@@ -217,7 +258,7 @@ public class AdornmentEditor : View
         AdornmentToEdit.Thickness = new (e.NewValue, AdornmentToEdit.Thickness.Top, AdornmentToEdit.Thickness.Right, AdornmentToEdit.Thickness.Bottom);
     }
 
-    private void Right_ValueChanging (object sender, CancelEventArgs<int> e)
+    private void Right_ValueChanging (object? sender, CancelEventArgs<int> e)
     {
         if (e.NewValue < 0 || AdornmentToEdit is null)
         {
@@ -229,7 +270,7 @@ public class AdornmentEditor : View
         AdornmentToEdit.Thickness = new (AdornmentToEdit.Thickness.Left, AdornmentToEdit.Thickness.Top, e.NewValue, AdornmentToEdit.Thickness.Bottom);
     }
 
-    private void Bottom_ValueChanging (object sender, CancelEventArgs<int> e)
+    private void Bottom_ValueChanging (object? sender, CancelEventArgs<int> e)
     {
         if (e.NewValue < 0 || AdornmentToEdit is null)
         {

+ 68 - 75
UICatalog/Scenarios/AdornmentsEditor.cs → UICatalog/Scenarios/Editors/AdornmentsEditor.cs

@@ -1,6 +1,5 @@
 #nullable enable
 using System;
-using System.Text;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios;
@@ -12,19 +11,16 @@ public class AdornmentsEditor : View
 {
     public AdornmentsEditor ()
     {
-        //ColorScheme = Colors.ColorSchemes ["Dialog"];
         Title = "AdornmentsEditor";
 
         Width = Dim.Auto (DimAutoStyle.Content);
         Height = Dim.Auto (DimAutoStyle.Content);
 
-        //SuperViewRendersLineCanvas = true;
-
         CanFocus = true;
 
         TabStop = TabBehavior.TabGroup;
 
-        _expandButton = new ()
+        ExpandButton = new ()
         {
             Orientation = Orientation.Horizontal
         };
@@ -32,19 +28,12 @@ public class AdornmentsEditor : View
         Initialized += AdornmentsEditor_Initialized;
     }
 
-    private readonly ViewDiagnosticFlags _savedDiagnosticFlags = Diagnostics;
     private View? _viewToEdit;
 
-    private Label? _lblView; // Text describing the vi
-
     private MarginEditor? _marginEditor;
     private BorderEditor? _borderEditor;
     private PaddingEditor? _paddingEditor;
 
-    // TODO: Move Diagnostics to a separate Editor class (DiagnosticsEditor?).
-    private CheckBox? _diagPaddingCheckBox;
-    private CheckBox? _diagRulerCheckBox;
-
     /// <summary>
     ///     Gets or sets whether the AdornmentsEditor should automatically select the View to edit
     ///     based on the values of <see cref="AutoSelectSuperView"/> and <see cref="AutoSelectAdornments"/>.
@@ -73,20 +62,58 @@ public class AdornmentsEditor : View
 
             _viewToEdit = value;
 
-            if (_viewToEdit is not Adornment)
+            if (_marginEditor is { })
+            {
+                _marginEditor.AdornmentToEdit = _viewToEdit?.Margin ?? null;
+            }
+
+            if (_borderEditor is { })
             {
-                _marginEditor!.AdornmentToEdit = _viewToEdit?.Margin ?? null;
-                _borderEditor!.AdornmentToEdit = _viewToEdit?.Border ?? null;
-                _paddingEditor!.AdornmentToEdit = _viewToEdit?.Padding ?? null;
+                _borderEditor.AdornmentToEdit = _viewToEdit?.Border ?? null;
             }
 
-            if (_lblView is { })
+            if (_paddingEditor is { })
             {
-                _lblView.Text = $"{_viewToEdit?.GetType ().Name}: {_viewToEdit?.Id}" ?? string.Empty;
+                _paddingEditor.AdornmentToEdit = _viewToEdit?.Padding ?? null;
             }
+
+            if (_viewToEdit is not Adornment)
+            {
+                Enabled = true;
+            }
+            else
+            {
+                Enabled = false;
+            }
+
+            Padding.Text = $"View: {GetIdentifyingString(_viewToEdit)}";
         }
     }
 
+    private string GetIdentifyingString (View? view)
+    {
+        if (view is null)
+        {
+            return "null";
+        }
+
+        if (!string.IsNullOrEmpty (view.Id))
+        {
+            return view.Id;
+        }
+
+        if (!string.IsNullOrEmpty (view.Title))
+        {
+            return view.Title;
+        }
+
+        if (!string.IsNullOrEmpty (view.Text))
+        {
+            return view.Text;
+        }
+
+        return view.GetType ().Name;
+    }
 
     private void NavigationOnFocusedChanged (object? sender, EventArgs e)
     {
@@ -137,43 +164,40 @@ public class AdornmentsEditor : View
     }
 
     /// <inheritdoc/>
-    protected override void Dispose (bool disposing)
-    {
-        Diagnostics = _savedDiagnosticFlags;
-        base.Dispose (disposing);
-    }
+    protected override void Dispose (bool disposing) { base.Dispose (disposing); }
 
-    private readonly ExpanderButton? _expandButton;
+    public ExpanderButton? ExpandButton { get; }
 
-    public ExpanderButton? ExpandButton => _expandButton;
+    public bool ShowViewIdentifier
+    {
+        get => Padding.Thickness != Thickness.Empty;
+        set
+        {
+            if (value)
+            {
+                Padding.Thickness = new (0, 2, 0, 0);
+            }
+            else
+            {
+                Padding.Thickness =Thickness.Empty;
+            }
+        }
+    }
 
     private void AdornmentsEditor_Initialized (object? sender, EventArgs e)
     {
         BorderStyle = LineStyle.Dotted;
 
-        Border.Add (_expandButton!);
-
-        _lblView = new ()
-        {
-            X = 0,
-            Y = 0,
-            Height = 2
-        };
-        _lblView.TextFormatter.WordWrap = true;
-        _lblView.TextFormatter.MultiLine = true;
-        _lblView.HotKeySpecifier = (Rune)'\uffff';
-        Add (_lblView);
+        Border.Add (ExpandButton!);
 
         _marginEditor = new ()
         {
             X = 0,
-            Y = Pos.Bottom (_lblView),
+            Y = 0,
             SuperViewRendersLineCanvas = true
         };
         Add (_marginEditor);
 
-        _lblView.Width = Dim.Width (_marginEditor);
-
         _borderEditor = new ()
         {
             X = Pos.Left (_marginEditor),
@@ -190,41 +214,10 @@ public class AdornmentsEditor : View
         };
         Add (_paddingEditor);
 
-        _diagPaddingCheckBox = new () { Text = "_Diagnostic Padding" };
-        _diagPaddingCheckBox.CheckedState = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Padding) ? CheckState.Checked : CheckState.UnChecked;
-
-        _diagPaddingCheckBox.CheckedStateChanging += (s, e) =>
-                                       {
-                                           if (e.NewValue == CheckState.Checked)
-                                           {
-                                               Diagnostics |= ViewDiagnosticFlags.Padding;
-                                           }
-                                           else
-                                           {
-                                               Diagnostics &= ~ViewDiagnosticFlags.Padding;
-                                           }
-                                       };
-
-        Add (_diagPaddingCheckBox);
-        _diagPaddingCheckBox.Y = Pos.Bottom (_paddingEditor);
-
-        _diagRulerCheckBox = new () { Text = "_Diagnostic Ruler" };
-        _diagRulerCheckBox.CheckedState = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Ruler) ? CheckState.Checked : CheckState.UnChecked;
-
-        _diagRulerCheckBox.CheckedStateChanging += (s, e) =>
-                                     {
-                                         if (e.NewValue == CheckState.Checked)
-                                         {
-                                             Diagnostics |= ViewDiagnosticFlags.Ruler;
-                                         }
-                                         else
-                                         {
-                                             Diagnostics &= ~ViewDiagnosticFlags.Ruler;
-                                         }
-                                     };
-
-        Add (_diagRulerCheckBox);
-        _diagRulerCheckBox.Y = Pos.Bottom (_diagPaddingCheckBox);
+
+        _marginEditor.AdornmentToEdit = _viewToEdit?.Margin ?? null;
+        _borderEditor.AdornmentToEdit = _viewToEdit?.Border ?? null;
+        _paddingEditor.AdornmentToEdit = _viewToEdit?.Padding ?? null;
 
         Application.MouseEvent += ApplicationOnMouseEvent;
         Application.Navigation!.FocusedChanged += NavigationOnFocusedChanged;

+ 34 - 32
UICatalog/Scenarios/ArrangementEditor.cs → UICatalog/Scenarios/Editors/ArrangementEditor.cs

@@ -24,43 +24,45 @@ public sealed class ArrangementEditor : View
 
         Initialized += ArrangementEditor_Initialized;
 
-        _arrangementSlider.Options = new List<SliderOption<ViewArrangement>> ();
+        _arrangementSlider.Options =
+        [
+            new SliderOption<ViewArrangement>
+            {
+                Legend = ViewArrangement.Movable.ToString (),
+                Data = ViewArrangement.Movable
+            },
 
-        _arrangementSlider.Options.Add (new SliderOption<ViewArrangement>
-        {
-            Legend = ViewArrangement.Movable.ToString (),
-            Data = ViewArrangement.Movable
-        });
+            new SliderOption<ViewArrangement>
+            {
+                Legend = ViewArrangement.LeftResizable.ToString (),
+                Data = ViewArrangement.LeftResizable
+            },
 
-        _arrangementSlider.Options.Add (new SliderOption<ViewArrangement>
-        {
-            Legend = ViewArrangement.LeftResizable.ToString (),
-            Data = ViewArrangement.LeftResizable
-        });
+            new SliderOption<ViewArrangement>
+            {
+                Legend = ViewArrangement.RightResizable.ToString (),
+                Data = ViewArrangement.RightResizable
+            },
 
-        _arrangementSlider.Options.Add (new SliderOption<ViewArrangement>
-        {
-            Legend = ViewArrangement.RightResizable.ToString (),
-            Data = ViewArrangement.RightResizable
-        });
+            new SliderOption<ViewArrangement>
+            {
+                Legend = ViewArrangement.TopResizable.ToString (),
+                Data = ViewArrangement.TopResizable
+            },
 
-        _arrangementSlider.Options.Add (new SliderOption<ViewArrangement>
-        {
-            Legend = ViewArrangement.TopResizable.ToString (),
-            Data = ViewArrangement.TopResizable
-        });
+            new SliderOption<ViewArrangement>
+            {
+                Legend = ViewArrangement.BottomResizable.ToString (),
+                Data = ViewArrangement.BottomResizable
+            },
 
-        _arrangementSlider.Options.Add (new SliderOption<ViewArrangement>
-        {
-            Legend = ViewArrangement.BottomResizable.ToString (),
-            Data = ViewArrangement.BottomResizable
-        });
+            new SliderOption<ViewArrangement>
+            {
+                Legend = ViewArrangement.Overlapped.ToString (),
+                Data = ViewArrangement.Overlapped
+            }
 
-        _arrangementSlider.Options.Add (new SliderOption<ViewArrangement>
-        {
-            Legend = ViewArrangement.Overlapped.ToString (),
-            Data = ViewArrangement.Overlapped
-        });
+        ];
 
         Add (_arrangementSlider);
     }
@@ -69,7 +71,7 @@ public sealed class ArrangementEditor : View
 
     private Label? _lblView; // Text describing the view being edited
 
-    private Slider<ViewArrangement> _arrangementSlider = new Slider<ViewArrangement> ()
+    private readonly Slider<ViewArrangement> _arrangementSlider = new Slider<ViewArrangement> ()
     {
         Orientation = Orientation.Vertical,
         UseMinimumSize = true,

+ 22 - 24
UICatalog/Scenarios/BorderEditor.cs → UICatalog/Scenarios/Editors/BorderEditor.cs

@@ -1,4 +1,5 @@
-using System;
+#nullable enable
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using Terminal.Gui;
@@ -7,9 +8,9 @@ namespace UICatalog.Scenarios;
 
 public class BorderEditor : AdornmentEditor
 {
-    private CheckBox _ckbTitle;
-    private RadioGroup _rbBorderStyle;
-    private CheckBox _ckbGradient;
+    private CheckBox? _ckbTitle;
+    private RadioGroup? _rbBorderStyle;
+    private CheckBox? _ckbGradient;
 
     public BorderEditor ()
     {
@@ -18,14 +19,14 @@ public class BorderEditor : AdornmentEditor
         AdornmentChanged += BorderEditor_AdornmentChanged;
     }
 
-    private void BorderEditor_AdornmentChanged (object sender, EventArgs e)
+    private void BorderEditor_AdornmentChanged (object? sender, EventArgs e)
     {
-        _ckbTitle.CheckedState = ((Border)AdornmentToEdit).Settings.FastHasFlags (BorderSettings.Title) ? CheckState.Checked : CheckState.UnChecked;
-        _rbBorderStyle.SelectedItem = (int)((Border)AdornmentToEdit).LineStyle;
-        _ckbGradient.CheckedState = ((Border)AdornmentToEdit).Settings.FastHasFlags (BorderSettings.Gradient) ? CheckState.Checked : CheckState.UnChecked;
+        _ckbTitle!.CheckedState = ((Border)AdornmentToEdit!).Settings.FastHasFlags (BorderSettings.Title) ? CheckState.Checked : CheckState.UnChecked;
+        _rbBorderStyle!.SelectedItem = (int)((Border)AdornmentToEdit).LineStyle;
+        _ckbGradient!.CheckedState = ((Border)AdornmentToEdit).Settings.FastHasFlags (BorderSettings.Gradient) ? CheckState.Checked : CheckState.UnChecked;
     }
 
-    private void BorderEditor_Initialized (object sender, EventArgs e)
+    private void BorderEditor_Initialized (object? sender, EventArgs e)
     {
         List<LineStyle> borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast<LineStyle> ().ToList ();
 
@@ -35,12 +36,11 @@ public class BorderEditor : AdornmentEditor
 
             Y = Pos.Bottom (Subviews [^1]),
             Width = Dim.Fill (),
-            SelectedItem = (int)(((Border)AdornmentToEdit)?.LineStyle ?? LineStyle.None),
+            SelectedItem = (int)(((Border)AdornmentToEdit!)?.LineStyle ?? LineStyle.None),
             BorderStyle = LineStyle.Single,
             Title = "Border St_yle",
             SuperViewRendersLineCanvas = true,
-            Enabled = AdornmentToEdit is { },
-            RadioLabels = borderStyleEnum.Select (e => e.ToString ()).ToArray ()
+            RadioLabels = borderStyleEnum.Select (style => style.ToString ()).ToArray ()
         };
         Add (_rbBorderStyle);
 
@@ -54,7 +54,6 @@ public class BorderEditor : AdornmentEditor
             CheckedState = CheckState.Checked,
             SuperViewRendersLineCanvas = true,
             Text = "Title",
-            Enabled = AdornmentToEdit is { }
         };
 
         _ckbTitle.CheckedStateChanging += OnCkbTitleOnToggle;
@@ -68,7 +67,6 @@ public class BorderEditor : AdornmentEditor
             CheckedState = CheckState.Checked,
             SuperViewRendersLineCanvas = true,
             Text = "Gradient",
-            Enabled = AdornmentToEdit is { }
         };
 
         _ckbGradient.CheckedStateChanging += OnCkbGradientOnToggle;
@@ -76,10 +74,10 @@ public class BorderEditor : AdornmentEditor
 
         return;
 
-        void OnRbBorderStyleOnSelectedItemChanged (object s, SelectedItemChangedArgs e)
+        void OnRbBorderStyleOnSelectedItemChanged (object? s, SelectedItemChangedArgs args)
         {
-            LineStyle prevBorderStyle = AdornmentToEdit.BorderStyle;
-            ((Border)AdornmentToEdit).LineStyle = (LineStyle)e.SelectedItem;
+            LineStyle prevBorderStyle = AdornmentToEdit!.BorderStyle;
+            ((Border)AdornmentToEdit).LineStyle = (LineStyle)args.SelectedItem;
 
             if (((Border)AdornmentToEdit).LineStyle == LineStyle.None)
             {
@@ -91,34 +89,34 @@ public class BorderEditor : AdornmentEditor
             }
 
             ((Border)AdornmentToEdit).SetNeedsDisplay ();
-            SetLayoutNeeded ();
+            SetNeedsLayout ();
         }
 
-        void OnCkbTitleOnToggle (object sender, CancelEventArgs<CheckState> args)
+        void OnCkbTitleOnToggle (object? _, CancelEventArgs<CheckState> args)
         {
             if (args.NewValue == CheckState.Checked)
 
             {
-                ((Border)AdornmentToEdit).Settings |= BorderSettings.Title;
+                ((Border)AdornmentToEdit!).Settings |= BorderSettings.Title;
             }
             else
 
             {
-                ((Border)AdornmentToEdit).Settings &= ~BorderSettings.Title;
+                ((Border)AdornmentToEdit!).Settings &= ~BorderSettings.Title;
             }
         }
 
-        void OnCkbGradientOnToggle (object sender, CancelEventArgs<CheckState> args)
+        void OnCkbGradientOnToggle (object? _, CancelEventArgs<CheckState> args)
         {
             if (args.NewValue == CheckState.Checked)
 
             {
-                ((Border)AdornmentToEdit).Settings |= BorderSettings.Gradient;
+                ((Border)AdornmentToEdit!).Settings |= BorderSettings.Gradient;
             }
             else
 
             {
-                ((Border)AdornmentToEdit).Settings &= ~BorderSettings.Gradient;
+                ((Border)AdornmentToEdit!).Settings &= ~BorderSettings.Gradient;
             }
         }
     }

+ 300 - 0
UICatalog/Scenarios/Editors/DimEditor.cs

@@ -0,0 +1,300 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+/// <summary>
+///     Provides an editor UI for the Margin, Border, and Padding of a View.
+/// </summary>
+public class DimEditor : View
+{
+    public DimEditor ()
+    {
+        Title = "Dim";
+
+        BorderStyle = LineStyle.Rounded;
+
+        Width = Dim.Auto (DimAutoStyle.Content);
+        Height = Dim.Auto (DimAutoStyle.Content);
+
+        CanFocus = true;
+
+        _expandButton = new ()
+        {
+            Orientation = Orientation.Vertical
+        };
+
+
+        TabStop = TabBehavior.TabGroup;
+
+        Initialized += DimEditor_Initialized;
+
+        AddCommand (Command.Accept, () => true);
+    }
+
+    private View? _viewToEdit;
+
+    private int _value;
+    private RadioGroup? _dimRadioGroup;
+    private TextField? _valueEdit;
+
+    /// <summary>
+    ///     Gets or sets whether the DimEditor should automatically select the View to edit
+    ///     based on the values of <see cref="AutoSelectSuperView"/> and <see cref="AutoSelectAdornments"/>.
+    /// </summary>
+    public bool AutoSelectViewToEdit { get; set; }
+
+    /// <summary>
+    ///     Gets or sets the View that will scope the behavior of <see cref="AutoSelectViewToEdit"/>.
+    /// </summary>
+    public View? AutoSelectSuperView { get; set; }
+
+    /// <summary>
+    ///     Gets or sets whether auto select with the mouse will select Adornments or just Views.
+    /// </summary>
+    public bool AutoSelectAdornments { get; set; }
+
+    public View? ViewToEdit
+    {
+        get => _viewToEdit;
+        set
+        {
+            if (_viewToEdit == value)
+            {
+                return;
+            }
+
+            if (value is null && _viewToEdit is { })
+            {
+                _viewToEdit.SubviewsLaidOut -= View_LayoutComplete;
+            }
+
+            _viewToEdit = value;
+
+            if (_viewToEdit is { })
+            {
+                _viewToEdit.SubviewsLaidOut += View_LayoutComplete;
+
+                _viewToEdit.SubviewLayout += (sender, args) =>
+                                             {
+
+                                             };
+            }
+        }
+    }
+
+    private void View_LayoutComplete (object? sender, LayoutEventArgs args)
+    {
+        UpdateSettings ();
+    }
+
+    private bool _updatingSettings = false;
+
+    private void UpdateSettings ()
+    {
+        if (ViewToEdit is null)
+        {
+            return;
+        }
+
+        _updatingSettings = true;
+
+        Dim? dim;
+        if (Dimension == Dimension.Width)
+        {
+            dim = ViewToEdit.Width;
+        }
+        else
+        {
+            dim = ViewToEdit.Height;
+        }
+
+        try
+        {
+            _dimRadioGroup!.SelectedItem = _dimNames.IndexOf (_dimNames.First (s => dim!.ToString ().StartsWith(s)));
+        }
+        catch (InvalidOperationException e)
+        {
+            // This is a hack to work around the fact that the Pos enum doesn't have an "Align" value yet
+            Debug.WriteLine ($"{e}");
+        }
+
+        _valueEdit!.Enabled = false;
+        switch (dim)
+        {
+            case DimAbsolute absolute:
+                _valueEdit.Enabled = true;
+                _value = absolute.Size;
+                _valueEdit!.Text = _value.ToString ();
+                break;
+            case DimFill fill:
+                var margin = fill.Margin as DimAbsolute;
+                _valueEdit.Enabled = margin is {};
+                _value = margin?.Size ?? 0;
+                _valueEdit!.Text = _value.ToString ();
+                break;
+            case DimFunc func:
+                _valueEdit.Enabled = true;
+                _value = func.Fn ();
+                _valueEdit!.Text = _value.ToString ();
+                break;
+            case DimPercent percent:
+                _valueEdit.Enabled = true;
+                _value = percent.Percentage;
+                _valueEdit!.Text = _value.ToString ();
+                break;
+            default:
+                _valueEdit!.Text = dim!.ToString ();
+                break;
+        }
+
+        _updatingSettings = false;
+    }
+
+    private void NavigationOnFocusedChanged (object? sender, EventArgs e)
+    {
+        if (AutoSelectSuperView is null)
+        {
+            return;
+        }
+
+        if (ApplicationNavigation.IsInHierarchy (this, Application.Navigation!.GetFocused ()))
+        {
+            return;
+        }
+
+        if (!ApplicationNavigation.IsInHierarchy (AutoSelectSuperView, Application.Navigation!.GetFocused ()))
+        {
+            return;
+        }
+
+        ViewToEdit = Application.Navigation!.GetFocused ();
+    }
+
+    private void ApplicationOnMouseEvent (object? sender, MouseEventArgs e)
+    {
+        if (e.Flags != MouseFlags.Button1Clicked || !AutoSelectViewToEdit)
+        {
+            return;
+        }
+
+        if ((AutoSelectSuperView is { } && !AutoSelectSuperView.FrameToScreen ().Contains (e.Position))
+            || FrameToScreen ().Contains (e.Position))
+        {
+            return;
+        }
+
+        View? view = e.View;
+
+        if (view is null)
+        {
+            return;
+        }
+
+        if (view is Adornment adornment)
+        {
+            ViewToEdit = AutoSelectAdornments ? adornment : adornment.Parent;
+        }
+        else
+        {
+            ViewToEdit = view;
+        }
+    }
+
+    /// <inheritdoc/>
+    protected override void Dispose (bool disposing)
+    {
+        base.Dispose (disposing);
+    }
+
+    private readonly ExpanderButton? _expandButton;
+
+    public ExpanderButton? ExpandButton => _expandButton;
+
+    public Dimension Dimension { get; set; }
+
+    private void DimEditor_Initialized (object? sender, EventArgs e)
+    {
+        Border.Add (_expandButton!);
+
+        var label = new Label
+        {
+            X = 0, Y = 0,
+            Text = $"{Title}:"
+        };
+        Add (label);
+        _dimRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = _radioItems };
+        _dimRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
+        _valueEdit = new ()
+        {
+            X = Pos.Right (label) + 1,
+            Y = 0,
+            Width = Dim.Func (() => _radioItems.Max (i => i.GetColumns ()) - label.Frame.Width + 1),
+            Text = $"{_value}"
+        };
+
+        _valueEdit.Accepting += (s, args) =>
+        {
+            try
+            {
+                _value = int.Parse (_valueEdit.Text);
+                DimChanged ();
+            }
+            catch
+            {
+                // ignored
+            }
+            args.Cancel = true;
+        };
+        Add (_valueEdit);
+
+        Add (_dimRadioGroup);
+
+        Application.MouseEvent += ApplicationOnMouseEvent;
+        Application.Navigation!.FocusedChanged += NavigationOnFocusedChanged;
+    }
+
+    private void OnRadioGroupOnSelectedItemChanged (object? s, SelectedItemChangedArgs selected) { DimChanged (); }
+
+    // These need to have same order 
+    private readonly List<string> _dimNames = ["Absolute", "Auto", "Fill", "Func", "Percent",];
+    private readonly string [] _radioItems = ["Absolute(n)", "Auto", "Fill(n)", "Func(()=>n)", "Percent(n)",];
+
+    private void DimChanged ()
+    {
+        if (ViewToEdit == null || _updatingSettings)
+        {
+            return;
+        }
+
+        try
+        {
+            Dim? dim = _dimRadioGroup!.SelectedItem switch
+            {
+                0 => Dim.Absolute (_value),
+                1 => Dim.Auto (),
+                2 => Dim.Fill (_value),
+                3 => Dim.Func (() => _value),
+                4 => Dim.Percent (_value),
+                _ => Dimension == Dimension.Width ? ViewToEdit.Width : ViewToEdit.Height
+            };
+
+            if (Dimension == Dimension.Width)
+            {
+                ViewToEdit.Width = dim;
+            }
+            else
+            {
+                ViewToEdit.Height = dim;
+            }
+        }
+        catch (Exception e)
+        {
+            MessageBox.ErrorQuery ("Exception", e.Message, "Ok");
+        }
+    }
+}

+ 14 - 9
UICatalog/Scenarios/ExpanderButton.cs → UICatalog/Scenarios/Editors/ExpanderButton.cs

@@ -1,4 +1,5 @@
-using System;
+#nullable enable
+using System;
 using System.Text;
 using Terminal.Gui;
 
@@ -36,7 +37,6 @@ public class ExpanderButton : Button
         Height = 1;
         NoDecorations = true;
         NoPadding = true;
-        ShadowStyle = ShadowStyle.None;
 
         AddCommand (Command.HotKey, Toggle);
         AddCommand (Command.Toggle, Toggle);
@@ -47,7 +47,12 @@ public class ExpanderButton : Button
         Initialized += ExpanderButton_Initialized;
     }
 
-    private void ExpanderButton_Initialized (object sender, EventArgs e) { ExpandOrCollapse (Collapsed); }
+    private void ExpanderButton_Initialized (object? sender, EventArgs e)
+    {
+        ShadowStyle = ShadowStyle.None;
+
+        ExpandOrCollapse (Collapsed);
+    }
 
     private Orientation _orientation = Orientation.Horizontal;
 
@@ -102,7 +107,7 @@ public class ExpanderButton : Button
     /// <summary>
     ///     Fired when the orientation has changed. Can be cancelled.
     /// </summary>
-    public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
+    public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
 
     /// <summary>
     ///     The glyph to display when the view is collapsed.
@@ -146,7 +151,7 @@ public class ExpanderButton : Button
     /// <summary>
     ///     Fired when the orientation has changed. Can be cancelled.
     /// </summary>
-    public event EventHandler<CancelEventArgs<bool>> CollapsedChanging;
+    public event EventHandler<CancelEventArgs<bool>>? CollapsedChanging;
 
     /// <summary>
     ///     Collapses or Expands the view.
@@ -159,13 +164,13 @@ public class ExpanderButton : Button
         return true;
     }
 
-    private Dim _previousDim;
+    private Dim? _previousDim;
 
     private void ExpandOrCollapse (bool collapse)
     {
         Text = $"{(Collapsed ? CollapsedGlyph : ExpandedGlyph)}";
 
-        View superView = SuperView;
+        View? superView = SuperView;
 
         if (superView is Adornment adornment)
         {
@@ -182,12 +187,12 @@ public class ExpanderButton : Button
             // Collapse
             if (Orientation == Orientation.Vertical)
             {
-                _previousDim = superView.Height;
+                _previousDim = superView!.Height!;
                 superView.Height = 1;
             }
             else
             {
-                _previousDim = superView.Width;
+                _previousDim = superView!.Width!;
                 superView.Width = 1;
             }
         }

+ 228 - 0
UICatalog/Scenarios/Editors/LayoutEditor.cs

@@ -0,0 +1,228 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+/// <summary>
+///     Provides an editor UI for the Margin, Border, and Padding of a View.
+/// </summary>
+public class LayoutEditor : View
+{
+    public LayoutEditor ()
+    {
+        Title = "_LayoutEditor";
+
+        Width = Dim.Auto (DimAutoStyle.Content);
+        Height = Dim.Auto (DimAutoStyle.Content);
+
+        CanFocus = true;
+
+        TabStop = TabBehavior.TabGroup;
+
+        _expandButton = new ()
+        {
+            Orientation = Orientation.Vertical
+        };
+
+        Initialized += LayoutEditor_Initialized;
+
+        AddCommand (Command.Accept, () => true);
+    }
+
+    private View? _viewToEdit;
+
+    private readonly List<string> _dimNames = ["Auto", "Percent", "Fill", "Absolute"];
+
+    private PosEditor? _xEditor;
+    private PosEditor? _yEditor;
+
+    private DimEditor? _widthEditor;
+    private DimEditor? _heightEditor;
+
+    /// <summary>
+    ///     Gets or sets whether the LayoutEditor should automatically select the View to edit
+    ///     based on the values of <see cref="AutoSelectSuperView"/> and <see cref="AutoSelectAdornments"/>.
+    /// </summary>
+    public bool AutoSelectViewToEdit { get; set; }
+
+    /// <summary>
+    ///     Gets or sets the View that will scope the behavior of <see cref="AutoSelectViewToEdit"/>.
+    /// </summary>
+    public View? AutoSelectSuperView { get; set; }
+
+    /// <summary>
+    ///     Gets or sets whether auto select with the mouse will select Adornments or just Views.
+    /// </summary>
+    public bool AutoSelectAdornments { get; set; }
+
+    public View? ViewToEdit
+    {
+        get => _viewToEdit;
+        set
+        {
+            if (_viewToEdit == value)
+            {
+                return;
+            }
+
+            if (value is null && _viewToEdit is { })
+            {
+                _viewToEdit.SubviewsLaidOut -= View_LayoutComplete;
+            }
+
+            _viewToEdit = value;
+
+            if (_viewToEdit is { })
+            {
+                _viewToEdit.SubviewsLaidOut += View_LayoutComplete;
+            }
+
+            if (_xEditor is { })
+            {
+                _xEditor.ViewToEdit = _viewToEdit;
+            }
+
+            if (_yEditor is { })
+            {
+                _yEditor.ViewToEdit = _viewToEdit;
+            }
+
+            if (_widthEditor is { })
+            {
+                _widthEditor.ViewToEdit = _viewToEdit;
+            }
+
+            if (_heightEditor is { })
+            {
+                _heightEditor.ViewToEdit = _viewToEdit;
+            }
+
+        }
+    }
+
+    private void View_LayoutComplete (object? sender, LayoutEventArgs args)
+    {
+        UpdateSettings ();
+    }
+
+    private bool _updatingSettings = false;
+
+    private void UpdateSettings ()
+    {
+        if (ViewToEdit is null)
+        {
+            return;
+        }
+
+        _updatingSettings = true;
+
+        _updatingSettings = false;
+    }
+
+    private void NavigationOnFocusedChanged (object? sender, EventArgs e)
+    {
+        if (AutoSelectSuperView is null)
+        {
+            return;
+        }
+
+        if (ApplicationNavigation.IsInHierarchy (this, Application.Navigation!.GetFocused ()))
+        {
+            return;
+        }
+
+        if (!ApplicationNavigation.IsInHierarchy (AutoSelectSuperView, Application.Navigation!.GetFocused ()))
+        {
+            return;
+        }
+
+        ViewToEdit = Application.Navigation!.GetFocused ();
+    }
+
+    private void ApplicationOnMouseEvent (object? sender, MouseEventArgs e)
+    {
+        if (e.Flags != MouseFlags.Button1Clicked || !AutoSelectViewToEdit)
+        {
+            return;
+        }
+
+        if ((AutoSelectSuperView is { } && !AutoSelectSuperView.FrameToScreen ().Contains (e.Position))
+            || FrameToScreen ().Contains (e.Position))
+        {
+            return;
+        }
+
+        View? view = e.View;
+
+        if (view is null)
+        {
+            return;
+        }
+
+        if (view is Adornment adornment)
+        {
+            ViewToEdit = AutoSelectAdornments ? adornment : adornment.Parent;
+        }
+        else
+        {
+            ViewToEdit = view;
+        }
+    }
+
+    /// <inheritdoc/>
+    protected override void Dispose (bool disposing)
+    {
+        base.Dispose (disposing);
+    }
+
+    private readonly ExpanderButton? _expandButton;
+
+    public ExpanderButton? ExpandButton => _expandButton;
+
+    private void LayoutEditor_Initialized (object? sender, EventArgs e)
+    {
+        BorderStyle = LineStyle.Rounded;
+
+        Border.Add (_expandButton!);
+
+        _xEditor = new ()
+        {
+            Title = "_X",
+            BorderStyle = LineStyle.None,
+            Dimension = Dimension.Width
+        };
+
+        _yEditor = new ()
+        {
+            Title = "_Y",
+            BorderStyle = LineStyle.None,
+            Dimension = Dimension.Height,
+            X = Pos.Right(_xEditor) + 1
+        };
+
+
+        _widthEditor = new ()
+        {
+            Title = "_Width",
+            BorderStyle = LineStyle.None,
+            Dimension = Dimension.Width,
+            X = Pos.Right(_yEditor) + 1
+        };
+
+        _heightEditor = new ()
+        {
+            Title = "_Height",
+            BorderStyle = LineStyle.None,
+            Dimension = Dimension.Height,
+            X = Pos.Right (_widthEditor) + 1
+        };
+
+        Add (_xEditor, _yEditor, _widthEditor, _heightEditor);
+
+        Application.MouseEvent += ApplicationOnMouseEvent;
+        Application.Navigation!.FocusedChanged += NavigationOnFocusedChanged;
+    }
+}

+ 7 - 8
UICatalog/Scenarios/MarginEditor.cs → UICatalog/Scenarios/Editors/MarginEditor.cs

@@ -1,5 +1,5 @@
-using System;
-using System.Linq;
+#nullable enable
+using System;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios;
@@ -13,17 +13,17 @@ public class MarginEditor : AdornmentEditor
         AdornmentChanged += MarginEditor_AdornmentChanged;
     }
 
-    RadioGroup _rgShadow;
+    private RadioGroup? _rgShadow;
 
-    private void MarginEditor_AdornmentChanged (object sender, EventArgs e)
+    private void MarginEditor_AdornmentChanged (object? sender, EventArgs e)
     {
         if (AdornmentToEdit is { })
         {
-            _rgShadow.SelectedItem = (int)((Margin)AdornmentToEdit).ShadowStyle;
+            _rgShadow!.SelectedItem = (int)((Margin)AdornmentToEdit).ShadowStyle;
         }
     }
 
-    private void MarginEditor_Initialized (object sender, EventArgs e)
+    private void MarginEditor_Initialized (object? sender, EventArgs e)
     {
         _rgShadow = new RadioGroup
         {
@@ -33,7 +33,6 @@ public class MarginEditor : AdornmentEditor
             SuperViewRendersLineCanvas = true,
             Title = "_Shadow",
             BorderStyle = LineStyle.Dashed,
-            Enabled = AdornmentToEdit is { },
             RadioLabels = Enum.GetNames (typeof (ShadowStyle)),
         };
 
@@ -44,7 +43,7 @@ public class MarginEditor : AdornmentEditor
 
         _rgShadow.SelectedItemChanged += (sender, args) =>
                                         {
-                                            ((Margin)AdornmentToEdit).ShadowStyle = (ShadowStyle)args.SelectedItem;
+                                            ((Margin)AdornmentToEdit!).ShadowStyle = (ShadowStyle)args.SelectedItem;
                                         };
 
         Add (_rgShadow);

+ 2 - 1
UICatalog/Scenarios/PaddingEditor.cs → UICatalog/Scenarios/Editors/PaddingEditor.cs

@@ -1,4 +1,5 @@
-namespace UICatalog.Scenarios;
+#nullable enable
+namespace UICatalog.Scenarios;
 
 public class PaddingEditor : AdornmentEditor
 {

+ 292 - 0
UICatalog/Scenarios/Editors/PosEditor.cs

@@ -0,0 +1,292 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+/// <summary>
+///     Provides an editor UI for the Margin, Border, and Padding of a View.
+/// </summary>
+public class PosEditor : View
+{
+    public PosEditor ()
+    {
+        Title = "Pos";
+
+        BorderStyle = LineStyle.Rounded;
+
+        Width = Dim.Auto (DimAutoStyle.Content);
+        Height = Dim.Auto (DimAutoStyle.Content);
+
+        CanFocus = true;
+
+        _expandButton = new ()
+        {
+            Orientation = Orientation.Vertical
+        };
+
+
+        TabStop = TabBehavior.TabGroup;
+
+        Initialized += PosEditor_Initialized;
+
+        AddCommand (Command.Accept, () => true);
+    }
+
+    private View? _viewToEdit;
+
+    private int _value;
+    private RadioGroup? _posRadioGroup;
+    private TextField? _valueEdit;
+
+    /// <summary>
+    ///     Gets or sets whether the PosEditor should automatically select the View to edit
+    ///     based on the values of <see cref="AutoSelectSuperView"/> and <see cref="AutoSelectAdornments"/>.
+    /// </summary>
+    public bool AutoSelectViewToEdit { get; set; }
+
+    /// <summary>
+    ///     Gets or sets the View that will scope the behavior of <see cref="AutoSelectViewToEdit"/>.
+    /// </summary>
+    public View? AutoSelectSuperView { get; set; }
+
+    /// <summary>
+    ///     Gets or sets whether auto select with the mouse will select Adornments or just Views.
+    /// </summary>
+    public bool AutoSelectAdornments { get; set; }
+
+    public View? ViewToEdit
+    {
+        get => _viewToEdit;
+        set
+        {
+            if (_viewToEdit == value)
+            {
+                return;
+            }
+
+            if (value is null && _viewToEdit is { })
+            {
+                _viewToEdit.SubviewsLaidOut -= View_LayoutComplete;
+            }
+
+            _viewToEdit = value;
+
+            if (_viewToEdit is { })
+            {
+                _viewToEdit.SubviewsLaidOut += View_LayoutComplete;
+            }
+        }
+    }
+
+    private void View_LayoutComplete (object? sender, LayoutEventArgs args)
+    {
+        UpdateSettings ();
+    }
+
+    private bool _updatingSettings = false;
+
+    private void UpdateSettings ()
+    {
+        if (ViewToEdit is null)
+        {
+            return;
+        }
+
+        _updatingSettings = true;
+
+        Pos? pos;
+        if (Dimension == Dimension.Width)
+        {
+            pos = ViewToEdit.X;
+        }
+        else
+        {
+            pos = ViewToEdit.Y;
+        }
+
+        try
+        {
+            _posRadioGroup!.SelectedItem = _posNames.IndexOf (_posNames.First (s => pos.ToString ().Contains (s)));
+        }
+        catch (InvalidOperationException e)
+        {
+            // This is a hack to work around the fact that the Pos enum doesn't have an "Align" value yet
+            Debug.WriteLine ($"{e}");
+        }
+
+        _valueEdit!.Enabled = false;
+        switch (pos)
+        {
+            case PosPercent percent:
+                _valueEdit.Enabled = true;
+                _value = percent.Percent;
+                _valueEdit!.Text = _value.ToString ();
+                break;
+            case PosAbsolute absolute:
+                _valueEdit.Enabled = true;
+                _value = absolute.Position;
+                _valueEdit!.Text = _value.ToString ();
+                break;
+            case PosFunc func:
+                _valueEdit.Enabled = true;
+                _value = func.Fn ();
+                _valueEdit!.Text = _value.ToString ();
+                break;
+            default:
+                _valueEdit!.Text = pos.ToString ();
+                break;
+        }
+
+        _updatingSettings = false;
+    }
+
+    private void NavigationOnFocusedChanged (object? sender, EventArgs e)
+    {
+        if (AutoSelectSuperView is null)
+        {
+            return;
+        }
+
+        if (ApplicationNavigation.IsInHierarchy (this, Application.Navigation!.GetFocused ()))
+        {
+            return;
+        }
+
+        if (!ApplicationNavigation.IsInHierarchy (AutoSelectSuperView, Application.Navigation!.GetFocused ()))
+        {
+            return;
+        }
+
+        ViewToEdit = Application.Navigation!.GetFocused ();
+    }
+
+    private void ApplicationOnMouseEvent (object? sender, MouseEventArgs e)
+    {
+        if (e.Flags != MouseFlags.Button1Clicked || !AutoSelectViewToEdit)
+        {
+            return;
+        }
+
+        if ((AutoSelectSuperView is { } && !AutoSelectSuperView.FrameToScreen ().Contains (e.Position))
+            || FrameToScreen ().Contains (e.Position))
+        {
+            return;
+        }
+
+        View? view = e.View;
+
+        if (view is null)
+        {
+            return;
+        }
+
+        if (view is Adornment adornment)
+        {
+            ViewToEdit = AutoSelectAdornments ? adornment : adornment.Parent;
+        }
+        else
+        {
+            ViewToEdit = view;
+        }
+    }
+
+    /// <inheritdoc/>
+    protected override void Dispose (bool disposing)
+    {
+        base.Dispose (disposing);
+    }
+
+    private readonly ExpanderButton? _expandButton;
+
+    public ExpanderButton? ExpandButton => _expandButton;
+
+    public Dimension Dimension { get; set; }
+
+    private void PosEditor_Initialized (object? sender, EventArgs e)
+    {
+        Border.Add (_expandButton!);
+
+        var label = new Label
+        {
+            X = 0, Y = 0,
+            Text = $"{Title}:"
+        };
+        Add (label);
+        _posRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = _radioItems };
+        _posRadioGroup.SelectedItemChanged += OnRadioGroupOnSelectedItemChanged;
+        _valueEdit = new ()
+        {
+            X = Pos.Right (label) + 1,
+            Y = 0,
+            Width = Dim.Func (() => _radioItems.Max (i => i.GetColumns ()) - label.Frame.Width + 1),
+            Text = $"{_value}"
+        };
+
+        _valueEdit.Accepting += (s, args) =>
+        {
+            try
+            {
+                _value = int.Parse (_valueEdit.Text);
+                PosChanged ();
+            }
+            catch
+            {
+                // ignored
+            }
+            args.Cancel = true;
+        };
+        Add (_valueEdit);
+
+        Add (_posRadioGroup);
+
+        Application.MouseEvent += ApplicationOnMouseEvent;
+        Application.Navigation!.FocusedChanged += NavigationOnFocusedChanged;
+    }
+
+    private void OnRadioGroupOnSelectedItemChanged (object? s, SelectedItemChangedArgs selected) { PosChanged (); }
+
+    // These need to have same order 
+    private readonly List<string> _posNames = ["Absolute", "Align", "AnchorEnd", "Center", "Func", "Percent",];
+    private readonly string [] _radioItems = ["Absolute(n)", "Align", "AnchorEnd", "Center", "Func(()=>n)", "Percent(n)",];
+
+    private void PosChanged ()
+    {
+        if (ViewToEdit == null || _updatingSettings)
+        {
+            return;
+        }
+
+        try
+        {
+            Pos? pos = _posRadioGroup!.SelectedItem switch
+            {
+                0 => Pos.Absolute (_value),
+                1 => Pos.Align (Alignment.Start),
+                2 => new PosAnchorEnd (),
+                3 => Pos.Center (),
+                4 => Pos.Func (() => _value),
+                5 => Pos.Percent (_value),
+                _ => Dimension == Dimension.Width ? ViewToEdit.X : ViewToEdit.Y
+            };
+
+            if (Dimension == Dimension.Width)
+            {
+                ViewToEdit.X = pos;
+            }
+            else
+            {
+                ViewToEdit.Y = pos;
+            }
+            SetNeedsLayout ();
+
+        }
+        catch (Exception e)
+        {
+            MessageBox.ErrorQuery ("Exception", e.Message, "Ok");
+        }
+    }
+}

+ 2 - 2
UICatalog/Scenarios/GraphViewExample.cs

@@ -126,7 +126,7 @@ public class GraphViewExample : Scenario
                          {
                              Checked = View.Diagnostics
                                        == (ViewDiagnosticFlags
-                                               .Padding
+                                               .Thickness
                                            | ViewDiagnosticFlags
                                                .Ruler),
                              CheckType = MenuItemCheckStyle.Checked
@@ -211,7 +211,7 @@ public class GraphViewExample : Scenario
         _miDiags.Checked = !_miDiags.Checked;
 
         View.Diagnostics = _miDiags.Checked == true
-                               ? ViewDiagnosticFlags.Padding
+                               ? ViewDiagnosticFlags.Thickness
                                  | ViewDiagnosticFlags.Ruler
                                : ViewDiagnosticFlags.Off;
         Application.Refresh ();

+ 1 - 1
UICatalog/Scenarios/LineDrawing.cs

@@ -286,7 +286,7 @@ public class DrawingArea : View
 
         // TODO: This is a hack to work around overlapped views not drawing correctly.
         // without this the toolbox disappears
-        SuperView?.SetLayoutNeeded();
+        SuperView?.SetNeedsLayout();
 
         return true;
     }

+ 1 - 1
UICatalog/Scenarios/MenuBarScenario.cs

@@ -107,7 +107,7 @@ public class MenuBarScenario : Scenario
                                };
 
         // There's no focus change event, so this is a bit of a hack.
-        menuBar.LayoutComplete += (s, e) => { _focusedView.Text = appWindow.MostFocused?.ToString () ?? "None"; };
+        menuBar.SubviewsLaidOut += (s, e) => { _focusedView.Text = appWindow.MostFocused?.ToString () ?? "None"; };
 
         var openBtn = new Button { X = Pos.Center (), Y = 4, Text = "_Open Menu", IsDefault = true };
         openBtn.Accepting += (s, e) => { menuBar.OpenMenu (); };

+ 1 - 0
UICatalog/Scenarios/Navigation.cs

@@ -26,6 +26,7 @@ public class Navigation : Scenario
             X = 0,
             Y = 0,
             AutoSelectViewToEdit = true,
+            ShowViewIdentifier = true,
             TabStop = TabBehavior.NoStop
         };
         app.Add (adornmentsEditor);

+ 1 - 0
UICatalog/Scenarios/NumericUpDownDemo.cs

@@ -23,6 +23,7 @@ public class NumericUpDownDemo : Scenario
             X = 0,
             Y = 0,
             AutoSelectViewToEdit = true,
+            ShowViewIdentifier = true,
         };
         app.Add (editor);
 

+ 3 - 1
UICatalog/Scenarios/ProgressBarStyles.cs

@@ -35,7 +35,9 @@ public class ProgressBarStyles : Scenario
 
         var editor = new AdornmentsEditor ()
         {
-            AutoSelectViewToEdit = false
+            AutoSelectViewToEdit = false,
+            ShowViewIdentifier = true
+
         };
         app.Add (editor);
 

+ 2 - 0
UICatalog/Scenarios/ShadowStyles.cs

@@ -24,6 +24,8 @@ public class ShadowStyles : Scenario
         var editor = new AdornmentsEditor ()
         {
             AutoSelectViewToEdit = true,
+            ShowViewIdentifier = true,
+
         };
         app.Add (editor);
 

+ 1 - 1
UICatalog/Scenarios/Sliders.cs

@@ -86,7 +86,7 @@ public class Sliders : Scenario
             AllowEmpty = false
         };
 
-        single.LayoutStarted += (s, e) =>
+        single.SubviewLayout += (s, e) =>
                                 {
                                     if (single.Orientation == Orientation.Horizontal)
                                     {

+ 1 - 1
UICatalog/Scenarios/TextViewAutocompletePopup.cs

@@ -120,7 +120,7 @@ public class TextViewAutocompletePopup : Scenario
                                       );
         appWindow.Add (statusBar);
 
-        appWindow.LayoutStarted += Win_LayoutStarted;
+        appWindow.SubviewLayout += Win_LayoutStarted;
 
         // Run - Start the application.
         Application.Run (appWindow);

+ 3 - 1
UICatalog/Scenarios/ViewExperiments.cs

@@ -24,7 +24,9 @@ public class ViewExperiments : Scenario
         {
             X = 0,
             Y = 0,
-            TabStop = TabBehavior.NoStop
+            TabStop = TabBehavior.NoStop,
+            AutoSelectViewToEdit = true,
+            ShowViewIdentifier = true
         };
         app.Add (editor);
 

+ 1 - 1
UICatalog/Scenarios/VkeyPacketSimulator.cs

@@ -287,7 +287,7 @@ public class VkeyPacketSimulator : Scenario
                      ..outputVerticalRuler.Viewport.Height];
         }
 
-        win.LayoutComplete += Win_LayoutComplete;
+        win.SubviewsLaidOut += Win_LayoutComplete;
 
         Application.Run (win);
         win.Dispose ();

+ 12 - 12
UICatalog/UICatalog.cs

@@ -564,7 +564,7 @@ public class UICatalogApp
                                              {
                                                  if (_statusBar.NeedsLayout)
                                                  {
-                                                    // throw new Exception ("DimFunc.Fn aborted because dependent View needs layout.");
+                                                    throw new LayoutException ("DimFunc.Fn aborted because dependent View needs layout.");
                                                  }
                                                  return _statusBar.Frame.Height;
                                              })),
@@ -590,7 +590,7 @@ public class UICatalogApp
                                              {
                                                  if (_statusBar.NeedsLayout)
                                                  {
-                                                    // throw new Exception ("DimFunc.Fn aborted because dependent View needs layout.");
+                                                     throw new LayoutException ("DimFunc.Fn aborted because dependent View needs layout.");
                                                  }
                                                  return _statusBar.Frame.Height;
                                              })),
@@ -792,7 +792,7 @@ public class UICatalogApp
         {
             const string OFF = "View Diagnostics: _Off";
             const string RULER = "View Diagnostics: _Ruler";
-            const string PADDING = "View Diagnostics: _Padding";
+            const string THICKNESS = "View Diagnostics: _Thickness";
             const string Hover = "View Diagnostics: _Hover";
             var index = 0;
 
@@ -809,7 +809,7 @@ public class UICatalogApp
 
                 if (GetDiagnosticsTitle (ViewDiagnosticFlags.Off) == item.Title)
                 {
-                    item.Checked = !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Padding)
+                    item.Checked = !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Thickness)
                                    && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Ruler)
                                    && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Hover);
                 }
@@ -824,12 +824,12 @@ public class UICatalogApp
 
                                    if (item.Title == t && item.Checked == false)
                                    {
-                                       _diagnosticFlags &= ~(ViewDiagnosticFlags.Padding | ViewDiagnosticFlags.Ruler | ViewDiagnosticFlags.Hover);
+                                       _diagnosticFlags &= ~(ViewDiagnosticFlags.Thickness | ViewDiagnosticFlags.Ruler | ViewDiagnosticFlags.Hover);
                                        item.Checked = true;
                                    }
                                    else if (item.Title == t && item.Checked == true)
                                    {
-                                       _diagnosticFlags |= ViewDiagnosticFlags.Padding | ViewDiagnosticFlags.Ruler | ViewDiagnosticFlags.Hover;
+                                       _diagnosticFlags |= ViewDiagnosticFlags.Thickness | ViewDiagnosticFlags.Ruler | ViewDiagnosticFlags.Hover;
                                        item.Checked = false;
                                    }
                                    else
@@ -851,7 +851,7 @@ public class UICatalogApp
                                        if (menuItem.Title == t)
                                        {
                                            menuItem.Checked = !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Ruler)
-                                                              && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Padding)
+                                                              && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Thickness)
                                                               && !_diagnosticFlags.HasFlag (ViewDiagnosticFlags.Hover);
                                        }
                                        else if (menuItem.Title != t)
@@ -873,7 +873,7 @@ public class UICatalogApp
                 {
                     "Off" => OFF,
                     "Ruler" => RULER,
-                    "Padding" => PADDING,
+                    "Thickness" => THICKNESS,
                     "Hover" => Hover,
                     _ => ""
                 };
@@ -884,7 +884,7 @@ public class UICatalogApp
                 return title switch
                 {
                     RULER => ViewDiagnosticFlags.Ruler,
-                    PADDING => ViewDiagnosticFlags.Padding,
+                    THICKNESS => ViewDiagnosticFlags.Thickness,
                     Hover => ViewDiagnosticFlags.Hover,
                     _ => null!
                 };
@@ -905,14 +905,14 @@ public class UICatalogApp
                         }
 
                         break;
-                    case ViewDiagnosticFlags.Padding:
+                    case ViewDiagnosticFlags.Thickness:
                         if (add)
                         {
-                            _diagnosticFlags |= ViewDiagnosticFlags.Padding;
+                            _diagnosticFlags |= ViewDiagnosticFlags.Thickness;
                         }
                         else
                         {
-                            _diagnosticFlags &= ~ViewDiagnosticFlags.Padding;
+                            _diagnosticFlags &= ~ViewDiagnosticFlags.Thickness;
                         }
 
                         break;

+ 1 - 1
UICatalog/UICatalog.csproj

@@ -26,7 +26,7 @@
     <EmbeddedResource Include="Resources\config.json" />
   </ItemGroup>
   <ItemGroup>
-  <None Update="./Scenarios/Spinning_globe_dark_small.gif" CopyToOutputDirectory="PreserveNewest" />
+  <None Update="Scenarios\AnimationScenario\Spinning_globe_dark_small.gif" CopyToOutputDirectory="PreserveNewest" />
   </ItemGroup>
   <ItemGroup>
     <PackageReference Include="JetBrains.Annotations" Version="[2024.2.0,)" PrivateAssets="all" />

+ 2 - 2
UnitTests/Dialogs/DialogTests.cs

@@ -1108,7 +1108,7 @@ public class DialogTests
                          switch (iterations)
                          {
                              case 0:
-                                 Top.SetLayoutNeeded();
+                                 Top.SetNeedsLayout();
                                  Top.SetNeedsDisplay();
                                  Refresh ();
 
@@ -1449,7 +1449,7 @@ public class DialogTests
         RunState runState = Begin (dlg);
 
         dlg.SetNeedsDisplay();
-        dlg.SetLayoutNeeded ();
+        dlg.SetNeedsLayout ();
         dlg.Layout ();
         dlg.Draw ();
 

+ 4 - 4
UnitTests/Drawing/ThicknessTests.cs

@@ -59,7 +59,7 @@ public class ThicknessTests (ITestOutputHelper output)
                                      new Rectangle (0, 0, Application.Driver!.Cols, Application.Driver!.Rows),
                                      (Rune)' '
                                     );
-        t.Draw (r, ViewDiagnosticFlags.Padding, "Test");
+        t.Draw (r, ViewDiagnosticFlags.Thickness, "Test");
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
@@ -74,7 +74,7 @@ public class ThicknessTests (ITestOutputHelper output)
                                      new Rectangle (0, 0, Application.Driver!.Cols, Application.Driver!.Rows),
                                      (Rune)' '
                                     );
-        t.Draw (r, ViewDiagnosticFlags.Padding, "Test");
+        t.Draw (r, ViewDiagnosticFlags.Thickness, "Test");
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
@@ -103,7 +103,7 @@ public class ThicknessTests (ITestOutputHelper output)
                                      new Rectangle (0, 0, Application.Driver!.Cols, Application.Driver!.Rows),
                                      (Rune)' '
                                     );
-        t.Draw (r, ViewDiagnosticFlags.Padding, "Test");
+        t.Draw (r, ViewDiagnosticFlags.Thickness, "Test");
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"
@@ -132,7 +132,7 @@ public class ThicknessTests (ITestOutputHelper output)
                                      new Rectangle (0, 0, Application.Driver!.Cols, Application.Driver!.Rows),
                                      (Rune)' '
                                     );
-        t.Draw (r, ViewDiagnosticFlags.Padding, "Test");
+        t.Draw (r, ViewDiagnosticFlags.Thickness, "Test");
 
         TestHelpers.AssertDriverContentsWithFrameAre (
                                                       @"

+ 2 - 2
UnitTests/UICatalog/ScenarioTests.cs

@@ -289,7 +289,7 @@ public class ScenarioTests : TestsAllViews
             // Remove existing class, if any
             if (_curView != null)
             {
-                _curView.LayoutComplete -= LayoutCompleteHandler;
+                _curView.SubviewsLaidOut -= LayoutCompleteHandler;
                 _hostPane.Remove (_curView);
                 _curView.Dispose ();
                 _curView = null;
@@ -594,7 +594,7 @@ public class ScenarioTests : TestsAllViews
             UpdateSettings (view);
             UpdateTitle (view);
 
-            view.LayoutComplete += LayoutCompleteHandler;
+            view.SubviewsLaidOut += LayoutCompleteHandler;
 
             return view;
         }

+ 1 - 1
UnitTests/View/Adornment/AdornmentSubViewTests.cs

@@ -76,7 +76,7 @@ public class AdornmentSubViewTests (ITestOutputHelper output)
         view.EndInit ();
         var raised = false;
 
-        subView.LayoutStarted += LayoutStarted;
+        subView.SubviewLayout += LayoutStarted;
         view.Margin.Thickness = new Thickness (1, 2, 3, 4);
         view.Layout ();
         Assert.True (raised);

+ 2 - 2
UnitTests/View/Adornment/AdornmentTests.cs

@@ -352,7 +352,7 @@ public class AdornmentTests (ITestOutputHelper output)
         parent.BeginInit ();
         parent.EndInit ();
 
-        parent.LayoutStarted += LayoutStarted;
+        parent.SubviewLayout += LayoutStarted;
         parent.Margin.Thickness = new Thickness (1, 2, 3, 4);
         Assert.True (parent.NeedsLayout);
         Assert.True (parent.Margin.NeedsLayout);
@@ -375,7 +375,7 @@ public class AdornmentTests (ITestOutputHelper output)
         parent.BeginInit ();
         parent.EndInit ();
 
-        parent.Margin.LayoutStarted += LayoutStarted;
+        parent.Margin.SubviewLayout += LayoutStarted;
         parent.Margin.Thickness = new Thickness (1, 2, 3, 4);
         Assert.True (parent.NeedsLayout);
         Assert.True (parent.Margin.NeedsLayout);

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

@@ -9,7 +9,7 @@ public class MarginTests (ITestOutputHelper output)
     public void Margin_Uses_SuperView_ColorScheme ()
     {
         ((FakeDriver)Application.Driver!).SetBufferSize (5, 5);
-        View.Diagnostics = ViewDiagnosticFlags.Padding;
+        View.Diagnostics = ViewDiagnosticFlags.Thickness;
 
         var view = new View { Height = 3, Width = 3 };
         view.Margin.Thickness = new (1);

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

@@ -11,7 +11,7 @@ public class PaddingTests (ITestOutputHelper output)
         ((FakeDriver)Application.Driver!).SetBufferSize (5, 5);
         var view = new View { Height = 3, Width = 3 };
         view.Padding.Thickness = new (1);
-        view.Padding.Diagnostics = ViewDiagnosticFlags.Padding;
+        view.Padding.Diagnostics = ViewDiagnosticFlags.Thickness;
 
         view.ColorScheme = new()
         {

+ 2 - 2
UnitTests/View/DiagnosticsTests.cs

@@ -21,8 +21,8 @@ public class DiagnosticTests ()
         Assert.Equal (ViewDiagnosticFlags.Off, View.Diagnostics);
 
         // View.Diagnostics can be set to a new value.
-        View.Diagnostics = ViewDiagnosticFlags.Padding;
-        Assert.Equal (ViewDiagnosticFlags.Padding, View.Diagnostics);
+        View.Diagnostics = ViewDiagnosticFlags.Thickness;
+        Assert.Equal (ViewDiagnosticFlags.Thickness, View.Diagnostics);
 
         // Ensure we turn off at the end of the test
         View.Diagnostics = ViewDiagnosticFlags.Off;

+ 3 - 3
UnitTests/View/Draw/AllViewsDrawTests.cs

@@ -26,12 +26,12 @@ public class AllViewsDrawTests (ITestOutputHelper _output) : TestsAllViews
         view.DrawComplete += (s, e) => drawCompleteCount++;
 
         var layoutStartedCount = 0;
-        view.LayoutStarted += (s, e) => layoutStartedCount++;
+        view.SubviewLayout += (s, e) => layoutStartedCount++;
 
         var layoutCompleteCount = 0;
-        view.LayoutComplete += (s, e) => layoutCompleteCount++;
+        view.SubviewsLaidOut += (s, e) => layoutCompleteCount++;
 
-        view.SetLayoutNeeded ();
+        view.SetNeedsLayout ();
         view.Layout ();
 
         Assert.Equal (0, drawCompleteCount);

+ 2 - 2
UnitTests/View/Draw/DrawTests.cs

@@ -43,7 +43,7 @@ public class DrawTests (ITestOutputHelper _output)
             Width = 3, Height = 3
         };
         view.Margin.Thickness = new (1);
-        view.Margin.Diagnostics = ViewDiagnosticFlags.Padding;
+        view.Margin.Diagnostics = ViewDiagnosticFlags.Thickness;
         view.BeginInit ();
         view.EndInit ();
         view.Draw ();
@@ -693,7 +693,7 @@ public class DrawTests (ITestOutputHelper _output)
 
         // BUGBUG: v2 - it's bogus to reference .Frame before BeginInit. And why is the clip being set anyway???
 
-        top.LayoutComplete += Top_LayoutComplete;
+        top.SubviewsLaidOut += Top_LayoutComplete;
         Application.Begin (top);
 
         Application.Refresh ();

+ 2 - 2
UnitTests/View/Draw/NeedsDisplayTests.cs

@@ -106,7 +106,7 @@ public class NeedsDisplayTests ()
         Assert.False (view.NeedsDisplay);
         Assert.False (view.NeedsLayout);
 
-        view.SetLayoutNeeded ();
+        view.SetNeedsLayout ();
         Assert.True (view.NeedsDisplay);
         Assert.True (view.NeedsLayout);
     }
@@ -126,7 +126,7 @@ public class NeedsDisplayTests ()
         view.SetRelativeLayout (Application.Screen.Size);
         Assert.False (view.NeedsDisplay);
 
-        view.SetLayoutNeeded ();
+        view.SetNeedsLayout ();
 
         // SRL won't change anything since the view is Absolute
         view.SetRelativeLayout (Application.Screen.Size);

+ 3 - 3
UnitTests/View/Layout/LayoutTests.cs

@@ -26,12 +26,12 @@ public class LayoutTests (ITestOutputHelper _output) : TestsAllViews
         view.DrawContent += (s, e) => drawContentCount++;
 
         var layoutStartedCount = 0;
-        view.LayoutStarted += (s, e) => layoutStartedCount++;
+        view.SubviewLayout += (s, e) => layoutStartedCount++;
 
         var layoutCompleteCount = 0;
-        view.LayoutComplete += (s, e) => layoutCompleteCount++;
+        view.SubviewsLaidOut += (s, e) => layoutCompleteCount++;
 
-        view.SetLayoutNeeded ();
+        view.SetNeedsLayout ();
         view.SetNeedsDisplay();
         view.Layout ();
 

+ 1 - 1
UnitTests/View/Layout/Pos.CombineTests.cs

@@ -120,7 +120,7 @@ public class PosCombineTests (ITestOutputHelper output)
         f.X = Pos.X (Application.Top) + Pos.X (v2) - Pos.X (v1);
         f.Y = Pos.Y (Application.Top) + Pos.Y (v2) - Pos.Y (v1);
 
-        Application.Top.LayoutComplete += (s, e) =>
+        Application.Top.SubviewsLaidOut += (s, e) =>
         {
             Assert.Equal (0, Application.Top.Frame.X);
             Assert.Equal (0, Application.Top.Frame.Y);

+ 45 - 24
UnitTests/View/Layout/SetLayoutTests.cs

@@ -91,8 +91,8 @@ public class SetLayoutTests (ITestOutputHelper output)
         var view = new View { Id = "view" };
         bool layoutStartedRaised = false;
         bool layoutCompleteRaised = false;
-        superView.LayoutStarted += (sender, e) => layoutStartedRaised = true;
-        superView.LayoutComplete += (sender, e) => layoutCompleteRaised = true;
+        superView.SubviewLayout += (sender, e) => layoutStartedRaised = true;
+        superView.SubviewsLaidOut += (sender, e) => layoutCompleteRaised = true;
 
         superView.Add (view);
 
@@ -114,10 +114,10 @@ public class SetLayoutTests (ITestOutputHelper output)
         var superView = new View { Id = "superView" };
         int layoutStartedRaised = 0;
         int layoutCompleteRaised = 0;
-        superView.LayoutStarted += (sender, e) => layoutStartedRaised++;
-        superView.LayoutComplete += (sender, e) => layoutCompleteRaised++;
+        superView.SubviewLayout += (sender, e) => layoutStartedRaised++;
+        superView.SubviewsLaidOut += (sender, e) => layoutCompleteRaised++;
 
-        superView.SetLayoutNeeded ();
+        superView.SetNeedsLayout ();
         superView.LayoutSubviews ();
         Assert.Equal (1, layoutStartedRaised);
         Assert.Equal (1, layoutCompleteRaised);
@@ -208,11 +208,11 @@ public class SetLayoutTests (ITestOutputHelper output)
         var borderLayoutStartedCount = 0;
         var borderLayoutCompleteCount = 0;
 
-        view.LayoutStarted += (sender, e) => layoutStartedCount++;
-        view.LayoutComplete += (sender, e) => layoutCompleteCount++;
+        view.SubviewLayout += (sender, e) => layoutStartedCount++;
+        view.SubviewsLaidOut += (sender, e) => layoutCompleteCount++;
 
-        view.Border.LayoutStarted += (sender, e) => borderLayoutStartedCount++;
-        view.Border.LayoutComplete += (sender, e) => borderLayoutCompleteCount++;
+        view.Border.SubviewLayout += (sender, e) => borderLayoutStartedCount++;
+        view.Border.SubviewsLaidOut += (sender, e) => borderLayoutCompleteCount++;
 
 
         superView.Add (view);
@@ -239,10 +239,10 @@ public class SetLayoutTests (ITestOutputHelper output)
         Assert.Equal (1, layoutStartedCount);
         Assert.Equal (1, layoutCompleteCount);
 
-        superView.SetLayoutNeeded ();
+        superView.SetNeedsLayout ();
         superView.LayoutSubviews ();
-        Assert.Equal (2, borderLayoutStartedCount);
-        Assert.Equal (2, borderLayoutCompleteCount);
+        Assert.Equal (1, borderLayoutStartedCount);
+        Assert.Equal (1, borderLayoutCompleteCount);
         Assert.Equal (2, layoutStartedCount);
         Assert.Equal (2, layoutCompleteCount);
 
@@ -250,8 +250,9 @@ public class SetLayoutTests (ITestOutputHelper output)
     }
 
     [Fact]
-    public void LayoutSubviews__Honors_IsLayoutNeeded ()
+    public void LayoutSubviews_Honors_IsLayoutNeeded ()
     {
+        // No adornment subviews
         var superView = new View ();
         var view = new View ();
 
@@ -261,34 +262,54 @@ public class SetLayoutTests (ITestOutputHelper output)
         var borderLayoutStartedCount = 0;
         var borderLayoutCompleteCount = 0;
 
-        view.LayoutStarted += (sender, e) => layoutStartedCount++;
-        view.LayoutComplete += (sender, e) => layoutCompleteCount++;
+        view.SubviewLayout += (sender, e) => layoutStartedCount++;
+        view.SubviewsLaidOut += (sender, e) => layoutCompleteCount++;
 
-        view.Border.LayoutStarted += (sender, e) => borderLayoutStartedCount++;
-        view.Border.LayoutComplete += (sender, e) => borderLayoutCompleteCount++;
+        view.Border.SubviewLayout += (sender, e) => borderLayoutStartedCount++;
+        view.Border.SubviewsLaidOut += (sender, e) => borderLayoutCompleteCount++;
 
 
         superView.Add (view);
 
         superView.LayoutSubviews ();
-        Assert.Equal (1, borderLayoutStartedCount);
-        Assert.Equal (1, borderLayoutCompleteCount);
+        Assert.Equal (0, borderLayoutStartedCount);
+        Assert.Equal (0, borderLayoutCompleteCount);
         Assert.Equal (1, layoutStartedCount);
         Assert.Equal (1, layoutCompleteCount);
 
         superView.LayoutSubviews ();
-        Assert.Equal (1, borderLayoutStartedCount);
-        Assert.Equal (1, borderLayoutCompleteCount);
+        Assert.Equal (0, borderLayoutStartedCount);
+        Assert.Equal (0, borderLayoutCompleteCount);
         Assert.Equal (1, layoutStartedCount);
         Assert.Equal (1, layoutCompleteCount);
 
-        superView.SetLayoutNeeded ();
+        superView.SetNeedsLayout ();
         superView.LayoutSubviews ();
-        Assert.Equal (2, borderLayoutStartedCount);
-        Assert.Equal (2, borderLayoutCompleteCount);
+        Assert.Equal (0, borderLayoutStartedCount);
+        Assert.Equal (0, borderLayoutCompleteCount);
         Assert.Equal (2, layoutStartedCount);
         Assert.Equal (2, layoutCompleteCount);
 
+        // With Border subview
+        view.Border.Add (new View ());
+        superView.LayoutSubviews ();
+        Assert.Equal (1, borderLayoutStartedCount);
+        Assert.Equal (1, borderLayoutCompleteCount);
+        Assert.Equal (3, layoutStartedCount);
+        Assert.Equal (3, layoutCompleteCount);
+
+        superView.LayoutSubviews ();
+        Assert.Equal (1, borderLayoutStartedCount);
+        Assert.Equal (1, borderLayoutCompleteCount);
+        Assert.Equal (3, layoutStartedCount);
+        Assert.Equal (3, layoutCompleteCount);
+
+        superView.SetNeedsLayout ();
+        superView.LayoutSubviews ();
+        Assert.Equal (2, borderLayoutStartedCount);
+        Assert.Equal (2, borderLayoutCompleteCount);
+        Assert.Equal (4, layoutStartedCount);
+
         superView.Dispose ();
     }
     

+ 4 - 4
UnitTests/View/ViewTests.cs

@@ -491,13 +491,13 @@ At 0,0
 
         RunState runState = Application.Begin (top);
 
-        top.LayoutComplete += (s, e) => { Assert.Equal (new (0, 0, 80, 25), top._needsDisplayRect); };
+        top.SubviewsLaidOut += (s, e) => { Assert.Equal (new (0, 0, 80, 25), top._needsDisplayRect); };
 
-        frame.LayoutComplete += (s, e) => { Assert.Equal (new (0, 0, 40, 8), frame._needsDisplayRect); };
+        frame.SubviewsLaidOut += (s, e) => { Assert.Equal (new (0, 0, 40, 8), frame._needsDisplayRect); };
 
-        label.LayoutComplete += (s, e) => { Assert.Equal (new (0, 0, 38, 1), label._needsDisplayRect); };
+        label.SubviewsLaidOut += (s, e) => { Assert.Equal (new (0, 0, 38, 1), label._needsDisplayRect); };
 
-        view.LayoutComplete += (s, e) => { Assert.Equal (new (0, 0, 13, 1), view._needsDisplayRect); };
+        view.SubviewsLaidOut += (s, e) => { Assert.Equal (new (0, 0, 13, 1), view._needsDisplayRect); };
 
         Assert.Equal (new (0, 0, 80, 25), top.Frame);
         Assert.Equal (new (20, 8, 40, 8), frame.Frame);

+ 1 - 1
UnitTests/Views/ScrollBarViewTests.cs

@@ -828,7 +828,7 @@ This is a test
                                                             textView.SetNeedsDisplay ();
                                                         };
 
-        textView.LayoutComplete += (s, e) =>
+        textView.SubviewsLaidOut += (s, e) =>
                                    {
                                        scrollBar.Size = textView.Lines;
                                        scrollBar.Position = textView.TopRow;

+ 1 - 1
UnitTests/Views/ScrollViewTests.cs

@@ -1109,7 +1109,7 @@ public class ScrollViewTests (ITestOutputHelper output)
 
             labelFill = new () { Width = Dim.Fill (), Height = Dim.Fill (), Visible = false };
 
-            labelFill.LayoutComplete += (s, e) =>
+            labelFill.SubviewsLaidOut += (s, e) =>
                                         {
                                             var fillText = new StringBuilder ();
 

+ 1 - 1
UnitTests/Views/StatusBarTests.cs

@@ -134,7 +134,7 @@ public class StatusBarTests
         Assert.True (sb.CanFocus);
         Assert.Equal (Colors.ColorSchemes ["Menu"], sb.ColorScheme);
         Assert.Equal (0, sb.X);
-        Assert.Equal ("AnchorEnd()", sb.Y.ToString ());
+        Assert.Equal ("AnchorEnd", sb.Y.ToString ());
         Assert.Equal (Dim.Fill (), sb.Width);
         sb.Layout ();
         Assert.Equal (1, sb.Frame.Height);

+ 4 - 4
UnitTests/Views/ToplevelTests.cs

@@ -695,12 +695,12 @@ public partial class ToplevelTests (ITestOutputHelper output)
         Assert.False (subTop.IsLoaded);
         Assert.Equal (new (0, 0, 20, 10), view.Frame);
 
-        view.LayoutStarted += ViewLayoutStarted;
+        view.SubviewLayout += ViewLayoutStarted;
 
         void ViewLayoutStarted (object sender, LayoutEventArgs e)
         {
             Assert.Equal (new (0, 0, 20, 10), view._needsDisplayRect);
-            view.LayoutStarted -= ViewLayoutStarted;
+            view.SubviewLayout -= ViewLayoutStarted;
         }
 
         Application.Begin (top);
@@ -749,7 +749,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
 
         Application.RaiseMouseEvent (new () { ScreenPosition = new (9, 9), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition });
         Assert.Equal (win.Border, Application.MouseGrabView);
-        top.SetLayoutNeeded ();
+        top.SetNeedsLayout ();
         top.LayoutSubviews ();
         Assert.Equal (new (6, 6, 191, 91), win.Frame);
         Application.Refresh ();
@@ -760,7 +760,7 @@ public partial class ToplevelTests (ITestOutputHelper output)
                                       ScreenPosition = new (5, 5), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
                                   });
         Assert.Equal (win.Border, Application.MouseGrabView);
-        top.SetLayoutNeeded ();
+        top.SetNeedsLayout ();
         top.LayoutSubviews ();
         Assert.Equal (new (2, 2, 195, 95), win.Frame);
         Application.Refresh ();