From 45a10831286213147158cf745a86cafe019cd363 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 22 Jan 2024 11:00:41 -0700 Subject: [PATCH] Experiments in DimAutoStyle.Text --- Terminal.Gui/Text/TextFormatter.cs | 11 ++-- Terminal.Gui/View/Layout/PosDim.cs | 43 ++++++++------- Terminal.Gui/View/Layout/ViewLayout.cs | 47 ++++++++++------- Terminal.Gui/View/ViewText.cs | 31 ++++++++--- UICatalog/Scenarios/DimAutoDemo.cs | 66 +++++++++++++++--------- UnitTests/View/Text/AutoSizeTextTests.cs | 60 ++++++++++----------- 6 files changed, 155 insertions(+), 103 deletions(-) diff --git a/Terminal.Gui/Text/TextFormatter.cs b/Terminal.Gui/Text/TextFormatter.cs index dbcd6ef11..a15d43399 100644 --- a/Terminal.Gui/Text/TextFormatter.cs +++ b/Terminal.Gui/Text/TextFormatter.cs @@ -866,7 +866,7 @@ namespace Terminal.Gui { } /// - /// Calculates the rectangle required to hold text, assuming no word wrapping or justification. + /// Calculates the rectangle required to hold a formatted string of text. /// /// The x location of the rectangle /// The y location of the rectangle @@ -1303,9 +1303,12 @@ namespace Terminal.Gui { public Size GetFormattedSize () { var lines = Lines; - var width = Lines.Max (line => line.GetColumns ()); - var height = Lines.Count; - return new Size (width, height); + if (Lines.Count > 0) { + var width = Lines.Max (line => line.GetColumns ()); + var height = Lines.Count; + return new Size (width, height); + } + return Size.Empty; } /// diff --git a/Terminal.Gui/View/Layout/PosDim.cs b/Terminal.Gui/View/Layout/PosDim.cs index b4ca7ad45..edae232b6 100644 --- a/Terminal.Gui/View/Layout/PosDim.cs +++ b/Terminal.Gui/View/Layout/PosDim.cs @@ -666,27 +666,39 @@ public class Dim { internal virtual int Anchor (int width) => 0; /// - /// Specifies how will compute the dimension. + /// Specifies how will compute the dimension. /// public enum DimAutoStyle { /// - /// The dimension will be computed from the view's . NOT CURRENTLY SUPPORTED. + /// The dimension will be computed using both the view's and + /// . + /// The larger of the corresponding text dimension or Subview in + /// with the largest corresponding position plus dimension will determine the dimension. /// - Text, + Auto, /// - /// The dimension will be computed from the view's . + /// The Subview in with the largest corresponding position plus dimension + /// will determine the dimension. + /// The corresponding dimension of the view's will be ignored. /// - Subviews + Subviews, + + /// + /// The corresponding dimension of the view's , formatted using the settings, + /// will be used to determine the dimension. + /// The corresponding dimensions of the will be ignored. + /// + Text } /// - /// Creates a object that automatically sizes the view to fit all of the view's SubViews. + /// Creates a object that automatically sizes the view to fit all of the view's SubViews and/or Text. /// /// - /// This initializes a with two SubViews. The view will be automatically sized to fit the two - /// SubViews. - /// + /// This initializes a with two SubViews. The view will be automatically sized to fit the two + /// SubViews. + /// /// 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.AutoSize (), Height = Dim.AutoSize () }; @@ -695,19 +707,12 @@ public class Dim { /// /// The AutoSize object. /// - /// Specifies how will compute the dimension. The default is - /// . NOT CURRENTLY SUPPORTED. + /// Specifies how will compute the dimension. The default is . /// - /// Specifies the minimum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED. + /// Specifies the minimum dimension that view will be automatically sized to. /// Specifies the maximum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED. - public static Dim Auto (DimAutoStyle style = DimAutoStyle.Subviews, Dim min = null, Dim max = null) + public static Dim Auto (DimAutoStyle style = DimAutoStyle.Auto, Dim min = null, Dim max = null) { - //if (style == DimAutoStyle.Text) { - // throw new NotImplementedException (@"DimAutoStyle.Text is not implemented."); - //} - //if (min != null) { - // throw new NotImplementedException (@"min is not implemented"); - //} if (max != null) { throw new NotImplementedException (@"max is not implemented"); } diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs index 05e663025..eb75f1426 100644 --- a/Terminal.Gui/View/Layout/ViewLayout.cs +++ b/Terminal.Gui/View/Layout/ViewLayout.cs @@ -783,7 +783,7 @@ public partial class View { if (AutoSize) { // Note this is global to this function and used as such within the local functions defined // below. In v2 AutoSize will be re-factored to not need to be dealt with in this function. - autosize = GetAutoSize (); + autosize = GetTextAutoSize (); } // TODO: Since GetNewLocationAndDimension does not depend on View, it can be moved into PosDim.cs @@ -826,20 +826,27 @@ public partial class View { case Dim.DimAuto auto: var thickness = GetAdornmentsThickness (); - //newDimension = GetNewDimension (auto._min, location, dimension, autosize); - if (width) { - var max = int.Max (GetAutoSize ().Width, auto._min?.Anchor (superviewBounds.Width) ?? 0); - if (auto._style == Dim.DimAutoStyle.Subviews) { - max = Subviews.Count == 0 ? 0 : Subviews.Where (v => v.X is not Pos.PosAnchorEnd).Max (v => v.Frame.X + v.Frame.Width); + var text = 0; + var subviews = 0; + + if (auto._style is Dim.DimAutoStyle.Text or Dim.DimAutoStyle.Auto) { + if (Id == "multiLine") { + } - newDimension = int.Max (max + thickness.Left + thickness.Right, auto._min?.Anchor (superviewBounds.Width) ?? 0); - } else { - var max = int.Max (GetAutoSize ().Height, auto._min?.Anchor (superviewBounds.Height) ?? 0); - if (auto._style == Dim.DimAutoStyle.Subviews) { - max = Subviews.Count == 0 ? 0 : Subviews.Where (v => v.Y is not Pos.PosAnchorEnd).Max (v => v.Frame.Y + v.Frame.Height); - } - newDimension = int.Max (max + thickness.Top + thickness.Bottom, auto._min?.Anchor (superviewBounds.Height) ?? 0); - } + + text = int.Max (width ? TextFormatter.Size.Width : TextFormatter.Size.Height, + auto._min?.Anchor (width ? superviewBounds.Width : superviewBounds.Height) ?? 0); + } + + if (auto._style is Dim.DimAutoStyle.Subviews or Dim.DimAutoStyle.Auto) { + subviews = Subviews.Count == 0 ? 0 : Subviews + .Where (v => width ? v.X is not Pos.PosAnchorEnd : v.Y is not Pos.PosAnchorEnd) + .Max (v => width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height); + } + + var max = int.Max (text, subviews); + newDimension = int.Max (width ? max + thickness.Left + thickness.Right : max + thickness.Top + thickness.Bottom, + auto._min?.Anchor (width ? superviewBounds.Width : superviewBounds.Height) ?? 0); break; case Dim.DimAbsolute: @@ -875,7 +882,7 @@ public partial class View { // TODO: Move combine logic into PosCombine? // TODO: Move combine logic into PosCombine? int left, right; - (left, newDimension) = GetNewLocationAndDimension (width, superviewBounds, combine._left, dim, autosizeDimension); + (left, newDimension) = GetNewLocationAndDimension (width, superviewBounds, combine._left, dim, autosizeDimension); (right, newDimension) = GetNewLocationAndDimension (width, superviewBounds, combine._right, dim, autosizeDimension); if (combine._add) { newLocation = left + right; @@ -980,7 +987,7 @@ public partial class View { } return; case Pos.PosCombine pc: - CollectPos (pc._left, from, ref nNodes, ref nEdges); + CollectPos (pc._left, from, ref nNodes, ref nEdges); CollectPos (pc._right, from, ref nNodes, ref nEdges); break; } @@ -999,7 +1006,7 @@ public partial class View { } return; case Dim.DimCombine dc: - CollectDim (dc._left, from, ref nNodes, ref nEdges); + CollectDim (dc._left, from, ref nNodes, ref nEdges); CollectDim (dc._right, from, ref nNodes, ref nEdges); break; } @@ -1015,7 +1022,7 @@ public partial class View { } CollectPos (v.X, v, ref nNodes, ref nEdges); CollectPos (v.Y, v, ref nNodes, ref nEdges); - CollectDim (v.Width, v, ref nNodes, ref nEdges); + CollectDim (v.Width, v, ref nNodes, ref nEdges); CollectDim (v.Height, v, ref nNodes, ref nEdges); } } @@ -1153,7 +1160,7 @@ public partial class View { CheckDimAuto (); - LayoutAdornments (); + LayoutAdornments (); var oldBounds = Bounds; OnLayoutStarted (new LayoutEventArgs { OldBounds = oldBounds }); @@ -1211,7 +1218,7 @@ public partial class View { } var boundsChanged = true; - var newFrameSize = GetAutoSize (); + var newFrameSize = GetTextAutoSize (); if (IsInitialized && newFrameSize != Frame.Size) { if (ValidatePosDim) { // BUGBUG: This ain't right, obviously. We need to figure out how to handle this. diff --git a/Terminal.Gui/View/ViewText.cs b/Terminal.Gui/View/ViewText.cs index 6f412bf27..d9c5905c0 100644 --- a/Terminal.Gui/View/ViewText.cs +++ b/Terminal.Gui/View/ViewText.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; namespace Terminal.Gui; @@ -289,17 +290,33 @@ public partial class View { return; } - TextFormatter.Size = new Size (Bounds.Size.Width + GetHotKeySpecifierLength (), - Bounds.Size.Height + GetHotKeySpecifierLength (false)); + var w = Bounds.Size.Width + GetHotKeySpecifierLength (); + if (Width is Dim.DimAuto widthAuto && widthAuto._style != Dim.DimAutoStyle.Subviews) { + w = SuperView?.Bounds.Width ?? 0; + TextFormatter.Size = new Size (SuperView?.Bounds.Width ?? 0, Bounds.Size.Height + GetHotKeySpecifierLength ()); + w = TextFormatter.GetFormattedSize ().Width; + } else { + TextFormatter.Size = new Size (w, SuperView?.Bounds.Height ?? 0); + } + + var h = Bounds.Size.Height + GetHotKeySpecifierLength (); + if (Height is Dim.DimAuto heightAuto && heightAuto._style != Dim.DimAutoStyle.Subviews) { + h = SuperView?.Bounds.Height ?? 0; + h = TextFormatter.GetFormattedSize ().Height; + } + TextFormatter.Size = new Size (w, h); } + // TODO: Refactor this to return the Bounds size, not Frame. Move code that accounts for + // TODO: Thickness out to callers. /// - /// Gets the Frame dimensions required to fit within using the text - /// specified by the - /// property and accounting for any characters. + /// Gets the size of the required to fit within using the text + /// formatting settings of and accounting for . /// - /// The the needs to be set to fit the text. - public Size GetAutoSize () + /// + /// + /// The of the required to fit the formatted text. + public Size GetTextAutoSize () { var x = 0; var y = 0; diff --git a/UICatalog/Scenarios/DimAutoDemo.cs b/UICatalog/Scenarios/DimAutoDemo.cs index 670d6a095..c9dc351e0 100644 --- a/UICatalog/Scenarios/DimAutoDemo.cs +++ b/UICatalog/Scenarios/DimAutoDemo.cs @@ -16,36 +16,57 @@ public class DimAutoDemo : Scenario { public override void Setup () { - var textField = new TextField { Text = "", X = 1, Y = 0, Width = 20, Height = 1 }; + var textEdit = new TextView { Text = "", X = 1, Y = 0, Width = 20, Height = 4 }; var hlabel = new Label { - Text = textField.Text, - X = Pos.Left (textField) + 1, - Y = Pos.Bottom (textField), + Text = textEdit.Text, + X = Pos.Left (textEdit) + 1, + Y = Pos.Bottom (textEdit), AutoSize = false, Width = Dim.Auto (style: DimAutoStyle.Text, min: 20), ColorScheme = Colors.ColorSchemes["Error"] }; var vlabel = new Label { - Text = textField.Text, - X = Pos.Left (textField), - Y = Pos.Bottom (textField) + 1, + Text = textEdit.Text, + X = Pos.Left (textEdit), + Y = Pos.Bottom (textEdit) + 1, AutoSize = false, - Height = Dim.Auto (style: DimAutoStyle.Text, min: 10), + Height = Dim.Auto (style: DimAutoStyle.Text, min: 8), ColorScheme = Colors.ColorSchemes ["Error"], TextDirection = TextDirection.TopBottom_LeftRight }; - textField.TextChanged += (s, e) => { - hlabel.Text = textField.Text; - vlabel.Text = textField.Text; + var heightAuto = new View () { + X = Pos.Right (vlabel) + 1, + Y = Pos.Bottom (hlabel) + 1, + Width = 10, + Height = Dim.Auto(), + ColorScheme = Colors.ColorSchemes ["Error"] + }; + heightAuto.Id = "heightAuto"; + + var widthAuto = new View () { + X = Pos.Right (heightAuto) + 1, + Y = Pos.Bottom (hlabel) + 1, + Width = Dim.Auto (), + Height = 3, + ColorScheme = Colors.ColorSchemes ["Error"] + }; + heightAuto.Id = "widthAuto"; + + + textEdit.ContentsChanged += (s, e) => { + hlabel.Text = textEdit.Text; + vlabel.Text = textEdit.Text; + heightAuto.Text = textEdit.Text; + widthAuto.Text = textEdit.Text; }; var movingButton = new Button () { - Text = "P_ress to make button move down.", - X = 2, - Y = Pos.Bottom (hlabel), + Text = "_Move down", + X = Pos.Right (vlabel), + Y = Pos.Bottom (heightAuto), Width = 10 }; movingButton.Clicked += (s, e) => { @@ -53,21 +74,20 @@ public class DimAutoDemo : Scenario { }; var resetButton = new Button () { - Text = "P_ut Button Back", - X = 30,//Pos.AnchorEnd () - 19, + Text = "_Reset Button", + X = Pos.Right(movingButton), Y = Pos.Top (movingButton), }; - var view = new FrameView () { - Title = "Type in the TextField to make View grow.", - X = 3, - Y = 3, - Width = Dim.Auto (min: 50), - Height = Dim.Auto (min: 10) + Title = "Type to make View grow", + X = 1, + Y = 1, + Width = Dim.Auto (style: DimAutoStyle.Subviews, min: 40), + Height = Dim.Auto (style: DimAutoStyle.Subviews, min: 10) }; view.ValidatePosDim = true; - view.Add (textField, hlabel, vlabel, resetButton, movingButton); + view.Add (textEdit, hlabel, vlabel, heightAuto, widthAuto, resetButton, movingButton); resetButton.Clicked += (s, e) => { movingButton.Y = Pos.Bottom (hlabel); diff --git a/UnitTests/View/Text/AutoSizeTextTests.cs b/UnitTests/View/Text/AutoSizeTextTests.cs index f90dbe219..2f49c232e 100644 --- a/UnitTests/View/Text/AutoSizeTextTests.cs +++ b/UnitTests/View/Text/AutoSizeTextTests.cs @@ -350,30 +350,30 @@ public class AutoSizeTextTests { Application.Begin (Application.Top); ((FakeDriver)Application.Driver).SetBufferSize (10, 4); - var size = view.GetAutoSize (); + var size = view.GetTextAutoSize (); Assert.Equal (new Size (text.Length, 1), size); view.Text = $"{text}\n{text}"; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (text.Length, 2), size); view.Text = $"{text}\n{text}\n{text}+"; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (text.Length + 1, 3), size); text = string.Empty; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (0, 0), size); text = "1"; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (1, 1), size); text = "界"; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (2, 1), size); } @@ -396,30 +396,30 @@ public class AutoSizeTextTests { Application.Begin (Application.Top); ((FakeDriver)Application.Driver).SetBufferSize (10, 4); - var size = view.GetAutoSize (); + var size = view.GetTextAutoSize (); Assert.Equal (new Size (1, text.Length), size); view.Text = $"{text}\n{text}"; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (2, text.Length), size); view.Text = $"{text}\n{text}\n{text}+"; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (3, text.Length + 1), size); text = string.Empty; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (0, 0), size); text = "1"; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (1, 1), size); text = "界"; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (2, 1), size); } @@ -442,30 +442,30 @@ public class AutoSizeTextTests { Application.Begin (Application.Top); ((FakeDriver)Application.Driver).SetBufferSize (10, 4); - var size = view.GetAutoSize (); + var size = view.GetTextAutoSize (); Assert.Equal (new Size (text.Length, 1), size); view.Text = $"{text}\n{text}"; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (text.Length, 2), size); view.Text = $"{text}\n{text}\n{text}+"; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (text.Length + 1, 3), size); text = string.Empty; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (0, 0), size); text = "1"; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (1, 1), size); text = "界"; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (2, 1), size); } @@ -488,30 +488,30 @@ public class AutoSizeTextTests { Application.Begin (Application.Top); ((FakeDriver)Application.Driver).SetBufferSize (10, 4); - var size = view.GetAutoSize (); + var size = view.GetTextAutoSize (); Assert.Equal (new Size (text.Length, 1), size); view.Text = $"{text}\n{text}"; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (text.Length, 2), size); view.Text = $"{text}\n{text}\n{text}+"; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (text.Length + 1, 3), size); text = string.Empty; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (0, 0), size); text = "1"; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (1, 1), size); text = "界"; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (2, 1), size); } @@ -534,30 +534,30 @@ public class AutoSizeTextTests { Application.Begin (Application.Top); ((FakeDriver)Application.Driver).SetBufferSize (10, 4); - var size = view.GetAutoSize (); + var size = view.GetTextAutoSize (); Assert.Equal (new Size (text.Length, 1), size); view.Text = $"{text}\n{text}"; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (text.Length, 2), size); view.Text = $"{text}\n{text}\n{text}+"; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (text.Length + 1, 3), size); text = string.Empty; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (0, 0), size); text = "1"; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (1, 1), size); text = "界"; view.Text = text; - size = view.GetAutoSize (); + size = view.GetTextAutoSize (); Assert.Equal (new Size (2, 1), size); }