diff --git a/Terminal.Gui/View/Layout/DimAuto.cs b/Terminal.Gui/View/Layout/DimAuto.cs index dc8a55aa9..d956fb94d 100644 --- a/Terminal.Gui/View/Layout/DimAuto.cs +++ b/Terminal.Gui/View/Layout/DimAuto.cs @@ -69,12 +69,15 @@ public class DimAuto () : Dim 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; } 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). subviewsSize = 0; diff --git a/Terminal.Gui/View/Layout/DimAutoStyle.cs b/Terminal.Gui/View/Layout/DimAutoStyle.cs index 4a3ecbb66..df7f7065c 100644 --- a/Terminal.Gui/View/Layout/DimAutoStyle.cs +++ b/Terminal.Gui/View/Layout/DimAutoStyle.cs @@ -10,11 +10,9 @@ namespace Terminal.Gui; public enum DimAutoStyle { /// - /// The dimensions will be computed based on the View's non-Text content. + /// The dimensions will be computed based on the View's and/or . /// - /// If is explicitly set (is not ) then - /// - /// will be used to determine the dimension. + /// If is , will be used to determine the dimension. /// /// /// Otherwise, the Subview in with the largest corresponding position plus dimension @@ -33,14 +31,14 @@ public enum DimAutoStyle /// will be used to determine the dimension. /// /// - /// The corresponding dimensions of the will be ignored. + /// The corresponding dimensions of and/or will be ignored. /// /// Text = 2, /// - /// The dimension will be computed using both the view's and - /// (whichever is larger). + /// The dimension will be computed using the largest of the view's , , and + /// corresponding dimension /// Auto = Content | Text, } \ No newline at end of file diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs index f9f55503c..110cc2238 100644 --- a/Terminal.Gui/View/Layout/ViewLayout.cs +++ b/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. 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.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.Y, nameof (view.Y)); diff --git a/Terminal.Gui/View/ViewContent.cs b/Terminal.Gui/View/ViewContent.cs index decd143d9..b94213d30 100644 --- a/Terminal.Gui/View/ViewContent.cs +++ b/Terminal.Gui/View/ViewContent.cs @@ -1,6 +1,4 @@ -using System.Diagnostics; - -namespace Terminal.Gui; +namespace Terminal.Gui; public partial class View { @@ -8,38 +6,9 @@ public partial class View internal Size? _contentSize; - /// - /// Sets the size of the View's content. - /// - /// - /// - /// By default, the content size is set to . - /// - /// - /// - /// - /// If , and the View has no visible subviews, will track the size of . - /// - /// - /// If , and the View has visible subviews, will track the maximum position plus size of any - /// visible Subviews - /// and Viewport.Location will track the minimum position and size of any visible Subviews. - /// - /// - /// If not , is set to the passed value and describes the portion of the content currently visible - /// to the user. This enables virtual scrolling. - /// - /// - /// If not , is set to the passed value and the behavior of will be to use the ContentSize - /// to determine the size of the view. - /// - /// - /// Negative sizes are not supported. - /// - /// 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)); } @@ -53,17 +22,80 @@ public partial class View OnContentSizeChanged (new (_contentSize)); } + /// + /// Gets or sets a value indicating whether the tracks the 's size or + /// not. + /// + /// + /// + /// + /// Value Result + /// + /// + /// + /// + /// + /// + /// + /// is tracking the 's size. Content scrolling will be + /// disabled. + /// + /// + /// The behavior of will be to use position and size of the Subviews + /// to + /// determine the size of the view, ignoring . + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// is independent of and + /// describes the portion of the content currently visible to the user, bound by + /// , enabling content scrolling. + /// + /// + /// The behavior of will be to use to + /// determine the + /// size of the view, ignoring the position and size of the Subviews. + /// + /// + /// + /// + /// + public bool ContentSizeTracksViewport + { + get => _contentSize is null; + set => _contentSize = value ? null : _contentSize; + } + /// /// Gets the size of the View's content. /// /// /// - /// Use to change to change the content size. + /// Negative sizes are not supported. /// /// - /// If the content size has not been explicitly set with , the value tracks + /// If not explicitly set, and the View has no visible subviews, will track the size of /// . /// + /// + /// If not explicitly set, and the View has visible subviews, will track the maximum + /// position + dimension of the Subviews, supporting with the + /// flag set. + /// + /// + /// If set describes the portion of the content currently visible to the user. This enables + /// virtual scrolling. + /// + /// + /// If set the behavior of will be to use the ContentSize to determine the size + /// of the view. + /// /// public Size ContentSize { @@ -72,7 +104,7 @@ public partial class View } /// - /// Called when has changed. + /// Called when has changed. /// /// /// @@ -83,6 +115,7 @@ public partial class View if (e.Cancel != true) { OnResizeNeeded (); + //SetNeedsLayout (); //SetNeedsDisplay (); } @@ -180,11 +213,12 @@ public partial class View /// /// /// Negative values for the location indicate the visible area is offset above (up-and-left) the View's virtual - /// . This enables scrolling up and to the left (e.g. in an image viewer that supports zoom + /// . This enables scrolling up and to the left (e.g. in an image viewer that supports + /// zoom /// where the image stays centered). /// /// - /// The property controls how scrolling is handled. + /// The property controls how scrolling is handled. /// /// /// If is the value of Viewport is indeterminate until @@ -211,6 +245,7 @@ public partial class View } Thickness thickness = GetAdornmentsThickness (); + return new ( _viewportLocation, new ( @@ -243,6 +278,7 @@ public partial class View } OnViewportChanged (new (IsInitialized ? Viewport : Rectangle.Empty, oldViewport)); + return; } @@ -293,7 +329,8 @@ public partial class View } /// - /// Fired when the changes. This event is fired after the has been updated. + /// Fired when the changes. This event is fired after the has been + /// updated. /// [CanBeNull] public event EventHandler ViewportChanged; @@ -302,10 +339,7 @@ public partial class View /// Called when the changes. Invokes the event. /// /// - protected virtual void OnViewportChanged (DrawEventArgs e) - { - ViewportChanged?.Invoke (this, e); - } + protected virtual void OnViewportChanged (DrawEventArgs e) { ViewportChanged?.Invoke (this, e); } /// /// Converts a -relative location and size to a screen-relative location and size. @@ -315,10 +349,7 @@ public partial class View /// /// Viewport-relative location and size. /// Screen-relative location and size. - 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) }; } /// /// Converts a -relative location to a screen-relative location. diff --git a/UnitTests/View/Layout/Dim.AutoTests.cs b/UnitTests/View/Layout/Dim.AutoTests.cs index 87e2dd95e..9abf6b315 100644 --- a/UnitTests/View/Layout/Dim.AutoTests.cs +++ b/UnitTests/View/Layout/Dim.AutoTests.cs @@ -972,6 +972,22 @@ public class DimAutoTests (ITestOutputHelper output) 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] public void DimAutoStyle_Content_IgnoresText_WhenContentSizeNotSet () { diff --git a/UnitTests/View/Layout/ViewportTests.cs b/UnitTests/View/Layout/ViewportTests.cs index 5ba27d6d0..8a2126303 100644 --- a/UnitTests/View/Layout/ViewportTests.cs +++ b/UnitTests/View/Layout/ViewportTests.cs @@ -340,16 +340,59 @@ public class ViewportTests (ITestOutputHelper output) } [Fact] - public void ContentSize_Matches_ViewportSize_If_Not_Set () + public void ContentSize_Tracks_ViewportSize_If_Not_Set () { View view = new () { Width = 1, Height = 1 }; + Assert.True (view.ContentSizeTracksViewport); 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] //[InlineData (0, 0, true)] //[InlineData (-1, 0, true)] diff --git a/UnitTests/View/SubviewTests.cs b/UnitTests/View/SubviewTests.cs index 6f9d84f54..768b5d1ea 100644 --- a/UnitTests/View/SubviewTests.cs +++ b/UnitTests/View/SubviewTests.cs @@ -75,7 +75,7 @@ public class SubviewTests w2.Dispose (); top2.Dispose (); } - + [Fact] [TestRespondersDisposed] public void Initialized_Event_Comparing_With_Added_Event () @@ -332,4 +332,45 @@ public class SubviewTests top.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); + } }