diff --git a/Terminal.Gui/View/Layout/PosDim.cs b/Terminal.Gui/View/Layout/PosDim.cs index 7fb8cab99..8a095f403 100644 --- a/Terminal.Gui/View/Layout/PosDim.cs +++ b/Terminal.Gui/View/Layout/PosDim.cs @@ -939,11 +939,18 @@ public class Dim 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); + if (us.IdealContentSize.HasValue) + { + subviewsSize = dimension == Dimension.Width ? us.IdealContentSize.Value.Width : us.IdealContentSize.Value.Height; + } + else + { + 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); diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs index c21c4312e..fb3b79769 100644 --- a/Terminal.Gui/View/Layout/ViewLayout.cs +++ b/Terminal.Gui/View/Layout/ViewLayout.cs @@ -91,10 +91,12 @@ public partial class View private void SetFrame (Rectangle frame) { var oldViewport = Rectangle.Empty; + var oldContentSize = Size.Empty; if (IsInitialized) { oldViewport = Viewport; + oldContentSize = ContentSize; } // This is the only place where _frame should be set directly. Use Frame = or SetFrame instead. @@ -341,6 +343,18 @@ public partial class View #region Layout Engine + + // @tig Notes on layout flow. Ignore for now. + // BeginLayout + // If !LayoutNeeded return + // If !SizeNeeded return + // Call OnLayoutStarted + // Views and subviews can update things + // + + + // EndLayout + /// /// Controls how the View's is computed during . If the style is /// set to , LayoutSubviews does not change the . If the style is @@ -780,6 +794,7 @@ public partial class View private void LayoutSubview (View v, Size contentSize) { + // BUGBUG: Calling SetRelativeLayout before LayoutSubviews is problematic. Need to resolve. v.SetRelativeLayout (contentSize); v.LayoutSubviews (); v.LayoutNeeded = false; @@ -794,6 +809,9 @@ public partial class View /// internal virtual void OnLayoutComplete (LayoutEventArgs args) { LayoutComplete?.Invoke (this, args); } + // BUGBUG: We need an API/event that is called from SetRelativeLayout instead of/in addition to + // BUGBUG: OnLayoutStarted which is called from LayoutSubviews. + /// /// Raises the event. Called from before any subviews /// have been laid out. diff --git a/Terminal.Gui/View/ViewContent.cs b/Terminal.Gui/View/ViewContent.cs index 164346ba2..f246f0a80 100644 --- a/Terminal.Gui/View/ViewContent.cs +++ b/Terminal.Gui/View/ViewContent.cs @@ -155,6 +155,8 @@ public partial class View } } + public Size? IdealContentSize { get; set; } + /// /// Called when changes. Invokes the event. /// @@ -319,10 +321,8 @@ public partial class View // Whenever DimAutoStyle.Text is set, ContentSize will match TextFormatter.Size. ContentSize = TextFormatter.Size; - } } - //SetRelativeLayout (SuperView?.ContentSize ?? new Size (int.MaxValue, int.MaxValue)); } return new ( @@ -357,7 +357,6 @@ public partial class View } OnViewportChanged (new (IsInitialized ? Viewport : Rectangle.Empty, oldViewport)); - return; } @@ -417,7 +416,10 @@ 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 to a screen-relative location. diff --git a/Terminal.Gui/Views/Slider.cs b/Terminal.Gui/Views/Slider.cs index ac2ab874f..738913512 100644 --- a/Terminal.Gui/Views/Slider.cs +++ b/Terminal.Gui/Views/Slider.cs @@ -265,11 +265,19 @@ public class Slider : View Enter += (s, e) => { }; - LayoutComplete += (s, e) => + // BUGBUG: This should not be needed - Need to ensure SetRelativeLayout gets called during EndInit + Initialized += (s, e) => + { + CalcSpacingConfig (); + SetContentSizeBestFit (); + }; + + LayoutStarted += (s, e) => { CalcSpacingConfig (); - SetBoundsBestFit (); + SetContentSizeBestFit (); }; + } #endregion @@ -374,13 +382,9 @@ public class Slider : View } /// - /// If the slider will be sized to fit the available space (the Viewport of the the - /// SuperView). + /// If , and will be automatically set + /// such that the slider will be optimally sized to fit the options using the various slider settings. /// - /// - /// For testing, if there is no SuperView, the slider will be sized based on what is - /// set to. - /// [ObsoleteAttribute ("Use Dim.Auto instead.", false)] public override bool AutoSize { @@ -389,10 +393,15 @@ public class Slider : View { _config._autoSize = value; - if (IsInitialized) + if (value) { - CalcSpacingConfig (); - SetBoundsBestFit (); + Width = Dim.Auto (Dim.DimAutoStyle.Subviews); + Height = Dim.Auto (Dim.DimAutoStyle.Subviews); + } + else + { + Width = ContentSize.Width; + Height = ContentSize.Height; } } } @@ -408,7 +417,7 @@ public class Slider : View if (IsInitialized) { CalcSpacingConfig (); - SetBoundsBestFit (); + SetContentSizeBestFit (); } } } @@ -456,7 +465,7 @@ public class Slider : View if (IsInitialized) { CalcSpacingConfig (); - SetBoundsBestFit (); + SetContentSizeBestFit (); } } @@ -474,7 +483,7 @@ public class Slider : View if (IsInitialized) { CalcSpacingConfig (); - SetBoundsBestFit (); + SetContentSizeBestFit (); } } } @@ -508,7 +517,7 @@ public class Slider : View } CalcSpacingConfig (); - SetBoundsBestFit (); + SetContentSizeBestFit (); } } @@ -537,7 +546,7 @@ public class Slider : View set { _config._showLegends = value; - SetBoundsBestFit (); + SetContentSizeBestFit (); } } @@ -767,9 +776,9 @@ public class Slider : View } /// Adjust the dimensions of the Slider to the best value if is true. - public void SetBoundsBestFit () + public void SetContentSizeBestFit () { - if (!IsInitialized || AutoSize == false) + if (!IsInitialized || !(Height is Dim.DimAuto && Width is Dim.DimAuto)) { return; } @@ -778,15 +787,16 @@ public class Slider : View if (_config._sliderOrientation == Orientation.Horizontal) { + // BUGBUG: For this View, ContentSize == Viewport.Size, so this works. But for correctness we should be setting ContentSize here Viewport = new ( Viewport.Location, new ( int.Min ( - SuperView.Viewport.Width - adornmentsThickness.Horizontal, + SuperView.ContentSize.Width - adornmentsThickness.Horizontal, CalcBestLength () ), int.Min ( - SuperView.Viewport.Height - adornmentsThickness.Vertical, + SuperView.ContentSize.Height - adornmentsThickness.Vertical, CalcThickness () ) ) @@ -794,15 +804,14 @@ public class Slider : View } else { - Viewport = new ( - Viewport.Location, + IdealContentSize = new ( new ( int.Min ( - SuperView.Viewport.Width - adornmentsThickness.Horizontal, + SuperView.ContentSize.Width - adornmentsThickness.Horizontal, CalcThickness () ), int.Min ( - SuperView.Viewport.Height - adornmentsThickness.Vertical, + SuperView.ContentSize.Height - adornmentsThickness.Vertical, CalcBestLength () ) ) diff --git a/UICatalog/Scenarios/ContentScrolling.cs b/UICatalog/Scenarios/ContentScrolling.cs index 9a92b4bc4..496eac77e 100644 --- a/UICatalog/Scenarios/ContentScrolling.cs +++ b/UICatalog/Scenarios/ContentScrolling.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using Terminal.Gui; @@ -384,6 +385,20 @@ public class ContentScrolling : Scenario longLabel.TextFormatter.WordWrap = true; view.Add (longLabel); + List options = new () { "Option 1", "Option 2", "Option 3" }; + Slider slider = new (options) + { + X = 0, + Y = Pos.Bottom (textField) + 1, + AutoSize = true, + Orientation = Orientation.Vertical, + Type = SliderType.Multiple, + AllowEmpty = false, + BorderStyle = LineStyle.Double, + Title = "_Slider" + }; + view.Add(slider); + editor.Initialized += (s, e) => { editor.ViewToEdit = view; }; app.Closed += (s, e) => View.Diagnostics = _diagnosticFlags; diff --git a/UnitTests/Views/SliderTests.cs b/UnitTests/Views/SliderTests.cs index 31bf77b63..4936781bf 100644 --- a/UnitTests/Views/SliderTests.cs +++ b/UnitTests/Views/SliderTests.cs @@ -486,5 +486,44 @@ public class SliderTests Assert.Throws (() => slider.Options = null); } + [Fact] + private void AutoSize_Respects_SuperView_ContentSize () + { + View view = new () + { + Width = Dim.Fill (), + Height = Dim.Fill (), + }; + + List options = new () { "01234", "01234" }; + Slider slider = new (options) + { + Orientation = Orientation.Vertical, + Type = SliderType.Multiple, + Width = Dim.Auto (Dim.DimAutoStyle.Subviews), + Height = Dim.Auto (Dim.DimAutoStyle.Subviews), + //IdealContentSize = new (6, 2) + }; + view.Add (slider); + view.BeginInit (); + view.EndInit (); + + // BUGBUG: This should not be needed. EndInit should have called LayoutSubviews + // BUGBUG: and LayoutSubviews should have + view.LayoutSubviews (); + + Size expectedSize = slider.Frame.Size; + + Assert.Equal (new (6, 2), expectedSize); + + view.ContentSize = new (1, 1); + + view.LayoutSubviews (); + slider.SetRelativeLayout (view.Viewport.Size); + + Assert.Equal(new (1, 1), slider.Frame.Size); + + } + // Add more tests for different scenarios and edge cases. }