浏览代码

Prototype Pos.Justify

Tig 1 年之前
父节点
当前提交
13134df595

+ 1 - 1
Terminal.Gui/Resources/config.json

@@ -24,7 +24,7 @@
   "Themes": [
     {
       "Default": {
-        "Dialog.DefaultButtonAlignment": "Center",
+        "Dialog.DefaultButtonAlignment": "Centered",
         "FrameView.DefaultBorderStyle": "Single",
         "Window.DefaultBorderStyle": "Single",
         "ColorSchemes": [

+ 283 - 147
Terminal.Gui/View/Layout/PosDim.cs

@@ -1,23 +1,10 @@
+using System.Diagnostics;
+using static System.Net.Mime.MediaTypeNames;
 using static Terminal.Gui.Dialog;
+using static Terminal.Gui.Dim;
 
 namespace Terminal.Gui;
 
-/// <summary>Determines the horizontal alignment of Views.</summary>
-public enum ViewAlignments
-{
-    /// <summary>Center-aligns the buttons (the default).</summary>
-    Center = 0,
-
-    /// <summary>Justifies the buttons</summary>
-    Justify,
-
-    /// <summary>Left-aligns the buttons</summary>
-    Left,
-
-    /// <summary>Right-aligns the buttons</summary>
-    Right
-}
-
 /// <summary>
 ///     Describes the position of a <see cref="View"/> which can be an absolute value, a percentage, centered, or
 ///     relative to the ending dimension. Integer values are implicitly convertible to an absolute <see cref="Pos"/>. These
@@ -203,12 +190,6 @@ public class Pos
     /// </example>
     public static Pos Center () { return new PosCenter (); }
 
-    public static Pos Justify (View[] views, ViewAlignments alignment)
-    {
-        return new PosJustify (views, alignment);
-    }
-
-
     /// <summary>Determines whether the specified object is equal to the current object.</summary>
     /// <param name="other">The object to compare with the current object. </param>
     /// <returns>
@@ -225,6 +206,14 @@ public class Pos
     /// <returns>The <see cref="Pos"/> returned from the function.</returns>
     public static Pos Function (Func<int> function) { return new PosFunc (function); }
 
+    /// <summary>
+    ///      Creates a <see cref="Pos"/> object that justifies a set of views according to the specified justification.
+    /// </summary>
+    /// <param name="views"></param>
+    /// <param name="justification"></param>
+    /// <returns></returns>
+    public static Pos Justify ( Justification justification) { return new PosJustify (justification); }
+
     /// <summary>Serves as the default hash function. </summary>
     /// <returns>A hash code for the current object.</returns>
     public override int GetHashCode () { return Anchor (0).GetHashCode (); }
@@ -361,14 +350,17 @@ public class Pos
     ///     height for y-coordinate calculation.
     /// </param>
     /// <param name="dim">The dimension of the View. It could be the current width or height.</param>
-    /// <param name="autosize">Obsolete; to be deprecated.</param>
-    /// <param name="autoSize">Obsolete; to be deprecated.</param>
+    /// <param name="us">The View that holds this Pos object.</param>
+    /// <param name="dimension">Width or Height</param>
     /// <returns>
     ///     The calculated position of the View. The way this position is calculated depends on the specific subclass of Pos
     ///     that
     ///     is used.
     /// </returns>
-    internal virtual int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize) { return Anchor (superviewDimension); }
+    internal virtual int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension)
+    {
+        return Anchor (superviewDimension);
+    }
 
     internal class PosAbsolute (int n) : Pos
     {
@@ -404,7 +396,7 @@ public class Pos
             return width - _offset;
         }
 
-        internal override int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize)
+        internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension)
         {
             int newLocation = Anchor (superviewDimension);
 
@@ -422,9 +414,9 @@ public class Pos
         public override string ToString () { return "Center"; }
         internal override int Anchor (int width) { return width / 2; }
 
-        internal override int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize)
+        internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension)
         {
-            int newDimension = Math.Max (dim.Calculate (0, superviewDimension, autosize, autoSize), 0);
+            int newDimension = Math.Max (dim.Calculate (0, superviewDimension, us, dimension), 0);
 
             return Anchor (superviewDimension - newDimension);
         }
@@ -450,11 +442,11 @@ public class Pos
             return la - ra;
         }
 
-        internal override int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize)
+        internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension)
         {
-            int newDimension = dim.Calculate (0, superviewDimension, autosize, autoSize);
-            int left = _left.Calculate (superviewDimension, dim, autosize, autoSize);
-            int right = _right.Calculate (superviewDimension, dim, autosize, autoSize);
+            int newDimension = dim.Calculate (0, superviewDimension, us, dimension);
+            int left = _left.Calculate (superviewDimension, dim, us, dimension);
+            int right = _right.Calculate (superviewDimension, dim, us, dimension);
 
             if (_add)
             {
@@ -474,6 +466,79 @@ public class Pos
         internal override int Anchor (int width) { return (int)(width * _factor); }
     }
 
+
+    /// <summary>
+    /// Enables justification of a set of views. 
+    /// </summary>
+    public class PosJustify : Pos
+    {
+        private readonly Justification _justification;
+
+        /// <summary>
+        /// Enables justification of a set of views.
+        /// </summary>
+        /// <param name="views">The set of views to justify according to <paramref name="justification"/>.</param>
+        /// <param name="justification"></param>
+        public PosJustify (Justification justification)
+        {
+            _justification = justification;
+        }
+
+        public override bool Equals (object other)
+        {
+            return other is PosJustify justify && justify._justification == _justification;
+        }
+
+        public override int GetHashCode () { return _justification.GetHashCode (); }
+
+
+        public override string ToString ()
+        {
+            return $"Justify(alignment={_justification})";
+        }
+
+        internal override int Anchor (int width)
+        {
+            return width;
+        }
+
+        internal override int Calculate (int superviewDimension, Dim dim, View us, Dimension dimension)
+        {
+            // Find all the views that are being justified - they have the same justification and opposite position as us
+            // Use linq to filter us.Superview.Subviews that match `dimension` and are at our same location in the opposite dimension (e.g. if dimension is Width, filter by Y)
+            // Then, pass the array of views to the Justify method
+            int [] dimensions;
+            int [] positions;
+
+            int ourIndex = 0;
+            if (dimension == Dimension.Width)
+            {
+                List<int> dimensionsList = new List<int> ();
+                for (int i = 0; i < us.SuperView.Subviews.Count; i++)
+                {
+                    if (us.SuperView.Subviews [i].Frame.Y == us.Frame.Y)
+                    {
+                        dimensionsList.Add (us.SuperView.Subviews [i].Frame.Width);
+
+                        if (us.SuperView.Subviews [i] == us)
+                        {
+                            ourIndex = dimensionsList.Count - 1;
+                        }
+                    }
+                }
+                dimensions = dimensionsList.ToArray ();
+                positions = new Justifier ().Justify (dimensions, _justification, superviewDimension);
+            }
+            else
+            {
+                dimensions = us.SuperView.Subviews.Where (v => v.Frame.X == us.Frame.X).Select(v => v.Frame.Height ).ToArray ();
+                positions = new Justifier ().Justify (dimensions, _justification, superviewDimension);
+            }
+
+            return positions [ourIndex];
+        }
+
+    }
     // Helper class to provide dynamic value by the execution of a function that returns an integer.
     internal class PosFunc (Func<int> n) : Pos
     {
@@ -484,11 +549,29 @@ public class Pos
         internal override int Anchor (int width) { return _function (); }
     }
 
+    /// <summary>
+    /// Describes which side of the view to use for the position.
+    /// </summary>
     public enum Side
     {
+        /// <summary>
+        /// The left (X) side of the view.
+        /// </summary>
         Left = 0,
+
+        /// <summary>
+        /// The top (Y) side of the view.
+        /// </summary>
         Top = 1,
+
+        /// <summary>
+        /// The right (X + Width) side of the view.
+        /// </summary>
         Right = 2,
+
+        /// <summary>
+        /// The bottom (Y + Height) side of the view.
+        /// </summary>
         Bottom = 3
     }
 
@@ -503,8 +586,8 @@ public class Pos
         {
             string sideString = side switch
             {
-                Side.Left => "x",
-                Side.Top => "y",
+                Side.Left => "left",
+                Side.Top => "top",
                 Side.Right => "right",
                 Side.Bottom => "bottom",
                 _ => "unknown"
@@ -530,92 +613,6 @@ public class Pos
             };
         }
     }
-
-
-    /// <summary>
-    /// Enables justification of a set of views. 
-    /// </summary>
-    public class PosJustify : Pos
-    {
-        private readonly View [] _views;
-        private readonly ViewAlignments _alignment;
-
-        /// <summary>
-        /// Enables justification of a set of views.
-        /// </summary>
-        /// <param name="views">The set of views to justify according to <paramref name="alignment"/>.</param>
-        /// <param name="alignment"></param>
-        public PosJustify (View [] views, ViewAlignments alignment)
-        {
-            _alignment = alignment;
-            _views = views;
-        }
-
-        public override bool Equals (object other)
-        {
-            return other is PosJustify justify && justify._views == _views && justify._alignment == _alignment;
-        }
-
-        public override int GetHashCode () { return _views.GetHashCode (); }
-
-
-        public override string ToString ()
-        {
-            return $"Justify(views={_views},alignment={_alignment})";
-        }
-
-        internal override int Anchor (int width)
-        {
-            if (_views.Length == 0 || !_views [0].IsInitialized)
-            {
-                return 0;
-            }
-            int spacing = 0;
-            switch (_alignment)
-            {
-                case ViewAlignments.Center:
-                    // Center spacing is sum of the widths of the views - width / number of views
-                    spacing = (width - _views.Select (v => v.Frame.Width).Sum ()) / _views.Length;
-                    
-                    // How do I know which view we are?
-                    View us = _views.Where (v => v.X.Equals (this)).First();
-
-                    if (_views [0] == us)
-                    {
-                        return spacing;
-                    }
-                    // Calculate the position of the previous (left or above us) view
-                    int previous = _views.Where (v => v.X.Equals (us)).First().Frame.Left;
-
-                    return previous + spacing;
-                //case ViewAlignments.Left:
-                //    return Left (width);
-                //case ViewAlignments.Right:
-                //    return Right (width);
-                //case ViewAlignments.Justify:
-                //    return Justify (width);
-                default:
-                    return 0;
-
-            }
-        }
-
-        //internal override int Calculate (int superviewDimension, Dim dim, int autosize, bool autoSize)
-        //{
-        //    // Assuming autosize is the size that the View would have if it were to automatically adjust its size based on its content
-        //    // and autoSize is a boolean value that indicates whether the View should automatically adjust its size based on its content
-        //    if (autoSize)
-        //    {
-        //        return autosize;
-        //    }
-        //    else
-        //    {
-        //        // Assuming dim.Calculate returns the calculated size of the View
-        //        return dim.Calculate (_views.Frame.Left, _views.Frame.Width, autosize, autoSize);
-        //    }
-        //}
-
-    }
 }
 
 /// <summary>
@@ -638,6 +635,15 @@ public class Pos
 ///             </listheader>
 ///             <item>
 ///                 <term>
+///                     <see cref="Dim.Auto"/>
+///                 </term>
+///                 <description>
+///                     Creates a <see cref="Dim"/> object that automatically sizes the view to fit
+///                     the view's SubViews.
+///                 </description>
+///             </item>
+///             <item>
+///                 <term>
 ///                     <see cref="Dim.Function(Func{int})"/>
 ///                 </term>
 ///                 <description>
@@ -687,6 +693,85 @@ public class Pos
 /// </remarks>
 public class Dim
 {
+    /// <summary>
+    ///     Specifies how <see cref="DimAuto"/> will compute the dimension.
+    /// </summary>
+    public enum DimAutoStyle
+    {
+        /// <summary>
+        ///     The dimension will be computed using both the view's <see cref="View.Text"/> and
+        ///     <see cref="View.Subviews"/> (whichever is larger).
+        /// </summary>
+        Auto,
+
+        /// <summary>
+        ///     The Subview in <see cref="View.Subviews"/> with the largest corresponding position plus dimension
+        ///     will determine the dimension.
+        ///     The corresponding dimension of the view's <see cref="View.Text"/> will be ignored.
+        /// </summary>
+        Subviews,
+
+        /// <summary>
+        ///     The corresponding dimension of the view's <see cref="View.Text"/>, formatted using the
+        ///     <see cref="View.TextFormatter"/> settings,
+        ///     will be used to determine the dimension.
+        ///     The corresponding dimensions of the <see cref="View.Subviews"/> will be ignored.
+        /// </summary>
+        Text
+    }
+
+
+    /// <summary>
+    /// 
+    /// </summary>
+    public enum Dimension
+    {
+        /// <summary>
+        /// No dimension specified.
+        /// </summary>
+        None = 0,
+
+        /// <summary>
+        /// The height dimension.
+        /// </summary>
+        Height = 1,
+
+        /// <summary>
+        /// The width dimension.
+        /// </summary>
+        Width = 2
+    }
+
+
+    /// <summary>
+    ///     Creates a <see cref="Dim"/> object that automatically sizes the view to fit all of the view's SubViews and/or Text.
+    /// </summary>
+    /// <example>
+    ///     This initializes a <see cref="View"/> with two SubViews. The view will be automatically sized to fit the two
+    ///     SubViews.
+    /// <code>
+    /// var button = new Button () { Text = "Click Me!", X = 1, Y = 1, Width = 10, Height = 1 };
+    /// var textField = new TextField { Text = "Type here", X = 1, Y = 2, Width = 20, Height = 1 };
+    /// var view = new Window () { Title = "MyWindow", X = 0, Y = 0, Width = Dim.Auto (), Height = Dim.Auto () };
+    /// view.Add (button, textField);
+    /// </code>
+    /// </example>
+    /// <returns>The <see cref="Dim"/> object.</returns>
+    /// <param name="style">
+    ///     Specifies how <see cref="DimAuto"/> will compute the dimension. The default is <see cref="DimAutoStyle.Auto"/>.
+    /// </param>
+    /// <param name="min">Specifies the minimum dimension that view will be automatically sized to.</param>
+    /// <param name="max">Specifies the maximum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED.</param>
+    public static Dim Auto (DimAutoStyle style = DimAutoStyle.Auto, Dim min = null, Dim max = null)
+    {
+        if (max != null)
+        {
+            throw new NotImplementedException (@"max is not implemented");
+        }
+
+        return new DimAuto (style, min, max);
+    }
+
     /// <summary>Determines whether the specified object is equal to the current object.</summary>
     /// <param name="other">The object to compare with the current object. </param>
     /// <returns>
@@ -817,24 +902,22 @@ public class Dim
 
     /// <summary>
     ///     Calculates and returns the dimension of a <see cref="View"/> object. It takes into account the location of the
-    ///     <see cref="View"/>, its current size, and whether it should automatically adjust its size based on its content.
+    ///     <see cref="View"/>, it's SuperView's ContentSize, and whether it should automatically adjust its size based on its content.
     /// </summary>
     /// <param name="location">
     ///     The starting point from where the size calculation begins. It could be the left edge for width calculation or the
     ///     top edge for height calculation.
     /// </param>
-    /// <param name="dimension">The current size of the View. It could be the current width or height.</param>
-    /// <param name="autosize">Obsolete; To be deprecated.</param>
-    /// <param name="autoSize">Obsolete; To be deprecated.</param>
+    /// <param name="superviewContentSize">The size of the SuperView's content. It could be width or height.</param>
+    /// <param name="us">The View that holds this Pos object.</param>
+    /// <param name="dimension">Width or Height</param>
     /// <returns>
     ///     The calculated size of the View. The way this size is calculated depends on the specific subclass of Dim that
     ///     is used.
     /// </returns>
-    internal virtual int Calculate (int location, int dimension, int autosize, bool autoSize)
+    internal virtual int Calculate (int location, int superviewContentSize, View us, Dimension dimension)
     {
-        int newDimension = Math.Max (Anchor (dimension - location), 0);
-
-        return autoSize && autosize > newDimension ? autosize : newDimension;
+        return Math.Max (Anchor (superviewContentSize - location), 0);
     }
 
     internal class DimAbsolute (int n) : Dim
@@ -845,15 +928,75 @@ public class Dim
         public override string ToString () { return $"Absolute({_n})"; }
         internal override int Anchor (int width) { return _n; }
 
-        internal override int Calculate (int location, int dimension, int autosize, bool autoSize)
+        internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension)
         {
             // DimAbsolute.Anchor (int width) ignores width and returns n
-            int newDimension = Math.Max (Anchor (0), 0);
-
-            return autoSize && autosize > newDimension ? autosize : newDimension;
+            return Math.Max (Anchor (0), 0);
         }
     }
 
+    internal class DimAuto (DimAutoStyle style, Dim min, Dim max) : Dim
+    {
+        internal readonly Dim _max = max;
+        internal readonly Dim _min = min;
+        internal readonly DimAutoStyle _style = style;
+        internal int Size;
+
+        public override bool Equals (object other) { return other is DimAuto auto && auto._min == _min && auto._max == _max && auto._style == _style; }
+        public override int GetHashCode () { return HashCode.Combine (base.GetHashCode (), _min, _max, _style); }
+        public override string ToString () { return $"Auto({_style},{_min},{_max})"; }
+
+        internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension)
+        {
+            if (us == null)
+            {
+                return _max?.Anchor (0) ?? 0;
+            }
+
+            var textSize = 0;
+            var subviewsSize = 0;
+
+            int autoMin = _min?.Anchor (superviewContentSize) ?? 0;
+
+            if (superviewContentSize < autoMin)
+            {
+                Debug.WriteLine ($"WARNING: DimAuto specifies a min size ({autoMin}), but the SuperView's bounds are smaller ({superviewContentSize}).");
+
+                return superviewContentSize;
+            }
+
+            if (_style is Dim.DimAutoStyle.Text or Dim.DimAutoStyle.Auto)
+            {
+                textSize = int.Max (autoMin, dimension == Dimension.Width ? us.TextFormatter.Size.Width : us.TextFormatter.Size.Height);
+            }
+
+            if (_style is Dim.DimAutoStyle.Subviews or Dim.DimAutoStyle.Auto)
+            {
+                subviewsSize = us.Subviews.Count == 0
+                                   ? 0
+                                   : us.Subviews
+                                         .Where (v => dimension == Dimension.Width ? v.X is not Pos.PosAnchorEnd : v.Y is not Pos.PosAnchorEnd)
+                                         .Max (v => dimension == Dimension.Width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height);
+            }
+
+            int max = int.Max (textSize, subviewsSize);
+
+            Thickness thickness = us.GetAdornmentsThickness ();
+
+            if (dimension == Dimension.Width)
+            {
+                max += thickness.Horizontal;
+            }
+            else
+            {
+                max += thickness.Vertical;
+            }
+
+            max = int.Max (max, autoMin);
+            return int.Min (max, _max?.Anchor (superviewContentSize) ?? superviewContentSize);
+        }
+
+    }
     internal class DimCombine (bool add, Dim left, Dim right) : Dim
     {
         internal bool _add = add;
@@ -874,10 +1017,10 @@ public class Dim
             return la - ra;
         }
 
-        internal override int Calculate (int location, int dimension, int autosize, bool autoSize)
+        internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension)
         {
-            int leftNewDim = _left.Calculate (location, dimension, autosize, autoSize);
-            int rightNewDim = _right.Calculate (location, dimension, autosize, autoSize);
+            int leftNewDim = _left.Calculate (location, superviewContentSize, us, dimension);
+            int rightNewDim = _right.Calculate (location, superviewContentSize, us, dimension);
 
             int newDimension;
 
@@ -890,8 +1033,9 @@ public class Dim
                 newDimension = Math.Max (0, leftNewDim - rightNewDim);
             }
 
-            return autoSize && autosize > newDimension ? autosize : newDimension;
+            return newDimension;
         }
+
     }
 
     internal class DimFactor (float factor, bool remaining = false) : Dim
@@ -905,11 +1049,9 @@ public class Dim
         public override string ToString () { return $"Factor({_factor},{_remaining})"; }
         internal override int Anchor (int width) { return (int)(width * _factor); }
 
-        internal override int Calculate (int location, int dimension, int autosize, bool autoSize)
+        internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension)
         {
-            int newDimension = _remaining ? Math.Max (Anchor (dimension - location), 0) : Anchor (dimension);
-
-            return autoSize && autosize > newDimension ? autosize : newDimension;
+            return _remaining ? Math.Max (Anchor (superviewContentSize - location), 0) : Anchor (superviewContentSize);
         }
     }
 
@@ -932,12 +1074,6 @@ public class Dim
         internal override int Anchor (int width) { return _function (); }
     }
 
-    public enum Dimension
-    {
-        Height = 0,
-        Width = 1
-    }
-
     internal class DimView : Dim
     {
         private readonly Dimension _side;
@@ -979,4 +1115,4 @@ public class Dim
             };
         }
     }
-}
+}

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

@@ -1025,10 +1025,10 @@ public partial class View
             autoSize = GetAutoSize ();
         }
 
-        int newX = _x.Calculate (superviewContentSize.Width, _width, autoSize.Width, AutoSize);
-        int newW = _width.Calculate (newX, superviewContentSize.Width, autoSize.Width, AutoSize);
-        int newY = _y.Calculate (superviewContentSize.Height, _height, autoSize.Height, AutoSize);
-        int newH = _height.Calculate (newY, superviewContentSize.Height, autoSize.Height, AutoSize);
+        int newX = _x.Calculate (superviewContentSize.Width, _width, this, Dim.Dimension.Width);
+        int newW = _width.Calculate (newX, superviewContentSize.Width, this, Dim.Dimension.Width);
+        int newY = _y.Calculate (superviewContentSize.Height, _height, this, Dim.Dimension.Height);
+        int newH = _height.Calculate (newY, superviewContentSize.Height, this, Dim.Dimension.Height);
 
         Rectangle newFrame = new (newX, newY, newW, newH);
 

+ 7 - 7
Terminal.Gui/Views/Dialog.cs

@@ -94,7 +94,7 @@ public class Dialog : Window
     }
 
     /// <summary>Determines how the <see cref="Dialog"/> <see cref="Button"/>s are aligned along the bottom of the dialog.</summary>
-    public ViewAlignments ButtonAlignment { get; set; }
+    public Justification ButtonAlignment { get; set; }
 
     /// <summary>Optional buttons to lay out at the bottom of the dialog.</summary>
     public Button [] Buttons
@@ -114,11 +114,11 @@ public class Dialog : Window
         }
     }
 
-    /// <summary>The default <see cref="ViewAlignments"/> for <see cref="Dialog"/>.</summary>
+    /// <summary>The default <see cref="Justification"/> for <see cref="Dialog"/>.</summary>
     /// <remarks>This property can be set in a Theme.</remarks>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     [JsonConverter (typeof (JsonStringEnumConverter))]
-    public static ViewAlignments DefaultButtonAlignment { get; set; } = ViewAlignments.Center;
+    public static Justification DefaultButtonAlignment { get; set; } = Justification.Centered;
 
     /// <summary>
     ///     Adds a <see cref="Button"/> to the <see cref="Dialog"/>, its layout will be controlled by the
@@ -185,7 +185,7 @@ public class Dialog : Window
 
         switch (ButtonAlignment)
         {
-            case ViewAlignments.Center:
+            case Justification.Centered:
                 // Center Buttons
                 shiftLeft = (Viewport.Width - buttonsWidth - _buttons.Count - 1) / 2 + 1;
 
@@ -208,7 +208,7 @@ public class Dialog : Window
 
                 break;
 
-            case ViewAlignments.Justify:
+            case Justification.Justified:
                 // Justify Buttons
                 // leftmost and rightmost buttons are hard against edges. The rest are evenly spaced.
 
@@ -243,7 +243,7 @@ public class Dialog : Window
 
                 break;
 
-            case ViewAlignments.Left:
+            case Justification.Left:
                 // Left Align Buttons
                 Button prevButton = _buttons [0];
                 prevButton.X = 0;
@@ -259,7 +259,7 @@ public class Dialog : Window
 
                 break;
 
-            case ViewAlignments.Right:
+            case Justification.Right:
                 // Right align buttons
                 shiftLeft = _buttons [_buttons.Count - 1].Frame.Width;
                 _buttons [_buttons.Count - 1].X = Pos.AnchorEnd (shiftLeft);

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

@@ -85,7 +85,7 @@ public class Wizard : Dialog
     {
         // Using Justify causes the Back and Next buttons to be hard justified against
         // the left and right edge
-        ButtonAlignment = ViewAlignments.Justify;
+        ButtonAlignment = Justification.Justified;
         BorderStyle = LineStyle.Double;
 
         //// Add a horiz separator

+ 4 - 4
UICatalog/Scenarios/ComputedLayout.cs

@@ -408,10 +408,10 @@ public class ComputedLayout : Scenario
         app.Add (rightButton);
 
 
-        // Center three buttons with 5 spaces between them
-        leftButton.X = Pos.Justify (buttons, ViewAlignments.Center);
-        centerButton.X = Pos.Justify (buttons, ViewAlignments.Center);
-        rightButton.X = Pos.Justify (buttons, ViewAlignments.Center);
+        // Center three buttons with 
+        leftButton.X = Pos.Justify (Justification.Centered);
+        centerButton.X = Pos.Justify (Justification.Centered);
+        rightButton.X = Pos.Justify (Justification.Centered);
 
         Application.Run (app);
         app.Dispose ();

+ 2 - 2
UICatalog/Scenarios/Dialogs.cs

@@ -137,7 +137,7 @@ public class Dialogs : Scenario
         {
             X = Pos.Right (label) + 1,
             Y = Pos.Top (label),
-            RadioLabels = new [] { "_Center", "_Justify", "_Left", "_Right" }
+            RadioLabels = new [] { "_Centered", "_Justified", "_Left", "_Right" }
         };
         frame.Add (styleRadioGroup);
 
@@ -265,7 +265,7 @@ public class Dialogs : Scenario
             dialog = new Dialog
             {
                 Title = titleEdit.Text,
-                ButtonAlignment = (ViewAlignments)styleRadioGroup.SelectedItem,
+                ButtonAlignment = (Justification)styleRadioGroup.SelectedItem,
                 Buttons = buttons.ToArray ()
             };
 

+ 3 - 3
UnitTests/Configuration/ThemeScopeTests.cs

@@ -29,12 +29,12 @@ public class ThemeScopeTests
     {
         Reset ();
         Assert.NotEmpty (Themes);
-        Assert.Equal (ViewAlignments.Center, Dialog.DefaultButtonAlignment);
+        Assert.Equal (Justification.Centered, Dialog.DefaultButtonAlignment);
 
-        Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = ViewAlignments.Right;
+        Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = Justification.Right;
 
         ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
-        Assert.Equal (ViewAlignments.Right, Dialog.DefaultButtonAlignment);
+        Assert.Equal (Justification.Right, Dialog.DefaultButtonAlignment);
         Reset ();
     }
 

+ 3 - 3
UnitTests/Configuration/ThemeTests.cs

@@ -77,15 +77,15 @@ public class ThemeTests
     public void TestSerialize_RoundTrip ()
     {
         var theme = new ThemeScope ();
-        theme ["Dialog.DefaultButtonAlignment"].PropertyValue = ViewAlignments.Right;
+        theme ["Dialog.DefaultButtonAlignment"].PropertyValue = Justification.Right;
 
         string json = JsonSerializer.Serialize (theme, _jsonOptions);
 
         var deserialized = JsonSerializer.Deserialize<ThemeScope> (json, _jsonOptions);
 
         Assert.Equal (
-                      ViewAlignments.Right,
-                      (ViewAlignments)deserialized ["Dialog.DefaultButtonAlignment"].PropertyValue
+                      Justification.Right,
+                      (Justification)deserialized ["Dialog.DefaultButtonAlignment"].PropertyValue
                      );
         Reset ();
     }

+ 43 - 43
UnitTests/Dialogs/DialogTests.cs

@@ -32,7 +32,7 @@ public class DialogTests
             Title = title,
             Width = width,
             Height = 1,
-            ButtonAlignment = ViewAlignments.Center,
+            ButtonAlignment = Justification.Centered,
             Buttons = [new Button { Text = btn1Text }]
         };
 
@@ -57,7 +57,7 @@ public class DialogTests
             Title = title,
             Width = width,
             Height = 1,
-            ButtonAlignment = ViewAlignments.Justify,
+            ButtonAlignment = Justification.Justified,
             Buttons = [new Button { Text = btn1Text }]
         };
 
@@ -82,7 +82,7 @@ public class DialogTests
             Title = title,
             Width = width,
             Height = 1,
-            ButtonAlignment = ViewAlignments.Right,
+            ButtonAlignment = Justification.Right,
             Buttons = [new Button { Text = btn1Text }]
         };
 
@@ -107,7 +107,7 @@ public class DialogTests
             Title = title,
             Width = width,
             Height = 1,
-            ButtonAlignment = ViewAlignments.Left,
+            ButtonAlignment = Justification.Left,
             Buttons = [new Button { Text = btn1Text }]
         };
 
@@ -155,7 +155,7 @@ public class DialogTests
         (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Center,
+                                                    Justification.Centered,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text },
@@ -172,7 +172,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                       title,
                                                       width,
-                                                      ViewAlignments.Justify,
+                                                      Justification.Justified,
                                                       new Button { Text = btn1Text },
                                                       new Button { Text = btn2Text },
                                                       new Button { Text = btn3Text },
@@ -189,7 +189,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                       title,
                                                       width,
-                                                      ViewAlignments.Right,
+                                                      Justification.Right,
                                                       new Button { Text = btn1Text },
                                                       new Button { Text = btn2Text },
                                                       new Button { Text = btn3Text },
@@ -206,7 +206,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                       title,
                                                       width,
-                                                      ViewAlignments.Left,
+                                                      Justification.Left,
                                                       new Button { Text = btn1Text },
                                                       new Button { Text = btn2Text },
                                                       new Button { Text = btn3Text },
@@ -248,7 +248,7 @@ public class DialogTests
         (runstate, Dialog dlg) = RunButtonTestDialog (
                                                       title,
                                                       width,
-                                                      ViewAlignments.Center,
+                                                      Justification.Centered,
                                                       new Button { Text = btn1Text },
                                                       new Button { Text = btn2Text },
                                                       new Button { Text = btn3Text },
@@ -280,7 +280,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Justify,
+                                                    Justification.Justified,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text },
@@ -296,7 +296,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Right,
+                                                    Justification.Right,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text },
@@ -312,7 +312,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Left,
+                                                    Justification.Left,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text },
@@ -353,7 +353,7 @@ public class DialogTests
         (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Center,
+                                                    Justification.Centered,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text },
@@ -370,7 +370,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Justify,
+                                                    Justification.Justified,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text },
@@ -387,7 +387,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Right,
+                                                    Justification.Right,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text },
@@ -404,7 +404,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Left,
+                                                    Justification.Left,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text },
@@ -447,7 +447,7 @@ public class DialogTests
         (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Center,
+                                                    Justification.Centered,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text },
@@ -464,7 +464,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Justify,
+                                                    Justification.Justified,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text },
@@ -481,7 +481,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Right,
+                                                    Justification.Right,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text },
@@ -498,7 +498,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Left,
+                                                    Justification.Left,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text },
@@ -530,7 +530,7 @@ public class DialogTests
         (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Center,
+                                                    Justification.Centered,
                                                     new Button { Text = btnText }
                                                    );
 
@@ -547,7 +547,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Justify,
+                                                    Justification.Justified,
                                                     new Button { Text = btnText }
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -562,7 +562,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Right,
+                                                    Justification.Right,
                                                     new Button { Text = btnText }
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -577,7 +577,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Left,
+                                                    Justification.Left,
                                                     new Button { Text = btnText }
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -594,7 +594,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Center,
+                                                    Justification.Centered,
                                                     new Button { Text = btnText }
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -609,7 +609,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Justify,
+                                                    Justification.Justified,
                                                     new Button { Text = btnText }
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -624,7 +624,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Right,
+                                                    Justification.Right,
                                                     new Button { Text = btnText }
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -639,7 +639,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Left,
+                                                    Justification.Left,
                                                     new Button { Text = btnText }
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -673,7 +673,7 @@ public class DialogTests
         (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Center,
+                                                    Justification.Centered,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text }
@@ -689,7 +689,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Justify,
+                                                    Justification.Justified,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text }
@@ -705,7 +705,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Right,
+                                                    Justification.Right,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text }
@@ -721,7 +721,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Left,
+                                                    Justification.Left,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text },
                                                     new Button { Text = btn3Text }
@@ -755,7 +755,7 @@ public class DialogTests
         (runstate, Dialog dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Center,
+                                                    Justification.Centered,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text }
                                                    );
@@ -770,7 +770,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Justify,
+                                                    Justification.Justified,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text }
                                                    );
@@ -785,7 +785,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Right,
+                                                    Justification.Right,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text }
                                                    );
@@ -800,7 +800,7 @@ public class DialogTests
         (runstate, dlg) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Left,
+                                                    Justification.Left,
                                                     new Button { Text = btn1Text },
                                                     new Button { Text = btn2Text }
                                                    );
@@ -837,7 +837,7 @@ public class DialogTests
         // Default (Center)
         button1 = new Button { Text = btn1Text };
         button2 = new Button { Text = btn2Text };
-        (runstate, dlg) = RunButtonTestDialog (title, width, ViewAlignments.Center, button1, button2);
+        (runstate, dlg) = RunButtonTestDialog (title, width, Justification.Centered, button1, button2);
         button1.Visible = false;
         RunIteration (ref runstate, ref firstIteration);
         buttonRow = $@"{CM.Glyphs.VLine}         {btn2} {CM.Glyphs.VLine}";
@@ -849,7 +849,7 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
         button1 = new Button { Text = btn1Text };
         button2 = new Button { Text = btn2Text };
-        (runstate, dlg) = RunButtonTestDialog (title, width, ViewAlignments.Justify, button1, button2);
+        (runstate, dlg) = RunButtonTestDialog (title, width, Justification.Justified, button1, button2);
         button1.Visible = false;
         RunIteration (ref runstate, ref firstIteration);
         buttonRow = $@"{CM.Glyphs.VLine}          {btn2}{CM.Glyphs.VLine}";
@@ -861,7 +861,7 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
         button1 = new Button { Text = btn1Text };
         button2 = new Button { Text = btn2Text };
-        (runstate, dlg) = RunButtonTestDialog (title, width, ViewAlignments.Right, button1, button2);
+        (runstate, dlg) = RunButtonTestDialog (title, width, Justification.Right, button1, button2);
         button1.Visible = false;
         RunIteration (ref runstate, ref firstIteration);
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -872,7 +872,7 @@ public class DialogTests
         Assert.Equal (width, buttonRow.Length);
         button1 = new Button { Text = btn1Text };
         button2 = new Button { Text = btn2Text };
-        (runstate, dlg) = RunButtonTestDialog (title, width, ViewAlignments.Left, button1, button2);
+        (runstate, dlg) = RunButtonTestDialog (title, width, Justification.Left, button1, button2);
         button1.Visible = false;
         RunIteration (ref runstate, ref firstIteration);
         buttonRow = $@"{CM.Glyphs.VLine}        {btn2}  {CM.Glyphs.VLine}";
@@ -1301,7 +1301,7 @@ public class DialogTests
         (runstate, Dialog _) = RunButtonTestDialog (
                                                     title,
                                                     width,
-                                                    ViewAlignments.Center,
+                                                    Justification.Centered,
                                                     new Button { Text = btnText }
                                                    );
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
@@ -1347,7 +1347,7 @@ public class DialogTests
         int width = buttonRow.Length;
         d.SetBufferSize (buttonRow.Length, 3);
 
-        (runstate, Dialog _) = RunButtonTestDialog (title, width, ViewAlignments.Center, null);
+        (runstate, Dialog _) = RunButtonTestDialog (title, width, Justification.Centered, null);
         TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
 
         End (runstate);
@@ -1356,7 +1356,7 @@ public class DialogTests
     private (RunState, Dialog) RunButtonTestDialog (
         string title,
         int width,
-        ViewAlignments align,
+        Justification align,
         params Button [] btns
     )
     {

+ 416 - 121
UnitTests/View/Layout/DimTests.cs

@@ -25,61 +25,62 @@ public class DimTests
     }
 
     [Fact]
-    public void DimAbsolute_GetDimension_ReturnsCorrectValue ()
+    public void DimAbsolute_Calculate_ReturnsCorrectValue ()
     {
         var dim = new DimAbsolute (10);
-        var result = dim.Calculate (0, 100, 50, false);
+        var result = dim.Calculate (0, 100, null, Dim.Dimension.None);
         Assert.Equal (10, result);
     }
 
     [Fact]
-    public void DimCombine_GetDimension_ReturnsCorrectValue ()
+    public void DimCombine_Calculate_ReturnsCorrectValue ()
     {
         var dim1 = new DimAbsolute (10);
         var dim2 = new DimAbsolute (20);
         var dim = dim1 + dim2;
-        var result = dim.Calculate (0, 100, 50, false);
+        var result = dim.Calculate (0, 100, null, Dim.Dimension.None);
         Assert.Equal (30, result);
     }
 
     [Fact]
-    public void DimFactor_GetDimension_ReturnsCorrectValue ()
+    public void DimFactor_Calculate_ReturnsCorrectValue ()
     {
         var dim = new DimFactor (0.5f);
-        var result = dim.Calculate (0, 100, 50, false);
+        var result = dim.Calculate (0, 100, null, Dim.Dimension.None);
         Assert.Equal (50, result);
     }
 
     [Fact]
-    public void DimFill_GetDimension_ReturnsCorrectValue ()
+    public void DimFill_Calculate_ReturnsCorrectValue ()
     {
         var dim = Dim.Fill ();
-        var result = dim.Calculate (0, 100, 50, false);
+        var result = dim.Calculate (0, 100, null, Dim.Dimension.None);
         Assert.Equal (100, result);
     }
 
     [Fact]
-    public void DimFunc_GetDimension_ReturnsCorrectValue ()
+    public void DimFunc_Calculate_ReturnsCorrectValue ()
     {
         var dim = new DimFunc (() => 10);
-        var result = dim.Calculate (0, 100, 50, false);
+        var result = dim.Calculate (0, 100, null, Dim.Dimension.None);
         Assert.Equal (10, result);
     }
 
     [Fact]
-    public void DimView_GetDimension_ReturnsCorrectValue ()
+    public void DimView_Calculate_ReturnsCorrectValue ()
     {
         var view = new View { Width = 10 };
         var dim = new DimView (view, Dimension.Width);
-        var result = dim.Calculate (0, 100, 50, false);
+        var result = dim.Calculate (0, 100, null, Dim.Dimension.None);
         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]
     [AutoInitShutdown]
-    public void Dim_Add_Operator ()
+    public void Add_Operator ()
     {
         Toplevel top = new ();
 
@@ -372,7 +373,7 @@ public class DimTests
     /// <summary>This is an intentionally obtuse test. See https://github.com/gui-cs/Terminal.Gui/issues/2461</summary>
     [Fact]
     [TestRespondersDisposed]
-    public void DimCombine_ObtuseScenario_Throw_If_SuperView_Refs_SubView ()
+    public void Combine_ObtuseScenario_Throw_If_SuperView_Refs_SubView ()
     {
         var t = new View { Width = 80, Height = 25 };
 
@@ -420,55 +421,159 @@ public class DimTests
 
     // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
     // TODO: A new test that calls SetRelativeLayout directly is needed.
+    [Fact]
+    [TestRespondersDisposed]
+    public void Combine_View_Not_Added_Throws ()
+    {
+        var t = new View { Width = 80, Height = 50 };
+
+        var super = new View { Width = Dim.Width (t) - 2, Height = Dim.Height (t) - 2 };
+        t.Add (super);
+
+        var sub = new View ();
+        super.Add (sub);
+
+        var v1 = new View { Width = Dim.Width (super) - 2, Height = Dim.Height (super) - 2 };
+        var v2 = new View { Width = Dim.Width (v1) - 2, Height = Dim.Height (v1) - 2 };
+        sub.Add (v1);
+
+        // v2 not added to sub; should cause exception on Layout since it's referenced by sub.
+        sub.Width = Dim.Fill () - Dim.Width (v2);
+        sub.Height = Dim.Fill () - Dim.Height (v2);
+
+        t.BeginInit ();
+        t.EndInit ();
+
+        Assert.Throws<InvalidOperationException> (() => t.LayoutSubviews ());
+        t.Dispose ();
+        v2.Dispose ();
+    }
+
+    // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
+    // TODO: A new test that calls SetRelativeLayout directly is needed.
+    [Fact]
+    [TestRespondersDisposed]
+    public void
+        Dim_Validation_Does_Not_Throw_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute ()
+    {
+        var t = new View { Width = 80, Height = 25, Text = "top" };
+
+        var w = new Window { Width = Dim.Fill (), Height = Dim.Sized (10) };
+        var v = new View { Width = Dim.Width (w) - 2, Height = Dim.Percent (10), Text = "v" };
+
+        w.Add (v);
+        t.Add (w);
+
+        Assert.Equal (LayoutStyle.Absolute, t.LayoutStyle);
+        Assert.Equal (LayoutStyle.Computed, w.LayoutStyle);
+        Assert.Equal (LayoutStyle.Computed, v.LayoutStyle);
+
+        t.LayoutSubviews ();
+        Assert.Equal (2, v.Width = 2);
+        Assert.Equal (2, v.Height = 2);
+
+        // Force v to be LayoutStyle.Absolute;
+        v.Frame = new Rectangle (0, 1, 3, 4);
+        Assert.Equal (LayoutStyle.Absolute, v.LayoutStyle);
+        t.LayoutSubviews ();
+
+        Assert.Equal (2, v.Width = 2);
+        Assert.Equal (2, v.Height = 2);
+        t.Dispose ();
+    }
+
+    [Fact]
+    public void Fill_Equal ()
+    {
+        var margin1 = 0;
+        var margin2 = 0;
+        Dim dim1 = Dim.Fill (margin1);
+        Dim dim2 = Dim.Fill (margin2);
+        Assert.Equal (dim1, dim2);
+    }
+
+    // Tests that Dim.Fill honors the margin parameter correctly
     [Theory]
-    [AutoInitShutdown]
-    [InlineData (0, true)]
-    [InlineData (0, false)]
-    [InlineData (50, true)]
-    [InlineData (50, false)]
-    public void DimPercentPlusOne (int startingDistance, bool testHorizontal)
+    [InlineData (0, true, 25)]
+    [InlineData (0, false, 25)]
+    [InlineData (1, true, 24)]
+    [InlineData (1, false, 24)]
+    [InlineData (2, true, 23)]
+    [InlineData (2, false, 23)]
+    [InlineData (-2, true, 27)]
+    [InlineData (-2, false, 27)]
+    public void Fill_Margin (int margin, bool width, int expected)
     {
-        var container = new View { Width = 100, Height = 100 };
+        var super = new View { Width = 25, Height = 25 };
 
-        var label = new Label
+        var view = new View
         {
-            AutoSize = false,
-            X = testHorizontal ? startingDistance : 0,
-            Y = testHorizontal ? 0 : startingDistance,
-            Width = testHorizontal ? Dim.Percent (50) + 1 : 1,
-            Height = testHorizontal ? 1 : Dim.Percent (50) + 1
+            X = 0,
+            Y = 0,
+            Width = width ? Dim.Fill (margin) : 1,
+            Height = width ? 1 : Dim.Fill (margin)
         };
 
-        container.Add (label);
-        var top = new Toplevel ();
-        top.Add (container);
-        top.BeginInit ();
-        top.EndInit ();
-        top.LayoutSubviews ();
+        super.Add (view);
+        super.BeginInit ();
+        super.EndInit ();
+        super.LayoutSubviews ();
 
-        Assert.Equal (100, container.Frame.Width);
-        Assert.Equal (100, container.Frame.Height);
+        Assert.Equal (25, super.Frame.Width);
+        Assert.Equal (25, super.Frame.Height);
 
-        if (testHorizontal)
+        if (width)
         {
-            Assert.Equal (51, label.Frame.Width);
-            Assert.Equal (1, label.Frame.Height);
+            Assert.Equal (expected, view.Frame.Width);
+            Assert.Equal (1, view.Frame.Height);
         }
         else
         {
-            Assert.Equal (1, label.Frame.Width);
-            Assert.Equal (51, label.Frame.Height);
+            Assert.Equal (1, view.Frame.Width);
+            Assert.Equal (expected, view.Frame.Height);
         }
     }
 
-    [Fact]
-    public void Fill_Equal ()
+    // Tests that Dim.Fill fills the dimension REMAINING from the View's X position to the end of the super view's width
+    [Theory]
+    [InlineData (0, true, 25)]
+    [InlineData (0, false, 25)]
+    [InlineData (1, true, 24)]
+    [InlineData (1, false, 24)]
+    [InlineData (2, true, 23)]
+    [InlineData (2, false, 23)]
+    [InlineData (-2, true, 27)]
+    [InlineData (-2, false, 27)]
+    public void Fill_Offset (int offset, bool width, int expected)
     {
-        var margin1 = 0;
-        var margin2 = 0;
-        Dim dim1 = Dim.Fill (margin1);
-        Dim dim2 = Dim.Fill (margin2);
-        Assert.Equal (dim1, dim2);
+        var super = new View { Width = 25, Height = 25 };
+
+        var view = new View
+        {
+            X = width ? offset : 0,
+            Y = width ? 0 : offset,
+            Width = width ? Dim.Fill () : 1,
+            Height = width ? 1 : Dim.Fill ()
+        };
+
+        super.Add (view);
+        super.BeginInit ();
+        super.EndInit ();
+        super.LayoutSubviews ();
+
+        Assert.Equal (25, super.Frame.Width);
+        Assert.Equal (25, super.Frame.Height);
+
+        if (width)
+        {
+            Assert.Equal (expected, view.Frame.Width);
+            Assert.Equal (1, view.Frame.Height);
+        }
+        else
+        {
+            Assert.Equal (1, view.Frame.Width);
+            Assert.Equal (expected, view.Frame.Height);
+        }
     }
 
     // TODO: Other Dim.Height tests (e.g. Equal?)
@@ -670,45 +775,45 @@ public class DimTests
 
         t.Ready += (s, e) =>
                    {
-                                            Assert.Equal ("Absolute(100)", w.Width.ToString ());
-                                            Assert.Equal ("Absolute(100)", w.Height.ToString ());
-                                            Assert.Equal (100, w.Frame.Width);
-                                            Assert.Equal (100, w.Frame.Height);
-
-                                            Assert.Equal ("Factor(0.5,False)", f1.Width.ToString ());
-                                            Assert.Equal ("Absolute(5)", f1.Height.ToString ());
-                                            Assert.Equal (49, f1.Frame.Width); // 50-1=49
-                                            Assert.Equal (5, f1.Frame.Height);
-
-                                            Assert.Equal ("Fill(0)", f2.Width.ToString ());
-                                            Assert.Equal ("Absolute(5)", f2.Height.ToString ());
-                                            Assert.Equal (49, f2.Frame.Width); // 50-1=49
-                                            Assert.Equal (5, f2.Frame.Height);
-
-                    #if DEBUG
+                       Assert.Equal ("Absolute(100)", w.Width.ToString ());
+                       Assert.Equal ("Absolute(100)", w.Height.ToString ());
+                       Assert.Equal (100, w.Frame.Width);
+                       Assert.Equal (100, w.Frame.Height);
+
+                       Assert.Equal ("Factor(0.5,False)", f1.Width.ToString ());
+                       Assert.Equal ("Absolute(5)", f1.Height.ToString ());
+                       Assert.Equal (49, f1.Frame.Width); // 50-1=49
+                       Assert.Equal (5, f1.Frame.Height);
+
+                       Assert.Equal ("Fill(0)", f2.Width.ToString ());
+                       Assert.Equal ("Absolute(5)", f2.Height.ToString ());
+                       Assert.Equal (49, f2.Frame.Width); // 50-1=49
+                       Assert.Equal (5, f2.Frame.Height);
+
+#if DEBUG
                        Assert.Equal ($"Combine(View(Width,FrameView(f1){f1.Border.Frame})-Absolute(2))", v1.Width.ToString ());
-                    #else
+#else
                        Assert.Equal ($"Combine(View(Width,FrameView(){f1.Border.Frame})-Absolute(2))", v1.Width.ToString ());
-                    #endif
+#endif
                        Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ());
                        Assert.Equal (47, v1.Frame.Width); // 49-2=47
                        Assert.Equal (89, v1.Frame.Height); // 98-5-2-2=89
 
-                   #if DEBUG
+#if DEBUG
                        Assert.Equal (
                                      $"Combine(View(Width,FrameView(f2){f2.Frame})-Absolute(2))",
                                      v2.Width.ToString ()
-                   #else
+#else
                        Assert.Equal (
                                      $"Combine(View(Width,FrameView(){f2.Frame})-Absolute(2))",
                                      v2.Width.ToString ()
-                   #endif
+#endif
                                     );
-                   #if DEBUG
+#if DEBUG
                        Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ());
-                   #else
+#else
                        Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ());
-                   #endif
+#endif
                        Assert.Equal (47, v2.Frame.Width); // 49-2=47
                        Assert.Equal (89, v2.Frame.Height); // 98-5-2-2=89
 
@@ -757,22 +862,22 @@ public class DimTests
                        Assert.Equal (5, f2.Frame.Height);
 
                        v1.Text = "Button1";
-                   #if DEBUG
+#if DEBUG
                        Assert.Equal ($"Combine(View(Width,FrameView(f1){f1.Frame})-Absolute(2))", v1.Width.ToString ());
-                   #else
+#else
                        Assert.Equal ($"Combine(View(Width,FrameView(){f1.Frame})-Absolute(2))", v1.Width.ToString ());
-                   #endif
+#endif
                        Assert.Equal ("Combine(Fill(0)-Absolute(2))", v1.Height.ToString ());
                        Assert.Equal (97, v1.Frame.Width);   // 99-2=97
                        Assert.Equal (189, v1.Frame.Height); // 198-2-7=189
 
                        v2.Text = "Button2";
 
-                   #if DEBUG
-                   Assert.Equal ( $"Combine(View(Width,FrameView(f2){f2.Frame})-Absolute(2))", v2.Width.ToString ());
-                   #else
+#if DEBUG
+                       Assert.Equal ($"Combine(View(Width,FrameView(f2){f2.Frame})-Absolute(2))", v2.Width.ToString ());
+#else
                        Assert.Equal ($"Combine(View(Width,FrameView(){f2.Frame})-Absolute(2))", v2.Width.ToString ());
-                   #endif
+#endif
                        Assert.Equal ("Combine(Fill(0)-Absolute(2))", v2.Height.ToString ());
                        Assert.Equal (97, v2.Frame.Width);   // 99-2=97
                        Assert.Equal (189, v2.Frame.Height); // 198-2-7=189
@@ -782,7 +887,7 @@ public class DimTests
                        Assert.Equal ("Factor(0.1,False)", v3.Height.ToString ());
 
                        // 198*10%=19 * Percent is related to the super-view if it isn't null otherwise the view width
-                       Assert.Equal (19, v3.Frame.Width );
+                       Assert.Equal (19, v3.Frame.Width);
                        // 199*10%=19
                        Assert.Equal (19, v3.Frame.Height);
 
@@ -793,20 +898,20 @@ public class DimTests
                        Assert.Equal (50, v4.Frame.Width);
                        Assert.Equal (50, v4.Frame.Height);
                        v4.AutoSize = true;
-                       Assert.Equal ("Absolute(11)", v4.Width.ToString ());
-                       Assert.Equal ("Absolute(1)", v4.Height.ToString ());
+                       Assert.Equal (Dim.Auto (DimAutoStyle.Text), v4.Width);
+                       Assert.Equal (Dim.Auto (DimAutoStyle.Text), v4.Height);
                        Assert.Equal (11, v4.Frame.Width); // 11 is the text length and because is Dim.DimAbsolute
                        Assert.Equal (1, v4.Frame.Height); // 1 because is Dim.DimAbsolute
 
                        v5.Text = "Button5";
 
-                   #if DEBUG
+#if DEBUG
                        Assert.Equal ($"Combine(View(Width,Button(v1){v1.Frame})-View(Width,Button(v3){v3.Frame}))", v5.Width.ToString ());
                        Assert.Equal ($"Combine(View(Height,Button(v1){v1.Frame})-View(Height,Button(v3){v3.Frame}))", v5.Height.ToString ());
-                   #else
+#else
                        Assert.Equal ($"Combine(View(Width,Button(){v1.Frame})-View(Width,Button(){v3.Frame}))", v5.Width.ToString ());
                        Assert.Equal ($"Combine(View(Height,Button(){v1.Frame})-View(Height,Button(){v3.Frame}))", v5.Height.ToString ());
-                   #endif
+#endif
 
                        Assert.Equal (78, v5.Frame.Width);   // 97-9=78
                        Assert.Equal (170, v5.Frame.Height); // 189-19=170
@@ -880,6 +985,89 @@ public class DimTests
         Assert.Throws<ArgumentException> (() => dim = Dim.Percent (1000001));
     }
 
+    [Theory]
+    [InlineData (0, false, true, 12)]
+    [InlineData (0, false, false, 12)]
+    [InlineData (1, false, true, 12)]
+    [InlineData (1, false, false, 12)]
+    [InlineData (2, false, true, 12)]
+    [InlineData (2, false, false, 12)]
+
+    [InlineData (0, true, true, 12)]
+    [InlineData (0, true, false, 12)]
+    [InlineData (1, true, true, 12)]
+    [InlineData (1, true, false, 12)]
+    [InlineData (2, true, true, 11)]
+    [InlineData (2, true, false, 11)]
+    public void Percent_Position (int position, bool usePosition, bool width, int expected)
+    {
+        var super = new View { Width = 25, Height = 25 };
+
+        var view = new View
+        {
+            X = width ? position : 0,
+            Y = width ? 0 : position,
+            Width = width ? Dim.Percent (50, usePosition) : 1,
+            Height = width ? 1 : Dim.Percent (50, usePosition)
+        };
+
+        super.Add (view);
+        super.BeginInit ();
+        super.EndInit ();
+        super.LayoutSubviews ();
+
+        Assert.Equal (25, super.Frame.Width);
+        Assert.Equal (25, super.Frame.Height);
+
+        if (width)
+        {
+            Assert.Equal (expected, view.Frame.Width);
+            Assert.Equal (1, view.Frame.Height);
+        }
+        else
+        {
+            Assert.Equal (1, view.Frame.Width);
+            Assert.Equal (expected, view.Frame.Height);
+        }
+    }
+
+    [Theory]
+    [InlineData (0, true)]
+    [InlineData (0, false)]
+    [InlineData (50, true)]
+    [InlineData (50, false)]
+    public void Percent_PlusOne (int startingDistance, bool testHorizontal)
+    {
+        var super = new View { Width = 100, Height = 100 };
+
+        var view = new View
+        {
+            X = testHorizontal ? startingDistance : 0,
+            Y = testHorizontal ? 0 : startingDistance,
+            Width = testHorizontal ? Dim.Percent (50) + 1 : 1,
+            Height = testHorizontal ? 1 : Dim.Percent (50) + 1
+        };
+
+        super.Add (view);
+        super.BeginInit ();
+        super.EndInit ();
+        super.LayoutSubviews ();
+
+        Assert.Equal (100, super.Frame.Width);
+        Assert.Equal (100, super.Frame.Height);
+
+        if (testHorizontal)
+        {
+            Assert.Equal (51, view.Frame.Width);
+            Assert.Equal (1, view.Frame.Height);
+        }
+        else
+        {
+            Assert.Equal (1, view.Frame.Width);
+            Assert.Equal (51, view.Frame.Height);
+        }
+    }
+
     [Fact]
     public void Percent_SetsValue ()
     {
@@ -898,47 +1086,24 @@ public class DimTests
     // TODO: A new test that calls SetRelativeLayout directly is needed.
     [Fact]
     [TestRespondersDisposed]
-    public void PosCombine_View_Not_Added_Throws ()
+    public void Referencing_SuperView_Does_Not_Throw ()
     {
-        var t = new View { Width = 80, Height = 50 };
-
-        var super = new View { Width = Dim.Width (t) - 2, Height = Dim.Height (t) - 2 };
-        t.Add (super);
-
-        var sub = new View ();
-        super.Add (sub);
-
-        var v1 = new View { Width = Dim.Width (super) - 2, Height = Dim.Height (super) - 2 };
-        var v2 = new View { Width = Dim.Width (v1) - 2, Height = Dim.Height (v1) - 2 };
-        sub.Add (v1);
-
-        // v2 not added to sub; should cause exception on Layout since it's referenced by sub.
-        sub.Width = Dim.Fill () - Dim.Width (v2);
-        sub.Height = Dim.Fill () - Dim.Height (v2);
-
-        t.BeginInit ();
-        t.EndInit ();
+        var super = new View { Width = 10, Height = 10, Text = "super" };
 
-        Assert.Throws<InvalidOperationException> (() => t.LayoutSubviews ());
-        t.Dispose ();
-        v2.Dispose ();
-    }
+        var view = new View
+        {
+            Width = Dim.Width (super), // this is allowed
+            Height = Dim.Height (super), // this is allowed
+            Text = "view"
+        };
 
-    [Fact]
-    [TestRespondersDisposed]
-    public void SetsValue ()
-    {
-        var testVal = Rectangle.Empty;
-        var testValView = new View { Frame = testVal };
-        Dim dim = Dim.Width (testValView);
-        Assert.Equal ($"View(Width,View(){testVal})", dim.ToString ());
-        testValView.Dispose ();
+        super.Add (view);
+        super.BeginInit ();
+        super.EndInit ();
 
-        testVal = new Rectangle (1, 2, 3, 4);
-        testValView = new View { Frame = testVal };
-        dim = Dim.Width (testValView);
-        Assert.Equal ($"View(Width,View(){testVal})", dim.ToString ());
-        testValView.Dispose ();
+        Exception exception = Record.Exception (super.LayoutSubviews);
+        Assert.Null (exception);
+        super.Dispose ();
     }
 
     [Fact]
@@ -982,6 +1147,119 @@ public class DimTests
         Assert.Equal ($"Absolute({testVal})", dim.ToString ());
     }
 
+    // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
+    // TODO: A new test that calls SetRelativeLayout directly is needed.
+    [Fact]
+    [AutoInitShutdown]
+    public void Subtract_Operator ()
+    {
+        Toplevel top = new Toplevel ();
+
+        var view = new View { X = 0, Y = 0, Width = 20, Height = 0 };
+        var field = new TextField { X = 0, Y = Pos.Bottom (view), Width = 20 };
+        var count = 20;
+        List<Label> listLabels = new ();
+
+        for (var i = 0; i < count; i++)
+        {
+            field.Text = $"Label {i}";
+            var label = new Label { X = 0, Y = view.Viewport.Height, /*Width = 20,*/ Text = field.Text };
+            view.Add (label);
+            Assert.Equal ($"Label {i}", label.Text);
+            Assert.Equal ($"Absolute({i})", label.Y.ToString ());
+            listLabels.Add (label);
+
+            Assert.Equal ($"Absolute({i})", view.Height.ToString ());
+            view.Height += 1;
+            Assert.Equal ($"Absolute({i + 1})", view.Height.ToString ());
+        }
+
+        field.KeyDown += (s, k) =>
+                         {
+                             if (k.KeyCode == KeyCode.Enter)
+                             {
+                                 Assert.Equal ($"Label {count - 1}", listLabels [count - 1].Text);
+                                 view.Remove (listLabels [count - 1]);
+                                 listLabels [count - 1].Dispose ();
+
+                                 Assert.Equal ($"Absolute({count})", view.Height.ToString ());
+                                 view.Height -= 1;
+                                 count--;
+                                 Assert.Equal ($"Absolute({count})", view.Height.ToString ());
+                             }
+                         };
+
+        Application.Iteration += (s, a) =>
+                                 {
+                                     while (count > 0)
+                                     {
+                                         field.NewKeyDownEvent (new Key (KeyCode.Enter));
+                                     }
+
+                                     Application.RequestStop ();
+                                 };
+
+        var win = new Window ();
+        win.Add (view);
+        win.Add (field);
+
+        top.Add (win);
+
+        Application.Run (top);
+        top.Dispose ();
+
+        Assert.Equal (0, count);
+    }
+
+    // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
+    // TODO: A new test that calls SetRelativeLayout directly is needed.
+    [Fact]
+    [TestRespondersDisposed]
+    public void SyperView_Referencing_SubView_Throws ()
+    {
+        var super = new View { Width = 10, Height = 10, Text = "super" };
+        var view2 = new View { Width = 10, Height = 10, Text = "view2" };
+
+        var view = new View
+        {
+            Width = Dim.Width (view2), // this is not allowed
+            Height = Dim.Height (view2), // this is not allowed
+            Text = "view"
+        };
+
+        view.Add (view2);
+        super.Add (view);
+        super.BeginInit ();
+        super.EndInit ();
+
+        Assert.Throws<InvalidOperationException> (super.LayoutSubviews);
+        super.Dispose ();
+    }
+
+    // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
+    // TODO: A new test that calls SetRelativeLayout directly is needed.
+    [Fact]
+    [TestRespondersDisposed]
+    public void Validation_Does_Not_Throw_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Null ()
+    {
+        var t = new View { Width = 80, Height = 25, Text = "top" };
+
+        var w = new Window
+        {
+            X = 1,
+            Y = 2,
+            Width = 4,
+            Height = 5,
+            Title = "w"
+        };
+        t.Add (w);
+        t.LayoutSubviews ();
+
+        Assert.Equal (3, w.Width = 3);
+        Assert.Equal (4, w.Height = 4);
+        t.Dispose ();
+    }
+
     [Fact]
     [TestRespondersDisposed]
     public void Width_Equals ()
@@ -1039,4 +1317,21 @@ public class DimTests
         Dim dim = Dim.Width (null);
         Assert.Throws<NullReferenceException> (() => dim.ToString ());
     }
+
+    [Fact]
+    [TestRespondersDisposed]
+    public void Width_SetsValue ()
+    {
+        var testVal = Rectangle.Empty;
+        var testValView = new View { Frame = testVal };
+        Dim dim = Dim.Width (testValView);
+        Assert.Equal ($"View(Width,View(){testVal})", dim.ToString ());
+        testValView.Dispose ();
+
+        testVal = new Rectangle (1, 2, 3, 4);
+        testValView = new View { Frame = testVal };
+        dim = Dim.Width (testValView);
+        Assert.Equal ($"View(Width,View(){testVal})", dim.ToString ());
+        testValView.Dispose ();
+    }
 }

+ 188 - 42
UnitTests/View/Layout/PosTests.cs

@@ -7,58 +7,58 @@ namespace Terminal.Gui.ViewTests;
 public class PosTests (ITestOutputHelper output)
 {
     [Fact]
-    public void PosAbsolute_GetLocation_ReturnsExpectedValue ()
+    public void PosAbsolute_Calculate_ReturnsExpectedValue ()
     {
         var posAbsolute = new PosAbsolute (5);
-        var result = posAbsolute.Calculate (10, new DimAbsolute (2), 1, false);
+        var result = posAbsolute.Calculate (10, new DimAbsolute (2), null, Dimension.None);
         Assert.Equal (5, result);
     }
 
     [Fact]
-    public void PosAnchorEnd_GetLocation_ReturnsExpectedValue ()
+    public void PosAnchorEnd_Calculate_ReturnsExpectedValue ()
     {
         var posAnchorEnd = new PosAnchorEnd (5);
-        var result = posAnchorEnd.Calculate (10, new DimAbsolute (2), 1, false);
+        var result = posAnchorEnd.Calculate (10, new DimAbsolute (2), null, Dimension.None);
         Assert.Equal (5, result);
     }
 
     [Fact]
-    public void PosCenter_GetLocation_ReturnsExpectedValue ()
+    public void PosCenter_Calculate_ReturnsExpectedValue ()
     {
         var posCenter = new PosCenter ();
-        var result = posCenter.Calculate (10, new DimAbsolute (2), 1, false);
+        var result = posCenter.Calculate (10, new DimAbsolute (2), null, Dimension.None);
         Assert.Equal (4, result);
     }
 
     [Fact]
-    public void PosCombine_GetLocation_ReturnsExpectedValue ()
+    public void PosCombine_Calculate_ReturnsExpectedValue ()
     {
         var posCombine = new PosCombine (true, new PosAbsolute (5), new PosAbsolute (3));
-        var result = posCombine.Calculate (10, new DimAbsolute (2), 1, false);
+        var result = posCombine.Calculate (10, new DimAbsolute (2), null, Dimension.None);
         Assert.Equal (8, result);
     }
 
     [Fact]
-    public void PosFactor_GetLocation_ReturnsExpectedValue ()
+    public void PosFactor_Calculate_ReturnsExpectedValue ()
     {
         var posFactor = new PosFactor (0.5f);
-        var result = posFactor.Calculate (10, new DimAbsolute (2), 1, false);
+        var result = posFactor.Calculate (10, new DimAbsolute (2), null, Dimension.None);
         Assert.Equal (5, result);
     }
 
     [Fact]
-    public void PosFunc_GetLocation_ReturnsExpectedValue ()
+    public void PosFunc_Calculate_ReturnsExpectedValue ()
     {
         var posFunc = new PosFunc (() => 5);
-        var result = posFunc.Calculate (10, new DimAbsolute (2), 1, false);
+        var result = posFunc.Calculate (10, new DimAbsolute (2), null, Dimension.None);
         Assert.Equal (5, result);
     }
 
     [Fact]
-    public void PosView_GetLocation_ReturnsExpectedValue ()
+    public void PosView_Calculate_ReturnsExpectedValue ()
     {
         var posView = new PosView (new View { Frame = new Rectangle (5, 5, 10, 10) }, 0);
-        var result = posView.Calculate (10, new DimAbsolute (2), 1, false);
+        var result = posView.Calculate (10, new DimAbsolute (2), null, Dimension.None);
         Assert.Equal (5, result);
     }
 
@@ -93,8 +93,61 @@ public class PosTests (ITestOutputHelper output)
         Assert.Equal ("Center", pos.ToString ());
     }
 
+    // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
+    // TODO: A new test that calls SetRelativeLayout directly is needed.
+    [Fact]
+    public void Combine_Referencing_Same_View ()
+    {
+        var super = new View { Width = 10, Height = 10, Text = "super" };
+        var view1 = new View { Width = 2, Height = 2, Text = "view1" };
+        var view2 = new View { Width = 2, Height = 2, Text = "view2" };
+        view2.X = Pos.AnchorEnd () - (Pos.Right (view2) - Pos.Left (view2));
+
+        super.Add (view1, view2);
+        super.BeginInit ();
+        super.EndInit ();
+
+        Exception exception = Record.Exception (super.LayoutSubviews);
+        Assert.Null (exception);
+        Assert.Equal (new Rectangle (0, 0, 10, 10), super.Frame);
+        Assert.Equal (new Rectangle (0, 0, 2, 2), view1.Frame);
+        // AnchorEnd (0) would be 10. AnchorEnd () is 10 - 2 = 8. Right (view2) - Left (view2) = 2. 8 - 2 = 6
+        Assert.Equal (new Rectangle (6,0, 2, 2), view2.Frame);
+
+        super.Dispose ();
+    }
+
+    // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
+    // TODO: A new test that calls SetRelativeLayout directly is needed.
+    [Fact]
+    [TestRespondersDisposed]
+    public void Combine_WHY_Throws ()
+    {
+        Application.Init (new FakeDriver ());
+
+        Toplevel t = new Toplevel();
+
+        var w = new Window { X = Pos.Left (t) + 2, Y = Pos.Top (t) + 2 };
+        var f = new FrameView ();
+        var v1 = new View { X = Pos.Left (w) + 2, Y = Pos.Top (w) + 2 };
+        var v2 = new View { X = Pos.Left (v1) + 2, Y = Pos.Top (v1) + 2 };
+
+        f.Add (v1); // v2 not added
+        w.Add (f);
+        t.Add (w);
+
+        f.X = Pos.X (v2) - Pos.X (v1);
+        f.Y = Pos.Y (v2) - Pos.Y (v1);
+
+        Assert.Throws<InvalidOperationException> (() => Application.Run (t));
+        t.Dispose ();
+        Application.Shutdown ();
+
+        v2.Dispose ();
+    }
+
     [Fact]
-    public void DoNotReturnPosCombine ()
+    public void DoesNotReturnPosCombine ()
     {
         var v = new View { Id = "V" };
 
@@ -338,30 +391,6 @@ public class PosTests (ITestOutputHelper output)
         Assert.NotEqual (pos1, pos2);
     }
 
-    [Fact]
-    public void Percent_SetsValue ()
-    {
-        float f = 0;
-        Pos pos = Pos.Percent (f);
-        Assert.Equal ($"Factor({f / 100:0.###})", pos.ToString ());
-        f = 0.5F;
-        pos = Pos.Percent (f);
-        Assert.Equal ($"Factor({f / 100:0.###})", pos.ToString ());
-        f = 100;
-        pos = Pos.Percent (f);
-        Assert.Equal ($"Factor({f / 100:0.###})", pos.ToString ());
-    }
-
-    [Fact]
-    public void Percent_ThrowsOnIvalid ()
-    {
-        Pos pos = Pos.Percent (0);
-        Assert.Throws<ArgumentException> (() => pos = Pos.Percent (-1));
-        Assert.Throws<ArgumentException> (() => pos = Pos.Percent (101));
-        Assert.Throws<ArgumentException> (() => pos = Pos.Percent (100.0001F));
-        Assert.Throws<ArgumentException> (() => pos = Pos.Percent (1000001));
-    }
-
     // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
     // TODO: A new test that calls SetRelativeLayout directly is needed.
     [Fact]
@@ -572,7 +601,7 @@ public class PosTests (ITestOutputHelper output)
     [AutoInitShutdown]
     [InlineData (true)]
     [InlineData (false)]
-    public void PosPercentPlusOne (bool testHorizontal)
+    public void Percent_PlusOne (bool testHorizontal)
     {
         var container = new View { Width = 100, Height = 100 };
 
@@ -604,12 +633,36 @@ public class PosTests (ITestOutputHelper output)
         }
     }
 
+    [Fact]
+    public void Percent_SetsValue ()
+    {
+        float f = 0;
+        Pos pos = Pos.Percent (f);
+        Assert.Equal ($"Factor({f / 100:0.###})", pos.ToString ());
+        f = 0.5F;
+        pos = Pos.Percent (f);
+        Assert.Equal ($"Factor({f / 100:0.###})", pos.ToString ());
+        f = 100;
+        pos = Pos.Percent (f);
+        Assert.Equal ($"Factor({f / 100:0.###})", pos.ToString ());
+    }
+
+    [Fact]
+    public void Percent_ThrowsOnIvalid ()
+    {
+        Pos pos = Pos.Percent (0);
+        Assert.Throws<ArgumentException> (() => pos = Pos.Percent (-1));
+        Assert.Throws<ArgumentException> (() => pos = Pos.Percent (101));
+        Assert.Throws<ArgumentException> (() => pos = Pos.Percent (100.0001F));
+        Assert.Throws<ArgumentException> (() => pos = Pos.Percent (1000001));
+    }
+
     // TODO: Test Left, Top, Right bottom Equal
 
     /// <summary>Tests Pos.Left, Pos.X, Pos.Top, Pos.Y, Pos.Right, and Pos.Bottom set operations</summary>
     [Fact]
     [TestRespondersDisposed]
-    public void PosSide_SetsValue ()
+    public void Side_SetsValue ()
     {
         string side; // used in format string
         var testRect = Rectangle.Empty;
@@ -834,7 +887,7 @@ public class PosTests (ITestOutputHelper output)
     }
 
     [Fact]
-    public void SetSide_Null_Throws ()
+    public void Side_SetToNull_Throws ()
     {
         Pos pos = Pos.Left (null);
         Assert.Throws<NullReferenceException> (() => pos.ToString ());
@@ -854,4 +907,97 @@ public class PosTests (ITestOutputHelper output)
         pos = Pos.Right (null);
         Assert.Throws<NullReferenceException> (() => pos.ToString ());
     }
+
+    // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
+    // TODO: A new test that calls SetRelativeLayout directly is needed.
+    [Fact]
+    [TestRespondersDisposed]
+    public void Subtract_Operator ()
+    {
+        Application.Init (new FakeDriver ());
+
+        Toplevel top = new Toplevel ();
+
+        var view = new View { X = 0, Y = 0, Width = 20, Height = 20 };
+        var field = new TextField { X = 0, Y = 0, Width = 20 };
+        var count = 20;
+        List<View> listViews = new ();
+
+        for (var i = 0; i < count; i++)
+        {
+            field.Text = $"View {i}";
+            var view2 = new View { X = 0, Y = field.Y, Width = 20, Text = field.Text };
+            view.Add (view2);
+            Assert.Equal ($"View {i}", view2.Text);
+            Assert.Equal ($"Absolute({i})", field.Y.ToString ());
+            listViews.Add (view2);
+
+            Assert.Equal ($"Absolute({i})", field.Y.ToString ());
+            field.Y += 1;
+            Assert.Equal ($"Absolute({i + 1})", field.Y.ToString ());
+        }
+
+        field.KeyDown += (s, k) =>
+                         {
+                             if (k.KeyCode == KeyCode.Enter)
+                             {
+                                 Assert.Equal ($"View {count - 1}", listViews [count - 1].Text);
+                                 view.Remove (listViews [count - 1]);
+                                 listViews [count - 1].Dispose ();
+
+                                 Assert.Equal ($"Absolute({count})", field.Y.ToString ());
+                                 field.Y -= 1;
+                                 count--;
+                                 Assert.Equal ($"Absolute({count})", field.Y.ToString ());
+                             }
+                         };
+
+        Application.Iteration += (s, a) =>
+                                 {
+                                     while (count > 0)
+                                     {
+                                         field.NewKeyDownEvent (new Key (KeyCode.Enter));
+                                     }
+
+                                     Application.RequestStop ();
+                                 };
+
+        var win = new Window ();
+        win.Add (view);
+        win.Add (field);
+
+        top.Add (win);
+
+        Application.Run (top);
+        top.Dispose ();
+        Assert.Equal (0, count);
+
+        // Shutdown must be called to safely clean up Application if Init has been called
+        Application.Shutdown ();
+    }
+
+    // TODO: This actually a SetRelativeLayout/LayoutSubViews test and should be moved
+    // TODO: A new test that calls SetRelativeLayout directly is needed.
+    [Fact]
+    public void Validation_Does_Not_Throw_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Null ()
+    {
+        Application.Init (new FakeDriver ());
+
+        Toplevel t = new Toplevel ();
+
+        var w = new Window { X = 1, Y = 2, Width = 3, Height = 5 };
+        t.Add (w);
+
+        t.Ready += (s, e) =>
+                   {
+                       Assert.Equal (2, w.X = 2);
+                       Assert.Equal (2, w.Y = 2);
+                   };
+
+        Application.Iteration += (s, a) => Application.RequestStop ();
+
+        Application.Run (t);
+        t.Dispose ();
+        Application.Shutdown ();
+    }
 }