瀏覽代碼

Rewrote SetRelativeLayout to move logic in to the Pos & Dim classes. Massively beefed up unit tests.

Tig 1 年之前
父節點
當前提交
e8467f25c4

+ 98 - 11
Terminal.Gui/View/Layout/PosDim.cs

@@ -1,4 +1,6 @@
-namespace Terminal.Gui;
+using static Terminal.Gui.Pos;
+
+namespace Terminal.Gui;
 
 /// <summary>
 ///     Describes the position of a <see cref="View"/> which can be an absolute value, a percentage, centered, or
@@ -319,6 +321,12 @@ public class Pos
         }
     }
 
+    internal virtual int GetLocation (int superviewDimension, Dim dim, int autosize, bool autoSize)
+    {
+        return this.Anchor (superviewDimension);
+    }
+
+
     internal class PosAbsolute (int n) : Pos
     {
         private readonly int _n = n;
@@ -354,12 +362,29 @@ public class Pos
             }
             return width - _offset;
         }
+
+        internal override int GetLocation (int superviewDimension, Dim dim, int autosize, bool autoSize)
+        {
+            int newLocation = this.Anchor (superviewDimension);
+            if (this.UseDimForOffset)
+            {
+                newLocation -= dim.Anchor (superviewDimension);
+            }
+            return newLocation;
+        }
+
     }
 
     internal class PosCenter : Pos
     {
         public override string ToString () { return "Center"; }
         internal override int Anchor (int width) { return width / 2; }
+        internal override int GetLocation (int superviewDimension, Dim dim, int autosize, bool autoSize)
+        {
+            var newDimension = Math.Max (dim.GetDimension (0, superviewDimension, autosize, autoSize), 0);
+            return Anchor (superviewDimension - newDimension);
+        }
+
     }
 
     internal class PosCombine (bool add, Pos left, Pos right) : Pos
@@ -381,6 +406,19 @@ public class Pos
 
             return la - ra;
         }
+
+        internal override int GetLocation (int superviewDimension, Dim dim, int autosize, bool autoSize)
+        {
+            int newDimension = dim.GetDimension (0, superviewDimension, autosize, autoSize);
+            int left = _left.GetLocation (superviewDimension, dim, autosize, autoSize);
+            int right = _right.GetLocation (superviewDimension, dim, autosize, autoSize);
+            if (_add)
+            {
+                return left + right;
+            }
+
+            return left - right;
+        }
     }
 
     internal class PosFactor (float n) : Pos
@@ -645,6 +683,13 @@ public class Dim
 
     internal virtual int Anchor (int width) { return 0; }
 
+    internal virtual int GetDimension (int location, int dimension, int autosize, bool autoSize)
+    {
+        int newDimension = Math.Max (Anchor (dimension - location), 0);
+        return autoSize && autosize > newDimension ? autosize : newDimension;
+    }
+
+
     // BUGBUG: newPos is never used.
     private static void SetDimCombine (Dim left, DimCombine newPos) { (left as DimView)?.Target.SetNeedsLayout (); }
 
@@ -655,6 +700,15 @@ public class Dim
         public override int GetHashCode () { return _n.GetHashCode (); }
         public override string ToString () { return $"Absolute({_n})"; }
         internal override int Anchor (int width) { return _n; }
+
+        internal override int GetDimension (int location, int dimension, int autosize, bool autoSize)
+        {
+            // DimAbsolute.Anchor (int width) ignores width and returns n
+            int newDimension = Math.Max (Anchor (0), 0);
+
+            // BUGBUG: AutoSize does two things: makes text fit AND changes the view's dimensions
+            return autoSize && autosize > newDimension ? autosize : newDimension;
+        }
     }
 
     internal class DimCombine (bool add, Dim left, Dim right) : Dim
@@ -676,6 +730,24 @@ public class Dim
 
             return la - ra;
         }
+
+        internal override int GetDimension (int location, int dimension, int autosize, bool autoSize)
+        {
+            int leftNewDim = _left.GetDimension (location, dimension, autosize, autoSize);
+            int rightNewDim = _right.GetDimension (location, dimension, autosize, autoSize);
+
+            int newDimension;
+            if (_add)
+            {
+                newDimension = leftNewDim + rightNewDim;
+            }
+            else
+            {
+                newDimension = Math.Max (0, leftNewDim - rightNewDim);
+            }
+
+            return autoSize && autosize > newDimension ? autosize : newDimension;
+        }
     }
 
     internal class DimFactor (float n, bool r = false) : Dim
@@ -688,6 +760,21 @@ public class Dim
         public bool IsFromRemaining () { return _remaining; }
         public override string ToString () { return $"Factor({_factor},{_remaining})"; }
         internal override int Anchor (int width) { return (int)(width * _factor); }
+
+        internal override int GetDimension (int location, int dimension, int autosize, bool autoSize)
+        {
+            int newDimension;
+            if (_remaining)
+            {
+                newDimension = Math.Max (Anchor (dimension - location), 0);
+            }
+            else
+            {
+                newDimension = Anchor (dimension);
+            }
+
+            return autoSize && autosize > newDimension ? autosize : newDimension;
+        }
     }
 
     internal class DimFill (int margin) : Dim
@@ -723,11 +810,11 @@ public class Dim
             }
 
             string tside = side switch
-                           {
-                               0 => "Height",
-                               1 => "Width",
-                               _ => "unknown"
-                           };
+            {
+                0 => "Height",
+                1 => "Width",
+                _ => "unknown"
+            };
 
             return $"View({tside},{Target})";
         }
@@ -735,11 +822,11 @@ public class Dim
         internal override int Anchor (int width)
         {
             return side switch
-                   {
-                       0 => Target.Frame.Height,
-                       1 => Target.Frame.Width,
-                       _ => 0
-                   };
+            {
+                0 => Target.Frame.Height,
+                1 => Target.Frame.Width,
+                _ => 0
+            };
         }
     }
 }

+ 8 - 166
Terminal.Gui/View/Layout/ViewLayout.cs

@@ -1011,7 +1011,6 @@ public partial class View
         Debug.Assert (_width is { });
         Debug.Assert (_height is { });
 
-        int newX, newW, newY, newH;
         var autosize = Size.Empty;
 
         if (AutoSize)
@@ -1021,176 +1020,19 @@ public partial class View
             autosize = GetAutoSize ();
         }
 
-        // TODO: Since GetNewLocationAndDimension does not depend on View, it can be moved into PosDim.cs
-        // TODO: to make architecture more clean. Do this after DimAuto is implemented and the 
-        // TODO: View.AutoSize stuff is removed.
-
-        // Returns the new dimension (width or height) and location (x or y) for the View given
-        //   the superview's Viewport
-        //   the current Pos (View.X or View.Y)
-        //   the current Dim (View.Width or View.Height)
-        // This method is called recursively if pos is Pos.PosCombine
-        (int newLocation, int newDimension) GetNewLocationAndDimension (
-            bool width,
-            Size superviewContentSize,
-            Pos pos,
-            Dim dim,
-            int autosizeDimension
-        )
-        {
-            // Gets the new dimension (width or height, dependent on `width`) of the given Dim given:
-            //   location: the current location (x or y)
-            //   dimension: the new dimension (width or height) (if relevant for Dim type)
-            //   autosize: the size to use if autosize = true
-            // This method is recursive if d is Dim.DimCombine
-            int GetNewDimension (Dim d, int location, int dimension, int autosize)
-            {
-                int newDimension;
-
-                switch (d)
-                {
-                    case Dim.DimCombine combine:
-                        // TODO: Move combine logic into DimCombine?
-                        int leftNewDim = GetNewDimension (combine._left, location, dimension, autosize);
-                        int rightNewDim = GetNewDimension (combine._right, location, dimension, autosize);
-
-                        if (combine._add)
-                        {
-                            newDimension = leftNewDim + rightNewDim;
-                        }
-                        else
-                        {
-                            newDimension = leftNewDim - rightNewDim;
-                        }
-
-                        newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
-
-                        break;
-
-                    case Dim.DimFactor factor when !factor.IsFromRemaining ():
-                        newDimension = d.Anchor (dimension);
-                        newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
-
-                        break;
-
-                    case Dim.DimAbsolute:
-                        // DimAbsolute.Anchor (int width) ignores width and returns n
-                        newDimension = Math.Max (d.Anchor (0), 0);
-
-                        // BUGBUG: AutoSize does two things: makes text fit AND changes the view's dimensions
-                        newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
-
-                        break;
-
-                    case Dim.DimFill:
-                    default:
-                        newDimension = Math.Max (d.Anchor (dimension - location), 0);
-                        newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
-
-                        break;
-                }
-
-                return newDimension;
-            }
-
-            int newDimension, newLocation;
-            int superviewDimension = width ? superviewContentSize.Width : superviewContentSize.Height;
-
-            // Determine new location
-            switch (pos)
-            {
-                case Pos.PosCenter posCenter:
-                    // For Center, the dimension is dependent on location, but we need to force getting the dimension first
-                    // using a location of 0
-                    newDimension = Math.Max (GetNewDimension (dim, 0, superviewDimension, autosizeDimension), 0);
-                    newLocation = posCenter.Anchor (superviewDimension - newDimension);
-
-                    newDimension = Math.Max (
-                                             GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension),
-                                             0
-                                            );
-                    break;
-
-                case Pos.PosCombine combine:
-                    // TODO: Move combine logic into PosCombine?
-                    int left, right;
-
-                    (left, newDimension) = GetNewLocationAndDimension (
-                                                                       width,
-                                                                       superviewContentSize,
-                                                                       combine._left,
-                                                                       dim,
-                                                                       autosizeDimension
-                                                                      );
-
-                    (right, newDimension) = GetNewLocationAndDimension (
-                                                                        width,
-                                                                        superviewContentSize,
-                                                                        combine._right,
-                                                                        dim,
-                                                                        autosizeDimension
-                                                                       );
-
-                    if (combine._add)
-                    {
-                        newLocation = left + right;
-                    }
-                    else
-                    {
-                        newLocation = left - right;
-                    }
-
-                    newDimension = Math.Max (
-                                             GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension),
-                                             0
-                                            );
-
-                    break;
-
-                case Pos.PosAnchorEnd anchorEnd:
-                    newLocation = anchorEnd.Anchor (superviewDimension);
-                    if (anchorEnd.UseDimForOffset)
-                    {
-                        newLocation -= dim.Anchor (superviewDimension);
-                    }
-
-                    newDimension = Math.Max (
-                                             GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension),
-                                             0
-                                            );
-                    break;
-
-                case Pos.PosAbsolute:
-                case Pos.PosFactor:
-                case Pos.PosFunc:
-                case Pos.PosView:
-                default:
-                    newLocation = pos?.Anchor (superviewDimension) ?? 0;
-
-                    newDimension = Math.Max (
-                                             GetNewDimension (dim, newLocation, superviewDimension, autosizeDimension),
-                                             0
-                                            );
-
-                    break;
-            }
-
-            return (newLocation, newDimension);
-        }
-
-        // horizontal/width
-        (newX, newW) = GetNewLocationAndDimension (true, superviewContentSize, _x, _width, autosize.Width);
-
-        // vertical/height
-        (newY, newH) = GetNewLocationAndDimension (false, superviewContentSize, _y, _height, autosize.Height);
+        int newX, newW, newY, newH;
+        newX = _x.GetLocation (superviewContentSize.Width, _width, autosize.Width, AutoSize);
+        newW = _width.GetDimension (newX, superviewContentSize.Width, autosize.Width, AutoSize);
+        newY = _y.GetLocation (superviewContentSize.Height, _height, autosize.Height, AutoSize);
+        newH = _height.GetDimension (newY, superviewContentSize.Height, autosize.Height, AutoSize);
 
-        Rectangle r = new (newX, newY, newW, newH);
+        Rectangle newFrame = new (newX, newY, newW, newH);
 
-        if (Frame != r)
+        if (Frame != newFrame)
         {
             // Set the frame. Do NOT use `Frame` as it overwrites X, Y, Width, and Height, making
             // the view LayoutStyle.Absolute.
-            SetFrame (r);
+            SetFrame (newFrame);
 
             if (_x is Pos.PosAbsolute)
             {

+ 53 - 0
UnitTests/View/Layout/DimTests.cs

@@ -1,6 +1,8 @@
 using System.Globalization;
 using System.Text;
 using Xunit.Abstractions;
+using static Terminal.Gui.Dim;
+
 
 // Alias Console to MockConsole so we don't accidentally use Console
 using Console = Terminal.Gui.FakeConsole;
@@ -22,6 +24,57 @@ public class DimTests
         Thread.CurrentThread.CurrentUICulture = culture;
     }
 
+    [Fact]
+    public void DimAbsolute_GetDimension_ReturnsCorrectValue ()
+    {
+        var dim = new DimAbsolute (10);
+        var result = dim.GetDimension (0, 100, 50, false);
+        Assert.Equal (10, result);
+    }
+
+    [Fact]
+    public void DimCombine_GetDimension_ReturnsCorrectValue ()
+    {
+        var dim1 = new DimAbsolute (10);
+        var dim2 = new DimAbsolute (20);
+        var dim = dim1 + dim2;
+        var result = dim.GetDimension (0, 100, 50, false);
+        Assert.Equal (30, result);
+    }
+
+    [Fact]
+    public void DimFactor_GetDimension_ReturnsCorrectValue ()
+    {
+        var dim = new DimFactor (0.5f);
+        var result = dim.GetDimension (0, 100, 50, false);
+        Assert.Equal (50, result);
+    }
+
+    [Fact]
+    public void DimFill_GetDimension_ReturnsCorrectValue ()
+    {
+        var dim = Dim.Fill ();
+        var result = dim.GetDimension (0, 100, 50, false);
+        Assert.Equal (100, result);
+    }
+
+    [Fact]
+    public void DimFunc_GetDimension_ReturnsCorrectValue ()
+    {
+        var dim = new DimFunc (() => 10);
+        var result = dim.GetDimension (0, 100, 50, false);
+        Assert.Equal (10, result);
+    }
+
+    [Fact]
+    public void DimView_GetDimension_ReturnsCorrectValue ()
+    {
+        var view = new View { Width = 10 };
+        var dim = new DimView (view, 1);
+        var result = dim.GetDimension (0, 100, 50, false);
+        Assert.Equal (10, result);
+    }
+
     // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
     // A new test that does not depend on Application is needed.
     [Fact]

+ 58 - 0
UnitTests/View/Layout/PosTests.cs

@@ -1,9 +1,67 @@
 using Xunit.Abstractions;
+using static Terminal.Gui.Dim;
+using static Terminal.Gui.Pos;
 
 namespace Terminal.Gui.ViewTests;
 
 public class PosTests (ITestOutputHelper output)
 {
+    [Fact]
+    public void PosAbsolute_GetLocation_ReturnsExpectedValue ()
+    {
+        var posAbsolute = new PosAbsolute (5);
+        var result = posAbsolute.GetLocation (10, new DimAbsolute (2), 1, false);
+        Assert.Equal (5, result);
+    }
+
+    [Fact]
+    public void PosAnchorEnd_GetLocation_ReturnsExpectedValue ()
+    {
+        var posAnchorEnd = new PosAnchorEnd (5);
+        var result = posAnchorEnd.GetLocation (10, new DimAbsolute (2), 1, false);
+        Assert.Equal (5, result);
+    }
+
+    [Fact]
+    public void PosCenter_GetLocation_ReturnsExpectedValue ()
+    {
+        var posCenter = new PosCenter ();
+        var result = posCenter.GetLocation (10, new DimAbsolute (2), 1, false);
+        Assert.Equal (4, result);
+    }
+
+    [Fact]
+    public void PosCombine_GetLocation_ReturnsExpectedValue ()
+    {
+        var posCombine = new PosCombine (true, new PosAbsolute (5), new PosAbsolute (3));
+        var result = posCombine.GetLocation (10, new DimAbsolute (2), 1, false);
+        Assert.Equal (8, result);
+    }
+
+    [Fact]
+    public void PosFactor_GetLocation_ReturnsExpectedValue ()
+    {
+        var posFactor = new PosFactor (0.5f);
+        var result = posFactor.GetLocation (10, new DimAbsolute (2), 1, false);
+        Assert.Equal (5, result);
+    }
+
+    [Fact]
+    public void PosFunc_GetLocation_ReturnsExpectedValue ()
+    {
+        var posFunc = new PosFunc (() => 5);
+        var result = posFunc.GetLocation (10, new DimAbsolute (2), 1, false);
+        Assert.Equal (5, result);
+    }
+
+    [Fact]
+    public void PosView_GetLocation_ReturnsExpectedValue ()
+    {
+        var posView = new PosView (new View { Frame = new Rectangle (5, 5, 10, 10) }, 0);
+        var result = posView.GetLocation (10, new DimAbsolute (2), 1, false);
+        Assert.Equal (5, result);
+    }
+
     [Fact]
     public void At_Equal ()
     {