Ver código fonte

Added unit tests

Tig 1 ano atrás
pai
commit
f7c451b5fa

+ 4 - 1
Terminal.Gui/View/Layout/DimAuto.cs

@@ -69,12 +69,15 @@ public class DimAuto () : Dim
 
 
         if (Style.FastHasFlags (DimAutoStyle.Content))
         if (Style.FastHasFlags (DimAutoStyle.Content))
         {
         {
-            if (us._contentSize is { })
+            if (!us.ContentSizeTracksViewport)
             {
             {
+                // ContentSize was explicitly set. Ignore subviews.
                 subviewsSize = dimension == Dimension.Width ? us.ContentSize.Width : us.ContentSize.Height;
                 subviewsSize = dimension == Dimension.Width ? us.ContentSize.Width : us.ContentSize.Height;
             }
             }
             else
             else
             {
             {
+                // ContentSize was NOT explicitly set. Use subviews to determine size.
+
                 // TODO: This whole body of code is a WIP (for https://github.com/gui-cs/Terminal.Gui/pull/3451).
                 // TODO: This whole body of code is a WIP (for https://github.com/gui-cs/Terminal.Gui/pull/3451).
                 subviewsSize = 0;
                 subviewsSize = 0;
 
 

+ 5 - 7
Terminal.Gui/View/Layout/DimAutoStyle.cs

@@ -10,11 +10,9 @@ namespace Terminal.Gui;
 public enum DimAutoStyle
 public enum DimAutoStyle
 {
 {
     /// <summary>
     /// <summary>
-    ///     The dimensions will be computed based on the View's non-Text content.
+    ///     The dimensions will be computed based on the View's <see cref="View.ContentSize"/> and/or <see cref="View.Subviews"/>.
     ///     <para>
     ///     <para>
-    ///         If <see cref="View.ContentSize"/> is explicitly set (is not <see langword="null"/>) then
-    ///         <see cref="View.ContentSize"/>
-    ///         will be used to determine the dimension.
+    ///         If <see cref="View.ContentSizeTracksViewport"/> is <see langword="true"/>, <see cref="View.ContentSize"/> will be used to determine the dimension.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         Otherwise, the Subview in <see cref="View.Subviews"/> with the largest corresponding position plus dimension
     ///         Otherwise, the Subview in <see cref="View.Subviews"/> with the largest corresponding position plus dimension
@@ -33,14 +31,14 @@ public enum DimAutoStyle
     ///         will be used to determine the dimension.
     ///         will be used to determine the dimension.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         The corresponding dimensions of the <see cref="View.Subviews"/> will be ignored.
+    ///         The corresponding dimensions of <see cref="View.ContentSize"/> and/or <see cref="View.Subviews"/> will be ignored.
     ///     </para>
     ///     </para>
     /// </summary>
     /// </summary>
     Text = 2,
     Text = 2,
 
 
     /// <summary>
     /// <summary>
-    ///     The dimension will be computed using both the view's <see cref="View.Text"/> and
-    ///     <see cref="View.Subviews"/> (whichever is larger).
+    ///     The dimension will be computed using the largest of the view's <see cref="View.Text"/>, <see cref="View.ContentSize"/>, and
+    ///     <see cref="View.Subviews"/> corresponding dimension
     /// </summary>
     /// </summary>
     Auto = Content | Text,
     Auto = Content | Text,
 }
 }

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

@@ -1072,13 +1072,13 @@ public partial class View
         // Verify none of the subviews are using Dim objects that depend on the SuperView's dimensions.
         // Verify none of the subviews are using Dim objects that depend on the SuperView's dimensions.
         foreach (View view in Subviews)
         foreach (View view in Subviews)
         {
         {
-            if (widthAuto is { } && widthAuto.Style.FastHasFlags (DimAutoStyle.Content) && _contentSize is null)
+            if (widthAuto is { } && widthAuto.Style.FastHasFlags (DimAutoStyle.Content) && ContentSizeTracksViewport)
             {
             {
                 ThrowInvalid (view, view.Width, nameof (view.Width));
                 ThrowInvalid (view, view.Width, nameof (view.Width));
                 ThrowInvalid (view, view.X, nameof (view.X));
                 ThrowInvalid (view, view.X, nameof (view.X));
             }
             }
 
 
-            if (heightAuto is { } && heightAuto.Style.FastHasFlags (DimAutoStyle.Content) && _contentSize is null)
+            if (heightAuto is { } && heightAuto.Style.FastHasFlags (DimAutoStyle.Content) && ContentSizeTracksViewport)
             {
             {
                 ThrowInvalid (view, view.Height, nameof (view.Height));
                 ThrowInvalid (view, view.Height, nameof (view.Height));
                 ThrowInvalid (view, view.Y, nameof (view.Y));
                 ThrowInvalid (view, view.Y, nameof (view.Y));

+ 78 - 47
Terminal.Gui/View/ViewContent.cs

@@ -1,6 +1,4 @@
-using System.Diagnostics;
-
-namespace Terminal.Gui;
+namespace Terminal.Gui;
 
 
 public partial class View
 public partial class View
 {
 {
@@ -8,38 +6,9 @@ public partial class View
 
 
     internal Size? _contentSize;
     internal Size? _contentSize;
 
 
-    /// <summary>
-    ///     Sets the size of the View's content.
-    /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         By default, the content size is set to <see langword="null"/>.
-    ///     </para>
-    /// </remarks>
-    /// <param name="contentSize">
-    ///     <para>
-    ///         If <see langword="null"/>, and the View has no visible subviews, <see cref="ContentSize"/> will track the size of <see cref="Viewport"/>.
-    ///     </para>
-    ///     <para>
-    ///         If <see langword="null"/>, and the View has visible subviews, <see cref="ContentSize"/> will track the maximum position plus size of any
-    ///         visible Subviews
-    ///         and <c>Viewport.Location</c>  will track the minimum position and size of any visible Subviews.
-    ///     </para>
-    ///     <para>
-    ///         If not <see langword="null"/>, <see cref="ContentSize"/> is set to the passed value and <see cref="Viewport"/> describes the portion of the content currently visible
-    ///         to the user. This enables virtual scrolling.
-    ///     </para>
-    ///     <para>
-    ///         If not <see langword="null"/>, <see cref="ContentSize"/> is set to the passed value and the behavior of <see cref="DimAutoStyle.Content"/> will be to use the ContentSize
-    ///         to determine the size of the view.
-    ///     </para>
-    ///     <para>
-    ///         Negative sizes are not supported.
-    ///     </para>
-    /// </param>
     private void SetContentSize (Size? contentSize)
     private void SetContentSize (Size? contentSize)
     {
     {
-        if (ContentSize.Width < 0 || ContentSize.Height < 0)
+        if (contentSize is { } && (contentSize.Value.Width < 0 || contentSize.Value.Height < 0))
         {
         {
             throw new ArgumentException (@"ContentSize cannot be negative.", nameof (contentSize));
             throw new ArgumentException (@"ContentSize cannot be negative.", nameof (contentSize));
         }
         }
@@ -53,17 +22,80 @@ public partial class View
         OnContentSizeChanged (new (_contentSize));
         OnContentSizeChanged (new (_contentSize));
     }
     }
 
 
+    /// <summary>
+    ///     Gets or sets a value indicating whether the <see cref="ContentSize"/> tracks the <see cref="Viewport"/>'s size or
+    ///     not.
+    /// </summary>
+    /// <remarks>
+    ///     <list type="bullet">
+    ///         <listheader>
+    ///             <term>Value</term> <description>Result</description>
+    ///         </listheader>
+    ///         <item>
+    ///             <term>
+    ///                 <see langword="true"/>
+    ///             </term>
+    ///             <description>
+    ///                 <para>
+    ///                     <see cref="ContentSize"/> is tracking the <see cref="Viewport"/>'s size. Content scrolling will be
+    ///                     disabled.
+    ///                 </para>
+    ///                 <para>
+    ///                     The behavior of <see cref="DimAutoStyle.Content"/> will be to use position and size of the Subviews
+    ///                     to
+    ///                     determine the size of the view, ignoring <see cref="ContentSize"/>.
+    ///                 </para>
+    ///             </description>
+    ///         </item>
+    ///         <item>
+    ///             <term>
+    ///                 <see langword="false"/>
+    ///             </term>
+    ///             <description>
+    ///                 <para>
+    ///                     <see cref="ContentSize"/> is independent of <see cref="Viewport"/> and <see cref="Viewport"/>
+    ///                     describes the portion of the content currently visible to the user, bound by
+    ///                     <see cref="ContentSize"/>, enabling content scrolling.
+    ///                 </para>
+    ///                 <para>
+    ///                     The behavior of <see cref="DimAutoStyle.Content"/> will be to use <see cref="ContentSize"/> to
+    ///                     determine the
+    ///                     size of the view, ignoring the position and size of the Subviews.
+    ///                 </para>
+    ///             </description>
+    ///         </item>
+    ///     </list>
+    /// </remarks>
+    public bool ContentSizeTracksViewport
+    {
+        get => _contentSize is null;
+        set => _contentSize = value ? null : _contentSize;
+    }
+
     /// <summary>
     /// <summary>
     ///     Gets the size of the View's content.
     ///     Gets the size of the View's content.
     /// </summary>
     /// </summary>
     /// <remarks>
     /// <remarks>
     ///     <para>
     ///     <para>
-    ///         Use <see cref="SetContentSize"/> to change to change the content size.
+    ///         Negative sizes are not supported.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         If the content size has not been explicitly set with <see cref="SetContentSize"/>, the value tracks
+    ///         If not explicitly set, and the View has no visible subviews, <see cref="ContentSize"/> will track the size of
     ///         <see cref="Viewport"/>.
     ///         <see cref="Viewport"/>.
     ///     </para>
     ///     </para>
+    ///     <para>
+    ///         If not explicitly set, and the View has visible subviews, <see cref="ContentSize"/> will track the maximum
+    ///         position + dimension of the Subviews, supporting <see cref="Dim.Auto"/> with the
+    ///         <see cref="DimAutoStyle.Content"/> flag set.
+    ///     </para>
+    ///     <para>
+    ///         If set <see cref="Viewport"/> describes the portion of the content currently visible to the user. This enables
+    ///         virtual scrolling.
+    ///     </para>
+    ///     <para>
+    ///         If set the behavior of <see cref="DimAutoStyle.Content"/> will be to use the ContentSize to determine the size
+    ///         of the view.
+    ///     </para>
     /// </remarks>
     /// </remarks>
     public Size ContentSize
     public Size ContentSize
     {
     {
@@ -72,7 +104,7 @@ public partial class View
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// Called when <see cref="ContentSize"/> has changed.
+    ///     Called when <see cref="ContentSize"/> has changed.
     /// </summary>
     /// </summary>
     /// <param name="e"></param>
     /// <param name="e"></param>
     /// <returns></returns>
     /// <returns></returns>
@@ -83,6 +115,7 @@ public partial class View
         if (e.Cancel != true)
         if (e.Cancel != true)
         {
         {
             OnResizeNeeded ();
             OnResizeNeeded ();
+
             //SetNeedsLayout ();
             //SetNeedsLayout ();
             //SetNeedsDisplay ();
             //SetNeedsDisplay ();
         }
         }
@@ -180,11 +213,12 @@ public partial class View
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         Negative values for the location indicate the visible area is offset above (up-and-left) the View's virtual
     ///         Negative values for the location indicate the visible area is offset above (up-and-left) the View's virtual
-    ///         <see cref="ContentSize"/>. This enables scrolling up and to the left (e.g. in an image viewer that supports zoom
+    ///         <see cref="ContentSize"/>. This enables scrolling up and to the left (e.g. in an image viewer that supports
+    ///         zoom
     ///         where the image stays centered).
     ///         where the image stays centered).
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         The <see cref="ViewportSettings"/> property controls how scrolling is handled. 
+    ///         The <see cref="ViewportSettings"/> property controls how scrolling is handled.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         If <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/> the value of Viewport is indeterminate until
     ///         If <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/> the value of Viewport is indeterminate until
@@ -211,6 +245,7 @@ public partial class View
             }
             }
 
 
             Thickness thickness = GetAdornmentsThickness ();
             Thickness thickness = GetAdornmentsThickness ();
+
             return new (
             return new (
                         _viewportLocation,
                         _viewportLocation,
                         new (
                         new (
@@ -243,6 +278,7 @@ public partial class View
             }
             }
 
 
             OnViewportChanged (new (IsInitialized ? Viewport : Rectangle.Empty, oldViewport));
             OnViewportChanged (new (IsInitialized ? Viewport : Rectangle.Empty, oldViewport));
+
             return;
             return;
         }
         }
 
 
@@ -293,7 +329,8 @@ public partial class View
     }
     }
 
 
     /// <summary>
     /// <summary>
-    ///     Fired when the <see cref="Viewport"/> changes. This event is fired after the <see cref="Viewport"/> has been updated.
+    ///     Fired when the <see cref="Viewport"/> changes. This event is fired after the <see cref="Viewport"/> has been
+    ///     updated.
     /// </summary>
     /// </summary>
     [CanBeNull]
     [CanBeNull]
     public event EventHandler<DrawEventArgs> ViewportChanged;
     public event EventHandler<DrawEventArgs> ViewportChanged;
@@ -302,10 +339,7 @@ public partial class View
     ///     Called when the <see cref="Viewport"/> changes. Invokes the <see cref="ViewportChanged"/> event.
     ///     Called when the <see cref="Viewport"/> changes. Invokes the <see cref="ViewportChanged"/> event.
     /// </summary>
     /// </summary>
     /// <param name="e"></param>
     /// <param name="e"></param>
-    protected virtual void OnViewportChanged (DrawEventArgs e)
-    {
-        ViewportChanged?.Invoke (this, e);
-    }
+    protected virtual void OnViewportChanged (DrawEventArgs e) { ViewportChanged?.Invoke (this, e); }
 
 
     /// <summary>
     /// <summary>
     ///     Converts a <see cref="Viewport"/>-relative location and size to a screen-relative location and size.
     ///     Converts a <see cref="Viewport"/>-relative location and size to a screen-relative location and size.
@@ -315,10 +349,7 @@ public partial class View
     /// </remarks>
     /// </remarks>
     /// <param name="viewport">Viewport-relative location and size.</param>
     /// <param name="viewport">Viewport-relative location and size.</param>
     /// <returns>Screen-relative location and size.</returns>
     /// <returns>Screen-relative location and size.</returns>
-    public Rectangle ViewportToScreen (in Rectangle viewport)
-    {
-        return viewport with { Location = ViewportToScreen (viewport.Location) };
-    }
+    public Rectangle ViewportToScreen (in Rectangle viewport) { return viewport with { Location = ViewportToScreen (viewport.Location) }; }
 
 
     /// <summary>
     /// <summary>
     ///     Converts a <see cref="Viewport"/>-relative location to a screen-relative location.
     ///     Converts a <see cref="Viewport"/>-relative location to a screen-relative location.

+ 16 - 0
UnitTests/View/Layout/Dim.AutoTests.cs

@@ -972,6 +972,22 @@ public class DimAutoTests (ITestOutputHelper output)
         Assert.Equal (10, calculatedWidth);
         Assert.Equal (10, calculatedWidth);
     }
     }
 
 
+    [Fact]
+    public void DimAutoStyle_Content_IgnoresSubviews_When_ContentSize_Is_Set ()
+    {
+        var view = new View ();
+        var subview = new View () {
+                Frame = new Rectangle (50, 50, 1, 1)
+        };
+        view.ContentSize = new (10, 5);
+
+        var dim = Dim.Auto (DimAutoStyle.Content);
+
+        int calculatedWidth = dim.Calculate (0, 100, view, Dimension.Width);
+
+        Assert.Equal (10, calculatedWidth);
+    }
+
     [Fact]
     [Fact]
     public void DimAutoStyle_Content_IgnoresText_WhenContentSizeNotSet ()
     public void DimAutoStyle_Content_IgnoresText_WhenContentSizeNotSet ()
     {
     {

+ 44 - 1
UnitTests/View/Layout/ViewportTests.cs

@@ -340,16 +340,59 @@ public class ViewportTests (ITestOutputHelper output)
     }
     }
 
 
     [Fact]
     [Fact]
-    public void ContentSize_Matches_ViewportSize_If_Not_Set ()
+    public void ContentSize_Tracks_ViewportSize_If_Not_Set ()
     {
     {
         View view = new ()
         View view = new ()
         {
         {
             Width = 1,
             Width = 1,
             Height = 1
             Height = 1
         };
         };
+        Assert.True (view.ContentSizeTracksViewport);
         Assert.Equal (view.Viewport.Size, view.ContentSize);
         Assert.Equal (view.Viewport.Size, view.ContentSize);
     }
     }
 
 
+    [Fact]
+    public void ContentSize_Ignores_ViewportSize_If_Set ()
+    {
+        View view = new ()
+        {
+            Width = 1,
+            Height = 1,
+            ContentSize = new Size (5, 5)
+        };
+        Assert.False (view.ContentSizeTracksViewport);
+        Assert.NotEqual (view.Viewport.Size, view.ContentSize);
+    }
+
+    [Fact]
+    public void ContentSize_Tracks_ViewportSize_If_ContentSizeTracksViewport_Is_True ()
+    {
+        View view = new ()
+        {
+            Width = 1,
+            Height = 1,
+            ContentSize = new Size (5, 5)
+        };
+        view.Viewport = new (0, 0, 10, 10);
+        view.ContentSizeTracksViewport = true;
+        Assert.Equal (view.Viewport.Size, view.ContentSize);
+    }
+
+
+    [Fact]
+    public void ContentSize_Ignores_ViewportSize_If_ContentSizeTracksViewport_Is_False ()
+    {
+        View view = new ()
+        {
+            Width = 1,
+            Height = 1,
+            ContentSize = new Size (5, 5)
+        };
+        view.Viewport = new (0, 0, 10, 10);
+        view.ContentSizeTracksViewport = false;
+        Assert.NotEqual (view.Viewport.Size, view.ContentSize);
+    }
+
     //[Theory]
     //[Theory]
     //[InlineData (0, 0, true)]
     //[InlineData (0, 0, true)]
     //[InlineData (-1, 0, true)]
     //[InlineData (-1, 0, true)]

+ 42 - 1
UnitTests/View/SubviewTests.cs

@@ -75,7 +75,7 @@ public class SubviewTests
         w2.Dispose ();
         w2.Dispose ();
         top2.Dispose ();
         top2.Dispose ();
     }
     }
-    
+
     [Fact]
     [Fact]
     [TestRespondersDisposed]
     [TestRespondersDisposed]
     public void Initialized_Event_Comparing_With_Added_Event ()
     public void Initialized_Event_Comparing_With_Added_Event ()
@@ -332,4 +332,45 @@ public class SubviewTests
         top.Dispose ();
         top.Dispose ();
         view.Dispose ();
         view.Dispose ();
     }
     }
+
+    // TODO: Consider a feature that will change the ContentSize to fit the subviews.
+    [Fact]
+    public void Add_Does_Not_Impact_ContentSize ()
+    {
+        var view = new View ();
+        view.ContentSize = new Size (1, 1);
+
+        var subview = new View ()
+        {
+            X = 10,
+            Y = 10
+        };
+
+        Assert.Equal (new Size (1, 1), view.ContentSize);
+        view.Add (subview);
+        Assert.Equal (new Size (1, 1), view.ContentSize);
+    }
+
+    [Fact]
+    public void Remove_Does_Not_Impact_ContentSize ()
+    {
+        var view = new View ();
+        view.ContentSize = new Size (1, 1);
+
+        var subview = new View ()
+        {
+            X = 10,
+            Y = 10
+        };
+
+        Assert.Equal (new Size (1, 1), view.ContentSize);
+        view.Add (subview);
+        Assert.Equal (new Size (1, 1), view.ContentSize);
+
+        view.ContentSize = new Size (5, 5);
+        Assert.Equal (new Size (5, 5), view.ContentSize);
+
+        view.Remove (subview);
+        Assert.Equal (new Size (5, 5), view.ContentSize);
+    }
 }
 }