From e93eebb7083cacf9d421d58bae33392eca5c4006 Mon Sep 17 00:00:00 2001 From: Tig Date: Tue, 16 Jul 2024 18:40:51 -0600 Subject: [PATCH] Fixed little things. --- Terminal.Gui/Text/TextFormatter.cs | 11 +- Terminal.Gui/View/Layout/DimAuto.cs | 364 ++++++------------------- Terminal.Gui/View/Layout/ViewLayout.cs | 5 +- Terminal.Gui/View/ViewText.cs | 28 +- Terminal.Gui/Views/Dialog.cs | 4 +- Terminal.Gui/Views/Shortcut.cs | 4 +- UICatalog/Scenarios/Dialogs.cs | 20 +- UnitTests/Text/TextFormatterTests.cs | 62 +++++ UnitTests/View/Layout/Dim.AutoTests.cs | 328 +++++++++++----------- UnitTests/Views/ShortcutTests.cs | 3 +- 10 files changed, 364 insertions(+), 465 deletions(-) diff --git a/Terminal.Gui/Text/TextFormatter.cs b/Terminal.Gui/Text/TextFormatter.cs index 8b2eb47eb..84500c5ed 100644 --- a/Terminal.Gui/Text/TextFormatter.cs +++ b/Terminal.Gui/Text/TextFormatter.cs @@ -648,14 +648,21 @@ public class TextFormatter /// Gets the size required to hold the formatted text, given the constraints placed by . /// Causes a format, resetting to . /// The size required to hold the formatted text. - public Size FormatAndGetSize () + public Size FormatAndGetSize (Size? constrainSize = null) { - if (string.IsNullOrEmpty (Text) || Size.Height == 0 || Size.Width == 0) + if (constrainSize is null) + { + constrainSize = Size; + } + if (string.IsNullOrEmpty (Text) || constrainSize.Value.Height == 0 || constrainSize.Value.Width == 0) { return Size.Empty; } + Size prevSize = Size; + Size = constrainSize.Value; List lines = GetLines (); + Size = prevSize; if (lines.Count == 0) { diff --git a/Terminal.Gui/View/Layout/DimAuto.cs b/Terminal.Gui/View/Layout/DimAuto.cs index 473bb2fb5..fedc96188 100644 --- a/Terminal.Gui/View/Layout/DimAuto.cs +++ b/Terminal.Gui/View/Layout/DimAuto.cs @@ -66,25 +66,30 @@ public class DimAuto () : Dim int screen = dimension == Dimension.Width ? Application.Screen.Width * 4 : Application.Screen.Height * 4; int autoMax = MaximumContentDim?.GetAnchor (superviewContentSize) ?? screen; + Debug.Assert (autoMin <= autoMax, "MinimumContentDim must be less than or equal to MaximumContentDim."); + if (Style.FastHasFlags (DimAutoStyle.Text)) { if (dimension == Dimension.Width) { - us.TextFormatter.Size = new (superviewContentSize, 2048); + //us.TextFormatter.Size = new (superviewContentSize, 2048); textSize = us.TextFormatter.FormatAndGetSize ().Width; - us.TextFormatter.Size = new Size (textSize, 2048); + //us.TextFormatter.Size = new Size (textSize, 2048); } else { - if (us.TextFormatter.Size.Width == 0) - { - us.TextFormatter.Size = us.TextFormatter.GetAutoSize (); - } + //if (us.TextFormatter.Size.Width == 0) + //{ + // us.TextFormatter.Size = us.TextFormatter.GetAutoSize (); + //} textSize = us.TextFormatter.FormatAndGetSize ().Height; - us.TextFormatter.Size = us.TextFormatter.Size with { Height = textSize }; + //us.TextFormatter.Size = us.TextFormatter.Size with { Height = textSize }; } } + List viewsNeedingLayout = new List (); + + if (Style.FastHasFlags (DimAutoStyle.Content)) { if (!us.ContentSizeTracksViewport) @@ -105,14 +110,14 @@ public class DimAuto () : Dim // -------------------- Pos types that are dependent on `us.Subviews` // [ ] PosAlign - Position is dependent on other views with `GroupId` AND `us.ContentSize` // [x] PosView - Position is dependent on `subview.Target` - it can cause a change in `us.ContentSize` - // [x] PosCombine - Position is dependent if `Pos.Has ([one of the above]` - it can cause a change in `us.ContentSize` + // [x] PosCombine - Position is dependent if `Pos.Has [one of the above]` - it can cause a change in `us.ContentSize` // -------------------- Pos types that are dependent on `us.ContentSize` // [ ] PosAlign - Position is dependent on other views with `GroupId` AND `us.ContentSize` // [x] PosAnchorEnd - Position is dependent on `us.ContentSize` AND `subview.Frame` - it can cause a change in `us.ContentSize` - // [ ] PosCenter - Position is dependent `us.ContentSize` AND `subview.Frame` - // [ ] PosPercent - Position is dependent `us.ContentSize` - // [x] PosCombine - Position is dependent if `Pos.Has ([one of the above]` - it can cause a change in `us.ContentSize` + // [ ] PosCenter - Position is dependent `us.ContentSize` AND `subview.Frame` - + // [ ] PosPercent - Position is dependent `us.ContentSize` - Will always be 0 if there is no other content that makes the superview have a size. + // [x] PosCombine - Position is dependent if `Pos.Has [one of the above]` - it can cause a change in `us.ContentSize` // -------------------- Pos types that are not dependent on either `us.Subviews` or `us.ContentSize` // [ ] PosAbsolute - Position is fixed. @@ -120,12 +125,12 @@ public class DimAuto () : Dim // -------------------- Dim types that are dependent on `us.Subviews` // [x] DimView - Dimension is dependent on `subview.Target` - // [x] DimCombine - Dimension is dependent if `Dim.Has ([one of the above]` - it can cause a change in `us.ContentSize` + // [x] DimCombine - Dimension is dependent if `Dim.Has [one of the above]` - it can cause a change in `us.ContentSize` // -------------------- Dim types that are dependent on `us.ContentSize` - // [ ] DimFill - Dimension is dependent on `us.ContentSize` - // [ ] DimPercent - Dimension is dependent on `us.ContentSize` - // [ ] DimCombine - Dimension is dependent if `Dim.Has ([one of the above]` + // [ ] DimFill - Dimension is dependent on `us.ContentSize` - Will always be 0 if there is no other content that makes the superview have a size. + // [ ] DimPercent - Dimension is dependent on `us.ContentSize` - Will always be 0 if there is no other content that makes the superview have a size. + // [ ] DimCombine - Dimension is dependent if `Dim.Has [one of the above]` // -------------------- Dim types that are not dependent on either `us.Subviews` or `us.ContentSize` // [ ] DimAuto - Dimension is internally calculated @@ -143,23 +148,29 @@ public class DimAuto () : Dim List notDependentSubViews; if (dimension == Dimension.Width) { - notDependentSubViews = includedSubviews.Where (v => v.Width is { } && - (v.X is PosAbsolute or PosFunc || v.Width is DimAuto or DimAbsolute or DimFunc) && - !v.X.Has (typeof (PosAnchorEnd), out _) && - !v.X.Has (typeof (PosAlign), out _) && - !v.X.Has (typeof (PosView), out _) && - !v.Width.Has (typeof (DimView), out _) && - !v.X.Has (typeof (PosCenter), out _)).ToList (); + notDependentSubViews = includedSubviews.Where (v => v.Width is { } + && (v.X is PosAbsolute or PosFunc || v.Width is DimAuto or DimAbsolute or DimFunc) + && !v.X.Has (typeof (PosAnchorEnd), out _) + && !v.X.Has (typeof (PosAlign), out _) + && !v.X.Has (typeof (PosView), out _) + && !v.X.Has (typeof (PosCenter), out _) + && !v.Width.Has (typeof (DimView), out _) + && !v.Width.Has (typeof (DimFill), out _) + && !v.Width.Has (typeof (DimPercent), out _) + ).ToList (); } else { - notDependentSubViews = includedSubviews.Where (v => v.Height is { } && - (v.Y is PosAbsolute or PosFunc || v.Height is DimAuto or DimAbsolute or DimFunc) && - !v.Y.Has (typeof (PosAnchorEnd), out _) && - !v.Y.Has (typeof (PosAlign), out _) && - !v.Y.Has (typeof (PosView), out _) && - !v.Height.Has (typeof (DimView), out _) && - !v.Y.Has (typeof (PosCenter), out _)).ToList (); + notDependentSubViews = includedSubviews.Where (v => v.Height is { } + && (v.Y is PosAbsolute or PosFunc || v.Height is DimAuto or DimAbsolute or DimFunc) + && !v.Y.Has (typeof (PosAnchorEnd), out _) + && !v.Y.Has (typeof (PosAlign), out _) + && !v.Y.Has (typeof (PosView), out _) + && !v.Y.Has (typeof (PosCenter), out _) + && !v.Height.Has (typeof (DimView), out _) + && !v.Height.Has (typeof (DimFill), out _) + && !v.Height.Has (typeof (DimPercent), out _) + ).ToList (); } for (var i = 0; i < notDependentSubViews.Count; i++) @@ -170,12 +181,12 @@ public class DimAuto () : Dim if (dimension == Dimension.Width) { - int width = v.Width!.Calculate (0, 0, v, dimension); + int width = v.Width!.Calculate (0, superviewContentSize, v, dimension); size = v.X.GetAnchor (0) + width; } else { - int height = v.Height!.Calculate (0, 0, v, dimension); + int height = v.Height!.Calculate (0, superviewContentSize, v, dimension); size = v.Y.GetAnchor (0) + height; } @@ -199,6 +210,8 @@ public class DimAuto () : Dim centeredSubViews = us.Subviews.Where (v => v.Y.Has (typeof (PosCenter), out _)).ToList (); } + viewsNeedingLayout.AddRange (centeredSubViews); + int maxCentered = 0; for (var i = 0; i < centeredSubViews.Count; i++) @@ -207,48 +220,51 @@ public class DimAuto () : Dim if (dimension == Dimension.Width) { - int width = v.Width!.Calculate (0, 0, v, dimension); - maxCentered = (v.X.GetAnchor (0) + width) * 2; + int width = v.Width!.Calculate (0, screen, v, dimension); + maxCentered = (v.X.GetAnchor (0) + width); } else { - int height = v.Height!.Calculate (0, 0, v, dimension); - maxCentered = (v.Y.GetAnchor (0) + height) * 2; + int height = v.Height!.Calculate (0, screen, v, dimension); + maxCentered = (v.Y.GetAnchor (0) + height); } } maxCalculatedSize = int.Max (maxCalculatedSize, maxCentered); #endregion Centered #region Percent - // [ ] DimPercent - Dimension is dependent on `us.ContentSize` - List percentSubViews; - if (dimension == Dimension.Width) - { - percentSubViews = us.Subviews.Where (v => v.Width.Has (typeof (DimPercent), out _)).ToList (); - } - else - { - percentSubViews = us.Subviews.Where (v => v.Height.Has (typeof (DimPercent), out _)).ToList (); - } + //// [ ] DimPercent - Dimension is dependent on `us.ContentSize` + //// - DimPercent will always be 0 if there is no other content that makes the superview have a size. + //List dimPercentSubViews; + //if (dimension == Dimension.Width) + //{ + // dimPercentSubViews = us.Subviews.Where (v => v.Width.Has (typeof (DimPercent), out _)).ToList (); + //} + //else + //{ + // dimPercentSubViews = us.Subviews.Where (v => v.Height.Has (typeof (DimPercent), out _)).ToList (); + //} - int maxPercent = 0; + //viewsNeedingLayout.AddRange (dimPercentSubViews); - for (var i = 0; i < percentSubViews.Count; i++) - { - View v = percentSubViews [i]; + //int maxDimPercent = 0; - if (dimension == Dimension.Width) - { - int width = v.Width!.Calculate (0, 0, v, dimension); - maxPercent = (v.X.GetAnchor (0) + width); - } - else - { - int height = v.Height!.Calculate (0, 0, v, dimension); - maxPercent = (v.Y.GetAnchor (0) + height); - } - } - maxCalculatedSize = int.Max (maxCalculatedSize, maxPercent); + //for (var i = 0; i < dimPercentSubViews.Count; i++) + //{ + // View v = dimPercentSubViews [i]; + + // if (dimension == Dimension.Width) + // { + // int width = v.Width!.Calculate (0, superviewContentSize, null, dimension); + // maxDimPercent = (v.X.GetAnchor (0) + width); + // } + // else + // { + // int height = v.Height!.Calculate (0, superviewContentSize, null, dimension); + // maxDimPercent = (v.Y.GetAnchor (0) + height); + // } + //} + //maxCalculatedSize = int.Max (maxCalculatedSize, maxDimPercent); #endregion Percent @@ -317,6 +333,8 @@ public class DimAuto () : Dim anchoredSubViews = includedSubviews.Where (v => v.Y.Has (typeof (PosAnchorEnd), out _)).ToList (); } + viewsNeedingLayout.AddRange (anchoredSubViews); + int maxAnchorEnd = 0; for (var i = 0; i < anchoredSubViews.Count; i++) { @@ -325,13 +343,13 @@ public class DimAuto () : Dim // Need to set the relative layout for PosAnchorEnd subviews to calculate the size if (dimension == Dimension.Width) { - v.SetRelativeLayout (new Size (maxCalculatedSize, 0)); + v.SetRelativeLayout (new Size (maxCalculatedSize, screen)); } else { - v.SetRelativeLayout (new Size (0, maxCalculatedSize)); + v.SetRelativeLayout (new Size (screen, maxCalculatedSize)); } - maxAnchorEnd = dimension == Dimension.Width ? v.X.GetAnchor (maxCalculatedSize) + v.Frame.Width : v.Y.GetAnchor (maxCalculatedSize) + v.Frame.Height; + maxAnchorEnd = dimension == Dimension.Width ? v.X.GetAnchor (maxCalculatedSize + v.Frame.Width) : v.Y.GetAnchor (maxCalculatedSize + v.Frame.Height); } maxCalculatedSize = Math.Max (maxCalculatedSize, maxAnchorEnd); @@ -407,218 +425,6 @@ public class DimAuto () : Dim } } #endregion DimView - - - // [x] DimCombine - Dimension is dependent if `Dim.Has ([one of the above]` - it can cause a change in `us.ContentSize` - - - - - - - - // // ====================================================== - // // Now do PosAlign - It's dependent on other views with `GroupId` AND `us.ContentSize` - // // ====================================================== - // // [ ] PosAlign - Position is dependent on other views with `GroupId` AND `us.ContentSize` - // #region Aligned - - // int maxAlign = 0; - // if (dimension == Dimension.Width) - // { - // // Use Linq to get a list of distinct GroupIds from the subviews - // List groupIds = includedSubviews.Select (v => v.X is PosAlign posAlign ? posAlign.GroupId : -1).Distinct ().ToList (); - - // foreach (var groupId in groupIds) - // { - // List dimensionsList = new (); - - // // PERF: If this proves a perf issue, consider caching a ref to this list in each item - // List posAlignsInGroup = includedSubviews.Where ( - // v => - // { - // return dimension switch - // { - // Dimension.Width when v.X is PosAlign alignX => alignX.GroupId == groupId, - // Dimension.Height when v.Y is PosAlign alignY => alignY.GroupId == groupId, - // _ => false - // }; - // }) - // .Select (v => dimension == Dimension.Width ? v.X as PosAlign : v.Y as PosAlign) - // .ToList (); - - // if (posAlignsInGroup.Count == 0) - // { - // continue; - // } - // // BUGBUG: ignores adornments - - // maxAlign = PosAlign.CalculateMinDimension (groupId, includedSubviews, dimension); - // } - // } - // else - // { - - // // BUGBUG: Incompletge - // subviews = includedSubviews.Where (v => v.Y is PosAlign).ToList (); - // } - - // maxCalculatedSize = int.Max (maxCalculatedSize, maxAlign); - // #endregion Aligned - - // // TODO: This whole body of code is a WIP (forhttps://github.com/gui-cs/Terminal.Gui/issues/3499). - - - // List subviews; - - // #region Not Anchored and Are Not Dependent - // // Start with subviews that are not anchored to the end, aligned, or dependent on content size - // // [x] PosAnchorEnd - // // [x] PosAlign - // // [ ] PosCenter - // // [ ] PosPercent - // // [ ] PosView - // // [ ] PosFunc - // // [x] DimFill - // // [ ] DimPercent - // // [ ] DimFunc - // // [ ] DimView - // if (dimension == Dimension.Width) - // { - // subviews = includedSubviews.Where (v => v.X is not PosAnchorEnd - // && v.X is not PosAlign - // // && v.X is not PosCenter - // && v.Width is not DimAuto - // && v.Width is not DimFill).ToList (); - // } - // else - // { - // subviews = includedSubviews.Where (v => v.Y is not PosAnchorEnd - // && v.Y is not PosAlign - // // && v.Y is not PosCenter - // && v.Height is not DimAuto - // && v.Height is not DimFill).ToList (); - // } - - // for (var i = 0; i < subviews.Count; i++) - // { - // View v = subviews [i]; - - // int size = dimension == Dimension.Width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height; - - // if (size > maxCalculatedSize) - // { - // // BUGBUG: Should we break here? Or choose min/max? - // maxCalculatedSize = size; - // } - // } - // #endregion Not Anchored and Are Not Dependent - - - - // #region Auto - - - - // #endregion Auto - - // //#region Center - // //// Now, handle subviews that are Centered - // //if (dimension == Dimension.Width) - // //{ - // // subviews = us.Subviews.Where (v => v.X is PosCenter).ToList (); - // //} - // //else - // //{ - // // subviews = us.Subviews.Where (v => v.Y is PosCenter).ToList (); - // //} - - // //int maxCenter = 0; - // //for (var i = 0; i < subviews.Count; i++) - // //{ - // // View v = subviews [i]; - // // maxCenter = dimension == Dimension.Width ? v.Frame.Width : v.Frame.Height; - // //} - - // //subviewsSize += maxCenter; - // //#endregion Center - - // #region Are Dependent - // // Now, go back to those that are dependent on content size - - - // // Set relative layout for all DimAuto subviews - // List dimAutoSubViews; - // int maxAuto = 0; - // if (dimension == Dimension.Width) - // { - // dimAutoSubViews = includedSubviews.Where (v => v.Width is DimAuto).ToList (); - // } - // else - // { - // dimAutoSubViews = includedSubviews.Where (v => v.Height is DimAuto).ToList (); - // } - // for (var i = 0; i < dimAutoSubViews.Count; i++) - // { - // View v = dimAutoSubViews [i]; - - // if (dimension == Dimension.Width) - // { - // // BUGBUG: ignores adornments - - // v.SetRelativeLayout (new Size (autoMax - maxCalculatedSize, 0)); - // } - // else - // { - // // BUGBUG: ignores adornments - - // v.SetRelativeLayout (new Size (0, autoMax - maxCalculatedSize)); - // } - - // maxAuto = dimension == Dimension.Width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height; - - // if (maxAuto > maxCalculatedSize) - // { - // // BUGBUG: Should we break here? Or choose min/max? - // maxCalculatedSize = maxAuto; - // } - // } - - // // [x] DimFill - // // [ ] DimPercent - // if (dimension == Dimension.Width) - // { - // subviews = includedSubviews.Where (v => v.Width is DimFill).ToList (); - // } - // else - // { - // subviews = includedSubviews.Where (v => v.Height is DimFill).ToList (); - // } - - // int maxFill = 0; - // for (var i = 0; i < subviews.Count; i++) - // { - // View v = subviews [i]; - - // if (autoMax == int.MaxValue) - // { - // autoMax = superviewContentSize; - // } - // if (dimension == Dimension.Width) - // { - // // BUGBUG: ignores adornments - // v.SetRelativeLayout (new Size (autoMax - maxCalculatedSize, 0)); - // } - // else - // { - // // BUGBUG: ignores adornments - // v.SetRelativeLayout (new Size (0, autoMax - maxCalculatedSize)); - // } - // maxFill = dimension == Dimension.Width ? v.Frame.Width : v.Frame.Height; - // } - - // maxCalculatedSize += maxFill; - // #endregion Are Dependent } } @@ -632,11 +438,11 @@ public class DimAuto () : Dim // And, if max: is set, it wins if smaller max = int.Min (max, autoMax); - // ************** We now definitively know `us.ContentSize` *************** - int oppositeScreen = dimension == Dimension.Width ? Application.Screen.Height * 4 : Application.Screen.Width * 4 ; - foreach (var v in us.Subviews) + int oppositeScreen = dimension == Dimension.Width ? Application.Screen.Height * 4 : Application.Screen.Width * 4; + + foreach (var v in viewsNeedingLayout) { if (dimension == Dimension.Width) { diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs index 38463cbba..d3d241d20 100644 --- a/Terminal.Gui/View/Layout/ViewLayout.cs +++ b/Terminal.Gui/View/Layout/ViewLayout.cs @@ -573,6 +573,8 @@ public partial class View Debug.Assert (_height is { }); CheckDimAuto (); + SetTextFormatterSize (); + int newX, newW, newY, newH; // Calculate the new X, Y, Width, and Height @@ -630,7 +632,6 @@ public partial class View { SetTitleTextFormatterSize (); } - SetNeedsLayout (); SetNeedsDisplay (); } @@ -742,8 +743,6 @@ public partial class View Application.Top is { } && Application.Top != this && Application.Top.IsInitialized ? Application.Top.GetContentSize () : Application.Screen.Size; - SetTextFormatterSize (); - SetRelativeLayout (superViewContentSize); if (IsInitialized) diff --git a/Terminal.Gui/View/ViewText.cs b/Terminal.Gui/View/ViewText.cs index a995bc752..f91b97642 100644 --- a/Terminal.Gui/View/ViewText.cs +++ b/Terminal.Gui/View/ViewText.cs @@ -187,18 +187,38 @@ public partial class View if ((widthAuto is { } && widthAuto.Style.FastHasFlags (DimAutoStyle.Text)) || (heightAuto is { } && heightAuto.Style.FastHasFlags (DimAutoStyle.Text))) { - // BUGBUG: This ignores wordwrap and other formatting options. - size = TextFormatter.GetAutoSize (); + int width = 0; + int height = 0; if (widthAuto is null || !widthAuto.Style.FastHasFlags (DimAutoStyle.Text)) { - size.Width = GetContentSize ().Width; + width = GetContentSize ().Width; } if (heightAuto is null || !heightAuto.Style.FastHasFlags (DimAutoStyle.Text)) { - size.Height = GetContentSize ().Height; + height = GetContentSize ().Height; } + + if (widthAuto is { } && widthAuto.Style.FastHasFlags (DimAutoStyle.Text)) + { + if (height == 0 && heightAuto is { } && heightAuto.Style.FastHasFlags (DimAutoStyle.Text)) + { + height = Application.Screen.Height; + } + width = TextFormatter.FormatAndGetSize (new (Application.Screen.Width, height)).Width; + } + + if (heightAuto is { } && heightAuto.Style.FastHasFlags (DimAutoStyle.Text)) + { + if (width == 0 && widthAuto is { } && widthAuto.Style.FastHasFlags (DimAutoStyle.Text)) + { + width = Application.Screen.Height; + } + height = TextFormatter.FormatAndGetSize (new (width, Application.Screen.Height)).Height; + } + + size = new (width, height); } TextFormatter.Size = size; diff --git a/Terminal.Gui/Views/Dialog.cs b/Terminal.Gui/Views/Dialog.cs index f10e831a3..7b4da9355 100644 --- a/Terminal.Gui/Views/Dialog.cs +++ b/Terminal.Gui/Views/Dialog.cs @@ -33,14 +33,14 @@ public class Dialog : Window /// . /// [SerializableConfigurationProperty (Scope = typeof (ThemeScope))] - public static int DefaultMinimumWidth { get; set; } = 0; + public static int DefaultMinimumWidth { get; set; } = 80; /// /// Defines the default minimum Dialog height, as a percentage of the container width. Can be configured via /// . /// [SerializableConfigurationProperty (Scope = typeof (ThemeScope))] - public static int DefaultMinimumHeight { get; set; } = 0; + public static int DefaultMinimumHeight { get; set; } = 80; /// diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs index 7ddbe7c5a..7f1469522 100644 --- a/Terminal.Gui/Views/Shortcut.cs +++ b/Terminal.Gui/Views/Shortcut.cs @@ -484,7 +484,7 @@ public override string Text get => HelpView?.Text; set { - if (HelpView != null) + if (HelpView is {}) { HelpView.Text = value; ShowHide (); @@ -500,7 +500,7 @@ public string HelpText get => HelpView?.Text; set { - if (HelpView != null) + if (HelpView is {}) { HelpView.Text = value; ShowHide (); diff --git a/UICatalog/Scenarios/Dialogs.cs b/UICatalog/Scenarios/Dialogs.cs index c960669f4..26764b8a8 100644 --- a/UICatalog/Scenarios/Dialogs.cs +++ b/UICatalog/Scenarios/Dialogs.cs @@ -79,7 +79,12 @@ public class Dialogs : Scenario frame.Add (heightEdit); frame.Add ( - new Label { X = Pos.Right (widthEdit) + 2, Y = Pos.Top (widthEdit), Text = "If height & width are both 0," } + new Label + { + X = Pos.Right (widthEdit) + 2, + Y = Pos.Top (widthEdit), + Text = $"If width is 0, the dimension will be {Dialog.DefaultMinimumWidth}%." + } ); frame.Add ( @@ -87,7 +92,7 @@ public class Dialogs : Scenario { X = Pos.Right (heightEdit) + 2, Y = Pos.Top (heightEdit), - Text = "the Dialog will size to 80% of container." + Text = $"If height is 0, the dimension will be {Dialog.DefaultMinimumWidth}%." } ); @@ -263,16 +268,19 @@ public class Dialogs : Scenario Buttons = buttons.ToArray () }; - if (height != 0 || width != 0) + if (width != 0) + { + dialog.Width = width; + } + if (height != 0) { dialog.Height = height; - dialog.Width = width; } var add = new Button { X = Pos.Center (), - Y = Pos.Center (), + Y = Pos.Center () - 1, Text = "_Add a button" }; @@ -312,7 +320,7 @@ public class Dialogs : Scenario var addChar = new Button { X = Pos.Center (), - Y = Pos.Center () + 2, + Y = Pos.Center () + 1, Text = $"A_dd a {char.ConvertFromUtf32 (CODE_POINT)} to each button. This text is really long for a reason." }; diff --git a/UnitTests/Text/TextFormatterTests.cs b/UnitTests/Text/TextFormatterTests.cs index 85f3ced33..2aa04fe4f 100644 --- a/UnitTests/Text/TextFormatterTests.cs +++ b/UnitTests/Text/TextFormatterTests.cs @@ -6047,6 +6047,68 @@ B")] TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); } + + // Test that changing TextFormatter does not impact View dimensions if Dim.Auto is not in play + [Fact] + public void Not_Used_TextFormatter_Does_Not_Change_View_Size () + { + View view = new () + { + Text = "_1234" + }; + Assert.False (view.TextFormatter.AutoSize); + Assert.Equal (Size.Empty, view.Frame.Size); + + view.TextFormatter.Text = "ABC"; + Assert.False (view.TextFormatter.AutoSize); + Assert.Equal (Size.Empty, view.Frame.Size); + + view.TextFormatter.Alignment = Alignment.Fill; + Assert.False (view.TextFormatter.AutoSize); + Assert.Equal (Size.Empty, view.Frame.Size); + + view.TextFormatter.VerticalAlignment = Alignment.Center; + Assert.False (view.TextFormatter.AutoSize); + Assert.Equal (Size.Empty, view.Frame.Size); + + view.TextFormatter.HotKeySpecifier = (Rune)'*'; + Assert.False (view.TextFormatter.AutoSize); + Assert.Equal (Size.Empty, view.Frame.Size); + + view.TextFormatter.Text = "*ABC"; + Assert.False (view.TextFormatter.AutoSize); + Assert.Equal (Size.Empty, view.Frame.Size); + } + + + [Fact] + public void Not_Used_TextSettings_Do_Not_Change_View_Size () + { + View view = new () + { + Text = "_1234" + }; + Assert.False (view.TextFormatter.AutoSize); + Assert.Equal (Size.Empty, view.Frame.Size); + + view.TextAlignment = Alignment.Fill; + Assert.False (view.TextFormatter.AutoSize); + Assert.Equal (Size.Empty, view.Frame.Size); + + view.VerticalTextAlignment = Alignment.Center; + Assert.False (view.TextFormatter.AutoSize); + Assert.Equal (Size.Empty, view.Frame.Size); + + view.HotKeySpecifier = (Rune)'*'; + Assert.False (view.TextFormatter.AutoSize); + Assert.Equal (Size.Empty, view.Frame.Size); + + view.Text = "*ABC"; + Assert.False (view.TextFormatter.AutoSize); + Assert.Equal (Size.Empty, view.Frame.Size); + } + + #region FormatAndGetSizeTests // TODO: Add multi-line examples diff --git a/UnitTests/View/Layout/Dim.AutoTests.cs b/UnitTests/View/Layout/Dim.AutoTests.cs index e133b855c..d2dc5a46f 100644 --- a/UnitTests/View/Layout/Dim.AutoTests.cs +++ b/UnitTests/View/Layout/Dim.AutoTests.cs @@ -592,51 +592,6 @@ public class DimAutoTests (ITestOutputHelper output) Assert.Equal (expectedWidth, superView.Frame.Width); } - //// Test Dim.Fill - Fill should not impact width of the DimAuto superview - //[Theory] - //[InlineData (0, 0, 0, 10, 10)] - //[InlineData (0, 1, 0, 10, 10)] - //[InlineData (0, 11, 0, 10, 10)] - //[InlineData (0, 10, 0, 10, 10)] - //[InlineData (0, 5, 0, 10, 10)] - //[InlineData (1, 5, 0, 10, 9)] - //[InlineData (1, 10, 0, 10, 9)] - //[InlineData (0, 0, 1, 10, 9)] - //[InlineData (0, 10, 1, 10, 9)] - //[InlineData (0, 5, 1, 10, 9)] - //[InlineData (1, 5, 1, 10, 8)] - //[InlineData (1, 10, 1, 10, 8)] - //public void Width_Fill_Fills (int subX, int superMinWidth, int fill, int expectedSuperWidth, int expectedSubWidth) - //{ - // var superView = new View - // { - // X = 0, - // Y = 0, - // Width = Dim.Auto (minimumContentDim: superMinWidth), - // Height = 1, - // ValidatePosDim = true - // }; - - // var subView = new View - // { - // X = subX, - // Y = 0, - // Width = Dim.Fill (fill), - // Height = 1, - // ValidatePosDim = true - // }; - - // superView.Add (subView); - - // superView.BeginInit (); - // superView.EndInit (); - // superView.SetRelativeLayout (new (10, 1)); - // Assert.Equal (expectedSuperWidth, superView.Frame.Width); - // superView.LayoutSubviews (); - // Assert.Equal (expectedSubWidth, subView.Frame.Width); - // Assert.Equal (expectedSuperWidth, superView.Frame.Width); - //} - [Theory] [InlineData (0, 1, 1)] [InlineData (1, 1, 1)] @@ -726,6 +681,40 @@ public class DimAutoTests (ITestOutputHelper output) Assert.Equal (expectedSubWidth, subView.Frame.Width); } + #region DimAutoStyle.Auto tests + + [Fact] + public void DimAutoStyle_Auto_Text_Size_Is_Used () + { + var view = new View () + { + Text = "0123\n4567", + Width = Auto (), + Height = Auto (), + }; + + view.SetRelativeLayout (new Size (100, 100)); + Assert.Equal (new (4, 2), view.Frame.Size); + + var subView = new View () + { + Text = "ABCD", + Width = Auto (), + Height = Auto (), + }; + view.Add (subView); + + view.SetRelativeLayout (new Size (100, 100)); + Assert.Equal (new (4, 2), view.Frame.Size); + + subView.Text = "ABCDE"; + + view.SetRelativeLayout (new Size (100, 100)); + Assert.Equal (new (5, 2), view.Frame.Size); + } + + #endregion + [Fact] public void DimAutoStyle_Text_Viewport_Stays_Set () { @@ -755,84 +744,21 @@ public class DimAutoTests (ITestOutputHelper output) super.Dispose (); } - - // TextFormatter.Size normally tracks ContentSize, but with DimAuto, tracks the text size [Theory] [InlineData ("", 0, 0)] [InlineData (" ", 1, 1)] [InlineData ("01234", 5, 1)] - public void DimAutoStyle_Text_TextFormatter_Size_Ignores_ContentSize (string text, int expectedW, int expectedH) + public void DimAutoStyle_Text_Ignores_ContentSize (string text, int expectedW, int expectedH) { var view = new View (); view.Width = Auto (DimAutoStyle.Text); view.Height = Auto (DimAutoStyle.Text); view.SetContentSize (new (1, 1)); view.Text = text; - Assert.Equal (new (expectedW, expectedH), view.TextFormatter.Size); + view.SetRelativeLayout(Application.Screen.Size); + Assert.Equal (new (expectedW, expectedH), view.Frame.Size); } - - // Test that changing TextFormatter does not impact View dimensions if Dim.Auto is not in play - [Fact] - public void Not_Used_TextFormatter_Does_Not_Change_View_Size () - { - View view = new () - { - Text = "_1234" - }; - Assert.False (view.TextFormatter.AutoSize); - Assert.Equal (Size.Empty, view.Frame.Size); - - view.TextFormatter.Text = "ABC"; - Assert.False (view.TextFormatter.AutoSize); - Assert.Equal (Size.Empty, view.Frame.Size); - - view.TextFormatter.Alignment = Alignment.Fill; - Assert.False (view.TextFormatter.AutoSize); - Assert.Equal (Size.Empty, view.Frame.Size); - - view.TextFormatter.VerticalAlignment = Alignment.Center; - Assert.False (view.TextFormatter.AutoSize); - Assert.Equal (Size.Empty, view.Frame.Size); - - view.TextFormatter.HotKeySpecifier = (Rune)'*'; - Assert.False (view.TextFormatter.AutoSize); - Assert.Equal (Size.Empty, view.Frame.Size); - - view.TextFormatter.Text = "*ABC"; - Assert.False (view.TextFormatter.AutoSize); - Assert.Equal (Size.Empty, view.Frame.Size); - } - - - [Fact] - public void Not_Used_TextSettings_Do_Not_Change_View_Size () - { - View view = new () - { - Text = "_1234" - }; - Assert.False (view.TextFormatter.AutoSize); - Assert.Equal (Size.Empty, view.Frame.Size); - - view.TextAlignment = Alignment.Fill; - Assert.False (view.TextFormatter.AutoSize); - Assert.Equal (Size.Empty, view.Frame.Size); - - view.VerticalTextAlignment = Alignment.Center; - Assert.False (view.TextFormatter.AutoSize); - Assert.Equal (Size.Empty, view.Frame.Size); - - view.HotKeySpecifier = (Rune)'*'; - Assert.False (view.TextFormatter.AutoSize); - Assert.Equal (Size.Empty, view.Frame.Size); - - view.Text = "*ABC"; - Assert.False (view.TextFormatter.AutoSize); - Assert.Equal (Size.Empty, view.Frame.Size); - } - - [Fact] public void TextFormatter_Settings_Change_View_Size () { @@ -841,39 +767,50 @@ public class DimAutoTests (ITestOutputHelper output) Text = "_1234", Width = Dim.Auto () }; - Assert.False (view.TextFormatter.AutoSize); - Assert.NotEqual (Size.Empty, view.Frame.Size); + Assert.Equal (Size.Empty, view.Frame.Size); // Height is 0, so width is 0 regardless of text + + view.Height = 1; + view.SetRelativeLayout (Application.Screen.Size); + Assert.Equal (new Size (4, 1), view.Frame.Size); + Size lastSize = view.Frame.Size; view.TextAlignment = Alignment.Fill; - Assert.False (view.TextFormatter.AutoSize); - Assert.NotEqual (Size.Empty, view.Frame.Size); + Assert.Equal (lastSize, view.Frame.Size); view = new () { Text = "_1234", - Width = Dim.Auto () + Width = Dim.Auto (), + Height = 1 }; + view.SetRelativeLayout (Application.Screen.Size); + + lastSize = view.Frame.Size; view.VerticalTextAlignment = Alignment.Center; - Assert.False (view.TextFormatter.AutoSize); - Assert.NotEqual (Size.Empty, view.Frame.Size); + Assert.Equal (lastSize, view.Frame.Size); view = new () { Text = "_1234", - Width = Dim.Auto () + Width = Dim.Auto (), + Height = 1, }; + view.SetRelativeLayout (Application.Screen.Size); + lastSize = view.Frame.Size; view.HotKeySpecifier = (Rune)'*'; - Assert.False (view.TextFormatter.AutoSize); - Assert.NotEqual (Size.Empty, view.Frame.Size); + view.SetRelativeLayout (Application.Screen.Size); + Assert.NotEqual (lastSize, view.Frame.Size); view = new () { Text = "_1234", - Width = Dim.Auto () + Width = Dim.Auto (), + Height = 1 }; - view.Text = "*ABC"; - Assert.False (view.TextFormatter.AutoSize); - Assert.NotEqual (Size.Empty, view.Frame.Size); + view.SetRelativeLayout (Application.Screen.Size); + lastSize = view.Frame.Size; + view.Text = "*ABCD"; + Assert.NotEqual (lastSize, view.Frame.Size); } // Ensure TextFormatter.AutoSize is never used for View.Text @@ -930,7 +867,6 @@ public class DimAutoTests (ITestOutputHelper output) Assert.Equal (new (1, expected), view.TextFormatter.Size); } - [SetupFakeDriver] [Fact] public void Change_To_Non_Auto_Resets_ContentSize () @@ -1042,15 +978,16 @@ public class DimAutoTests (ITestOutputHelper output) } [Theory] - [InlineData (0, 50, 50)] - [InlineData (1, 50, 51)] - [InlineData (0, 25, 25)] - [InlineData (-1, 50, 49)] - public void With_Subview_Using_DimPercent (int subViewOffset, int percent, int expectedSize) + [InlineData (0, 0, 0, 0, 0, 0, 0, 0)] + [InlineData (0, 50, 0, 0, 0, 0, 0, 0)] + [InlineData (0, 0, 100, 100, 100, 100, 100, 100)] + [InlineData (0, 50, 100, 100, 100, 100, 100, 100)] + public void With_Subview_Using_DimPercent (int subViewOffset, int percent, int minWidth, int maxWidth, int minHeight, int maxHeight, int expectedWidth, int expectedHeight) { var view = new View () { - Width = 100, Height = 100 + Width = Dim.Auto (minimumContentDim: minWidth, maximumContentDim: maxWidth), + Height = Dim.Auto (minimumContentDim: minHeight, maximumContentDim: maxHeight) }; var subview = new View () { @@ -1061,48 +998,105 @@ public class DimAutoTests (ITestOutputHelper output) }; view.Add (subview); - view.BeginInit (); - view.EndInit (); - // Assuming the calculation is done after layout - int calculatedX = subview.X.Calculate (100, subview.Width, subview, Dimension.Width); - int calculatedY = subview.Y.Calculate (100, subview.Height, subview, Dimension.Height); - int calculatedWidth = subview.Width.Calculate (0, 100, view, Dimension.Width); - int calculatedHeight = subview.Height.Calculate (0, 100, view, Dimension.Height); + int calculatedX = view.X.Calculate (100, view.Width, view, Dimension.Width); + int calculatedY = view.Y.Calculate (100, view.Height, view, Dimension.Height); + int calculatedWidth = view.Width.Calculate (0, 100, view, Dimension.Width); + int calculatedHeight = view.Height.Calculate (0, 100, view, Dimension.Height); - Assert.Equal (20, calculatedWidth); // subview's width - Assert.Equal (10, calculatedHeight); // subview's height - Assert.Equal (50, calculatedX); // 50% of 100 (Width) - Assert.Equal (50, calculatedY); // 50% of 100 (Height) + Assert.Equal (expectedWidth, calculatedWidth); // subview's width + Assert.Equal (expectedHeight, calculatedHeight); // subview's height + Assert.Equal (subViewOffset, calculatedX); + Assert.Equal (subViewOffset, calculatedY); + + view.SetRelativeLayout (new (100, 100)); + view.LayoutSubviews (); + + Assert.Equal (expectedWidth * (percent / 100f), subview.Viewport.Width); + Assert.Equal (expectedHeight * (percent / 100f), subview.Viewport.Height); } [Theory] - [InlineData (0, 0, 100)] - [InlineData (1, 0, 100)] - [InlineData (0, 1, 100)] - [InlineData (1, 1, 100)] - public void With_Subview_Using_DimFill (int subViewOffset, int dimFillMargin, int expectedSize) + [InlineData (0, 0, 0, 0, 0, 0)] + [InlineData (0, 19, 0, 9, 0, 0)] + [InlineData (0, 20, 0, 10, 0, 0)] + [InlineData (0, 21, 0, 11, 0, 0)] + [InlineData (1, 21, 1, 11, 1, 1)] + [InlineData (21, 21, 11, 11, 21, 11)] + public void With_Subview_Using_DimFill (int minWidth, int maxWidth, int minHeight, int maxHeight, int expectedWidth, int expectedHeight) { - // BUGBUG: THis test is totally bogus. Dim.Fill isnot working right yet. var view = new View () { - Width = Dim.Auto (DimAutoStyle.Content, minimumContentDim: 100, maximumContentDim: 100), - Height = Dim.Auto (DimAutoStyle.Content, minimumContentDim: 100, maximumContentDim: 100), + Width = Dim.Auto (minimumContentDim: minWidth, maximumContentDim: maxWidth), + Height = Dim.Auto (minimumContentDim: minHeight, maximumContentDim: maxHeight) }; var subview = new View () { - X = subViewOffset, - Y = subViewOffset, - Width = Dim.Fill (dimFillMargin), - Height = Dim.Fill (dimFillMargin) + X = 0, + Y = 0, + Width = Fill (), + Height = Fill () }; view.Add (subview); - //view.LayoutSubviews (); - view.SetRelativeLayout (new (200, 200)); - Assert.Equal (expectedSize, view.Frame.Width); + // Assuming the calculation is done after layout + int calculatedX = view.X.Calculate (100, view.Width, view, Dimension.Width); + int calculatedY = view.Y.Calculate (100, view.Height, view, Dimension.Height); + int calculatedWidth = view.Width.Calculate (0, 100, view, Dimension.Width); + int calculatedHeight = view.Height.Calculate (0, 100, view, Dimension.Height); + + Assert.Equal (expectedWidth, calculatedWidth); + Assert.Equal (expectedHeight, calculatedHeight); + + Assert.Equal (0, calculatedX); + Assert.Equal (0, calculatedY); } + [Theory] + [InlineData (0, 0, 0, 0, 0, 0)] + [InlineData (0, 19, 0, 9, 2, 4)] + [InlineData (0, 20, 0, 10, 2, 4)] + [InlineData (0, 21, 0, 11, 2, 4)] + [InlineData (1, 21, 1, 11, 2, 4)] + [InlineData (21, 21, 11, 11, 21, 11)] + public void With_Subview_Using_DimFill_And_Another_Subview (int minWidth, int maxWidth, int minHeight, int maxHeight, int expectedWidth, int expectedHeight) + { + var view = new View () + { + Width = Dim.Auto (minimumContentDim: minWidth, maximumContentDim: maxWidth), + Height = Dim.Auto (minimumContentDim: minHeight, maximumContentDim: maxHeight) + }; + var absView = new View () + { + X = 1, + Y = 2, + Width = 1, + Height = 2 + }; + view.Add (absView); + var subview = new View () + { + X = 0, + Y = 0, + Width = Fill (), + Height = Fill () + }; + view.Add (subview); + + // Assuming the calculation is done after layout + int calculatedX = view.X.Calculate (100, view.Width, view, Dimension.Width); + int calculatedY = view.Y.Calculate (100, view.Height, view, Dimension.Height); + int calculatedWidth = view.Width.Calculate (0, 100, view, Dimension.Width); + int calculatedHeight = view.Height.Calculate (0, 100, view, Dimension.Height); + + Assert.Equal (expectedWidth, calculatedWidth); + Assert.Equal (expectedHeight, calculatedHeight); + + Assert.Equal (0, calculatedX); + Assert.Equal (0, calculatedY); + } + + [Fact] public void With_Subview_Using_DimFunc () { @@ -1150,7 +1144,7 @@ public class DimAutoTests (ITestOutputHelper output) [InlineData (0, 20, 0, 10, 20, 10)] [InlineData (0, 21, 0, 11, 21, 11)] [InlineData (1, 21, 1, 11, 21, 11)] - [InlineData (100, 21, 100, 11, 21, 11)] + [InlineData (21, 21, 11, 11, 21, 11)] public void With_Subview_Using_PosAbsolute (int minWidth, int maxWidth, int minHeight, int maxHeight, int expectedWidth, int expectedHeight) { var view = new View () @@ -1186,7 +1180,7 @@ public class DimAutoTests (ITestOutputHelper output) [InlineData (0, 20, 0, 10, 20, 10)] [InlineData (0, 21, 0, 11, 20, 10)] [InlineData (1, 21, 1, 11, 20, 10)] - [InlineData (100, 21, 100, 11, 21, 11)] + [InlineData (21, 21, 11, 11, 21, 11)] public void With_Subview_Using_PosPercent (int minWidth, int maxWidth, int minHeight, int maxHeight, int expectedWidth, int expectedHeight) { var view = new View () @@ -1228,7 +1222,7 @@ public class DimAutoTests (ITestOutputHelper output) [InlineData (0, 20, 0, 10, 20, 10)] [InlineData (0, 21, 0, 11, 21, 11)] [InlineData (1, 21, 1, 11, 21, 11)] - [InlineData (100, 21, 100, 11, 21, 11)] + [InlineData (21, 21, 11, 11, 21, 11)] public void With_Subview_Using_PosPercent_Combine (int minWidth, int maxWidth, int minHeight, int maxHeight, int expectedWidth, int expectedHeight) { var view = new View () @@ -1268,9 +1262,9 @@ public class DimAutoTests (ITestOutputHelper output) [InlineData (0, 0, 0, 0, 0, 0)] [InlineData (0, 19, 0, 9, 19, 9)] [InlineData (0, 20, 0, 10, 20, 10)] - [InlineData (0, 21, 0, 11, 21, 11)] - [InlineData (1, 21, 1, 11, 21, 11)] - [InlineData (100, 21, 100, 11, 21, 11)] + [InlineData (0, 21, 0, 11, 20, 10)] + [InlineData (1, 21, 1, 11, 20, 10)] + [InlineData (21, 21, 11, 11, 21, 11)] public void With_Subview_Using_PosCenter (int minWidth, int maxWidth, int minHeight, int maxHeight, int expectedWidth, int expectedHeight) { var view = new View () @@ -1313,7 +1307,7 @@ public class DimAutoTests (ITestOutputHelper output) [InlineData (0, 20, 0, 10, 20, 10)] [InlineData (0, 21, 0, 11, 21, 11)] [InlineData (1, 21, 1, 11, 21, 11)] - [InlineData (100, 21, 100, 11, 21, 11)] + [InlineData (21, 21, 11, 11, 21, 11)] public void With_Subview_Using_PosCenter_Combine (int minWidth, int maxWidth, int minHeight, int maxHeight, int expectedWidth, int expectedHeight) { var view = new View () @@ -1357,7 +1351,7 @@ public class DimAutoTests (ITestOutputHelper output) [InlineData (0, 20, 0, 10, 20, 10)] [InlineData (0, 21, 0, 11, 20, 10)] [InlineData (1, 21, 1, 11, 20, 10)] - [InlineData (100, 21, 100, 11, 21, 11)] + [InlineData (21, 21, 11, 11, 21, 11)] public void With_Subview_Using_PosAnchorEnd (int minWidth, int maxWidth, int minHeight, int maxHeight, int expectedWidth, int expectedHeight) { var view = new View () @@ -1469,12 +1463,14 @@ public class DimAutoTests (ITestOutputHelper output) View view = new () { Width = Auto (), + Height = 1, Text = text }; View subView = new () { Width = dimension, + Height = 1, }; view.Add (subView); diff --git a/UnitTests/Views/ShortcutTests.cs b/UnitTests/Views/ShortcutTests.cs index 21143b11a..c434f2dbe 100644 --- a/UnitTests/Views/ShortcutTests.cs +++ b/UnitTests/Views/ShortcutTests.cs @@ -38,7 +38,8 @@ public class ShortcutTests Assert.IsType (shortcut.Width); Assert.IsType (shortcut.Height); - + //shortcut.BeginInit(); + //shortcut.EndInit (); shortcut.LayoutSubviews (); shortcut.SetRelativeLayout (new (100, 100));