Experiments in DimAutoStyle.Text

This commit is contained in:
Tig Kindel
2024-01-22 11:00:41 -07:00
parent 7ecf844ccc
commit 45a1083128
6 changed files with 155 additions and 103 deletions

View File

@@ -866,7 +866,7 @@ namespace Terminal.Gui {
}
/// <summary>
/// Calculates the rectangle required to hold text, assuming no word wrapping or justification.
/// Calculates the rectangle required to hold a formatted string of text.
/// </summary>
/// <param name="x">The x location of the rectangle</param>
/// <param name="y">The y location of the rectangle</param>
@@ -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;
}
/// <summary>

View File

@@ -666,27 +666,39 @@ public class Dim {
internal virtual int Anchor (int width) => 0;
/// <summary>
/// Specifies how <see cref="DimAuto" /> will compute the dimension.
/// Specifies how <see cref="DimAuto" /> will compute the dimension.
/// </summary>
public enum DimAutoStyle {
/// <summary>
/// The dimension will be computed from the view's <see cref="View.Text" />. NOT CURRENTLY SUPPORTED.
/// The dimension will be computed using both the view's <see cref="View.Text"/> and
/// <see cref="View.Subviews" />.
/// The larger of the corresponding text dimension or Subview in <see cref="View.Subviews" />
/// with the largest corresponding position plus dimension will determine the dimension.
/// </summary>
Text,
Auto,
/// <summary>
/// The dimension will be computed from the view's <see cref="View.Subviews" />.
/// The Subview in <see cref="View.Subviews" /> with the largest corresponding position plus dimension
/// will determine the dimension.
/// The corresponding dimension of the view's <see cref="View.Text"/> will be ignored.
/// </summary>
Subviews
Subviews,
/// <summary>
/// The corresponding dimension of the view's <see cref="View.Text" />, formatted using the <see cref="View.TextFormatter"/> settings,
/// will be used to determine the dimension.
/// The corresponding dimensions of the <see cref="View.Subviews"/> will be ignored.
/// </summary>
Text
}
/// <summary>
/// Creates a <see cref="Dim" /> object that automatically sizes the view to fit all of the view's SubViews.
/// Creates a <see cref="Dim" /> object that automatically sizes the view to fit all of the view's SubViews and/or Text.
/// </summary>
/// <example>
/// This initializes a <see cref="View" /> with two SubViews. The view will be automatically sized to fit the two
/// SubViews.
/// <code>
/// This initializes a <see cref="View" /> with two SubViews. The view will be automatically sized to fit the two
/// SubViews.
/// <code>
/// 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 {
/// </example>
/// <returns>The AutoSize <see cref="Dim" /> object.</returns>
/// <param name="style">
/// Specifies how <see cref="DimAuto" /> will compute the dimension. The default is
/// <see cref="DimAutoStyle.Text" />. NOT CURRENTLY SUPPORTED.
/// Specifies how <see cref="DimAuto" /> will compute the dimension. The default is <see cref="DimAutoStyle.Auto" />.
/// </param>
/// <param name="min">Specifies the minimum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED.</param>
/// <param name="min">Specifies the minimum dimension that view will be automatically sized to.</param>
/// <param name="max">Specifies the maximum dimension that view will be automatically sized to. NOT CURRENTLY SUPPORTED.</param>
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");
}

View File

@@ -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.

View File

@@ -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.
/// <summary>
/// Gets the Frame dimensions required to fit <see cref="Text"/> within <see cref="Bounds"/> using the text
/// <see cref="Direction"/> specified by the
/// <see cref="TextFormatter"/> property and accounting for any <see cref="HotKeySpecifier"/> characters.
/// Gets the size of the <see cref="Frame"/> required to fit <see cref="Text"/> within <see cref="Bounds"/> using the text
/// formatting settings of <see cref="View.TextFormatter"/> and accounting for <see cref="HotKeySpecifier"/>.
/// </summary>
/// <returns>The <see cref="Size"/> the <see cref="Frame"/> needs to be set to fit the text.</returns>
public Size GetAutoSize ()
/// <remarks>
/// </remarks>
/// <returns>The <see cref="Size"/> of the <see cref="Frame"/> required to fit the formatted text.</returns>
public Size GetTextAutoSize ()
{
var x = 0;
var y = 0;

View File

@@ -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);

View File

@@ -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);
}