Browse Source

More prototyping

Tig 9 months ago
parent
commit
70bf627d62

+ 36 - 12
Terminal.Gui/Application/Application.Run.cs

@@ -94,13 +94,6 @@ public static partial class Application // Run (Begin, Run, End, Stop)
 
         var rs = new RunState (toplevel);
 
-        // View implements ISupportInitializeNotification which is derived from ISupportInitialize
-        if (!toplevel.IsInitialized)
-        {
-            toplevel.BeginInit ();
-            toplevel.EndInit ();
-        }
-
 #if DEBUG_IDISPOSABLE
         if (Top is { } && toplevel != Top && !TopLevels.Contains (Top))
         {
@@ -184,8 +177,19 @@ public static partial class Application // Run (Begin, Run, End, Stop)
             }
         }
 
-        toplevel.SetRelativeLayout (Driver!.Screen.Size);
-        toplevel.LayoutSubviews ();
+        // View implements ISupportInitializeNotification which is derived from ISupportInitialize
+        if (!toplevel.IsInitialized)
+        {
+            toplevel.BeginInit ();
+            toplevel.EndInit ();
+
+            if (toplevel.SetRelativeLayout (Driver!.Screen.Size))
+            {
+                toplevel.LayoutSubviews ();
+            }
+
+            // toplevel.SetLayoutNeeded();
+        }
 
         // Try to set initial focus to any TabStop
         if (!toplevel.HasFocus)
@@ -195,8 +199,6 @@ public static partial class Application // Run (Begin, Run, End, Stop)
 
         toplevel.OnLoaded ();
 
-        Refresh ();
-
         if (PositionCursor ())
         {
             Driver.UpdateCursor ();
@@ -489,9 +491,31 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     /// <summary>Triggers a refresh of the entire display.</summary>
     public static void Refresh ()
     {
+        bool clear = false;
+        foreach (Toplevel tl in TopLevels.Reverse ())
+        {
+            if (tl.IsLayoutNeeded ())
+            {
+                clear = true;
+
+                if (tl.SetRelativeLayout (Screen.Size))
+                {
+                    tl.LayoutSubviews ();
+                }
+            }
+        }
+
+        if (clear)
+        {
+            Driver!.ClearContents ();
+        }
+
         foreach (Toplevel tl in TopLevels.Reverse ())
         {
-            tl.LayoutSubviews ();
+            if (clear)
+            {
+                tl.SetNeedsDisplay();
+            }
             tl.Draw ();
         }
 

+ 4 - 2
Terminal.Gui/Application/Application.Screen.cs

@@ -35,8 +35,10 @@ public static partial class Application // Screen related stuff
 
         foreach (Toplevel t in TopLevels)
         {
-            t.SetRelativeLayout (args.Size.Value);
-            t.LayoutSubviews ();
+            if (t.SetRelativeLayout (args.Size.Value))
+            {
+                t.LayoutSubviews ();
+            }
             t.OnSizeChanging (new (args.Size));
         }
 

+ 2 - 1
Terminal.Gui/View/Adornment/Border.cs

@@ -565,7 +565,8 @@ public class Border : Adornment
 
                         break;
                 }
-                Application.Refresh ();
+
+                //Application.Refresh ();
 
                 return true;
             }

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

@@ -182,9 +182,9 @@ public abstract record Dim : IEqualityOperators<Dim, Dim, bool>
     /// </summary>
     /// <param name="dim">A reference to this <see cref="Dim"/> instance.</param>
     /// <returns></returns>
-    public bool Has<T> (out Dim dim) where T : Dim
+    public bool Has<T> (out T dim) where T : Dim
     {
-        dim = this;
+        dim = (this as T)!;
 
         return this switch
                {

+ 39 - 5
Terminal.Gui/View/Layout/DimAuto.cs

@@ -168,6 +168,7 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
                     {
                         int width = v.Width!.Calculate (0, superviewContentSize, v, dimension);
                         size = v.X.GetAnchor (0) + width;
+
                     }
                     else
                     {
@@ -414,11 +415,6 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
 
                 List<View> dimAutoSubViews;
 
-                if (dimension == Dimension.Width && us.GetType ().Name == "Bar" && us.Subviews.Count == 3)
-                {
-
-                }
-
                 if (dimension == Dimension.Width)
                 {
                     dimAutoSubViews = includedSubviews.Where (v => v.Width is { } && v.Width.Has<DimAuto> (out _)).ToList ();
@@ -450,6 +446,44 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
                 }
 
                 #endregion
+
+
+                #region DimFill
+                //// [ ] DimFill      - Dimension is internally calculated
+
+                //List<View> DimFillSubViews;
+
+                //if (dimension == Dimension.Width)
+                //{
+                //    DimFillSubViews = includedSubviews.Where (v => v.Width is { } && v.Width.Has<DimFill> (out _)).ToList ();
+                //}
+                //else
+                //{
+                //    DimFillSubViews = includedSubviews.Where (v => v.Height is { } && v.Height.Has<DimFill> (out _)).ToList ();
+                //}
+
+                //for (var i = 0; i < DimFillSubViews.Count; i++)
+                //{
+                //    View v = DimFillSubViews [i];
+
+                //    if (dimension == Dimension.Width)
+                //    {
+                //        v.SetRelativeLayout (new (maxCalculatedSize, 0));
+                //    }
+                //    else
+                //    {
+                //        v.SetRelativeLayout (new (0, maxCalculatedSize));
+                //    }
+
+                //    int maxDimFill = dimension == Dimension.Width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height;
+
+                //    if (maxDimFill > maxCalculatedSize)
+                //    {
+                //        maxCalculatedSize = maxDimFill;
+                //    }
+                //}
+
+                #endregion
             }
         }
 

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

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

+ 23 - 5
Terminal.Gui/View/Layout/PosView.cs

@@ -1,4 +1,6 @@
 #nullable enable
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 
 /// <summary>
@@ -10,19 +12,35 @@ namespace Terminal.Gui;
 ///         methods on the <see cref="Pos"/> class to create <see cref="Pos"/> objects instead.
 ///     </para>
 /// </remarks>
-/// <param name="View">The View the position is anchored to.</param>
-/// <param name="Side">The side of the View the position is anchored to.</param>
-public record PosView (View? View, Side Side) : Pos
+public record PosView : Pos
 {
+    /// <summary>
+    ///     Represents a position that is anchored to the side of another view.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This is a low-level API that is typically used internally by the layout system. Use the various static
+    ///         methods on the <see cref="Pos"/> class to create <see cref="Pos"/> objects instead.
+    ///     </para>
+    /// </remarks>
+    /// <param name="view">The View the position is anchored to.</param>
+    /// <param name="side">The side of the View the position is anchored to.</param>
+    public PosView (View view, Side side)
+    {
+        ArgumentNullException.ThrowIfNull (view);
+        Target = view;
+        Side = side;
+    }
+
     /// <summary>
     ///     Gets the View the position is anchored to.
     /// </summary>
-    public View? Target { get; } = View;
+    public View Target { get; }
 
     /// <summary>
     ///     Gets the side of the View the position is anchored to.
     /// </summary>
-    public Side Side { get; } = Side;
+    public Side Side { get; }
 
     /// <inheritdoc/>
     public override string ToString ()

+ 19 - 12
Terminal.Gui/View/View.Drawing.cs

@@ -218,19 +218,27 @@ public partial class View // Drawing APIs
             return;
         }
 
-        // TODO: This ensures overlapped views are drawn correctly. However, this is inefficient.
-        // TODO: The correct fix is to implement non-rectangular clip regions: https://github.com/gui-cs/Terminal.Gui/issues/3413
-        if (Arrangement.HasFlag (ViewArrangement.Overlapped))
+        if (IsLayoutNeeded ())
         {
-            SetNeedsDisplay ();
+            Debug.WriteLine ($"Layout should be de-coupled from drawing: {this}");
         }
 
-        if (!NeedsDisplay && !SubViewNeedsDisplay && !IsLayoutNeeded ())
+        //// TODO: This ensures overlapped views are drawn correctly. However, this is inefficient.
+        //// TODO: The correct fix is to implement non-rectangular clip regions: https://github.com/gui-cs/Terminal.Gui/issues/3413
+        //if ((this != Application.Top || this is Toplevel { Modal: true }) && Arrangement.HasFlag (ViewArrangement.Overlapped))
+        //{
+        //    SetNeedsDisplay ();
+        //}
+
+        if (!NeedsDisplay && !SubViewNeedsDisplay)
         {
             return;
         }
 
-        OnDrawAdornments ();
+        if (NeedsDisplay)
+        {
+            OnDrawAdornments ();
+        }
 
         if (ColorScheme is { })
         {
@@ -526,7 +534,7 @@ public partial class View // Drawing APIs
         }
 
         // BUGBUG: this clears way too frequently. Need to optimize this.
-        if (SuperView is { } || Arrangement.HasFlag (ViewArrangement.Overlapped))
+        if (NeedsDisplay/* || Arrangement.HasFlag (ViewArrangement.Overlapped)*/)
         {
             Clear ();
         }
@@ -558,23 +566,22 @@ public partial class View // Drawing APIs
                                                                      view => view.Visible
                                                                              && (view.NeedsDisplay
                                                                                  || view.SubViewNeedsDisplay
-                                                                                 || view.IsLayoutNeeded ()
-                                                                                 || view.Arrangement.HasFlag (ViewArrangement.Overlapped)
+                                                                                // || view.Arrangement.HasFlag (ViewArrangement.Overlapped)
                                                                                 ));
 
             foreach (View view in subviewsNeedingDraw)
             {
                 if (view.IsLayoutNeeded ())
                 {
-                    Debug.WriteLine ("Layout should be de-coupled from drawing");
-                    view.LayoutSubviews ();
+                    Debug.WriteLine ($"Layout should be de-coupled from drawing: {view}");
+                    //view.LayoutSubviews ();
                 }
 
                 // TODO: This ensures overlapped views are drawn correctly. However, this is inefficient.
                 // TODO: The correct fix is to implement non-rectangular clip regions: https://github.com/gui-cs/Terminal.Gui/issues/3413
                 if (view.Arrangement.HasFlag (ViewArrangement.Overlapped))
                 {
-                    view.SetNeedsDisplay ();
+                    // view.SetNeedsDisplay ();
                 }
 
                 view.Draw ();

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

@@ -85,8 +85,15 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
             view.EndInit ();
         }
 
-        CheckDimAuto ();
-        view.SetRelativeLayout(GetContentSize());
+
+        SetLayoutNeeded();
+        //CheckDimAuto ();
+       // view.SetRelativeLayout(Application.Screen.Size);
+
+        //if (view.Visible)
+        //{
+        //    SetNeedsDisplay();
+        //};
 
         return view;
     }

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

@@ -197,7 +197,14 @@ public partial class View // Layout APIs
     /// </remarks>
     public Rectangle Frame
     {
-        get => _frame;
+        get
+        {
+            if (_layoutNeeded)
+            {
+                //Debug.WriteLine("Frame_get with _layoutNeeded");
+            }
+            return _frame;
+        }
         set
         {
             if (_frame == value)
@@ -346,7 +353,7 @@ public partial class View // Layout APIs
             if (!IsInitialized)
             {
                 // Supports Unit Tests that don't call Begin/EndInit
-                SetRelativeLayout (GetBestGuessSuperViewContentSize ());
+                //SetRelativeLayout (GetBestGuessSuperViewContentSize ());
             }
 
             SetLayoutNeeded ();
@@ -395,7 +402,7 @@ public partial class View // Layout APIs
             if (!IsInitialized)
             {
                 // Supports Unit Tests that don't call Begin/EndInit
-                SetRelativeLayout (GetBestGuessSuperViewContentSize ());
+                //SetRelativeLayout (GetBestGuessSuperViewContentSize ());
             }
 
             SetLayoutNeeded ();
@@ -454,7 +461,7 @@ public partial class View // Layout APIs
             if (!IsInitialized)
             {
                 // Supports Unit Tests that don't call Begin/EndInit
-                SetRelativeLayout (GetBestGuessSuperViewContentSize ());
+                //SetRelativeLayout (GetBestGuessSuperViewContentSize ());
             }
 
             SetLayoutNeeded ();
@@ -513,7 +520,7 @@ public partial class View // Layout APIs
             if (!IsInitialized)
             {
                 // Supports Unit Tests that don't call Begin/EndInit
-                SetRelativeLayout (GetBestGuessSuperViewContentSize ());
+                //SetRelativeLayout (GetBestGuessSuperViewContentSize ());
             }
 
             SetLayoutNeeded ();
@@ -562,7 +569,8 @@ public partial class View // Layout APIs
     /// <param name="superviewContentSize">
     ///     The size of the SuperView's content (nominally the same as <c>this.SuperView.GetContentSize ()</c>).
     /// </param>
-    internal void SetRelativeLayout (Size superviewContentSize)
+    /// <returns><see langword="true"/> if successful. <see langword="false"/> means a dependent View still needs layout.</returns>
+    public bool SetRelativeLayout (Size superviewContentSize)
     {
         Debug.Assert (_x is { });
         Debug.Assert (_y is { });
@@ -571,31 +579,49 @@ public partial class View // Layout APIs
 
         CheckDimAuto ();
         SetTextFormatterSize ();
-
         int newX, newW, newY, newH;
 
-        // Calculate the new X, Y, Width, and Height
-        // If the Width or Height is Dim.Auto, calculate the Width or Height first. Otherwise, calculate the X or Y first.
-        if (_width is DimAuto)
-        {
-            newW = _width.Calculate (0, superviewContentSize.Width, this, Dimension.Width);
-            newX = _x.Calculate (superviewContentSize.Width, newW, this, Dimension.Width);
-        }
-        else
+        try
         {
-            newX = _x.Calculate (superviewContentSize.Width, _width, this, Dimension.Width);
-            newW = _width.Calculate (newX, superviewContentSize.Width, this, Dimension.Width);
-        }
+            // Calculate the new X, Y, Width, and Height
+            // If the Width or Height is Dim.Auto, calculate the Width or Height first. Otherwise, calculate the X or Y first.
+            if (_width.Has<DimAuto> (out _))
+            {
+                newW = _width.Calculate (0, superviewContentSize.Width, this, Dimension.Width);
+                newX = _x.Calculate (superviewContentSize.Width, newW, this, Dimension.Width);
+                if (newW != Frame.Width)
+                {
+                    // Pos.Calculate gave us a new position. We need to redo dimension
+                    newW = _width.Calculate (newX, superviewContentSize.Width, this, Dimension.Width);
+                }
+            }
+            else
+            {
+                newX = _x.Calculate (superviewContentSize.Width, _width, this, Dimension.Width);
+                newW = _width.Calculate (newX, superviewContentSize.Width, this, Dimension.Width);
+            }
+
+            if (_height.Has<DimAuto> (out _))
+            {
+                newH = _height.Calculate (0, superviewContentSize.Height, this, Dimension.Height);
+                newY = _y.Calculate (superviewContentSize.Height, newH, this, Dimension.Height);
+                if (newH != Frame.Height)
+                {
+                    // Pos.Calculate gave us a new position. We need to redo dimension
+                    newH = _height.Calculate (newY, superviewContentSize.Height, this, Dimension.Height);
+                }
+            }
+            else
+            {
+                newY = _y.Calculate (superviewContentSize.Height, _height, this, Dimension.Height);
+                newH = _height.Calculate (newY, superviewContentSize.Height, this, Dimension.Height);
+            }
 
-        if (_height is DimAuto)
-        {
-            newH = _height.Calculate (0, superviewContentSize.Height, this, Dimension.Height);
-            newY = _y.Calculate (superviewContentSize.Height, newH, this, Dimension.Height);
         }
-        else
+        catch (Exception e)
         {
-            newY = _y.Calculate (superviewContentSize.Height, _height, this, Dimension.Height);
-            newH = _height.Calculate (newY, superviewContentSize.Height, this, Dimension.Height);
+            // A Dim/PosFunc threw indicating it could not calculate (typically because a dependent View was not laid out).
+            return false;
         }
 
         Rectangle newFrame = new (newX, newY, newW, newH);
@@ -630,6 +656,9 @@ public partial class View // Layout APIs
             {
                 SetTitleTextFormatterSize ();
             }
+
+            SetNeedsDisplay ();
+            SuperView?.SetNeedsDisplay ();
         }
 
         if (TextFormatter.ConstrainToWidth is null)
@@ -641,6 +670,8 @@ public partial class View // Layout APIs
         {
             TextFormatter.ConstrainToHeight = GetContentSize ().Height;
         }
+
+        return true;
     }
 
     // TODO: remove virtual from this
@@ -686,9 +717,22 @@ public partial class View // Layout APIs
         CollectAll (this, ref nodes, ref edges);
         List<View> ordered = TopologicalSort (SuperView!, nodes, edges);
 
+        List<View> redo = new ();
         foreach (View v in ordered)
         {
-            LayoutSubview (v, contentSize);
+            if (!LayoutSubview (v, contentSize))
+            {
+                redo.Add (v);
+            }
+        }
+
+        bool layoutStillNeeded = false;
+        foreach (View v in redo)
+        {
+            if (!LayoutSubview (v, contentSize))
+            {
+                layoutStillNeeded = true;
+            }
         }
 
         // If the 'to' is rooted to 'from' it's a special-case.
@@ -697,20 +741,33 @@ public partial class View // Layout APIs
         {
             foreach ((View from, View to) in edges)
             {
+                Debug.Fail ("This is dead code?");
                 LayoutSubview (to, from.GetContentSize ());
             }
         }
 
-        _layoutNeeded = false;
+        _layoutNeeded = layoutStillNeeded;
 
         OnLayoutComplete (new (contentSize));
     }
 
-    private void LayoutSubview (View v, Size contentSize)
+    /// <summary>
+    ///     Lays out <see cref="subView"/>. 
+    /// </summary>
+    /// <param name="subView"></param>
+    /// <param name="contentSize"></param>
+    /// <returns><see langword="false"/>If the view could not be laid out (typically because a dependencies was not ready). </returns>
+    private bool LayoutSubview (View subView, Size contentSize)
     {
         // Note, SetRelativeLayout calls SetTextFormatterSize
-        v.SetRelativeLayout (contentSize);
-        v.LayoutSubviews ();
+        if (subView.SetRelativeLayout (contentSize))
+        {
+            subView.LayoutSubviews ();
+
+            return true;
+        }
+
+        return false;
     }
 
     /// <summary>
@@ -735,13 +792,13 @@ public partial class View // Layout APIs
     ///     layout.
     /// </summary>
     /// <returns></returns>
-    internal bool IsLayoutNeeded () => _layoutNeeded;
+    public bool IsLayoutNeeded () => _layoutNeeded;
 
     /// <summary>
     ///     INTERNAL API - Sets <see cref="_layoutNeeded"/> for this View and all of it's subviews and it's SuperView.
     ///     The main loop will call SetRelativeLayout and LayoutSubviews for any view with <see cref="IsLayoutNeeded"/> set.
     /// </summary>
-    internal void SetLayoutNeeded ()
+    public void SetLayoutNeeded ()
     {
         if (IsLayoutNeeded ())
         {
@@ -751,6 +808,10 @@ public partial class View // Layout APIs
 
         _layoutNeeded = true;
 
+        Margin?.SetLayoutNeeded ();
+        Border?.SetLayoutNeeded ();
+        Padding?.SetLayoutNeeded ();
+
         // Use a stack to avoid recursion
         Stack<View> stack = new Stack<View> (Subviews);
 
@@ -769,13 +830,12 @@ public partial class View // Layout APIs
 
         TextFormatter.NeedsFormat = true;
 
-        // Use a loop to avoid recursion for superview hierarchy
-        View? superView = SuperView;
-        while (superView != null && !superView.IsLayoutNeeded ())
+        if (this is Adornment adornment)
         {
-            superView._layoutNeeded = true;
-            superView = superView.SuperView;
+            adornment.Parent?.SetLayoutNeeded ();
         }
+
+        SuperView?.SetLayoutNeeded ();
     }
 
     /// <summary>
@@ -827,25 +887,25 @@ public partial class View // Layout APIs
     /// </param>
     internal void CollectDim (Dim? dim, View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
     {
-        switch (dim)
+        if (dim!.Has<DimView> (out var dv))
         {
-            case DimView dv:
-                // See #2461
-                //if (!from.InternalSubviews.Contains (dv.Target)) {
-                //	throw new InvalidOperationException ($"View {dv.Target} is not a subview of {from}");
-                //}
-                if (dv.Target != this)
-                {
-                    nEdges.Add ((dv.Target!, from));
-                }
+            if (dv.Target != this)
+            {
+                nEdges.Add ((dv.Target!, from));
+            }
+        }
 
-                return;
-            case DimCombine dc:
-                CollectDim (dc.Left, from, ref nNodes, ref nEdges);
-                CollectDim (dc.Right, from, ref nNodes, ref nEdges);
+        if (dim!.Has<DimCombine> (out var dc))
+        {
+            CollectDim (dc.Left, from, ref nNodes, ref nEdges);
+            CollectDim (dc.Right, from, ref nNodes, ref nEdges);
+        }
+
+        if (dim!.Has<DimFunc> (out var df))
+        {
 
-                break;
         }
+
     }
 
     /// <summary>
@@ -863,10 +923,7 @@ public partial class View // Layout APIs
         switch (pos)
         {
             case PosView pv:
-                // See #2461
-                //if (!from.InternalSubviews.Contains (pv.Target)) {
-                //	throw new InvalidOperationException ($"View {pv.Target} is not a subview of {from}");
-                //}
+                Debug.Assert (pv.Target is { });
                 if (pv.Target != this)
                 {
                     nEdges.Add ((pv.Target!, from));

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

@@ -363,7 +363,10 @@ public partial class View : Responder, ISupportInitializeNotification
             OnVisibleChanged ();
             VisibleChanged?.Invoke (this, EventArgs.Empty);
 
+            SetLayoutNeeded ();
+            SuperView?.SetLayoutNeeded();
             SetNeedsDisplay ();
+            SuperView?.SetNeedsDisplay();
         }
     }
 

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

@@ -31,7 +31,7 @@ public class Bar : View, IOrientation, IDesignable
         _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
         _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
 
-        Initialized += Bar_Initialized;
+       // Initialized += Bar_Initialized;
         MouseEvent += OnMouseEvent;
 
         if (shortcuts is null)
@@ -77,8 +77,10 @@ public class Bar : View, IOrientation, IDesignable
         }
     }
 
-    private void Bar_Initialized (object? sender, EventArgs e)
+    /// <inheritdoc />
+    public override void EndInit ()
     {
+        base.EndInit ();
         ColorScheme = Colors.ColorSchemes ["Menu"];
         LayoutBarItems (GetContentSize ());
     }
@@ -199,6 +201,9 @@ public class Bar : View, IOrientation, IDesignable
         LayoutBarItems (args.OldContentSize);
     }
 
+    // This is used to calculate the minimum width of the Bar when the width is NOT Dim.Auto
+    private int? _minimumDimAutoWidth;
+
     private void LayoutBarItems (Size contentSize)
     {
         View? prevBarItem = null;
@@ -229,7 +234,7 @@ public class Bar : View, IOrientation, IDesignable
                     minKeyWidth = int.Max (minKeyWidth, shortcut.KeyView.Text.GetColumns ());
                 }
 
-                var maxBarItemWidth = 0;
+                var _maxBarItemWidth = 0;
                 var totalHeight = 0;
 
                 for (var index = 0; index < Subviews.Count; index++)
@@ -246,7 +251,8 @@ public class Bar : View, IOrientation, IDesignable
                     if (barItem is Shortcut scBarItem)
                     {
                         scBarItem.MinimumKeyTextSize = minKeyWidth;
-                        maxBarItemWidth = Math.Max (maxBarItemWidth, scBarItem.Frame.Width);
+
+                        _maxBarItemWidth = Math.Max (_maxBarItemWidth, scBarItem.Frame.Width);
                     }
 
                     if (prevBarItem == null)
@@ -268,7 +274,7 @@ public class Bar : View, IOrientation, IDesignable
 
                 foreach (View barItem in Subviews)
                 {
-                    barItem.Width = maxBarItemWidth;
+                    barItem.Width = _maxBarItemWidth;
                 }
 
                 Height = Dim.Auto (DimAutoStyle.Content, totalHeight);

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

@@ -70,7 +70,9 @@ internal sealed class Menu : View
     {
         base.BeginInit ();
 
-        Frame = MakeFrame (Frame.X, Frame.Y, _barItems!.Children!, Parent);
+        var frame = MakeFrame (Frame.X, Frame.Y, _barItems!.Children!, Parent);
+        Width = frame.Width;
+        Height = frame.Height;
 
         if (_barItems.Children is { })
         {

+ 1 - 0
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -861,6 +861,7 @@ public class MenuBar : View, IDesignable
                 if (Application.Top is { })
                 {
                     Application.Top.Add (_openMenu);
+                   // _openMenu.SetRelativeLayout (Application.Screen.Size);
                 }
                 else
                 {

+ 46 - 31
Terminal.Gui/Views/Shortcut.cs

@@ -109,6 +109,7 @@ public class Shortcut : View, IOrientation, IDesignable
 
         CommandView = new ()
         {
+            Id = "CommandView",
             Width = Dim.Auto (),
             Height = Dim.Auto (DimAutoStyle.Auto, 1)
         };
@@ -116,14 +117,11 @@ public class Shortcut : View, IOrientation, IDesignable
         HelpView.Id = "_helpView";
         HelpView.CanFocus = false;
         HelpView.Text = helpText ?? string.Empty;
-        Add (HelpView);
 
         KeyView.Id = "_keyView";
         KeyView.CanFocus = false;
-        Add (KeyView);
 
         LayoutStarted += OnLayoutStarted;
-        Initialized += OnInitialized;
 
         key ??= Key.Empty;
         Key = key;
@@ -132,41 +130,58 @@ public class Shortcut : View, IOrientation, IDesignable
 
         return;
 
-        void OnInitialized (object? sender, EventArgs e)
-        {
-            SuperViewRendersLineCanvas = true;
-            Border.Settings &= ~BorderSettings.Title;
+    }
 
-            ShowHide ();
+    // Helper to set Width consistently
+    private Dim GetWidthDimAuto ()
+    {
+        // TODO: PosAlign.CalculateMinDimension is a hack. Need to figure out a better way of doing this.
+        return Dim.Auto (
+                         DimAutoStyle.Content,
+                         Dim.Func (() =>
+                                   {
+                                       if (Subviews [0].IsLayoutNeeded ())
+                                       {
+                                           // throw new Exception ();
+                                       }
+                                       return PosAlign.CalculateMinDimension (0, Subviews, Dimension.Width);
+                                   }),
+                         Dim.Func (() =>
+                                   {
+                                       return PosAlign.CalculateMinDimension (0, Subviews, Dimension.Width);
+                                   }))!;
+    }
 
-            // Force Width to DimAuto to calculate natural width and then set it back
-            Dim savedDim = Width;
-            Width = GetWidthDimAuto ();
-            _minimumDimAutoWidth = Frame.Width;
-            Width = savedDim;
+    /// <inheritdoc />
+    public override void EndInit ()
+    {
+        base.EndInit ();
 
-            SetCommandViewDefaultLayout ();
-            SetHelpViewDefaultLayout ();
-            SetKeyViewDefaultLayout ();
+        SuperViewRendersLineCanvas = true;
+        Border.Settings &= ~BorderSettings.Title;
 
-            SetColors ();
-        }
+        ShowHide ();
 
-        // Helper to set Width consistently
-        Dim GetWidthDimAuto ()
-        {
-            // TODO: PosAlign.CalculateMinDimension is a hack. Need to figure out a better way of doing this.
-            return Dim.Auto (
-                             DimAutoStyle.Content,
-                             Dim.Func (() => PosAlign.CalculateMinDimension (0, Subviews, Dimension.Width)),
-                             Dim.Func (() => PosAlign.CalculateMinDimension (0, Subviews, Dimension.Width)))!;
-        }
+        // Force Width to DimAuto to calculate natural width and then set it back
+        Dim? savedDim = Width;
+        Width = GetWidthDimAuto ();
+        // Force a SRL)
+        SetRelativeLayout (Application.Screen.Size);
+        _minimumNatrualWidth = Frame.Width;
+        Width = savedDim;
+
+        SetCommandViewDefaultLayout ();
+        SetHelpViewDefaultLayout ();
+        SetKeyViewDefaultLayout ();
+
+        SetColors ();
     }
 
     private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast;
 
     // This is used to calculate the minimum width of the Shortcut when the width is NOT Dim.Auto
-    private int? _minimumDimAutoWidth;
+    // It is calculated by setting Width to DimAuto temporarily and forcing layout
+    private int? _minimumNatrualWidth;
 
     /// <inheritdoc/>
     protected override bool OnHighlight (CancelEventArgs<HighlightStyle> args)
@@ -238,7 +253,7 @@ public class Shortcut : View, IOrientation, IDesignable
     {
         if (Width is DimAuto widthAuto)
         {
-            _minimumDimAutoWidth = Frame.Width;
+            _minimumNatrualWidth = Frame.Width;
         }
         else
         {
@@ -256,9 +271,9 @@ public class Shortcut : View, IOrientation, IDesignable
             // When Vertical, Command is first, then Help, then Key.
             // BUGBUG: This does not do what the above says.
             // TODO: Add Unit tests for this.
-            if (currentWidth < _minimumDimAutoWidth)
+            if (currentWidth < _minimumNatrualWidth)
             {
-                int delta = _minimumDimAutoWidth.Value - currentWidth;
+                int delta = _minimumNatrualWidth.Value - currentWidth;
                 int maxHelpWidth = int.Max (0, HelpView.Text.GetColumns () + Margin.Thickness.Horizontal - delta);
 
                 switch (maxHelpWidth)

+ 4 - 2
UICatalog/Scenarios/Navigation.cs

@@ -57,6 +57,7 @@ public class Navigation : Scenario
             Y = 0,
             Title = $"TopButton _{GetNextHotKey ()}"
         };
+        button.Accepting += (sender, args) => MessageBox.Query ("hi", button.Title, "_Ok");
 
         testFrame.Add (button);
 
@@ -98,9 +99,10 @@ public class Navigation : Scenario
 
                              progressBar.Fraction += 0.01f;
 
-                             Application.Wakeup ();
+                             //Application.Wakeup ();
 
-                             progressBar.SetNeedsDisplay ();
+                             Application.Invoke (() => progressBar.SetNeedsDisplay ());
+                            ;
                          };
         timer.Start ();
 

+ 22 - 33
UICatalog/UICatalog.cs

@@ -387,7 +387,7 @@ public class UICatalogApp
         // 'app' closed cleanly.
         foreach (Responder? inst in Responder.Instances)
         {
-            
+
             Debug.Assert (inst.WasDisposed);
         }
 
@@ -492,7 +492,6 @@ public class UICatalogApp
                         )
                 ]
             };
-            Add (menuBar);
 
             _statusBar = new ()
             {
@@ -500,9 +499,7 @@ public class UICatalogApp
                 AlignmentModes = AlignmentModes.IgnoreFirstOrLast,
                 CanFocus = false
             };
-            _statusBar.Height = _statusBar.Visible ? Dim.Auto () : 0;
-
-            Add (_statusBar);
+            _statusBar.Height = Dim.Auto (DimAutoStyle.Auto, minimumContentDim: Dim.Func (() => _statusBar.Visible ? 1 : 0), maximumContentDim: Dim.Func (() => _statusBar.Visible ? 1 : 0));
 
             ShVersion = new ()
             {
@@ -561,9 +558,16 @@ public class UICatalogApp
             CategoryList = new ()
             {
                 X = 0,
-                Y = Pos.Bottom (MenuBar),
+                Y = Pos.Bottom (menuBar),
                 Width = Dim.Auto (),
-                Height = Dim.Fill (Dim.Func (() => _statusBar.Frame.Height)),
+                Height = Dim.Fill (Dim.Func (() =>
+                                             {
+                                                 if (_statusBar.IsLayoutNeeded ())
+                                                 {
+                                                    // throw new Exception ("DimFunc.Fn aborted because dependent View needs layout.");
+                                                 }
+                                                 return _statusBar.Frame.Height;
+                                             })),
                 AllowsMarking = false,
                 CanFocus = true,
                 Title = "_Categories",
@@ -580,9 +584,16 @@ public class UICatalogApp
             ScenarioList = new ()
             {
                 X = Pos.Right (CategoryList) - 1,
-                Y = Pos.Bottom (MenuBar),
+                Y = Pos.Bottom (menuBar),
                 Width = Dim.Fill (),
-                Height = Dim.Fill (Dim.Func (() => _statusBar.Frame.Height)),
+                Height = Dim.Fill (Dim.Func (() =>
+                                             {
+                                                 if (_statusBar.IsLayoutNeeded ())
+                                                 {
+                                                    // throw new Exception ("DimFunc.Fn aborted because dependent View needs layout.");
+                                                 }
+                                                 return _statusBar.Frame.Height;
+                                             })),
                 //AllowsMarking = false,
                 CanFocus = true,
                 Title = "_Scenarios",
@@ -620,29 +631,6 @@ public class UICatalogApp
                                                  new () { MaxWidth = longestName, MinWidth = longestName, MinAcceptableWidth = longestName }
                                                 );
             ScenarioList.Style.ColumnStyles.Add (1, new () { MaxWidth = 1 });
-
-            //// Enable user to find & select a scenario by typing text
-            //// TableView does not (currently) have built-in CollectionNavigator support (the ability for the 
-            //// user to type and the items that match get selected). We implement it in the app instead. 
-            //ScenarioList.KeyDown += (s, a) =>
-            //                        {
-            //                            if (CollectionNavigatorBase.IsCompatibleKey (a))
-            //                            {
-            //                                int? newItem =
-            //                                    _scenarioCollectionNav?.GetNextMatchingItem (
-            //                                                                                 ScenarioList.SelectedRow,
-            //                                                                                 (char)a
-            //                                                                                );
-
-            //                                if (newItem is int v && newItem != -1)
-            //                                {
-            //                                    ScenarioList.SelectedRow = v;
-            //                                    ScenarioList.EnsureSelectedCellIsVisible ();
-            //                                    ScenarioList.SetNeedsDisplay ();
-            //                                    a.Handled = true;
-            //                                }
-            //                            }
-            //                        };
             ScenarioList.CellActivated += ScenarioView_OpenSelectedItem;
 
             // TableView typically is a grid where nav keys are biased for moving left/right.
@@ -657,8 +645,10 @@ public class UICatalogApp
             ScenarioList.MultiSelect = false;
             ScenarioList.KeyBindings.Remove (Key.A.WithCtrl);
 
+            Add (menuBar);
             Add (CategoryList);
             Add (ScenarioList);
+            Add (_statusBar);
 
             Loaded += LoadedHandler;
             Unloaded += UnloadedHandler;
@@ -1097,7 +1087,6 @@ public class UICatalogApp
                 _statusBar.VisibleChanged += (s, e) =>
                                             {
                                                 ShowStatusBar = _statusBar.Visible;
-                                                _statusBar.Height = _statusBar.Visible ? Dim.Auto () : 0;
                                             };
             }