Răsfoiți Sursa

almost ready

Tig 9 luni în urmă
părinte
comite
ed48864a16

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

@@ -227,7 +227,7 @@ public static partial class Application // Mouse handling
         {
             if (deepestViewUnderMouse is Adornment adornmentView)
             {
-                deepestViewUnderMouse = adornmentView.Parent!.SuperView;
+                deepestViewUnderMouse = adornmentView.Parent?.SuperView;
             }
             else
             {

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

@@ -137,7 +137,23 @@ 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)
+    {
+        View? parentOrSuperView = Parent;
+
+        if (parentOrSuperView is null)
+        {
+            // 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 Point.Empty;
+            }
+        }
+        return parentOrSuperView.ScreenToFrame (new (location.X - Frame.X, location.Y - Frame.Y));
+    }
 
     /// <summary>Does nothing for Adornment</summary>
     /// <returns></returns>
@@ -155,9 +171,15 @@ public class Adornment : View, IDesignable
         return true;
     }
 
+    /// <inheritdoc />
+    protected override bool OnDrawSubviews (Rectangle viewport)
+    {
+        return false;
+    }
+
     /// <summary>Does nothing for Adornment</summary>
     /// <returns></returns>
-    protected override bool OnRenderLineCanvas () { return false; }
+    protected override bool OnRenderLineCanvas () { return true; }
 
     /// <summary>
     ///     Adornments only render to their <see cref="Parent"/>'s or Parent's SuperView's LineCanvas, so setting this
@@ -169,6 +191,14 @@ public class Adornment : View, IDesignable
         set => throw new InvalidOperationException (@"Adornment can only render to their Parent or Parent's Superview.");
     }
 
+    /// <inheritdoc />
+    protected override bool OnDrawComplete (Rectangle viewport)
+    {
+
+
+        return false;
+    }
+
     /// <summary>
     ///     Indicates whether the specified Parent's SuperView-relative coordinates are within the Adornment's Thickness.
     /// </summary>

+ 4 - 3
Terminal.Gui/View/Adornment/Border.cs

@@ -56,6 +56,7 @@ public class Border : Adornment
     public Border (View parent) : base (parent)
     {
         Parent = parent;
+        Id = $"{Parent.Id}.{Border}";
         CanFocus = false;
         TabStop = TabBehavior.TabGroup;
 
@@ -683,13 +684,13 @@ public class Border : Adornment
             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 = GetFocusColor ();
             }
 
             Parent.TitleTextFormatter.Draw (
                                             new (borderBounds.X + 2, titleY, maxTitleWidth, 1),
-                                            Parent.HasFocus ? focus : Parent.GetNormalColor (),
-                                            Parent.HasFocus ? focus : Parent.GetHotNormalColor ());
+                                            Parent.HasFocus ? focus : GetNormalColor (),
+                                            Parent.HasFocus ? focus : GetHotNormalColor ());
         }
 
         if (canDrawBorder && LineStyle != LineStyle.None)

+ 45 - 0
Terminal.Gui/View/View.Drawing.cs

@@ -1,4 +1,7 @@
 #nullable enable
+using System.Diagnostics;
+using System.Reflection.Metadata;
+
 namespace Terminal.Gui;
 
 public partial class View // Drawing APIs
@@ -233,12 +236,42 @@ public partial class View // Drawing APIs
 
         DoRenderLineCanvas ();
 
+        DoDrawAdornmentSubViews ();
+
         ClearNeedsDisplay ();
 
         // We're done
         DoDrawComplete (Viewport);
     }
 
+    private void DoDrawAdornmentSubViews ()
+    {
+        if (Margin?.Subviews is { })
+        {
+            foreach (View subview in Margin.Subviews)
+            {
+                subview.SetNeedsDisplay ();
+            }
+            Margin?.DoDrawSubviews (Margin.Viewport);
+        }
+        if (Border?.Subviews is { })
+        {
+            foreach (View subview in Border.Subviews)
+            {
+                subview.SetNeedsDisplay ();
+            }
+            Border?.DoDrawSubviews (Border.Viewport);
+        }
+        if (Padding?.Subviews is { })
+        {
+            foreach (View subview in Padding.Subviews)
+            {
+                subview.SetNeedsDisplay ();
+            }
+            Padding?.DoDrawSubviews (Padding.Viewport);
+        }
+    }
+
     internal void DoDrawAdornments ()
     {
         if (OnDrawAdornments ())
@@ -270,6 +303,8 @@ public partial class View // Drawing APIs
 
     private void DoClearViewport (Rectangle viewport)
     {
+        Debug.Assert (viewport == Viewport);
+
         if (OnClearViewport (Viewport))
         {
             return;
@@ -303,6 +338,9 @@ public partial class View // Drawing APIs
 
     private void DoDrawText (Rectangle viewport)
     {
+        Debug.Assert(viewport == Viewport);
+
+
         if (OnDrawText (Viewport))
         {
             return;
@@ -320,6 +358,10 @@ public partial class View // Drawing APIs
         // TODO: If the output is not in the Viewport, do nothing
         var drawRect = new Rectangle (ContentToScreen (Point.Empty), GetContentSize ());
 
+        if (Id == "ScrollingDemoView")
+        {
+
+        }
         TextFormatter?.Draw (
                              drawRect,
                              HasFocus ? GetFocusColor () : GetNormalColor (),
@@ -347,6 +389,7 @@ public partial class View // Drawing APIs
 
     private void DoDrawContent (Rectangle viewport)
     {
+        Debug.Assert (viewport == Viewport);
         if (OnDrawContent (Viewport))
         {
             return;
@@ -407,6 +450,7 @@ public partial class View // Drawing APIs
 
     internal void DoDrawSubviews (Rectangle viewport)
     {
+        Debug.Assert (viewport == Viewport);
         if (OnDrawSubviews (Viewport))
         {
             return;
@@ -469,6 +513,7 @@ public partial class View // Drawing APIs
 
     private void DoDrawComplete (Rectangle viewport)
     {
+        Debug.Assert (viewport == Viewport);
         if (OnDrawComplete (Viewport))
         {
             return;

+ 15 - 4
Terminal.Gui/View/View.Layout.cs

@@ -470,7 +470,7 @@ public partial class View // Layout APIs
         }
         catch (LayoutException le)
         {
-            Debug.WriteLine ($"A Dim/PosFunc function threw (typically this is because a dependent View was not laid out)\n{le}.");
+            //Debug.WriteLine ($"A Dim/PosFunc function threw (typically this is because a dependent View was not laid out)\n{le}.");
             return false;
         }
 
@@ -581,11 +581,22 @@ public partial class View // Layout APIs
         }
 
         bool layoutStillNeeded = false;
-        foreach (View v in redo)
+        //foreach (View v in redo)
+        //{
+        //    if (!v.Layout (contentSize))
+        //    {
+        //        layoutStillNeeded = true;
+        //    }
+        //}
+        if (redo.Count > 0)
         {
-            if (!v.Layout (contentSize))
+            foreach (View v in ordered)
             {
-                layoutStillNeeded = true;
+                if (!v.Layout (contentSize))
+                {
+                    layoutStillNeeded = true;
+                    Debug.Fail("ugh");
+                }
             }
         }
 

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

@@ -664,17 +664,20 @@ public partial class View // Mouse APIs
 
             Adornment? found = null;
 
-            if (start.Margin.Contains (currentLocation))
+            if (start is not Adornment)
             {
-                found = start.Margin;
-            }
-            else if (start.Border.Contains (currentLocation))
-            {
-                found = start.Border;
-            }
-            else if (start.Padding.Contains (currentLocation))
-            {
-                found = start.Padding;
+                if (start.Margin.Contains (currentLocation))
+                {
+                    found = start.Margin;
+                }
+                else if (start.Border.Contains (currentLocation))
+                {
+                    found = start.Border;
+                }
+                else if (start.Padding.Contains (currentLocation))
+                {
+                    found = start.Padding;
+                }
             }
 
             Point viewportOffset = start.GetViewportOffsetFromFrame ();

+ 5 - 0
Terminal.Gui/Views/HexView.cs

@@ -604,6 +604,11 @@ public class HexView : View, IDesignable
             return false;
         }
 
+        if (keyEvent.IsAlt)
+        {
+            return false;
+        }
+
         if (_leftSideHasFocus)
         {
             int value;

+ 18 - 5
Terminal.Gui/Views/ListView.cs

@@ -123,11 +123,24 @@ public class ListView : View, IDesignable
 
         // Things this view knows how to do
         // 
-        // BUGBUG: Should return false if selection doesn't change (to support nav to next view)
-        AddCommand (Command.Up, () => MoveUp ());
-        // BUGBUG: Should return false if selection doesn't change (to support nav to next view)
-        AddCommand (Command.Down, () => MoveDown ());
+        AddCommand (Command.Up, (ctx) =>
+                                {
+                                    if (RaiseSelecting (ctx) == true)
+                                    {
+                                        return true;
+                                    }
+                                    return MoveUp ();
+                                });
+        AddCommand (Command.Down, (ctx) =>
+                                  {
+                                      if (RaiseSelecting (ctx) == true)
+                                      {
+                                          return true;
+                                      }
+                                      return MoveDown ();
+                                  });
 
+        // TODO: add RaiseSelecting to all of these
         AddCommand (Command.ScrollUp, () => ScrollVertical (-1));
         AddCommand (Command.ScrollDown, () => ScrollVertical (1));
         AddCommand (Command.PageUp, () => MovePageUp ());
@@ -342,7 +355,7 @@ public class ListView : View, IDesignable
             SetContentSize (new Size (_source?.Length ?? Viewport.Width, _source?.Count ?? Viewport.Width));
             if (IsInitialized)
             {
-                Viewport = Viewport with { Y = 0 };
+               // Viewport = Viewport with { Y = 0 };
             }
 
             KeystrokeNavigator.Collection = _source?.ToList ();

+ 12 - 2
Terminal.Gui/Views/NumericUpDown.cs

@@ -93,13 +93,18 @@ public class NumericUpDown<T> : View where T : notnull
 
         AddCommand (
                     Command.ScrollUp,
-                    () =>
+                    (ctx) =>
                     {
                         if (type == typeof (object))
                         {
                             return false;
                         }
 
+                        if (RaiseSelecting (ctx) is true)
+                        {
+                            return true;
+                        }
+
                         if (Value is { } && Increment is { })
                         {
                             Value = (dynamic)Value + (dynamic)Increment;
@@ -110,13 +115,18 @@ public class NumericUpDown<T> : View where T : notnull
 
         AddCommand (
                     Command.ScrollDown,
-                    () =>
+                    (ctx) =>
                     {
                         if (type == typeof (object))
                         {
                             return false;
                         }
 
+                        if (RaiseSelecting (ctx) is true)
+                        {
+                            return true;
+                        }
+
                         if (Value is { } && Increment is { })
                         {
                             Value = (dynamic)Value - (dynamic)Increment;

+ 51 - 12
UICatalog/Scenarios/AllViewsTester.cs

@@ -28,6 +28,7 @@ public class AllViewsTester : Scenario
 
     private FrameView _hostPane;
     private View _curView;
+    private EventLog _eventLog;
 
     public override void Main ()
     {
@@ -76,6 +77,12 @@ public class AllViewsTester : Scenario
                                                   }
                                               };
 
+        _classListView.Accepting += (sender, args) =>
+                                    {
+                                        _curView.SetFocus ();
+                                        args.Cancel = true;
+                                    };
+
         _adornmentsEditor = new ()
         {
             Title = "Adornments [_2]",
@@ -85,7 +92,7 @@ public class AllViewsTester : Scenario
             Height = Dim.Fill (),
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
             BorderStyle = LineStyle.Single,
-            AutoSelectViewToEdit = true,
+            AutoSelectViewToEdit = false,
             AutoSelectAdornments = false,
         };
 
@@ -101,7 +108,7 @@ public class AllViewsTester : Scenario
             Title = "Layout [_3]",
             X = Pos.Right (_adornmentsEditor),
             Y = 0,
-            Width = Dim.Fill (),
+            //Width = Dim.Fill (), // set below
             Height = Dim.Auto (),
             CanFocus = true,
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
@@ -113,7 +120,7 @@ public class AllViewsTester : Scenario
             Title = "Settings [_4]",
             X = Pos.Right (_adornmentsEditor),
             Y = Pos.Bottom (_layoutEditor),
-            Width = Dim.Fill (),
+            Width = Dim.Width(_layoutEditor),
             Height = Dim.Auto (),
             CanFocus = true,
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
@@ -162,30 +169,60 @@ public class AllViewsTester : Scenario
 
         _settingsPane.Add (label, _demoTextView);
 
+        _eventLog = new EventLog ()
+        {
+           // X = Pos.Right(_layoutEditor)
+        };
+        _eventLog.X = Pos.AnchorEnd ();
+        _eventLog.Y = 0;
+
+        _eventLog.Height = Dim.Height (_classListView);
+        //_eventLog.Width = 30;
+
+        _layoutEditor.Width = Dim.Fill (Dim.Func ((() =>
+                                                   {
+                                                       if (_eventLog.NeedsLayout)
+                                                       {
+                                                           // We have two choices:
+                                                           // 1) Call Layout explicitly
+                                                           // 2) Throw LayoutException so Layout tries again
+                                                           _eventLog.Layout ();
+                                                           // new LayoutException ("_eventLog");
+                                                       }
+                                                       return _eventLog.Frame.Width;
+                                                   })));
+
         _hostPane = new ()
         {
             X = Pos.Right (_adornmentsEditor),
             Y = Pos.Bottom (_settingsPane),
-            Width = Dim.Fill (),
-            Height = Dim.Fill (), // + 1 for status bar
+            Width = Dim.Width (_layoutEditor),
+            Height = Dim.Fill (),
             CanFocus = true,
-            TabStop = TabBehavior.TabGroup,
-            ColorScheme = Colors.ColorSchemes ["Base"]
+            TabStop = TabBehavior.TabStop,
+            ColorScheme = Colors.ColorSchemes ["Base"],
+            Arrangement = ViewArrangement.Resizable,
+            BorderStyle = LineStyle.RoundedDotted
         };
+        _hostPane.Border.ColorScheme = app.ColorScheme;
         _hostPane.Padding.Thickness = new (1);
         _hostPane.Padding.Diagnostics = ViewDiagnosticFlags.Ruler;
-        _hostPane.Padding.ColorScheme = Colors.ColorSchemes ["Error"];
+        _hostPane.Padding.ColorScheme = app.ColorScheme;
 
-        app.Add (_classListView, _adornmentsEditor, _layoutEditor, _settingsPane, _hostPane);
+        app.Add (_classListView, _adornmentsEditor, _layoutEditor, _settingsPane, _eventLog, _hostPane);
 
-        _classListView.SelectedItem = 0;
-        _classListView.SetFocus ();
+        app.Initialized += App_Initialized;
 
         Application.Run (app);
         app.Dispose ();
         Application.Shutdown ();
     }
 
+    private void App_Initialized (object sender, EventArgs e)
+    {
+        _classListView.SelectedItem = 0;
+        _classListView.SetFocus ();
+    }
 
     // TODO: Add Command.HotKey handler (pop a message box?)
     private void CreateCurrentView (Type type)
@@ -235,6 +272,8 @@ public class AllViewsTester : Scenario
 
         view.Id = "_curView";
         _curView = view;
+
+        _eventLog.ViewToLog = _curView;
         _hostPane.Add (_curView);
         _layoutEditor.ViewToEdit = _curView;
         _curView.SetNeedsLayout ();
@@ -277,7 +316,7 @@ public class AllViewsTester : Scenario
         UpdateHostTitle (_curView);
     }
 
-    private void UpdateHostTitle (View view) { _hostPane.Title = $"Demo of {view.GetType ().Name} [_0]"; }
+    private void UpdateHostTitle (View view) { _hostPane.Title = $"{view.GetType ().Name} [_0]"; }
 
     private void CurrentView_Initialized (object sender, EventArgs e)
     {

+ 1 - 1
UICatalog/Scenarios/CharacterMap.cs

@@ -81,7 +81,7 @@ public class CharacterMap : Scenario
 
         _categoryList = new () { X = Pos.Right (_charMap), Y = Pos.Bottom (jumpLabel), Height = Dim.Fill () };
         _categoryList.FullRowSelect = true;
-
+        _categoryList.MultiSelect = false;
         //jumpList.Style.ShowHeaders = false;
         //jumpList.Style.ShowHorizontalHeaderOverline = false;
         //jumpList.Style.ShowHorizontalHeaderUnderline = false;

+ 2 - 1
UICatalog/Scenarios/ContentScrolling.cs

@@ -17,6 +17,7 @@ public class ContentScrolling : Scenario
     {
         public ScrollingDemoView ()
         {
+            Id = "ScrollingDemoView";
             Width = Dim.Fill ();
             Height = Dim.Fill ();
             ColorScheme = Colors.ColorSchemes ["Base"];
@@ -163,7 +164,7 @@ public class ContentScrolling : Scenario
             Title = "Allow _Y < 0",
             X = Pos.Right (cbAllowNegativeX) + 1,
             Y = Pos.Bottom (frameLabel),
-            CanFocus = true
+            CanFocus = true,
         };
         cbAllowNegativeY.CheckedState = view.ViewportSettings.HasFlag (ViewportSettings.AllowNegativeY) ? CheckState.Checked : CheckState.UnChecked;
         cbAllowNegativeY.CheckedStateChanging += AllowNegativeY_Toggle;

+ 2 - 1
UICatalog/Scenarios/Editors/DimEditor.cs

@@ -29,7 +29,8 @@ public class DimEditor : View
         };
 
 
-        TabStop = TabBehavior.TabGroup;
+        TabStop = TabBehavior.TabStop;
+
 
         Initialized += DimEditor_Initialized;
 

+ 119 - 0
UICatalog/Scenarios/Editors/EventLog.cs

@@ -0,0 +1,119 @@
+#nullable enable
+using System;
+using System.Collections.ObjectModel;
+using System.Diagnostics.Tracing;
+using System.Text;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+/// <summary>
+///     An event log that automatically shows the events that are raised.
+/// </summary>
+/// <remarks>
+/// </remarks>
+/// </example>
+public class EventLog : ListView
+{
+    public EventLog ()
+    {
+        Title = "Event Log";
+        CanFocus = false;
+
+        X = Pos.AnchorEnd ();
+        Y = 0;
+        Width = Dim.Func (() => Math.Min (SuperView!.Viewport.Width / 2, MaxLength + GetAdornmentsThickness ().Horizontal));
+        Height = Dim.Fill ();
+
+        ExpandButton = new ()
+        {
+            Orientation = Orientation.Horizontal
+        };
+
+        Initialized += EventLog_Initialized;
+
+        BorderStyle = LineStyle.Dotted;
+    }
+    public ExpanderButton? ExpandButton { get; }
+
+    private readonly ObservableCollection<string> _eventSource = [];
+
+    private View? _viewToLog;
+
+    public View? ViewToLog
+    {
+        get => _viewToLog;
+        set
+        {
+            if (_viewToLog == value)
+            {
+                return;
+            }
+
+            _viewToLog = value;
+
+            if (_viewToLog is { })
+            {
+                _viewToLog.Initialized += (s, args) =>
+                                             {
+                                                 View? sender = s as View;
+                                                 _eventSource.Add ($"Initialized: {GetIdentifyingString (sender)}");
+                                                 MoveEnd ();
+                                             };
+
+                _viewToLog.MouseClick += (s, args) =>
+                {
+                    View? sender = s as View;
+                    _eventSource.Add ($"MouseClick: {args}");
+                    MoveEnd ();
+                };
+
+                _viewToLog.HandlingHotKey += (s, args) =>
+                                        {
+                                            View? sender = s as View;
+                                            _eventSource.Add ($"HandlingHotKey: {args.Context.Command} {args.Context.Data}");
+                                            MoveEnd ();
+                                        };
+                _viewToLog.Selecting += (s, args) =>
+                                        {
+                                            View? sender = s as View;
+                                            _eventSource.Add ($"Selecting: {args.Context.Command} {args.Context.Data}");
+                                            MoveEnd ();
+                                        };
+                _viewToLog.Accepting += (s, args) =>
+                                        {
+                                            View? sender = s as View;
+                                            _eventSource.Add ($"Accepting: {args.Context.Command} {args.Context.Data}");
+                                            MoveEnd ();
+                                        };
+            }
+        }
+    }
+
+    private void EventLog_Initialized (object? _, EventArgs e)
+    {
+
+        Border.Add (ExpandButton!);
+        Source = new ListWrapper<string> (_eventSource);
+
+    }
+    private string GetIdentifyingString (View? view)
+    {
+        if (view is null)
+        {
+            return "null";
+        }
+
+        if (!string.IsNullOrEmpty (view.Title))
+        {
+            return view.Title;
+        }
+
+        if (!string.IsNullOrEmpty (view.Text))
+        {
+            return view.Text;
+        }
+
+        return view.GetType ().Name;
+    }
+}

+ 11 - 9
UICatalog/Scenarios/Editors/ExpanderButton.cs

@@ -44,6 +44,8 @@ public class ExpanderButton : Button
 
         Orientation = Orientation.Vertical;
 
+        HighlightStyle = HighlightStyle.None;
+
         Initialized += ExpanderButton_Initialized;
     }
 
@@ -87,15 +89,15 @@ public class ExpanderButton : Button
             {
                 X = Pos.AnchorEnd ();
                 Y = 0;
-                CollapsedGlyph = new ('\u21d1'); // ⇑
-                ExpandedGlyph = new ('\u21d3'); // ⇓
+                CollapseGlyph = new ('\u21d1'); // ⇑
+                ExpandGlyph = new ('\u21d3'); // ⇓
             }
             else
             {
                 X = 0;
                 Y = Pos.AnchorEnd ();
-                CollapsedGlyph = new ('\u21d0'); // ⇐
-                ExpandedGlyph = new ('\u21d2'); // ⇒
+                CollapseGlyph = new ('\u21d0'); // ⇐
+                ExpandGlyph = new ('\u21d2'); // ⇒
             }
 
             ExpandOrCollapse (Collapsed);
@@ -110,14 +112,14 @@ public class ExpanderButton : Button
     public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
 
     /// <summary>
-    ///     The glyph to display when the view is collapsed.
+    ///     The glyph that indicates the button will collapse the view.
     /// </summary>
-    public Rune CollapsedGlyph { get; set; }
+    public Rune CollapseGlyph { get; set; }
 
     /// <summary>
-    ///     The glyph to display when the view is expanded.
+    ///     The glyph that indicates the button will expand the view.
     /// </summary>
-    public Rune ExpandedGlyph { get; set; }
+    public Rune ExpandGlyph { get; set; }
 
     private bool _collapsed;
 
@@ -168,7 +170,7 @@ public class ExpanderButton : Button
 
     private void ExpandOrCollapse (bool collapse)
     {
-        Text = $"{(Collapsed ? CollapsedGlyph : ExpandedGlyph)}";
+        Text = $"{(Collapsed ? ExpandGlyph : CollapseGlyph)}";
 
         View? superView = SuperView;
 

+ 1 - 1
UICatalog/Scenarios/Editors/PosEditor.cs

@@ -29,7 +29,7 @@ public class PosEditor : View
         };
 
 
-        TabStop = TabBehavior.TabGroup;
+        TabStop = TabBehavior.TabStop;
 
         Initialized += PosEditor_Initialized;
 

+ 14 - 2
UICatalog/Scenarios/Generic.cs

@@ -14,12 +14,24 @@ public sealed class MyScenario : Scenario
         // Setup - Create a top-level application window and configure it.
         Window appWindow = new ()
         {
+            Id = "appWindow",
             Title = GetQuitKeyAndName (),
+            Arrangement = ViewArrangement.Fixed
         };
 
-        var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "_Press me!" };
+        var button = new Button
+        {
+            Id = "button",
+            X = Pos.AnchorEnd(), Y = 0, Text = "_Press me!"
+        };
         button.Accepting += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "_Ok");
-        appWindow.Add (button);
+
+        button.HighlightStyle = HighlightStyle.None;
+        button.ShadowStyle = ShadowStyle.None;
+
+        appWindow.Border.Add (button);
+
+        //appWindow.Border.LineStyle = LineStyle.None;
 
         // Run - Start the application.
         Application.Run (appWindow);

+ 0 - 1
UICatalog/UICatalog.cs

@@ -387,7 +387,6 @@ public class UICatalogApp
         // 'app' closed cleanly.
         foreach (Responder? inst in Responder.Instances)
         {
-
             Debug.Assert (inst.WasDisposed);
         }
 

+ 11 - 11
UnitTests/View/Layout/Dim.AutoTests.cs

@@ -362,15 +362,15 @@ public partial class DimAutoTests (ITestOutputHelper output)
         subView.Height = 10;
 
         subView.Height = Percent (50);
-        Assert.Throws<InvalidOperationException> (() => superView.SetRelativeLayout (new (0, 0)));
+        Assert.Throws<LayoutException> (() => superView.SetRelativeLayout (new (0, 0)));
         subView.Height = 10;
 
         subView.X = Pos.Center ();
-        Assert.Throws<InvalidOperationException> (() => superView.SetRelativeLayout (new (0, 0)));
+        Assert.Throws<LayoutException> (() => superView.SetRelativeLayout (new (0, 0)));
         subView.X = 0;
 
         subView.Y = Pos.Center ();
-        Assert.Throws<InvalidOperationException> (() => superView.SetRelativeLayout (new (0, 0)));
+        Assert.Throws<LayoutException> (() => superView.SetRelativeLayout (new (0, 0)));
         subView.Y = 0;
 
         subView.Width = 10;
@@ -429,12 +429,12 @@ public partial class DimAutoTests (ITestOutputHelper output)
         subView.Height = 0;
 
         subView.Height = 3 + 5 + Percent (10);
-        Assert.Throws<InvalidOperationException> (() => superView.SetRelativeLayout (new (0, 0)));
+        Assert.Throws<LayoutException> (() => superView.SetRelativeLayout (new (0, 0)));
         subView.Height = 0;
 
         // Tests nested Combine
         subView.Height = 5 + new DimCombine (AddOrSubtract.Add, 3, new DimCombine (AddOrSubtract.Add, Percent (10), 9));
-        Assert.Throws<InvalidOperationException> (() => superView.SetRelativeLayout (new (0, 0)));
+        Assert.Throws<LayoutException> (() => superView.SetRelativeLayout (new (0, 0)));
     }
 
     [Fact]
@@ -483,28 +483,28 @@ public partial class DimAutoTests (ITestOutputHelper output)
         superView.SetRelativeLayout (new (0, 0)); // no throw
 
         subView.X = Pos.Center () + 3;
-        Assert.Throws<InvalidOperationException> (() => superView.SetRelativeLayout (new (0, 0)));
+        Assert.Throws<LayoutException> (() => superView.SetRelativeLayout (new (0, 0)));
         subView.X = 0;
 
         subView.X = 3 + Pos.Center ();
-        Assert.Throws<InvalidOperationException> (() => superView.SetRelativeLayout (new (0, 0)));
+        Assert.Throws<LayoutException> (() => superView.SetRelativeLayout (new (0, 0)));
         subView.X = 0;
 
         subView.X = 3 + 5 + Pos.Center ();
-        Assert.Throws<InvalidOperationException> (() => superView.SetRelativeLayout (new (0, 0)));
+        Assert.Throws<LayoutException> (() => superView.SetRelativeLayout (new (0, 0)));
         subView.X = 0;
 
         subView.X = 3 + 5 + Pos.Percent (10);
-        Assert.Throws<InvalidOperationException> (() => superView.SetRelativeLayout (new (0, 0)));
+        Assert.Throws<LayoutException> (() => superView.SetRelativeLayout (new (0, 0)));
         subView.X = 0;
 
         subView.X = Pos.Percent (10) + Pos.Center ();
-        Assert.Throws<InvalidOperationException> (() => superView.SetRelativeLayout (new (0, 0)));
+        Assert.Throws<LayoutException> (() => superView.SetRelativeLayout (new (0, 0)));
         subView.X = 0;
 
         // Tests nested Combine
         subView.X = 5 + new PosCombine (AddOrSubtract.Add, Pos.Right (subView2), new PosCombine (AddOrSubtract.Add, Pos.Center (), 9));
-        Assert.Throws<InvalidOperationException> (() => superView.SetRelativeLayout (new (0, 0)));
+        Assert.Throws<LayoutException> (() => superView.SetRelativeLayout (new (0, 0)));
         subView.X = 0;
     }
 

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

@@ -54,7 +54,7 @@ public class PosCombineTests (ITestOutputHelper output)
         f.X = Pos.X (v2) - Pos.X (v1);
         f.Y = Pos.Y (v2) - Pos.Y (v1);
 
-        Assert.Throws<InvalidOperationException> (() => Application.Run (t));
+        Assert.Throws<LayoutException> (() => Application.Run (t));
         t.Dispose ();
         Application.Shutdown ();
 
@@ -136,7 +136,7 @@ public class PosCombineTests (ITestOutputHelper output)
 
         Application.Iteration += (s, a) => Application.RequestStop ();
 
-        Assert.Throws<InvalidOperationException> (() => Application.Run ());
+        Assert.Throws<LayoutException> (() => Application.Run ());
         top.Dispose ();
         Application.ResetState (ignoreDisposed: true);
     }

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

@@ -90,7 +90,7 @@ public class PosTests ()
         f.X = Pos.X (v2) - Pos.X (v1);
         f.Y = Pos.Y (v2) - Pos.Y (v1);
 
-        Assert.Throws<InvalidOperationException> (() => Application.Run (t));
+        Assert.Throws<LayoutException> (() => Application.Run (t));
         t.Dispose ();
         Application.Shutdown ();
 

+ 1 - 1
UnitTests/View/Layout/SetLayoutTests.cs

@@ -169,7 +169,7 @@ public class SetLayoutTests (ITestOutputHelper output)
         var sub = new View ();
         super.Add (sub);
         super.Width = Dim.Width (sub);
-        Assert.Throws<InvalidOperationException> (() => root.LayoutSubviews ());
+        Assert.Throws<LayoutException> (() => root.Layout ());
         root.Dispose ();
         super.Dispose ();
     }

+ 4 - 4
UnitTests/View/Layout/TopologicalSortTests.cs

@@ -15,11 +15,11 @@ public class TopologicalSortTests (ITestOutputHelper output)
         var sub2 = new View ();
         sub1.Width = Dim.Width (sub2);
 
-        Assert.Throws<InvalidOperationException> (() => root.LayoutSubviews ());
+        Assert.Throws<LayoutException> (() => root.LayoutSubviews ());
 
         sub2.Width = Dim.Width (sub1);
 
-        Assert.Throws<InvalidOperationException> (() => root.LayoutSubviews ());
+        Assert.Throws<LayoutException> (() => root.LayoutSubviews ());
         root.Dispose ();
         sub1.Dispose ();
         sub2.Dispose ();
@@ -54,7 +54,7 @@ public class TopologicalSortTests (ITestOutputHelper output)
         superView.Y = Pos.Top (subView);
         superView.Add (subView);
 
-        Assert.Throws<InvalidOperationException> (() => top.LayoutSubviews ());
+        Assert.Throws<LayoutException> (() => top.LayoutSubviews ());
         superView.Dispose ();
     }
 
@@ -77,6 +77,6 @@ public class TopologicalSortTests (ITestOutputHelper output)
         sub.Width = Dim.Fill () - Dim.Width (v2);
         sub.Height = Dim.Fill () - Dim.Height (v2);
 
-        Assert.Throws<InvalidOperationException> (() => top.Layout ());
+        Assert.Throws<LayoutException> (() => top.Layout ());
     }
 }

+ 17 - 17
UnitTests/Views/TabViewTests.cs

@@ -103,7 +103,7 @@ public class TabViewTests (ITestOutputHelper output)
         Application.Shutdown ();
     }
 
-    [Fact (Skip = "#2491 - A good test for Tab nav, but currently broken. TabView has exposes some interesting edge cases.")]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [AutoInitShutdown]
     public void MouseClick_ChangesTab ()
     {
@@ -188,7 +188,7 @@ public class TabViewTests (ITestOutputHelper output)
         top.Dispose ();
     }
 
-    [Fact (Skip = "#2491 - A good test for Tab nav, but currently broken. TabView has exposes some interesting edge cases.")]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [AutoInitShutdown]
     public void MouseClick_Right_Left_Arrows_ChangesTab ()
     {
@@ -274,7 +274,7 @@ public class TabViewTests (ITestOutputHelper output)
         top.Dispose ();
     }
 
-    [Fact (Skip = "#2491 - A good test for Tab nav, but currently broken. TabView has exposes some interesting edge cases.")]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [AutoInitShutdown]
     public void MouseClick_Right_Left_Arrows_ChangesTab_With_Border ()
     {
@@ -369,7 +369,7 @@ public class TabViewTests (ITestOutputHelper output)
         top.Dispose ();
     }
 
-    [Fact (Skip="#2491 - A good test for Tab nav, but currently broken. TabView has exposes some interesting edge cases.")]
+    [Fact (Skip="#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [AutoInitShutdown]
     public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp ()
     {
@@ -595,7 +595,7 @@ public class TabViewTests (ITestOutputHelper output)
         Application.Shutdown ();
     }
 
-    [Fact]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [SetupFakeDriver]
     public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width3 ()
     {
@@ -604,7 +604,7 @@ public class TabViewTests (ITestOutputHelper output)
         tv.Height = 5;
         tv.Style = new () { ShowTopLine = false };
         tv.ApplyStyleChanges ();
-        tv.LayoutSubviews ();
+        tv.Layout ();
 
         tv.Draw ();
 
@@ -643,7 +643,7 @@ public class TabViewTests (ITestOutputHelper output)
                                                      );
     }
 
-    [Fact (Skip = "#2491 - A good test for Tab nav, but currently broken. TabView has exposes some interesting edge cases.")]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [SetupFakeDriver]
     public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames ()
     {
@@ -735,7 +735,7 @@ public class TabViewTests (ITestOutputHelper output)
                                                      );
     }
 
-    [Fact]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [SetupFakeDriver]
     public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width3 ()
     {
@@ -783,7 +783,7 @@ public class TabViewTests (ITestOutputHelper output)
                                                      );
     }
 
-    [Fact (Skip = "#2491 - A good test for Tab nav, but currently broken. TabView has exposes some interesting edge cases.")]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [SetupFakeDriver]
     public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames ()
     {
@@ -875,14 +875,14 @@ public class TabViewTests (ITestOutputHelper output)
                                                      );
     }
 
-    [Fact]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [SetupFakeDriver]
     public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width3 ()
     {
         TabView tv = GetTabView (out _, out _, false);
         tv.Width = 3;
         tv.Height = 5;
-        tv.LayoutSubviews ();
+        tv.Layout ();
 
         tv.Draw ();
 
@@ -904,7 +904,7 @@ public class TabViewTests (ITestOutputHelper output)
         TabView tv = GetTabView (out _, out _, false);
         tv.Width = 4;
         tv.Height = 5;
-        tv.LayoutSubviews ();
+        tv.Layout ();
 
         tv.Draw ();
 
@@ -919,7 +919,7 @@ public class TabViewTests (ITestOutputHelper output)
                                                      );
     }
 
-    [Fact (Skip = "#2491 - A good test for Tab nav, but currently broken. TabView has exposes some interesting edge cases.")]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [SetupFakeDriver]
     public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames ()
     {
@@ -1009,7 +1009,7 @@ public class TabViewTests (ITestOutputHelper output)
                                                      );
     }
 
-    [Fact (Skip = "#2491 - A good test for Tab nav, but currently broken. TabView has exposes some interesting edge cases.")]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [SetupFakeDriver]
     public void ShowTopLine_True_TabsOnBottom_False_With_Unicode ()
     {
@@ -1050,7 +1050,7 @@ public class TabViewTests (ITestOutputHelper output)
                                                      );
     }
 
-    [Fact]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [SetupFakeDriver]
     public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width3 ()
     {
@@ -1098,7 +1098,7 @@ public class TabViewTests (ITestOutputHelper output)
                                                      );
     }
 
-    [Fact (Skip = "#2491 - A good test for Tab nav, but currently broken. TabView has exposes some interesting edge cases.")]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [SetupFakeDriver]
     public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames ()
     {
@@ -1174,7 +1174,7 @@ public class TabViewTests (ITestOutputHelper output)
                                                      );
     }
 
-    [Fact (Skip = "#2491 - A good test for Tab nav, but currently broken. TabView has exposes some interesting edge cases.")]
+    [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")]
     [SetupFakeDriver]
     public void ShowTopLine_True_TabsOnBottom_True_With_Unicode ()
     {