Tig 1 year ago
parent
commit
3575a78e9d

+ 0 - 1
Terminal.Gui/Application.cs

@@ -528,7 +528,6 @@ public static partial class Application
 
         toplevel.SetRelativeLayout (Driver.Screen.Size);
 
-        // BUGBUG: This call is likely not needed.
         toplevel.LayoutSubviews ();
         toplevel.PositionToplevels ();
         toplevel.FocusFirst ();

+ 30 - 9
Terminal.Gui/View/Adornment/Border.cs

@@ -196,6 +196,26 @@ public class Border : Adornment
         set => _lineStyle = value;
     }
 
+    private bool _showTitle = true;
+
+    /// <summary>
+    ///     Gets or sets whether the title should be shown. The default is <see langword="true"/>.
+    /// </summary>
+    public bool ShowTitle
+    {
+        get => _showTitle;
+        set
+        {
+            if (value == _showTitle)
+            {
+                return;
+            }
+            _showTitle = value;
+
+            Parent?.SetNeedsDisplay ();
+        }
+    }
+
     #region Mouse Support
 
     private Color? _savedForeColor;
@@ -358,7 +378,7 @@ public class Border : Adornment
         }
     }
 
-#endregion Mouse Support
+    #endregion Mouse Support
 
     /// <inheritdoc/>
     public override void OnDrawContent (Rectangle viewport)
@@ -394,12 +414,13 @@ public class Border : Adornment
                                                 Math.Min (screenBounds.Width - 4, borderBounds.Width - 4)
                                                )
                                      );
+
         Parent.TitleTextFormatter.Size = new (maxTitleWidth, 1);
 
         int sideLineLength = borderBounds.Height;
         bool canDrawBorder = borderBounds is { Width: > 0, Height: > 0 };
 
-        if (!string.IsNullOrEmpty (Parent?.Title))
+        if (ShowTitle)
         {
             if (Thickness.Top == 2)
             {
@@ -431,13 +452,13 @@ public class Border : Adornment
             }
         }
 
-        if (canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && !string.IsNullOrEmpty (Parent?.Title))
+        if (canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && ShowTitle && !string.IsNullOrEmpty (Parent?.Title))
         {
-            var focus = Parent.GetNormalColor();
+            var focus = Parent.GetNormalColor ();
             if (Parent.SuperView is { } && Parent.SuperView?.Subviews!.Count (s => s.CanFocus) > 1)
             {
                 // Only use focus color if there are multiple focusable views
-                focus = Parent.GetFocusColor() ;
+                focus = Parent.GetFocusColor ();
             }
 
             Parent.TitleTextFormatter.Draw (
@@ -450,9 +471,9 @@ public class Border : Adornment
         {
             LineCanvas lc = Parent?.LineCanvas;
 
-            bool drawTop = Thickness.Top > 0 && Frame.Width > 1 && Frame.Height > 1;
+            bool drawTop = Thickness.Top > 0 && Frame.Width > 1 && Frame.Height >= 1;
             bool drawLeft = Thickness.Left > 0 && (Frame.Height > 1 || Thickness.Top == 0);
-            bool drawBottom = Thickness.Bottom > 0 && Frame.Width > 1;
+            bool drawBottom = Thickness.Bottom > 0 && Frame.Width > 1 && Frame.Height > 1;
             bool drawRight = Thickness.Right > 0 && (Frame.Height > 1 || Thickness.Top == 0);
 
             Attribute prevAttr = Driver.GetAttribute ();
@@ -470,7 +491,7 @@ public class Border : Adornment
             {
                 // ╔╡Title╞═════╗
                 // ╔╡╞═════╗
-                if (borderBounds.Width < 4 || string.IsNullOrEmpty (Parent?.Title))
+                if (borderBounds.Width < 4 || !ShowTitle || string.IsNullOrEmpty (Parent?.Title))
                 {
                     // ╔╡╞╗ should be ╔══╗
                     lc.AddLine (
@@ -620,7 +641,7 @@ public class Border : Adornment
                 }
 
                 // Redraw title 
-                if (drawTop && maxTitleWidth > 0 && !string.IsNullOrEmpty (Parent?.Title))
+                if (drawTop && maxTitleWidth > 0 && ShowTitle)
                 {
                     Parent.TitleTextFormatter.Draw (
                                                     new (borderBounds.X + 2, titleY, maxTitleWidth, 1),

+ 37 - 32
Terminal.Gui/View/Layout/DimAuto.cs

@@ -78,6 +78,7 @@ public class DimAuto () : Dim
                 // TODO: This whole body of code is a WIP (for https://github.com/gui-cs/Terminal.Gui/pull/3451).
                 subviewsSize = 0;
 
+                List<View> includedSubviews = us.Subviews.ToList();//.Where (v => !v.ExcludeFromLayout).ToList ();
                 List<View> subviews;
 
                 #region Not Anchored and Are Not Dependent
@@ -94,17 +95,17 @@ public class DimAuto () : Dim
                 // [ ] DimView
                 if (dimension == Dimension.Width)
                 {
-                    subviews = us.Subviews.Where (v => v.X is not PosAnchorEnd
-                                                       && v.X is not PosAlign
-                                                       && v.X is not PosCenter
-                                                       && v.Width is not DimFill).ToList ();
+                    subviews = includedSubviews.Where (v => v.X is not PosAnchorEnd
+                                                           && v.X is not PosAlign
+                                                           // && v.X is not PosCenter
+                                                           && v.Width is not DimFill).ToList ();
                 }
                 else
                 {
-                    subviews = us.Subviews.Where (v => v.Y is not PosAnchorEnd
-                                                       && v.Y is not PosAlign
-                                                       && v.Y is not PosCenter
-                                                       && v.Height is not DimFill).ToList ();
+                    subviews = includedSubviews.Where (v => v.Y is not PosAnchorEnd
+                                                           && v.Y is not PosAlign
+                                                           // && v.Y is not PosCenter
+                                                           && v.Height is not DimFill).ToList ();
                 }
 
                 for (var i = 0; i < subviews.Count; i++)
@@ -126,11 +127,11 @@ public class DimAuto () : Dim
                 // [x] PosAnchorEnd
                 if (dimension == Dimension.Width)
                 {
-                    subviews = us.Subviews.Where (v => v.X is PosAnchorEnd).ToList ();
+                    subviews = includedSubviews.Where (v => v.X is PosAnchorEnd).ToList ();
                 }
                 else
                 {
-                    subviews = us.Subviews.Where (v => v.Y is PosAnchorEnd).ToList ();
+                    subviews = includedSubviews.Where (v => v.Y is PosAnchorEnd).ToList ();
                 }
 
                 int maxAnchorEnd = 0;
@@ -143,26 +144,26 @@ public class DimAuto () : Dim
                 subviewsSize += maxAnchorEnd;
                 #endregion Anchored
 
-                #region Center
-                // Now, handle subviews that are Centered
-                if (dimension == Dimension.Width)
-                {
-                    subviews = us.Subviews.Where (v => v.X is PosCenter).ToList ();
-                }
-                else
-                {
-                    subviews = us.Subviews.Where (v => v.Y is PosCenter).ToList ();
-                }
-
-                int maxCenter = 0;
-                for (var i = 0; i < subviews.Count; i++)
-                {
-                    View v = subviews [i];
-                    maxCenter = dimension == Dimension.Width ? v.Frame.Width : v.Frame.Height;
-                }
-
-                subviewsSize += maxCenter;
-                #endregion Center
+                //#region Center
+                //// Now, handle subviews that are Centered
+                //if (dimension == Dimension.Width)
+                //{
+                //    subviews = us.Subviews.Where (v => v.X is PosCenter).ToList ();
+                //}
+                //else
+                //{
+                //    subviews = us.Subviews.Where (v => v.Y is PosCenter).ToList ();
+                //}
+
+                //int maxCenter = 0;
+                //for (var i = 0; i < subviews.Count; i++)
+                //{
+                //    View v = subviews [i];
+                //    maxCenter = dimension == Dimension.Width ? v.Frame.Width : v.Frame.Height;
+                //}
+
+                //subviewsSize += maxCenter;
+                //#endregion Center
 
                 #region Are Dependent
                 // Now, go back to those that are dependent on content size
@@ -170,11 +171,15 @@ public class DimAuto () : Dim
                 // [ ] DimPercent
                 if (dimension == Dimension.Width)
                 {
-                    subviews = us.Subviews.Where (v => v.Width is DimFill).ToList ();
+                    subviews = includedSubviews.Where (v => v.Width is DimFill
+                                                      // || v.X is PosCenter
+                                                     ).ToList ();
                 }
                 else
                 {
-                    subviews = us.Subviews.Where (v => v.Height is DimFill).ToList ();
+                    subviews = includedSubviews.Where (v => v.Height is DimFill
+                                                      //|| v.Y is PosCenter
+                                                     ).ToList ();
                 }
 
                 int maxFill = 0;

+ 13 - 3
Terminal.Gui/View/ViewDrawing.cs

@@ -236,6 +236,16 @@ public partial class View
 
         OnRenderLineCanvas ();
 
+        // TODO: This is a hack to force the border subviews to draw.
+        if (Border?.Subviews is { })
+        {
+            foreach (View view in Border.Subviews)
+            {
+                view.SetNeedsDisplay ();
+                view.Draw ();
+            }
+        }
+
         // Invoke DrawContentCompleteEvent
         OnDrawContentComplete (Viewport);
 
@@ -329,7 +339,7 @@ public partial class View
     public virtual Attribute GetFocusColor ()
     {
         ColorScheme cs = ColorScheme;
-        if (ColorScheme is null)
+        if (cs is null)
         {
             cs = new ();
         }
@@ -347,7 +357,7 @@ public partial class View
     {
         ColorScheme cs = ColorScheme;
 
-        if (ColorScheme is null)
+        if (cs is null)
         {
             cs = new ();
         }
@@ -365,7 +375,7 @@ public partial class View
     {
         ColorScheme cs = ColorScheme;
 
-        if (ColorScheme is null)
+        if (cs is null)
         {
             cs = new ();
         }

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

@@ -485,12 +485,12 @@ public class MenuBar : View
 
             if (i == _selected && IsMenuOpen)
             {
-                hotColor = i == _selected ? ColorScheme.HotFocus : ColorScheme.HotNormal;
-                normalColor = i == _selected ? ColorScheme.Focus : GetNormalColor ();
+                hotColor = i == _selected ? ColorScheme.HotFocus : GetHotNormalColor ();
+                normalColor = i == _selected ? GetFocusColor() : GetNormalColor ();
             }
             else
             {
-                hotColor = ColorScheme.HotNormal;
+                hotColor = GetHotNormalColor ();
                 normalColor = GetNormalColor ();
             }
 

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

@@ -2014,7 +2014,7 @@ public class TableView : View
 
     /// <summary>
     ///     Returns true if the <see cref="Table"/> is not set or all the columns in the <see cref="Table"/> have an
-    ///     explicit <see cref="ColumnStyle"/> that marks them <see cref="ColumnStyle.visible"/> <see langword="false"/>.
+    ///     explicit <see cref="ColumnStyle"/> that marks them <see cref="ColumnStyle.Visible"/> <see langword="false"/>.
     /// </summary>
     /// <returns></returns>
     private bool TableIsNullOrInvisible ()

+ 236 - 0
UICatalog/Scenarios/AdornmentEditor.cs

@@ -0,0 +1,236 @@
+using System;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+/// <summary>
+///     Provides a composable UI for editing the settings of an Adornment.
+/// </summary>
+public class AdornmentEditor : View
+{
+    private readonly ColorPicker _backgroundColorPicker = new ()
+    {
+        Title = "_BG",
+        BoxWidth = 1,
+        BoxHeight = 1,
+        BorderStyle = LineStyle.Single,
+        SuperViewRendersLineCanvas = true,
+        Enabled = false
+    };
+
+    private readonly ColorPicker _foregroundColorPicker = new ()
+    {
+        Title = "_FG",
+        BoxWidth = 1,
+        BoxHeight = 1,
+        BorderStyle = LineStyle.Single,
+        SuperViewRendersLineCanvas = true,
+        Enabled = false
+    };
+
+    private Adornment _adornment;
+    public Adornment AdornmentToEdit
+    {
+        get => _adornment;
+        set
+        {
+            if (value == _adornment)
+            {
+                return;
+            }
+
+            _adornment = value;
+
+            foreach (var subview in Subviews)
+            {
+                subview.Enabled = _adornment is { };
+            }
+
+            if (_adornment is null)
+            {
+                return;
+            }
+
+            if (IsInitialized)
+            {
+                _topEdit.Value = _adornment.Thickness.Top;
+                _leftEdit.Value = _adornment.Thickness.Left;
+                _bottomEdit.Value = _adornment.Thickness.Bottom;
+                _rightEdit.Value = _adornment.Thickness.Right;
+
+                _adornment.Initialized += (sender, args) =>
+                                          {
+                                              var cs = _adornment.ColorScheme;
+                                              _foregroundColorPicker.SelectedColor = cs.Normal.Foreground.GetClosestNamedColor ();
+                                              _backgroundColorPicker.SelectedColor = cs.Normal.Background.GetClosestNamedColor ();
+
+                                          };
+            }
+
+            OnAdornmentChanged ();
+        }
+    }
+
+    public event EventHandler<EventArgs> AdornmentChanged;
+
+    public void OnAdornmentChanged ()
+    {
+        AdornmentChanged?.Invoke (this, EventArgs.Empty);
+    }
+
+    private Buttons.NumericUpDown<int> _topEdit;
+    private Buttons.NumericUpDown<int> _leftEdit;
+    private Buttons.NumericUpDown<int> _bottomEdit;
+    private Buttons.NumericUpDown<int> _rightEdit;
+
+    public AdornmentEditor ()
+    {
+        Width = Dim.Auto (DimAutoStyle.Content);
+        Height = Dim.Auto (DimAutoStyle.Content);
+
+        BorderStyle = LineStyle.Dashed;
+        Initialized += AdornmentEditor_Initialized;
+    }
+
+    private void AdornmentEditor_Initialized (object sender, EventArgs e)
+    {
+        ExpanderButton expandButton;
+        Border.Add (expandButton = new ExpanderButton ());
+
+        _topEdit = new ()
+        {
+            X = Pos.Center (), Y = 0,
+            Enabled = false
+        };
+
+        _topEdit.ValueChanging += Top_ValueChanging;
+        Add (_topEdit);
+
+        _leftEdit = new ()
+        {
+            X = Pos.Left (_topEdit) - Pos.Func (() => _topEdit.Digits) - 2, Y = Pos.Bottom (_topEdit),
+            Enabled = false
+        };
+
+        _leftEdit.ValueChanging += Left_ValueChanging;
+        Add (_leftEdit);
+
+        _rightEdit = new ()
+        {
+            X = Pos.Right (_leftEdit) + 5, Y = Pos.Bottom (_topEdit),
+            Enabled = false
+        };
+
+        _rightEdit.ValueChanging += Right_ValueChanging;
+        Add (_rightEdit);
+
+        _bottomEdit = new ()
+        {
+            X = Pos.Center (), Y = Pos.Bottom (_leftEdit),
+            Enabled = false
+        };
+
+        _bottomEdit.ValueChanging += Bottom_ValueChanging;
+        Add (_bottomEdit);
+
+        var copyTop = new Button
+        {
+            X = Pos.Center (), Y = Pos.Bottom (_bottomEdit), Text = "Cop_y Top",
+            Enabled = false
+        };
+
+        copyTop.Accept += (s, e) =>
+                          {
+                              AdornmentToEdit.Thickness = new (_topEdit.Value);
+                              _leftEdit.Value = _rightEdit.Value = _bottomEdit.Value = _topEdit.Value;
+                          };
+        Add (copyTop);
+
+        // Foreground ColorPicker.
+        _foregroundColorPicker.X = 0;
+        _foregroundColorPicker.Y = Pos.Bottom (copyTop);
+
+        _foregroundColorPicker.ColorChanged += ColorPickerColorChanged ();
+        Add (_foregroundColorPicker);
+
+        // Background ColorPicker.
+        _backgroundColorPicker.X = Pos.Right (_foregroundColorPicker) - 1;
+        _backgroundColorPicker.Y = Pos.Top (_foregroundColorPicker);
+
+        _backgroundColorPicker.ColorChanged += ColorPickerColorChanged ();
+        Add (_backgroundColorPicker);
+
+        _topEdit.Value = AdornmentToEdit?.Thickness.Top ?? 0;
+        _leftEdit.Value = AdornmentToEdit?.Thickness.Left ?? 0;
+        _rightEdit.Value = AdornmentToEdit?.Thickness.Right ?? 0;
+        _bottomEdit.Value = AdornmentToEdit?.Thickness.Bottom ?? 0;
+
+        foreach (var subview in Subviews)
+        {
+            subview.Enabled = AdornmentToEdit is { };
+        }
+    }
+
+    private EventHandler<ColorEventArgs> ColorPickerColorChanged ()
+    {
+        return (o, a) =>
+               {
+                   if (AdornmentToEdit is null)
+                   {
+                       return;
+                   }
+                   AdornmentToEdit.ColorScheme = new (AdornmentToEdit.ColorScheme)
+                   {
+                       Normal = new (_foregroundColorPicker.SelectedColor, _backgroundColorPicker.SelectedColor)
+                   };
+               };
+    }
+
+    private void Top_ValueChanging (object sender, StateEventArgs<int> e)
+    {
+        if (e.NewValue < 0 || AdornmentToEdit is null)
+        {
+            e.Cancel = true;
+
+            return;
+        }
+
+        AdornmentToEdit.Thickness = new (AdornmentToEdit.Thickness.Left, e.NewValue, AdornmentToEdit.Thickness.Right, AdornmentToEdit.Thickness.Bottom);
+    }
+
+    private void Left_ValueChanging (object sender, StateEventArgs<int> e)
+    {
+        if (e.NewValue < 0 || AdornmentToEdit is null)
+        {
+            e.Cancel = true;
+
+            return;
+        }
+
+        AdornmentToEdit.Thickness = new (e.NewValue, AdornmentToEdit.Thickness.Top, AdornmentToEdit.Thickness.Right, AdornmentToEdit.Thickness.Bottom);
+    }
+
+    private void Right_ValueChanging (object sender, StateEventArgs<int> e)
+    {
+        if (e.NewValue < 0 || AdornmentToEdit is null)
+        {
+            e.Cancel = true;
+
+            return;
+        }
+
+        AdornmentToEdit.Thickness = new (AdornmentToEdit.Thickness.Left, AdornmentToEdit.Thickness.Top, e.NewValue, AdornmentToEdit.Thickness.Bottom);
+    }
+
+    private void Bottom_ValueChanging (object sender, StateEventArgs<int> e)
+    {
+        if (e.NewValue < 0 || AdornmentToEdit is null)
+        {
+            e.Cancel = true;
+
+            return;
+        }
+
+        AdornmentToEdit.Thickness = new (AdornmentToEdit.Thickness.Left, AdornmentToEdit.Thickness.Top, AdornmentToEdit.Thickness.Right, e.NewValue);
+    }
+}

+ 12 - 443
UICatalog/Scenarios/Adornments.cs

@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Terminal.Gui;
+using Terminal.Gui;
 
 namespace UICatalog.Scenarios;
 
@@ -10,27 +7,32 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Borders")]
 public class Adornments : Scenario
 {
-    private ViewDiagnosticFlags _diagnosticFlags;
-
     public override void Main ()
     {
         Application.Init ();
 
-        _diagnosticFlags = View.Diagnostics;
-
         Window app = new ()
         {
             Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
         };
 
-        var editor = new AdornmentsEditor ();
+        var editor = new AdornmentsEditor
+        {
+            AutoSelectViewToEdit = true,
+            // This is for giggles, to show that the editor can be moved around.
+            Arrangement = ViewArrangement.Movable,
+            X = Pos.AnchorEnd()
+
+        };
+        editor.Border.Thickness = new Thickness (1, 3, 1, 1);
+
         app.Add (editor);
 
         var window = new Window
         {
             Title = "The _Window",
             Arrangement = ViewArrangement.Movable,
-            X = Pos.Right (editor),
+           // X = Pos.Center (),
             Width = Dim.Percent (60),
             Height = Dim.Percent (80)
         };
@@ -126,442 +128,9 @@ public class Adornments : Scenario
 #endif
                               };
 
-        app.Closed += (s, e) => View.Diagnostics = _diagnosticFlags;
-
         Application.Run (app);
         app.Dispose ();
 
         Application.Shutdown ();
     }
-
-    /// <summary>
-    ///     Provides a composable UI for editing the settings of an Adornment.
-    /// </summary>
-    public class AdornmentEditor : View
-    {
-        private readonly ColorPicker _backgroundColorPicker = new ()
-        {
-            Title = "_BG",
-            BoxWidth = 1,
-            BoxHeight = 1,
-            BorderStyle = LineStyle.Single,
-            SuperViewRendersLineCanvas = true
-        };
-
-        private readonly ColorPicker _foregroundColorPicker = new ()
-        {
-            Title = "_FG",
-            BoxWidth = 1,
-            BoxHeight = 1,
-            BorderStyle = LineStyle.Single,
-            SuperViewRendersLineCanvas = true
-        };
-
-        private Buttons.NumericUpDown<int> _topEdit;
-        private Buttons.NumericUpDown<int> _leftEdit;
-        private Buttons.NumericUpDown<int> _bottomEdit;
-        private Buttons.NumericUpDown<int> _rightEdit;
-        private Thickness _thickness;
-        private bool _isUpdating;
-
-        public AdornmentEditor ()
-        {
-            Margin.Thickness = new (0);
-            BorderStyle = LineStyle.Double;
-            Initialized += AdornmentEditor_Initialized;
-        }
-
-        public Attribute Color
-        {
-            get => new (_foregroundColorPicker.SelectedColor, _backgroundColorPicker.SelectedColor);
-            set
-            {
-                _foregroundColorPicker.SelectedColor = value.Foreground.GetClosestNamedColor ();
-                _backgroundColorPicker.SelectedColor = value.Background.GetClosestNamedColor ();
-            }
-        }
-
-        public Thickness Thickness
-        {
-            get => _thickness;
-            set
-            {
-                if (_isUpdating)
-                {
-                    return;
-                }
-
-                _thickness = value;
-                ThicknessChanged?.Invoke (this, new () { Thickness = Thickness });
-
-                if (IsInitialized)
-                {
-                    _isUpdating = true;
-                    _topEdit.Value = _thickness.Top;
-                    _leftEdit.Value = _thickness.Left;
-                    _rightEdit.Value = _thickness.Right;
-                    _bottomEdit.Value = _thickness.Bottom;
-
-                    _isUpdating = false;
-                }
-            }
-        }
-
-        public event EventHandler<Attribute> AttributeChanged;
-        public event EventHandler<ThicknessEventArgs> ThicknessChanged;
-
-        private void AdornmentEditor_Initialized (object sender, EventArgs e)
-        {
-            SuperViewRendersLineCanvas = true;
-
-            _topEdit = new ()
-            {
-                X = Pos.Center (), Y = 0
-            };
-
-            _topEdit.ValueChanging += Top_ValueChanging;
-            Add (_topEdit);
-
-            _leftEdit = new ()
-            {
-                X = Pos.Left (_topEdit) - Pos.Func (() => _topEdit.Digits) - 2, Y = Pos.Bottom (_topEdit)
-            };
-
-            _leftEdit.ValueChanging += Left_ValueChanging;
-            Add (_leftEdit);
-
-            _rightEdit = new () { X = Pos.Right (_leftEdit) + 5, Y = Pos.Bottom (_topEdit) };
-
-            _rightEdit.ValueChanging += Right_ValueChanging;
-            Add (_rightEdit);
-
-            _bottomEdit = new () { X = Pos.Center (), Y = Pos.Bottom (_leftEdit) };
-
-            _bottomEdit.ValueChanging += Bottom_ValueChanging;
-            Add (_bottomEdit);
-
-            var copyTop = new Button { X = Pos.Center (), Y = Pos.Bottom (_bottomEdit), Text = "Cop_y Top" };
-
-            copyTop.Accept += (s, e) =>
-                              {
-                                  Thickness = new (Thickness.Top);
-                                  _leftEdit.Value = _rightEdit.Value = _bottomEdit.Value = _topEdit.Value;
-                              };
-            Add (copyTop);
-
-            // Foreground ColorPicker.
-            _foregroundColorPicker.X = -1;
-            _foregroundColorPicker.Y = Pos.Bottom (copyTop);
-            _foregroundColorPicker.SelectedColor = Color.Foreground.GetClosestNamedColor ();
-
-            _foregroundColorPicker.ColorChanged += (o, a) =>
-                                                       AttributeChanged?.Invoke (
-                                                                                 this,
-                                                                                 new (
-                                                                                      _foregroundColorPicker.SelectedColor,
-                                                                                      _backgroundColorPicker.SelectedColor
-                                                                                     )
-                                                                                );
-            Add (_foregroundColorPicker);
-
-            // Background ColorPicker.
-            _backgroundColorPicker.X = Pos.Right (_foregroundColorPicker) - 1;
-            _backgroundColorPicker.Y = Pos.Top (_foregroundColorPicker);
-            _backgroundColorPicker.SelectedColor = Color.Background.GetClosestNamedColor ();
-
-            _backgroundColorPicker.ColorChanged += (o, a) =>
-                                                       AttributeChanged?.Invoke (
-                                                                                 this,
-                                                                                 new (
-                                                                                      _foregroundColorPicker.SelectedColor,
-                                                                                      _backgroundColorPicker.SelectedColor
-                                                                                     )
-                                                                                );
-            Add (_backgroundColorPicker);
-
-            _topEdit.Value = Thickness.Top;
-            _leftEdit.Value = Thickness.Left;
-            _rightEdit.Value = Thickness.Right;
-            _bottomEdit.Value = Thickness.Bottom;
-
-            Width = Dim.Auto () - 1;
-            Height = Dim.Auto () - 1;
-            LayoutSubviews ();
-        }
-
-        private void Top_ValueChanging (object sender, StateEventArgs<int> e)
-        {
-            if (e.NewValue < 0)
-            {
-                e.Cancel = true;
-
-                return;
-            }
-
-            Thickness.Top = e.NewValue;
-        }
-
-        private void Left_ValueChanging (object sender, StateEventArgs<int> e)
-        {
-            if (e.NewValue < 0)
-            {
-                e.Cancel = true;
-
-                return;
-            }
-
-            Thickness.Left = e.NewValue;
-        }
-
-        private void Right_ValueChanging (object sender, StateEventArgs<int> e)
-        {
-            if (e.NewValue < 0)
-            {
-                e.Cancel = true;
-
-                return;
-            }
-
-            Thickness.Right = e.NewValue;
-        }
-
-        private void Bottom_ValueChanging (object sender, StateEventArgs<int> e)
-        {
-            if (e.NewValue < 0)
-            {
-                e.Cancel = true;
-
-                return;
-            }
-
-            Thickness.Bottom = e.NewValue;
-        }
-    }
-
-    /// <summary>
-    ///     Provides an editor UI for the Margin, Border, and Padding of a View.
-    /// </summary>
-    public class AdornmentsEditor : View
-    {
-        private AdornmentEditor _borderEditor;
-        private CheckBox _diagCheckBox;
-        private AdornmentEditor _marginEditor;
-        private string _origTitle = string.Empty;
-        private AdornmentEditor _paddingEditor;
-        private View _viewToEdit;
-
-        public AdornmentsEditor ()
-        {
-            ColorScheme = Colors.ColorSchemes ["Dialog"];
-
-            // TOOD: Use Dim.Auto
-            Width = 36;
-            Height = Dim.Fill ();
-        }
-
-        public View ViewToEdit
-        {
-            get => _viewToEdit;
-            set
-            {
-                _origTitle = value.Title;
-                _viewToEdit = value;
-
-                _marginEditor = new ()
-                {
-                    X = 0,
-                    Y = 0,
-                    Title = "_Margin",
-                    Thickness = _viewToEdit.Margin.Thickness,
-                    Color = new (_viewToEdit.Margin.ColorScheme?.Normal ?? ColorScheme.Normal),
-                    SuperViewRendersLineCanvas = true
-                };
-                _marginEditor.ThicknessChanged += Editor_ThicknessChanged;
-                _marginEditor.AttributeChanged += Editor_AttributeChanged;
-                Add (_marginEditor);
-
-                _borderEditor = new ()
-                {
-                    X = Pos.Left (_marginEditor),
-                    Y = Pos.Bottom (_marginEditor),
-                    Title = "B_order",
-                    Thickness = _viewToEdit.Border.Thickness,
-                    Color = new (_viewToEdit.Border.ColorScheme?.Normal ?? ColorScheme.Normal),
-                    SuperViewRendersLineCanvas = true
-                };
-                _borderEditor.ThicknessChanged += Editor_ThicknessChanged;
-                _borderEditor.AttributeChanged += Editor_AttributeChanged;
-                Add (_borderEditor);
-
-                List<LineStyle> borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast<LineStyle> ().ToList ();
-
-                var rbBorderStyle = new RadioGroup
-                {
-                    X = Pos.Right (_borderEditor) - 1,
-                    Y = Pos.Top (_borderEditor),
-                    SelectedItem = (int)_viewToEdit.Border.LineStyle,
-                    BorderStyle = LineStyle.Double,
-                    Title = "Border St_yle",
-                    SuperViewRendersLineCanvas = true,
-                    RadioLabels = borderStyleEnum.Select (
-                                                          e => e.ToString ()
-                                                         )
-                                                 .ToArray ()
-                };
-                Add (rbBorderStyle);
-
-                rbBorderStyle.SelectedItemChanged += (s, e) =>
-                                                     {
-                                                         LineStyle prevBorderStyle = _viewToEdit.BorderStyle;
-                                                         _viewToEdit.Border.LineStyle = (LineStyle)e.SelectedItem;
-
-                                                         if (_viewToEdit.Border.LineStyle == LineStyle.None)
-                                                         {
-                                                             _viewToEdit.Border.Thickness = new (0);
-                                                         }
-                                                         else if (prevBorderStyle == LineStyle.None && _viewToEdit.Border.LineStyle != LineStyle.None)
-                                                         {
-                                                             _viewToEdit.Border.Thickness = new (1);
-                                                         }
-
-                                                         _borderEditor.Thickness = new (
-                                                                                        _viewToEdit.Border.Thickness.Left,
-                                                                                        _viewToEdit.Border.Thickness.Top,
-                                                                                        _viewToEdit.Border.Thickness.Right,
-                                                                                        _viewToEdit.Border.Thickness.Bottom
-                                                                                       );
-                                                         _viewToEdit.SetNeedsDisplay ();
-                                                         LayoutSubviews ();
-                                                     };
-
-                var ckbTitle = new CheckBox
-                {
-                    BorderStyle = LineStyle.Double,
-                    X = Pos.Left (_borderEditor),
-                    Y = Pos.Bottom (_borderEditor) - 1,
-
-                    //Width = Dim.Width (_borderEditor),
-                    Checked = true,
-                    SuperViewRendersLineCanvas = true,
-                    Text = "Show Title"
-                };
-
-                ckbTitle.Toggled += (sender, args) =>
-                                    {
-                                        if (ckbTitle.Checked == true)
-                                        {
-                                            //_viewToEdit.Title = _origTitle;
-                                        }
-                                        else
-                                        {
-                                            _viewToEdit.Title = string.Empty;
-                                        }
-                                    };
-                Add (ckbTitle);
-
-                _paddingEditor = new ()
-                {
-                    X = Pos.Left (_borderEditor),
-                    Y = Pos.Bottom (rbBorderStyle),
-                    Title = "_Padding",
-                    Thickness = _viewToEdit.Padding.Thickness,
-                    Color = new (_viewToEdit.Padding.ColorScheme?.Normal ?? ColorScheme.Normal),
-                    SuperViewRendersLineCanvas = true
-                };
-                _paddingEditor.ThicknessChanged += Editor_ThicknessChanged;
-                _paddingEditor.AttributeChanged += Editor_AttributeChanged;
-                Add (_paddingEditor);
-
-                _diagCheckBox = new () { Text = "_Diagnostics", Y = Pos.Bottom (_paddingEditor) };
-                _diagCheckBox.Checked = Diagnostics != ViewDiagnosticFlags.Off;
-
-                _diagCheckBox.Toggled += (s, e) =>
-                                         {
-                                             if (e.NewValue == true)
-                                             {
-                                                 Diagnostics =
-                                                     ViewDiagnosticFlags.Padding | ViewDiagnosticFlags.Ruler;
-                                             }
-                                             else
-                                             {
-                                                 Diagnostics = ViewDiagnosticFlags.Off;
-                                             }
-                                         };
-
-                Add (_diagCheckBox);
-
-                _viewToEdit.LayoutComplete += (s, e) =>
-                                              {
-                                                  if (ckbTitle.Checked == true)
-                                                  {
-                                                      _viewToEdit.Title = _origTitle;
-                                                  }
-                                                  else
-                                                  {
-                                                      _viewToEdit.Title = string.Empty;
-                                                  }
-                                              };
-            }
-        }
-
-        private void Editor_AttributeChanged (object sender, Attribute attr)
-        {
-            switch (sender.ToString ())
-            {
-                case var s when s == _marginEditor.ToString ():
-                    _viewToEdit.Margin.ColorScheme = new (_viewToEdit.Margin.ColorScheme) { Normal = attr };
-
-                    break;
-                case var s when s == _borderEditor.ToString ():
-                    _viewToEdit.Border.ColorScheme = new (_viewToEdit.Border.ColorScheme) { Normal = attr };
-
-                    break;
-                case var s when s == _paddingEditor.ToString ():
-                    _viewToEdit.Padding.ColorScheme =
-                        new (_viewToEdit.Padding.ColorScheme) { Normal = attr };
-
-                    break;
-            }
-        }
-
-        private void Editor_ThicknessChanged (object sender, ThicknessEventArgs e)
-        {
-            try
-            {
-                switch (sender.ToString ())
-                {
-                    case var s when s == _marginEditor.ToString ():
-                        _viewToEdit.Margin.Thickness = e.Thickness;
-
-                        break;
-                    case var s when s == _borderEditor.ToString ():
-                        _viewToEdit.Border.Thickness = e.Thickness;
-
-                        break;
-                    case var s when s == _paddingEditor.ToString ():
-                        _viewToEdit.Padding.Thickness = e.Thickness;
-
-                        break;
-                }
-            }
-            catch
-            {
-                switch (sender.ToString ())
-                {
-                    case var s when s == _marginEditor.ToString ():
-                        _viewToEdit.Margin.Thickness = e.PreviousThickness;
-
-                        break;
-                    case var s when s == _borderEditor.ToString ():
-                        _viewToEdit.Border.Thickness = e.PreviousThickness;
-
-                        break;
-                    case var s when s == _paddingEditor.ToString ():
-                        _viewToEdit.Padding.Thickness = e.PreviousThickness;
-
-                        break;
-                }
-            }
-        }
-    }
 }

+ 184 - 0
UICatalog/Scenarios/AdornmentsEditor.cs

@@ -0,0 +1,184 @@
+using System;
+using System.Text;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+/// <summary>
+///     Provides an editor UI for the Margin, Border, and Padding of a View.
+/// </summary>
+public class AdornmentsEditor : View
+{
+    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;
+    private readonly ViewDiagnosticFlags _savedDiagnosticFlags = Diagnostics;
+
+    public AdornmentsEditor ()
+    {
+        //ColorScheme = Colors.ColorSchemes ["Dialog"];
+        Title = $"AdornmentsEditor";
+
+        Width = Dim.Auto (DimAutoStyle.Content);
+        Height = Dim.Auto (DimAutoStyle.Content);
+
+        //SuperViewRendersLineCanvas = true;
+
+        Application.MouseEvent += Application_MouseEvent;
+        Initialized += AdornmentsEditor_Initialized;
+    }
+
+    /// <summary>
+    /// Gets or sets whether the AdornmentsEditor should automatically select the View to edit when the mouse is clicked
+    /// anywhere outside the editor.
+    /// </summary>
+    public bool AutoSelectViewToEdit { get; set; }
+
+    private void AdornmentsEditor_Initialized (object sender, EventArgs e)
+    {
+        BorderStyle = LineStyle.Dotted;
+
+        ExpanderButton expandButton = new ExpanderButton ()
+        {
+            Orientation = Orientation.Horizontal
+        };
+        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);
+
+        _marginEditor = new ()
+        {
+            X = 0,
+            Y = Pos.Bottom (_lblView),
+            SuperViewRendersLineCanvas = true
+        };
+        Add (_marginEditor);
+
+        _lblView.Width = Dim.Width (_marginEditor);
+
+        _borderEditor = new ()
+        {
+            X = Pos.Left (_marginEditor),
+            Y = Pos.Bottom (_marginEditor),
+            SuperViewRendersLineCanvas = true
+        };
+        Add (_borderEditor);
+
+        _paddingEditor = new ()
+        {
+            X = Pos.Left (_borderEditor),
+            Y = Pos.Bottom (_borderEditor),
+            SuperViewRendersLineCanvas = true
+        };
+        Add (_paddingEditor);
+
+        _diagPaddingCheckBox = new () { Text = "_Diagnostic Padding" };
+        _diagPaddingCheckBox.Checked = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Padding);
+
+        _diagPaddingCheckBox.Toggled += (s, e) =>
+                                        {
+                                            if (e.NewValue == true)
+                                            {
+                                                Diagnostics |= ViewDiagnosticFlags.Padding;
+                                            }
+                                            else
+                                            {
+                                                Diagnostics &= ~ViewDiagnosticFlags.Padding;
+                                            }
+                                        };
+
+        Add (_diagPaddingCheckBox);
+        _diagPaddingCheckBox.Y = Pos.Bottom (_paddingEditor);
+
+        _diagRulerCheckBox = new () { Text = "_Diagnostic Ruler" };
+        _diagRulerCheckBox.Checked = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Ruler);
+
+        _diagRulerCheckBox.Toggled += (s, e) =>
+                                      {
+                                          if (e.NewValue == true)
+                                          {
+                                              Diagnostics |= ViewDiagnosticFlags.Ruler;
+                                          }
+                                          else
+                                          {
+                                              Diagnostics &= ~ViewDiagnosticFlags.Ruler;
+                                          }
+                                      };
+
+        Add (_diagRulerCheckBox);
+        _diagRulerCheckBox.Y = Pos.Bottom (_diagPaddingCheckBox);
+
+        // BUGBUG: This should not be needed. There's some bug in the layout system that doesn't update the layout.
+        SuperView.LayoutSubviews();
+
+    }
+
+    private void Application_MouseEvent (object sender, MouseEvent e)
+    {
+        if (!AutoSelectViewToEdit || FrameToScreen ().Contains (e.Position))
+        {
+            return;
+        }
+
+        // TODO: Add a setting (property) so only subviews of a specified view are considered.
+        var view = e.View;
+        if (view is { } && e.Flags == MouseFlags.Button1Clicked)
+        {
+            if (view is Adornment adornment)
+            {
+                ViewToEdit = adornment.Parent;
+            }
+            else
+            {
+                ViewToEdit = view;
+            }
+        }
+    }
+
+    /// <inheritdoc />
+    protected override void Dispose (bool disposing)
+    {
+        View.Diagnostics = _savedDiagnosticFlags;
+        base.Dispose (disposing);
+    }
+
+    public View ViewToEdit
+    {
+        get => _viewToEdit;
+        set
+        {
+            if (_viewToEdit == value)
+            {
+                return;
+            }
+
+            _viewToEdit = value;
+
+
+            _marginEditor.AdornmentToEdit = _viewToEdit.Margin ?? null;
+            _borderEditor.AdornmentToEdit = _viewToEdit.Border ?? null;
+            _paddingEditor.AdornmentToEdit = _viewToEdit.Padding ?? null;
+
+            _lblView.Text = _viewToEdit.ToString ();
+
+            return;
+        }
+    }
+}

+ 55 - 60
UICatalog/Scenarios/AllViewsTester.cs

@@ -19,6 +19,7 @@ public class AllViewsTester : Scenario
     private ListView _classListView;
     private View _curView;
     private FrameView _hostPane;
+    private AdornmentsEditor _adornmentsEditor;
     private RadioGroup _hRadioGroup;
     private TextField _hText;
     private int _hVal;
@@ -48,58 +49,29 @@ public class AllViewsTester : Scenario
         Application.Init ();
         ConfigurationManager.Apply ();
 
-        var app = new Window ();
-        app.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
-
-        var statusBar = new StatusBar (
-                                       new StatusItem []
-                                       {
-                                           new (
-                                                Application.QuitKey,
-                                                $"{Application.QuitKey} to Quit",
-                                                () => Quit ()
-                                               ),
-                                           new (
-                                                KeyCode.F2,
-                                                "~F2~ Toggle Frame Ruler",
-                                                () =>
-                                                {
-                                                    View.Diagnostics ^=
-                                                        ViewDiagnosticFlags.Ruler;
-                                                    app.SetNeedsDisplay ();
-                                                }
-                                               ),
-                                           new (
-                                                KeyCode.F3,
-                                                "~F3~ Toggle Frame Padding",
-                                                () =>
-                                                {
-                                                    View.Diagnostics ^=
-                                                        ViewDiagnosticFlags.Padding;
-                                                    app.SetNeedsDisplay ();
-                                                }
-                                               )
-                                       }
-                                      );
-        app.Add (statusBar);
+        var app = new Window
+        {
+            Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
+            ColorScheme = Colors.ColorSchemes ["TopLevel"]
+        };
 
         _viewClasses = GetAllViewClassesCollection ()
                        .OrderBy (t => t.Name)
                        .Select (t => new KeyValuePair<string, Type> (t.Name, t))
                        .ToDictionary (t => t.Key, t => t.Value);
 
-        _leftPane = new()
+        _leftPane = new ()
         {
             X = 0,
             Y = 0,
             Width = Dim.Auto (DimAutoStyle.Content),
-            Height = Dim.Fill (1), // for status bar
+            Height = Dim.Fill (),
             CanFocus = false,
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
             Title = "Classes"
         };
 
-        _classListView = new()
+        _classListView = new ()
         {
             X = 0,
             Y = 0,
@@ -128,9 +100,26 @@ public class AllViewsTester : Scenario
                                               };
         _leftPane.Add (_classListView);
 
-        _settingsPane = new()
+        _adornmentsEditor = new ()
         {
             X = Pos.Right (_leftPane),
+            Y = 0,
+            Width = Dim.Auto (),
+            Height = Dim.Fill (),
+            ColorScheme = Colors.ColorSchemes ["TopLevel"],
+            BorderStyle = LineStyle.Single
+        };
+
+        var expandButton = new ExpanderButton
+        {
+            CanFocus = false,
+            Orientation = Orientation.Horizontal
+        };
+        _adornmentsEditor.Border.Add (expandButton);
+
+        _settingsPane = new ()
+        {
+            X = Pos.Right (_adornmentsEditor),
             Y = 0, // for menu
             Width = Dim.Fill (),
             Height = Dim.Auto (),
@@ -141,7 +130,7 @@ public class AllViewsTester : Scenario
 
         string [] radioItems = { "_Percent(x)", "_AnchorEnd", "_Center", "A_bsolute(x)" };
 
-        _locationFrame = new()
+        _locationFrame = new ()
         {
             X = 0,
             Y = 0,
@@ -153,9 +142,9 @@ public class AllViewsTester : Scenario
 
         var label = new Label { X = 0, Y = 0, Text = "X:" };
         _locationFrame.Add (label);
-        _xRadioGroup = new() { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
+        _xRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
         _xRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
-        _xText = new() { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_xVal}" };
+        _xText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_xVal}" };
 
         _xText.Accept += (s, args) =>
                          {
@@ -172,9 +161,9 @@ public class AllViewsTester : Scenario
         _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:" };
+        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 = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_yVal}" };
 
         _yText.Accept += (s, args) =>
                          {
@@ -187,11 +176,11 @@ public class AllViewsTester : Scenario
                              { }
                          };
         _locationFrame.Add (_yText);
-        _yRadioGroup = new() { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
+        _yRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
         _yRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
         _locationFrame.Add (_yRadioGroup);
 
-        _sizeFrame = new()
+        _sizeFrame = new ()
         {
             X = Pos.Right (_locationFrame),
             Y = Pos.Y (_locationFrame),
@@ -201,11 +190,11 @@ public class AllViewsTester : Scenario
         };
 
         radioItems = new [] { "Auto", "_Percent(width)", "_Fill(width)", "A_bsolute(width)" };
-        label = new() { X = 0, Y = 0, Text = "Width:" };
+        label = new () { X = 0, Y = 0, Text = "Width:" };
         _sizeFrame.Add (label);
-        _wRadioGroup = new() { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
+        _wRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
         _wRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
-        _wText = new() { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_wVal}" };
+        _wText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_wVal}" };
 
         _wText.Accept += (s, args) =>
                          {
@@ -234,9 +223,9 @@ public class AllViewsTester : Scenario
         _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:" };
+        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 = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_hVal}" };
 
         _hText.Accept += (s, args) =>
                          {
@@ -263,15 +252,15 @@ public class AllViewsTester : Scenario
                          };
         _sizeFrame.Add (_hText);
 
-        _hRadioGroup = new() { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
+        _hRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
         _hRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
         _sizeFrame.Add (_hRadioGroup);
 
         _settingsPane.Add (_sizeFrame);
 
-        label = new() { X = 0, Y = Pos.Bottom (_sizeFrame), Text = "_Orientation:" };
+        label = new () { X = 0, Y = Pos.Bottom (_sizeFrame), Text = "_Orientation:" };
 
-        _orientation = new()
+        _orientation = new ()
         {
             X = Pos.Right (label) + 1,
             Y = Pos.Top (label),
@@ -288,7 +277,7 @@ public class AllViewsTester : Scenario
                                             };
         _settingsPane.Add (label, _orientation);
 
-        label = new() { X = 0, Y = Pos.Bottom (_orientation), Text = "_Text:" };
+        label = new () { X = 0, Y = Pos.Bottom (_orientation), Text = "_Text:" };
 
         _demoTextView = new ()
         {
@@ -302,23 +291,27 @@ public class AllViewsTester : Scenario
         _demoTextView.ContentsChanged += (s, e) =>
                                          {
                                              _demoText = _demoTextView.Text;
-                                             _curView.Text = _demoText;
+
+                                             if (_curView is { })
+                                             {
+                                                 _curView.Text = _demoText;
+                                             }
                                          };
 
         _settingsPane.Add (label, _demoTextView);
 
-        _hostPane = new()
+        _hostPane = new ()
         {
-            X = Pos.Right (_leftPane),
+            X = Pos.Right (_adornmentsEditor),
             Y = Pos.Bottom (_settingsPane),
             Width = Dim.Fill (),
-            Height = Dim.Fill (1), // + 1 for status bar
+            Height = Dim.Fill (), // + 1 for status bar
             ColorScheme = Colors.ColorSchemes ["Dialog"]
         };
 
-        app.Add (_leftPane, _settingsPane, _hostPane);
+        app.Add (_leftPane, _adornmentsEditor, _settingsPane, _hostPane);
 
-        _curView = CreateClass (_viewClasses.First ().Value);
+        _classListView.SelectedItem = 0;
 
         Application.Run (app);
         app.Dispose ();
@@ -519,6 +512,8 @@ public class AllViewsTester : Scenario
 
     private void UpdateSettings (View view)
     {
+        _adornmentsEditor.ViewToEdit = view;
+
         var x = view.X.ToString ();
         var y = view.Y.ToString ();
         _xRadioGroup.SelectedItem = _posNames.IndexOf (_posNames.Where (s => x.Contains (s)).First ());

+ 85 - 0
UICatalog/Scenarios/BorderEditor.cs

@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+public class BorderEditor : AdornmentEditor
+{
+    private CheckBox _ckbTitle;
+    private RadioGroup _rbBorderStyle;
+
+    public BorderEditor ()
+    {
+        Title = "_Border";
+        Initialized += BorderEditor_Initialized;
+        AdornmentChanged += BorderEditor_AdornmentChanged;
+
+    }
+
+    private void BorderEditor_AdornmentChanged (object sender, EventArgs e)
+    {
+        _ckbTitle.Checked = ((Border)AdornmentToEdit).ShowTitle;
+        _rbBorderStyle.SelectedItem = (int)((Border)AdornmentToEdit).LineStyle;
+    }
+
+    private void BorderEditor_Initialized (object sender, EventArgs e)
+    {
+
+        List<LineStyle> borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast<LineStyle> ().ToList ();
+
+        _rbBorderStyle = new RadioGroup
+        {
+            X = 0,
+            Y = Pos.Bottom (Subviews [^1]),
+            Width = Dim.Width (Subviews [^2]) + Dim.Width (Subviews [^1]) - 1,
+            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 ()
+        };
+        Add (_rbBorderStyle);
+
+        _rbBorderStyle.SelectedItemChanged += OnRbBorderStyleOnSelectedItemChanged;
+
+        _ckbTitle = new CheckBox
+        {
+            X = 0,
+            Y = Pos.Bottom (_rbBorderStyle),
+
+            Checked = true,
+            SuperViewRendersLineCanvas = true,
+            Text = "Show Title",
+            Enabled = AdornmentToEdit is { }
+        };
+
+
+        _ckbTitle.Toggled += OnCkbTitleOnToggled;
+        Add (_ckbTitle);
+
+        return;
+
+        void OnRbBorderStyleOnSelectedItemChanged (object s, SelectedItemChangedArgs e)
+        {
+            LineStyle prevBorderStyle = AdornmentToEdit.BorderStyle;
+            ((Border)AdornmentToEdit).LineStyle = (LineStyle)e.SelectedItem;
+
+            if (((Border)AdornmentToEdit).LineStyle == LineStyle.None)
+            {
+                ((Border)AdornmentToEdit).Thickness = new (0);
+            }
+            else if (prevBorderStyle == LineStyle.None && ((Border)AdornmentToEdit).LineStyle != LineStyle.None)
+            {
+                ((Border)AdornmentToEdit).Thickness = new (1);
+            }
+
+            ((Border)AdornmentToEdit).SetNeedsDisplay ();
+            LayoutSubviews ();
+        }
+
+        void OnCkbTitleOnToggled (object sender, StateEventArgs<bool?> args) { ((Border)AdornmentToEdit).ShowTitle = args.NewValue!.Value; }
+    }
+}

+ 14 - 10
UICatalog/Scenarios/ContentScrolling.cs

@@ -1,9 +1,6 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
+using System.Collections.Generic;
 using System.Linq;
 using Terminal.Gui;
-using static UICatalog.Scenarios.Adornments;
 
 namespace UICatalog.Scenarios;
 
@@ -22,7 +19,9 @@ public class ContentScrolling : Scenario
             Width = Dim.Fill ();
             Height = Dim.Fill ();
             ColorScheme = Colors.ColorSchemes ["Base"];
-            Text = "Text (ScrollingDemoView.Text). This is long text.\nThe second line.\n3\n4\n5th line\nLine 6. This is a longer line that should wrap automatically.";
+
+            Text =
+                "Text (ScrollingDemoView.Text). This is long text.\nThe second line.\n3\n4\n5th line\nLine 6. This is a longer line that should wrap automatically.";
             CanFocus = true;
             BorderStyle = LineStyle.Rounded;
             Arrangement = ViewArrangement.Fixed;
@@ -105,11 +104,15 @@ public class ContentScrolling : Scenario
         Window app = new ()
         {
             Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
+
             // Use a different colorscheme so ViewSettings.ClearContentOnly is obvious
             ColorScheme = Colors.ColorSchemes ["Toplevel"]
         };
 
-        var editor = new AdornmentsEditor ();
+        var editor = new AdornmentsEditor
+        {
+            AutoSelectViewToEdit = true
+        };
         app.Add (editor);
 
         var view = new ScrollingDemoView
@@ -225,7 +228,7 @@ public class ContentScrolling : Scenario
             Y = Pos.Bottom (cbAllowYGreaterThanContentHeight)
         };
 
-        var contentSizeWidth = new Buttons.NumericUpDown<int>
+        Buttons.NumericUpDown<int> contentSizeWidth = new Buttons.NumericUpDown<int>
         {
             Value = view.ContentSize.Width,
             X = Pos.Right (labelContentSize) + 1,
@@ -252,7 +255,7 @@ public class ContentScrolling : Scenario
             Y = Pos.Top (labelContentSize)
         };
 
-        var contentSizeHeight = new Buttons.NumericUpDown<int>
+        Buttons.NumericUpDown<int> contentSizeHeight = new Buttons.NumericUpDown<int>
         {
             Value = view.ContentSize.Height,
             X = Pos.Right (labelComma) + 1,
@@ -352,8 +355,8 @@ public class ContentScrolling : Scenario
         {
             X = Pos.Center (),
             Y = Pos.Bottom (textView) + 1,
-            Width = 30,
-            Height = 10
+            Width = Dim.Auto(DimAutoStyle.Content, maximumContentDim: Dim.Func (() => view.ContentSize.Width)),
+            Height = Dim.Auto (DimAutoStyle.Content, maximumContentDim: Dim.Percent(20)),
         };
 
         charMap.Accept += (s, e) =>
@@ -386,6 +389,7 @@ public class ContentScrolling : Scenario
         view.Add (longLabel);
 
         List<object> options = new () { "Option 1", "Option 2", "Option 3" };
+
         Slider slider = new (options)
         {
             X = 0,

+ 230 - 0
UICatalog/Scenarios/ExpanderButton.cs

@@ -0,0 +1,230 @@
+using System;
+using System.Text;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+/// <summary>
+///     A Button that can expand or collapse a view.
+/// </summary>
+/// <remarks>
+///     <para>
+///         Add this button to a view's Border to allow the user to expand or collapse the view via either the keyboard
+///         (F4) or mouse.
+///     </para>
+///     <para>
+///         If <see cref="Orientation"/> is set to <see cref="Terminal.Gui.Orientation.Vertical"/>, the button will appear
+///         at the top/right.
+///         If <see cref="Orientation"/> is set to <see cref="Terminal.Gui.Orientation.Horizontal"/>, the button will
+///         appear at the
+///         bottom/left.
+///     </para>
+/// </remarks>
+/// <example>
+///     private void MyView_Initialized (object sender, EventArgs e)
+///     {
+///     Border.Add(new ExpanderButton ());
+///     ...
+/// </example>
+public class ExpanderButton : Button
+{
+    public ExpanderButton ()
+    {
+        CanFocus = false;
+
+        Width = 1;
+        Height = 1;
+        NoDecorations = true;
+        NoPadding = true;
+
+        AddCommand (Command.HotKey, Toggle);
+        AddCommand (Command.ToggleExpandCollapse, Toggle);
+        KeyBindings.Add (Key.F4, Command.ToggleExpandCollapse);
+
+        Orientation = Orientation.Vertical;
+
+        Initialized += ExpanderButton_Initialized;
+    }
+
+    private void ExpanderButton_Initialized (object sender, EventArgs e)
+    {
+        ExpandOrCollapse (Collapsed);
+    }
+
+    private Orientation _orientation = Orientation.Horizontal;
+
+    /// <summary>Orientation.</summary>
+    /// <remarks>
+    ///     <para>
+    ///         If <see cref="Orientation"/> is set to <see cref="Orientation.Vertical"/>, the button will appear at the
+    ///         top/right.
+    ///         If <see cref="Orientation"/> is set to <see cref="Orientation.Horizontal"/>, the button will appear at the
+    ///         bottom/left.
+    ///     </para>
+    /// </remarks>
+    public Orientation Orientation
+    {
+        get => _orientation;
+        set => OnOrientationChanging (value);
+    }
+
+    /// <summary>Called when the orientation is changing. Invokes the <see cref="OrientationChanging"/> event.</summary>
+    /// <param name="newOrientation"></param>
+    /// <returns>True of the event was cancelled.</returns>
+    protected virtual bool OnOrientationChanging (Orientation newOrientation)
+    {
+        var args = new OrientationEventArgs (newOrientation);
+        OrientationChanging?.Invoke (this, args);
+
+        if (!args.Cancel)
+        {
+            _orientation = newOrientation;
+
+            if (Orientation == Orientation.Vertical)
+            {
+                X = Pos.AnchorEnd ();
+                Y = 0;
+                CollapsedGlyph = new ('\u21d1'); // ⇑
+                ExpandedGlyph = new ('\u21d3'); // ⇓
+            }
+            else
+            {
+                X = 0;
+                Y = Pos.AnchorEnd ();
+                CollapsedGlyph = new ('\u21d0'); // ⇐
+                ExpandedGlyph = new ('\u21d2'); // ⇒
+            }
+
+            Text = $"{(Collapsed ? CollapsedGlyph : ExpandedGlyph)}";
+
+            ExpandOrCollapse (Collapsed);
+        }
+
+        return args.Cancel;
+    }
+
+    /// <summary>
+    ///     Fired when the orientation has changed. Can be cancelled by setting
+    ///     <see cref="OrientationEventArgs.Cancel"/> to true.
+    /// </summary>
+    public event EventHandler<OrientationEventArgs> OrientationChanging;
+
+    /// <summary>
+    ///     The glyph to display when the view is collapsed.
+    /// </summary>
+    public Rune CollapsedGlyph { get; set; }
+
+    /// <summary>
+    ///     The glyph to display when the view is expanded.
+    /// </summary>
+    public Rune ExpandedGlyph { get; set; }
+
+    private bool _collapsed;
+
+    /// <summary>
+    ///     Gets or sets a value indicating whether the view is collapsed.
+    /// </summary>
+    public bool Collapsed
+    {
+        get => _collapsed;
+        set => OnCollapsedChanging (value);
+    }
+
+    /// <summary>Called when the orientation is changing. Invokes the <see cref="OrientationChanging"/> event.</summary>
+    /// <param name="newOrientation"></param>
+    /// <returns>True of the event was cancelled.</returns>
+    protected virtual bool OnCollapsedChanging (bool newValue)
+    {
+        StateEventArgs<bool> args = new (Collapsed, newValue);
+        CollapsedChanging?.Invoke (this, args);
+
+        if (!args.Cancel)
+        {
+            _collapsed = newValue;
+
+            ExpandOrCollapse (_collapsed);
+
+            View superView = SuperView;
+            if (superView is Adornment adornment)
+            {
+                superView = adornment.Parent;
+            }
+
+            foreach (View subview in superView.Subviews)
+            {
+                subview.Visible = !Collapsed;
+                subview.Enabled = !Collapsed;
+            }
+
+            // BUGBUG: This should not be needed. There's some bug in the layout system that doesn't update the layout.
+            superView.SuperView?.LayoutSubviews ();
+        }
+
+        return args.Cancel;
+    }
+
+    /// <summary>
+    ///     Fired when the orientation has changed. Can be cancelled by setting
+    ///     <see cref="OrientationEventArgs.Cancel"/> to true.
+    /// </summary>
+    public event EventHandler<StateEventArgs<bool>> CollapsedChanging;
+
+    /// <summary>
+    ///     Collapses or Expands the view.
+    /// </summary>
+    /// <returns></returns>
+    public bool? Toggle ()
+    {
+        Collapsed = !Collapsed;
+
+        return true;
+    }
+
+    private Dim _previousDim;
+
+    private void ExpandOrCollapse (bool collapse)
+    {
+        View superView = SuperView;
+        if (superView is Adornment adornment)
+        {
+            superView = adornment.Parent;
+        }
+
+        if (superView is null)
+        {
+            return;
+        }
+
+        if (collapse)
+        {
+            // Collapse
+            if (Orientation == Orientation.Vertical)
+            {
+                _previousDim = superView.Height;
+                superView.Height = 1;
+            }
+            else
+            {
+                _previousDim = superView.Width;
+                superView.Width = 1;
+            }
+        }
+        else
+        {
+            if (_previousDim is null)
+            {
+                return;
+            }
+
+            // Expand
+            if (Orientation == Orientation.Vertical)
+            {
+                superView.Height = _previousDim;
+            }
+            else
+            {
+                superView.Width = _previousDim;
+            }
+        }
+    }
+}

+ 1 - 1
UICatalog/Scenarios/Generic.cs

@@ -14,7 +14,7 @@ public sealed class MyScenario : Scenario
         // Setup - Create a top-level application window and configure it.
         Window appWindow = new ()
         {
-            Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
+            Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
         };
 
         var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Press me!" };

+ 18 - 0
UICatalog/Scenarios/MarginEditor.cs

@@ -0,0 +1,18 @@
+using System;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+public class MarginEditor : AdornmentEditor
+{
+    public MarginEditor ()
+    {
+        Title = "_Margin";
+        Initialized += MarginEditor_Initialized;
+    }
+
+    private void MarginEditor_Initialized (object sender, EventArgs e)
+    {
+
+    }
+}

+ 9 - 0
UICatalog/Scenarios/PaddingEditor.cs

@@ -0,0 +1,9 @@
+namespace UICatalog.Scenarios;
+
+public class PaddingEditor : AdornmentEditor
+{
+    public PaddingEditor ()
+    {
+        Title = "_Padding";
+    }
+}

+ 23 - 14
UICatalog/Scenarios/ProgressBarStyles.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading;
@@ -20,20 +21,21 @@ public class ProgressBarStyles : Scenario
     private const uint _timerTick = 20;
     private Timer _fractionTimer;
     private Timer _pulseTimer;
-    private ViewDiagnosticFlags _diagnosticFlags;
+    private ListView _pbList;
 
     public override void Main ()
     {
         Application.Init ();
 
-        _diagnosticFlags = View.Diagnostics;
-
         Window app = new ()
         {
             Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}", BorderStyle = LineStyle.Single,
         };
 
-        var editor = new AdornmentsEditor ();
+        var editor = new AdornmentsEditor ()
+        {
+            AutoSelectViewToEdit = false
+        };
         app.Add (editor);
 
         View container = new ()
@@ -47,7 +49,7 @@ public class ProgressBarStyles : Scenario
 
         const float fractionStep = 0.01F;
 
-        var pbList = new ListView
+        _pbList = new ListView
         {
             Title = "Focused ProgressBar",
             Y = Pos.Align (Alignment.Start),
@@ -56,7 +58,7 @@ public class ProgressBarStyles : Scenario
             Height = Dim.Auto (),
             BorderStyle = LineStyle.Single
         };
-        container.Add (pbList);
+        container.Add (_pbList);
 
         #region ColorPicker
 
@@ -69,8 +71,8 @@ public class ProgressBarStyles : Scenario
             dialog.Initialized += (sender, args) =>
                                      {
                                          // TODO: Replace with Dim.Auto
-                                         dialog.X = pbList.Frame.X;
-                                         dialog.Y = pbList.Frame.Height;
+                                         dialog.X = _pbList.Frame.X;
+                                         dialog.Y = _pbList.Frame.Height;
                                      };
 
             dialog.LayoutComplete += (sender, args) =>
@@ -258,13 +260,13 @@ public class ProgressBarStyles : Scenario
         };
         container.Add (marqueesContinuousPB);
 
-        pbList.SetSource (
+        _pbList.SetSource (
                           container.Subviews.Where (v => v.GetType () == typeof (ProgressBar))
                                    .Select (v => v.Title)
                                    .ToList ()
                          );
 
-        pbList.SelectedItemChanged += (sender, e) =>
+        _pbList.SelectedItemChanged += (sender, e) =>
                                       {
                                           editor.ViewToEdit = container.Subviews.First (
                                                                                         v =>
@@ -272,7 +274,7 @@ public class ProgressBarStyles : Scenario
                                                                                             && v.Title == (string)e.Value
                                                                                        );
                                       };
-        pbList.SelectedItem = 0;
+        
 
         rbPBFormat.SelectedItemChanged += (s, e) =>
                                           {
@@ -288,6 +290,11 @@ public class ProgressBarStyles : Scenario
                                                                        marqueesContinuousPB.BidirectionalMarquee = (bool)!e.OldValue;
                                     };
 
+
+
+        app.Initialized += App_Initialized;
+        app.Unloaded += App_Unloaded;
+
         _pulseTimer = new Timer (
                                  _ =>
                                  {
@@ -300,9 +307,6 @@ public class ProgressBarStyles : Scenario
                                  0,
                                  300
                                 );
-
-        app.Unloaded += App_Unloaded;
-
         Application.Run (app);
         app.Dispose ();
         Application.Shutdown ();
@@ -326,4 +330,9 @@ public class ProgressBarStyles : Scenario
             app.Unloaded -= App_Unloaded;
         }
     }
+
+    private void App_Initialized (object sender, EventArgs e)
+    {
+        _pbList.SelectedItem = 0;
+    }
 }

+ 2 - 4
UICatalog/Scenarios/ViewExperiments.cs

@@ -232,13 +232,11 @@ public class ViewExperiments : Scenario
 
         view.X = Pos.Center ();
 
-        var editor = new Adornments.AdornmentsEditor
+        var editor = new AdornmentsEditor
         {
-            Title = "Adornments Editor",
             X = 0,
             Y = Pos.Bottom (containerLabel),
-            Width = Dim.Fill (),
-            ViewToEdit = view
+            AutoSelectViewToEdit = true
         };
 
         app.Add (editor);

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

@@ -501,7 +501,7 @@ public class BorderTests
             case 1:
                 //Assert.Equal (new (0, 0, 17, 0), subview.Frame);
                 expected = @"
-────────────────────";
+─┤1234├─────────────";
 
                 break;
             case 2:

+ 2 - 7
UnitTests/View/DrawTests.cs

@@ -428,7 +428,7 @@ public class DrawTests (ITestOutputHelper _output)
 
         view.Draw ();
 
-        TestHelpers.AssertDriverContentsWithFrameAre (string.Empty, _output);
+        TestHelpers.AssertDriverContentsWithFrameAre ("──", _output);
     }
 
     [Fact]
@@ -497,12 +497,7 @@ public class DrawTests (ITestOutputHelper _output)
 
         view.Draw ();
 
-        // BUGBUG: Wha? Is this right? Shouldn't it be "└┘"???
-        TestHelpers.AssertDriverContentsWithFrameAre (
-                                                      """
-
-                                                      ┌┐
-                                                      """,
+        TestHelpers.AssertDriverContentsWithFrameAre ("││",
                                                       _output
                                                      );
     }