瀏覽代碼

DimAuto fixes

Tig 9 月之前
父節點
當前提交
2e163eef75

+ 16 - 58
Terminal.Gui/View/Layout/DimAuto.cs

@@ -238,22 +238,12 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
                 List<int> groupIds = includedSubviews.Select (
                                                               v =>
                                                               {
-                                                                  if (dimension == Dimension.Width)
-                                                                  {
-                                                                      if (v.X.Has<PosAlign> (out Pos posAlign))
-                                                                      {
-                                                                          return ((PosAlign)posAlign).GroupId;
-                                                                      }
-                                                                  }
-                                                                  else
-                                                                  {
-                                                                      if (v.Y.Has<PosAlign> (out Pos posAlign))
-                                                                      {
-                                                                          return ((PosAlign)posAlign).GroupId;
-                                                                      }
-                                                                  }
-
-                                                                  return -1;
+                                                                  return dimension switch
+                                                                         {
+                                                                             Dimension.Width when v.X.Has<PosAlign> (out PosAlign posAlign) => ((PosAlign)posAlign).GroupId,
+                                                                             Dimension.Height when v.Y.Has<PosAlign> (out PosAlign posAlign) => ((PosAlign)posAlign).GroupId,
+                                                                             _ => -1
+                                                                         };
                                                               })
                                                      .Distinct ()
                                                      .ToList ();
@@ -261,18 +251,7 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
                 foreach (int groupId in groupIds.Where (g => g != -1))
                 {
                     // PERF: If this proves a perf issue, consider caching a ref to this list in each item
-                    List<PosAlign?> posAlignsInGroup = includedSubviews.Where (
-                                                                               v =>
-                                                                               {
-                                                                                   return dimension switch
-                                                                                   {
-                                                                                       Dimension.Width when v.X is PosAlign alignX => alignX.GroupId
-                                                                                           == groupId,
-                                                                                       Dimension.Height when v.Y is PosAlign alignY => alignY.GroupId
-                                                                                           == groupId,
-                                                                                       _ => false
-                                                                                   };
-                                                                               })
+                    List<PosAlign?> posAlignsInGroup = includedSubviews.Where (v => PosAlign.HasGroupId (v, dimension, groupId))
                                                                        .Select (v => dimension == Dimension.Width ? v.X as PosAlign : v.Y as PosAlign)
                                                                        .ToList ();
 
@@ -350,16 +329,9 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
 
                     // BUGBUG: The order may not be correct. May need to call TopologicalSort?
                     // TODO: Figure out a way to not have Calculate change the state of subviews (calling SRL).
-                    if (dimension == Dimension.Width)
-                    {
-                        v.SetRelativeLayout (new (maxCalculatedSize, 0));
-                    }
-                    else
-                    {
-                        v.SetRelativeLayout (new (0, maxCalculatedSize));
-                    }
-
-                    int maxPosView = dimension == Dimension.Width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height;
+                    int maxPosView = dimension == Dimension.Width
+                                         ? v.Frame.X + v.Width!.Calculate (0, maxCalculatedSize, v, dimension)
+                                         : v.Frame.Y + v.Height!.Calculate (0, maxCalculatedSize, v, dimension);
 
                     if (maxPosView > maxCalculatedSize)
                     {
@@ -391,16 +363,9 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
 
                     // BUGBUG: The order may not be correct. May need to call TopologicalSort?
                     // TODO: Figure out a way to not have Calculate change the state of subviews (calling SRL).
-                    if (dimension == Dimension.Width)
-                    {
-                        v.SetRelativeLayout (new (maxCalculatedSize, 0));
-                    }
-                    else
-                    {
-                        v.SetRelativeLayout (new (0, maxCalculatedSize));
-                    }
-
-                    int maxDimView = dimension == Dimension.Width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height;
+                    int maxDimView = dimension == Dimension.Width
+                                         ? v.Frame.X + v.Width!.Calculate (0, maxCalculatedSize, v, dimension)
+                                         : v.Frame.Y + v.Height!.Calculate (0, maxCalculatedSize, v, dimension);
 
                     if (maxDimView > maxCalculatedSize)
                     {
@@ -428,16 +393,9 @@ public record DimAuto (Dim? MaximumContentDim, Dim? MinimumContentDim, DimAutoSt
                 {
                     View v = dimAutoSubViews [i];
 
-                    if (dimension == Dimension.Width)
-                    {
-                        v.SetRelativeLayout (new (maxCalculatedSize, 0));
-                    }
-                    else
-                    {
-                        v.SetRelativeLayout (new (0, maxCalculatedSize));
-                    }
-
-                    int maxDimAuto= dimension == Dimension.Width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height;
+                    int maxDimAuto = dimension == Dimension.Width 
+                                        ? v.Frame.X + v.Width!.Calculate (0, maxCalculatedSize, v, dimension) 
+                                        : v.Frame.Y + v.Height!.Calculate (0, maxCalculatedSize, v, dimension);
 
                     if (maxDimAuto > maxCalculatedSize)
                     {

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

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

+ 25 - 39
Terminal.Gui/View/Layout/PosAlign.cs

@@ -63,17 +63,7 @@ public record PosAlign : Pos
         List<int> dimensionsList = new ();
 
         // PERF: If this proves a perf issue, consider caching a ref to this list in each item
-        List<View> viewsInGroup = views.Where (
-                                               v =>
-                                               {
-                                                   return dimension switch
-                                                   {
-                                                       Dimension.Width when v.X is PosAlign alignX => alignX.GroupId == groupId,
-                                                       Dimension.Height when v.Y is PosAlign alignY => alignY.GroupId == groupId,
-                                                       _ => false
-                                                   };
-                                               })
-                                       .ToList ();
+        List<View> viewsInGroup = views.Where (v => HasGroupId (v, dimension, groupId)).ToList ();
 
         if (viewsInGroup.Count == 0)
         {
@@ -99,6 +89,16 @@ public record PosAlign : Pos
         return dimensionsList.Sum ();
     }
 
+    internal static bool HasGroupId (View v, Dimension dimension, int groupId)
+    {
+        return dimension switch
+        {
+            Dimension.Width when v.X.Has<PosAlign> (out PosAlign pos) => pos.GroupId == groupId,
+            Dimension.Height when v.Y.Has<PosAlign> (out PosAlign pos) => pos.GroupId == groupId,
+            _ => false
+        };
+    }
+
     /// <summary>
     ///     Gets the identifier of a set of views that should be aligned together. When only a single
     ///     set of views in a SuperView is aligned, setting <see cref="GroupId"/> is not needed because it defaults to 0.
@@ -115,12 +115,18 @@ public record PosAlign : Pos
             return _cachedLocation.Value;
         }
 
+        IList<View>? groupViews;
         if (us.SuperView is null)
         {
-            return 0;
+            groupViews = new List<View> ();
+            groupViews.Add (us);
+        }
+        else
+        {
+            groupViews = us.SuperView!.Subviews;
         }
 
-        AlignAndUpdateGroup (GroupId, us.SuperView.Subviews, dimension, superviewDimension);
+        AlignAndUpdateGroup (GroupId, groupViews, dimension, superviewDimension);
 
         if (_cachedLocation.HasValue)
         {
@@ -145,31 +151,9 @@ public record PosAlign : Pos
         List<int> dimensionsList = new ();
 
         // PERF: If this proves a perf issue, consider caching a ref to this list in each item
-        List<PosAlign?> posAligns = views.Select (
-                                                  v =>
-                                                  {
-                                                      switch (dimension)
-                                                      {
-                                                          case Dimension.Width when v.X.Has<PosAlign> (out Pos pos):
-
-                                                              if (pos is PosAlign posAlignX && posAlignX.GroupId == groupId)
-                                                              {
-                                                                  return posAlignX;
-                                                              }
-
-                                                              break;
-                                                          case Dimension.Height when v.Y.Has<PosAlign> (out Pos pos):
-                                                              if (pos is PosAlign posAlignY && posAlignY.GroupId == groupId)
-                                                              {
-                                                                  return posAlignY;
-                                                              }
-
-                                                              break;
-                                                      }
-
-                                                      return null;
-                                                  })
-                                         .ToList ();
+        List<PosAlign?> posAligns = views.Where (v => PosAlign.HasGroupId (v, dimension, groupId))
+                                        .Select (v => dimension == Dimension.Width ? v.X as PosAlign : v.Y as PosAlign)
+                                        .ToList ();
 
         // PERF: We iterate over viewsInGroup multiple times here.
 
@@ -185,7 +169,9 @@ public record PosAlign : Pos
                     firstInGroup = posAligns [index]!.Aligner;
                 }
 
-                dimensionsList.Add (dimension == Dimension.Width ? views [index].Frame.Width : views [index].Frame.Height);
+                dimensionsList.Add (dimension == Dimension.Width 
+                                        ? views [index].Width!.Calculate(0, size, views [index], dimension) 
+                                        : views [index].Height!.Calculate (0, size, views [index], dimension));
             }
         }
 

+ 17 - 6
Terminal.Gui/View/View.Layout.cs

@@ -348,7 +348,7 @@ public partial class View // Layout APIs
 
             SetLayoutNeeded ();
 
-            if (IsAbsoluteLayout())
+            if (IsAbsoluteLayout ())
             {
                 // Implicit layout is ok here because all Pos/Dim are Absolute values.
                 Layout ();
@@ -546,13 +546,24 @@ public partial class View // Layout APIs
     /// <returns><see langword="false"/>If the view could not be laid out (typically because a dependencies was not ready). </returns>
     public bool Layout (Size contentSize)
     {
+        int bailAfter = 100;
         // Note, SetRelativeLayout calls SetTextFormatterSize
-        if (SetRelativeLayout (contentSize))
-        {
-            LayoutSubviews ();
+        //while (NeedsLayout)
+        //{
+            if (SetRelativeLayout (contentSize))
+            {
+                LayoutSubviews ();
 
-            return true;
-        }
+                Debug.Assert(!NeedsLayout);
+                return true;
+            }
+
+        //    if (--bailAfter == 0)
+        //    {
+        //        Debug.Write ($"Layout: After {100} tries, SetRelativeLayout was unable to complete.");
+        //        return false;
+        //    }
+        //}
 
         return false;
     }

+ 0 - 4
Terminal.Gui/Views/Shortcut.cs

@@ -137,10 +137,6 @@ public class Shortcut : View, IOrientation, IDesignable
                          DimAutoStyle.Content,
                          Dim.Func (() =>
                                    {
-                                       if (Subviews [0].NeedsLayout)
-                                       {
-                                           // throw new Exception ();
-                                       }
                                        return PosAlign.CalculateMinDimension (0, Subviews, Dimension.Width);
                                    }),
                          Dim.Func (() =>

+ 41 - 0
UnitTests/View/Layout/Dim.AutoTests.PosTypes.cs

@@ -45,6 +45,47 @@ public partial class DimAutoTests
 
     #endregion PosAbsolute
 
+    #region PosAlign
+
+    //[Theory]
+    //[InlineData (0, 0, 0, 0, 0, 0)]
+    //[InlineData (0, 19, 0, 9, 19, 9)]
+    //[InlineData (0, 20, 0, 10, 20, 10)]
+    //[InlineData (0, 21, 0, 11, 20, 10)]
+    //[InlineData (1, 21, 1, 11, 20, 10)]
+    //[InlineData (21, 21, 11, 11, 21, 11)]
+    //public void With_Subview_Using_PosAlign (int minWidth, int maxWidth, int minHeight, int maxHeight, int expectedWidth, int expectedHeight)
+    //{
+    //    var view = new View
+    //    {
+    //        Width = Dim.Auto (minimumContentDim: minWidth, maximumContentDim: maxWidth),
+    //        Height = Dim.Auto (minimumContentDim: minHeight, maximumContentDim: maxHeight)
+    //    };
+
+    //    var subview = new View
+    //    {
+    //        X = Pos.Align (Alignment.Center),
+    //        Y = Pos.Absolute (5),
+    //        Width = 20,
+    //        Height = 10
+    //    };
+    //    view.Add (subview);
+
+    //    // Assuming the calculation is done after layout
+    //    int calculatedX = view.X.Calculate (100, view.Width, view, Dimension.Width);
+    //    int calculatedY = view.Y.Calculate (100, view.Height, view, Dimension.Height);
+    //    int calculatedWidth = view.Width.Calculate (0, 100, view, Dimension.Width);
+    //    int calculatedHeight = view.Height.Calculate (0, 100, view, Dimension.Height);
+
+    //    Assert.Equal (expectedWidth, calculatedWidth);
+    //    Assert.Equal (expectedHeight, calculatedHeight);
+
+    //    Assert.Equal (0, calculatedX);
+    //    Assert.Equal (0, calculatedY);
+    //}
+
+    #endregion PosAlign
+
     #region PosPercent
 
     [Theory]

+ 31 - 0
UnitTests/View/Layout/Pos.AlignTests.cs

@@ -106,6 +106,25 @@ public class PosAlignTests
         Assert.IsType<PosAlign> (pos);
     }
 
+    [Fact]
+    public void PosAlign_Laysout ()
+    {
+        var view = new View ()
+        {
+            Id = "view",
+            X = Pos.Align (Alignment.Center),
+            Width = 1,
+            Height = 1
+        };
+        view.Layout (new (10, 10));
+
+        Assert.Equal (4, view.Frame.X);
+    }
+
+    // TODO: Test scenarios where views with matching GroupId's are added/removed from a Superview
+
+    // TODO: Make AlignAndUpdateGroup internal and write low-level unit tests for it
+
     [Fact]
     public void PosAlign_Set_View_X ()
     {
@@ -127,6 +146,18 @@ public class PosAlignTests
         view.X = posAlign;
         superView.Layout ();
         Assert.Equal (4, view.Frame.X);
+        superView.Remove (view);
+
+        view = new View ()
+        {
+            Id = "view",
+            X = posAlign,
+            Width = 1,
+            Height = 1
+        };
+        superView.Add (view);
+        superView.Layout ();
+        Assert.Equal (4, view.Frame.X);
 
         posAlign = Pos.Align (Alignment.End);
         view.X = posAlign;

+ 8 - 4
UnitTests/Views/ShortcutTests.cs

@@ -21,9 +21,9 @@ public class ShortcutTests
     {
         var shortcut = new Shortcut ();
         shortcut.BeginInit();
-        shortcut.EndInit();
-
+        shortcut.EndInit ();
         shortcut.Layout ();
+
         Assert.Equal (2, shortcut.Frame.Width);
         Assert.Equal (1, shortcut.Frame.Height);
         Assert.Equal (2, shortcut.Viewport.Width);
@@ -45,7 +45,9 @@ public class ShortcutTests
             Key = Key.A,
             HelpText = "0"
         };
-        shortcut.SetRelativeLayout (new (100, 100));
+        shortcut.BeginInit ();
+        shortcut.EndInit ();
+        shortcut.Layout ();
         Assert.Equal (8, shortcut.Frame.Width);
         Assert.Equal (1, shortcut.Frame.Height);
         Assert.Equal (8, shortcut.Viewport.Width);
@@ -68,7 +70,9 @@ public class ShortcutTests
             Key = Key.A,
             HelpText = "0"
         };
-        shortcut.SetRelativeLayout (new (100, 100));
+        shortcut.BeginInit ();
+        shortcut.EndInit ();
+        shortcut.Layout ();
         Assert.Equal (9, shortcut.Frame.Width);
         Assert.Equal (1, shortcut.Frame.Height);
         Assert.Equal (9, shortcut.Viewport.Width);