Refactored TextFormatter.Size setting logic

This commit is contained in:
Tig Kindel
2024-01-08 10:08:29 -07:00
parent d4ee0b382b
commit 304a6c914e
3 changed files with 159 additions and 100 deletions

View File

@@ -50,23 +50,50 @@ namespace Terminal.Gui {
Justified
}
/// TextDirection [H] = Horizontal [V] = Vertical
/// =============
/// LeftRight_TopBottom [H] Normal
/// TopBottom_LeftRight [V] Normal
///
/// RightLeft_TopBottom [H] Invert Text
/// TopBottom_RightLeft [V] Invert Lines
///
/// LeftRight_BottomTop [H] Invert Lines
/// BottomTop_LeftRight [V] Invert Text
///
/// RightLeft_BottomTop [H] Invert Text + Invert Lines
/// BottomTop_RightLeft [V] Invert Text + Invert Lines
///
/// <summary>
/// Text direction enumeration, controls how text is displayed.
/// </summary>
/// <remarks>
/// <para>TextDirection [H] = Horizontal [V] = Vertical</para>
/// <table>
/// <tr>
/// <th>TextDirection</th>
/// <th>Description</th>
/// </tr>
/// <tr>
/// <td>LeftRight_TopBottom [H]</td>
/// <td>Normal</td>
/// </tr>
/// <tr>
/// <td>TopBottom_LeftRight [V]</td>
/// <td>Normal</td>
/// </tr>
/// <tr>
/// <td>RightLeft_TopBottom [H]</td>
/// <td>Invert Text</td>
/// </tr>
/// <tr>
/// <td>TopBottom_RightLeft [V]</td>
/// <td>Invert Lines</td>
/// </tr>
/// <tr>
/// <td>LeftRight_BottomTop [H]</td>
/// <td>Invert Lines</td>
/// </tr>
/// <tr>
/// <td>BottomTop_LeftRight [V]</td>
/// <td>Invert Text</td>
/// </tr>
/// <tr>
/// <td>RightLeft_BottomTop [H]</td>
/// <td>Invert Text + Invert Lines</td>
/// </tr>
/// <tr>
/// <td>BottomTop_RightLeft [V]</td>
/// <td>Invert Text + Invert Lines</td>
/// </tr>
/// </table>
/// </remarks>
public enum TextDirection {
/// <summary>
/// Normal horizontal direction.
@@ -1087,13 +1114,15 @@ namespace Terminal.Gui {
}
/// <summary>
/// Used by <see cref="Text"/> to resize the view's <see cref="View.Bounds"/> with the <see cref="Size"/>.
/// Setting <see cref="AutoSize"/> to true only work if the <see cref="View.Width"/> and <see cref="View.Height"/> are null or
/// <see cref="LayoutStyle.Absolute"/> values and doesn't work with <see cref="LayoutStyle.Computed"/> layout,
/// to avoid breaking the <see cref="Pos"/> and <see cref="Dim"/> settings.
/// Gets or sets whether the <see cref="Size"/> should be automatically changed to fit the <see cref="Text"/>.
/// </summary>
/// <remarks>
/// Auto size is ignored if the <see cref="TextAlignment.Justified"/> and <see cref="VerticalTextAlignment.Justified"/> are used.
/// <para>
/// Used by <see cref="View.AutoSize"/> to resize the view's <see cref="View.Bounds"/> to fit <see cref="Size"/>.
/// </para>
/// <para>
/// AutoSize is ignored if <see cref="TextAlignment.Justified"/> and <see cref="VerticalTextAlignment.Justified"/> are used.
/// </para>
/// </remarks>
public bool AutoSize {
get => _autoSize;
@@ -1204,7 +1233,7 @@ namespace Terminal.Gui {
}
/// <summary>
/// Allows word wrap the to fit the available container width.
/// Gets or sets whether word wrap will be used to fit <see cref="Text"/> to <see cref="Size"/>.
/// </summary>
public bool WordWrap {
get => _wordWrap;
@@ -1212,10 +1241,10 @@ namespace Terminal.Gui {
}
/// <summary>
/// Gets or sets the size of the area the text will be constrained to when formatted.
/// Gets or sets the size <see cref="Text"/> will be constrained to when formatted.
/// </summary>
/// <remarks>
/// Does not return the size the formatted text; just the value that was set.
/// Does not return the size of the formatted text but the size that will be used to constrain the text when formatted.
/// </remarks>
public Size Size {
get => _size;
@@ -1325,11 +1354,16 @@ namespace Terminal.Gui {
}
/// <summary>
/// Gets or sets whether the <see cref="TextFormatter"/> needs to format the text when <see cref="Draw(Rect, Attribute, Attribute, Rect, bool, ConsoleDriver)"/> is called.
/// If it is <c>false</c> when Draw is called, the Draw call will be faster.
/// Gets or sets whether the <see cref="TextFormatter"/> needs to format the text.
/// </summary>
/// <remarks>
/// <para>
/// If <c>false</c> when Draw is called, the Draw call will be faster.
/// </para>
/// <para>
/// Used by <see cref="Draw(Rect, Attribute, Attribute, Rect, bool, ConsoleDriver)"/>
/// </para>
/// <para>
/// This is set to true when the properties of <see cref="TextFormatter"/> are set.
/// </para>
/// </remarks>

View File

@@ -87,7 +87,7 @@ public partial class View {
// TODO: Figure out if the below can be optimized.
if (IsInitialized /*|| LayoutStyle == LayoutStyle.Absolute*/) {
LayoutFrames ();
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
SetTextFormatterSize ();
SetNeedsLayout ();
SetNeedsDisplay ();
}
@@ -195,7 +195,7 @@ public partial class View {
/// <summary>
/// <para>
/// Indicates the LayoutStyle for the <see cref="View"/>.
/// Gets the LayoutStyle for the <see cref="View"/>.
/// </para>
/// <para>
/// If Absolute, the <see cref="View.X"/>, <see cref="View.Y"/>, <see cref="View.Width"/>, and
@@ -227,7 +227,7 @@ public partial class View {
/// <remarks>
/// <para>
/// If <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/> the value of Bounds is indeterminate until
/// the view has been initialized (<see creft="IsInitialized"/> is true) and <see cref="LayoutSubviews"/> has been
/// the view has been initialized (<see cref="IsInitialized"/> is true) and <see cref="LayoutSubviews"/> has been
/// called.
/// </para>
/// <para>
@@ -279,12 +279,12 @@ public partial class View {
/// <remarks>
/// <para>
/// If set to a relative value (e.g. <see cref="Pos.Center"/>) the value is indeterminate until the
/// view has been initialized (<see creft="IsInitialized"/> is true) and <see cref="SetRelativeLayout(Rect)"/> has been
/// view has been initialized (<see cref="IsInitialized"/> is true) and <see cref="SetRelativeLayout(Rect)"/> has been
/// called.
/// </para>
/// <para>
/// Changing this property will eventually (when the view is next drawn) cause the
/// <see cref="LayoutSubview(View, Rect)"/> and
/// <see cref="LayoutSubview(View, Rect)"/> and
/// <see cref="OnDrawContent(Rect)"/> methods to be called.
/// </para>
/// <para>
@@ -311,7 +311,7 @@ public partial class View {
/// <remarks>
/// <para>
/// If set to a relative value (e.g. <see cref="Pos.Center"/>) the value is indeterminate until the
/// view has been initialized (<see creft="IsInitialized"/> is true) and <see cref="SetRelativeLayout(Rect)"/> has been
/// view has been initialized (<see cref="IsInitialized"/> is true) and <see cref="SetRelativeLayout(Rect)"/> has been
/// called.
/// </para>
/// <para>
@@ -337,13 +337,13 @@ public partial class View {
}
/// <summary>
/// Gets or sets the width of the view.
/// Gets or sets the width dimension of the view.
/// </summary>
/// <value>The <see cref="Dim"/> object representing the width of the view (the number of columns).</value>
/// <remarks>
/// <para>
/// If set to a relative value (e.g. <see cref="Dim.Fill(int)"/>) the value is indeterminate until the
/// view has been initialized (<see creft="IsInitialized"/> is true) and <see cref="SetRelativeLayout(Rect)"/> has been
/// view has been initialized (<see cref="IsInitialized"/> is true) and <see cref="SetRelativeLayout(Rect)"/> has been
/// called.
/// </para>
/// <para>
@@ -377,13 +377,13 @@ public partial class View {
}
/// <summary>
/// Gets or sets the height of the view.
/// Gets or sets the height dimension of the view.
/// </summary>
/// <value>The <see cref="Dim"/> object representing the height of the view (the number of rows).</value>
/// <remarks>
/// <para>
/// If set to a relative value (e.g. <see cref="Dim.Fill(int)"/>) the value is indeterminate until the
/// view has been initialized (<see creft="IsInitialized"/> is true) and <see cref="SetRelativeLayout(Rect)"/> has been
/// view has been initialized (<see cref="IsInitialized"/> is true) and <see cref="SetRelativeLayout(Rect)"/> has been
/// called.
/// </para>
/// <para>
@@ -422,7 +422,7 @@ public partial class View {
/// <remarks>
/// Setting this to <see langword="true"/> will enable validation of <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>,
/// and <see cref="Height"/>
/// during set operations and in <see cref="LayoutSubviews"/>.If invalid settings are discovered exceptions will be thrown
/// during set operations and in <see cref="LayoutSubviews"/>. If invalid settings are discovered exceptions will be thrown
/// indicating the error.
/// This will impose a performance penalty and thus should only be used for debugging.
/// </remarks>
@@ -440,8 +440,12 @@ public partial class View {
/// if <see cref="Text"/> won't fit the view will be resized as needed.
/// </para>
/// <para>
/// In addition, if <see cref="ValidatePosDim"/> is <see langword="true"/> the new values of <see cref="Width"/> and
/// <see cref="Height"/> must be of the same types of the existing one to avoid breaking the <see cref="Dim"/> settings.
/// If <see cref="AutoSize"/> is set to <see langword="true"/> then <see cref="Width"/> and <see cref="Height"/>
/// will be changed to <see cref="Dim.DimAbsolute"/> if they are not already.
/// </para>
/// <para>
/// If <see cref="AutoSize"/> is set to <see langword="false"/> then <see cref="Width"/> and <see cref="Height"/>
/// will left unchanged.
/// </para>
/// </summary>
public virtual bool AutoSize {
@@ -585,7 +589,7 @@ public partial class View {
if (IsInitialized) {
SetFrameToFitText ();
LayoutFrames ();
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
SetTextFormatterSize ();
SetNeedsLayout ();
SetNeedsDisplay ();
}
@@ -853,14 +857,14 @@ public partial class View {
if (IsInitialized) {
// TODO: Figure out what really is needed here. All unit tests (except AutoSize) pass as-is
//LayoutFrames ();
//TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
SetTextFormatterSize ();
SetNeedsLayout ();
//SetNeedsDisplay ();
}
// BUGBUG: Why is this AFTER setting Frame? Seems duplicative.
if (!SetFrameToFitText ()) {
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
SetTextFormatterSize ();
}
}
}
@@ -1084,7 +1088,7 @@ public partial class View {
var oldBounds = Bounds;
OnLayoutStarted (new LayoutEventArgs { OldBounds = oldBounds });
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
SetTextFormatterSize ();
// Sort out the dependencies of the X, Y, Width, Height properties
var nodes = new HashSet<View> ();
@@ -1163,49 +1167,6 @@ public partial class View {
return boundsChanged;
}
/// <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.
/// </summary>
/// <returns>The <see cref="Size"/> of the view required to fit the text.</returns>
public Size GetAutoSize ()
{
var x = 0;
var y = 0;
if (IsInitialized) {
x = Bounds.X;
y = Bounds.Y;
}
var rect = TextFormatter.CalcRect (x, y, TextFormatter.Text, TextFormatter.Direction);
var newWidth = rect.Size.Width - GetHotKeySpecifierLength () + Margin.Thickness.Horizontal + Border.Thickness.Horizontal + Padding.Thickness.Horizontal;
var newHeight = rect.Size.Height - GetHotKeySpecifierLength (false) + Margin.Thickness.Vertical + Border.Thickness.Vertical + Padding.Thickness.Vertical;
return new Size (newWidth, newHeight);
}
bool IsValidAutoSize (out Size autoSize)
{
var rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
autoSize = new Size (rect.Size.Width - GetHotKeySpecifierLength (),
rect.Size.Height - GetHotKeySpecifierLength (false));
return !(ValidatePosDim && (!(Width is Dim.DimAbsolute) || !(Height is Dim.DimAbsolute)) ||
_frame.Size.Width != rect.Size.Width - GetHotKeySpecifierLength () ||
_frame.Size.Height != rect.Size.Height - GetHotKeySpecifierLength (false));
}
bool IsValidAutoSizeWidth (Dim width)
{
var rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
var dimValue = width.Anchor (0);
return !(ValidatePosDim && !(width is Dim.DimAbsolute) || dimValue != rect.Size.Width - GetHotKeySpecifierLength ());
}
bool IsValidAutoSizeHeight (Dim height)
{
var rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
var dimValue = height.Anchor (0);
return !(ValidatePosDim && !(height is Dim.DimAbsolute) || dimValue != rect.Size.Height - GetHotKeySpecifierLength (false));
}
/// <summary>
/// Determines if the View's <see cref="Width"/> can be set to a new value.

View File

@@ -26,6 +26,9 @@ public partial class View {
/// <see cref="HotKeySpecifier"/> to
/// <c>(Rune)0xffff</c>.
/// </para>
/// <para>
/// If <see cref="AutoSize"/> is <c>true</c>, the <see cref="Bounds"/> will be adjusted to fit the text.
/// </para>
/// </remarks>
public virtual string Text {
get => _text;
@@ -69,6 +72,11 @@ public partial class View {
/// Gets or sets how the View's <see cref="Text"/> is aligned horizontally when drawn. Changing this property will
/// redisplay the <see cref="View"/>.
/// </summary>
/// <remarks>
/// <para>
/// If <see cref="AutoSize"/> is <c>true</c>, the <see cref="Bounds"/> will be adjusted to fit the text.
/// </para>
/// </remarks>
/// <value>The text alignment.</value>
public virtual TextAlignment TextAlignment {
get => TextFormatter.Alignment;
@@ -83,6 +91,11 @@ public partial class View {
/// Gets or sets how the View's <see cref="Text"/> is aligned vertically when drawn. Changing this property will redisplay
/// the <see cref="View"/>.
/// </summary>
/// <remarks>
/// <para>
/// If <see cref="AutoSize"/> is <c>true</c>, the <see cref="Bounds"/> will be adjusted to fit the text.
/// </para>
/// </remarks>
/// <value>The text alignment.</value>
public virtual VerticalTextAlignment VerticalTextAlignment {
get => TextFormatter.VerticalAlignment;
@@ -96,6 +109,11 @@ public partial class View {
/// Gets or sets the direction of the View's <see cref="Text"/>. Changing this property will redisplay the
/// <see cref="View"/>.
/// </summary>
/// <remarks>
/// <para>
/// If <see cref="AutoSize"/> is <c>true</c>, the <see cref="Bounds"/> will be adjusted to fit the text.
/// </para>
/// </remarks>
/// <value>The text alignment.</value>
public virtual TextDirection TextDirection {
get => TextFormatter.Direction;
@@ -134,7 +152,7 @@ public partial class View {
} else {
SetFrameToFitText ();
}
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
SetTextFormatterSize ();
SetNeedsDisplay ();
}
@@ -210,18 +228,18 @@ public partial class View {
}
/// <summary>
/// Gets the width or height of the <see cref="Terminal.Gui.TextFormatter.HotKeySpecifier"/> characters
/// Gets the width or height of the <see cref="TextFormatter.HotKeySpecifier"/> characters
/// in the <see cref="Text"/> property.
/// </summary>
/// <remarks>
/// Only the first hotkey specifier found in <see cref="Text"/> is supported.
/// Only the first HotKey specifier found in <see cref="Text"/> is supported.
/// </remarks>
/// <param name="isWidth">
/// If <see langword="true"/> (the default) the width required for the hotkey specifier is returned. Otherwise the height
/// If <see langword="true"/> (the default) the width required for the HotKey specifier is returned. Otherwise the height
/// is returned.
/// </param>
/// <returns>
/// The number of characters required for the <see cref="Terminal.Gui.TextFormatter.HotKeySpecifier"/>. If the text
/// The number of characters required for the <see cref="TextFormatter.HotKeySpecifier"/>. If the text
/// direction specified
/// by <see cref="TextDirection"/> does not match the <paramref name="isWidth"/> parameter, <c>0</c> is returned.
/// </returns>
@@ -238,31 +256,77 @@ public partial class View {
}
/// <summary>
/// Gets the dimensions required for <see cref="Text"/> ignoring a <see cref="Terminal.Gui.TextFormatter.HotKeySpecifier"/>
/// .
/// Gets the dimensions required for <see cref="Text"/> ignoring a <see cref="TextFormatter.HotKeySpecifier"/>.
/// </summary>
/// <returns></returns>
public Size GetSizeNeededForTextWithoutHotKey () => new (TextFormatter.Size.Width - GetHotKeySpecifierLength (),
internal Size GetSizeNeededForTextWithoutHotKey () => new (TextFormatter.Size.Width - GetHotKeySpecifierLength (),
TextFormatter.Size.Height - GetHotKeySpecifierLength (false));
/// <summary>
/// Gets the dimensions required for <see cref="Text"/> accounting for a
/// <see cref="Terminal.Gui.TextFormatter.HotKeySpecifier"/> .
/// Sets <see cref="TextFormatter"/>.Size to the current <see cref="Bounds"/> size, adjusted for
/// <see cref="TextFormatter.HotKeySpecifier"/>.
/// </summary>
/// <remarks>
/// Use this API to set <see cref="TextFormatter.Size"/> when the view has changed such that the
/// size required to fit the text has changed.
/// changes.
/// </remarks>
/// <returns></returns>
public Size GetTextFormatterSizeNeededForTextAndHotKey ()
internal void SetTextFormatterSize ()
{
if (!IsInitialized) {
return Size.Empty;
TextFormatter.Size = Size.Empty;
}
if (string.IsNullOrEmpty (TextFormatter.Text)) {
return Bounds.Size;
TextFormatter.Size = Bounds.Size;
}
// BUGBUG: This IGNORES what Text is set to, using on only the current View size. This doesn't seem to make sense.
// BUGBUG: This uses Frame; in v2 it should be Bounds
return new Size (Bounds.Size.Width + GetHotKeySpecifierLength (),
TextFormatter.Size = new Size (Bounds.Size.Width + GetHotKeySpecifierLength (),
Bounds.Size.Height + GetHotKeySpecifierLength (false));
}
/// <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.
/// </summary>
/// <returns>The <see cref="Size"/> the <see cref="Frame"/> needs to be set to fit the text.</returns>
public Size GetAutoSize ()
{
var x = 0;
var y = 0;
if (IsInitialized) {
x = Bounds.X;
y = Bounds.Y;
}
var rect = TextFormatter.CalcRect (x, y, TextFormatter.Text, TextFormatter.Direction);
var newWidth = rect.Size.Width - GetHotKeySpecifierLength () + Margin.Thickness.Horizontal + Border.Thickness.Horizontal + Padding.Thickness.Horizontal;
var newHeight = rect.Size.Height - GetHotKeySpecifierLength (false) + Margin.Thickness.Vertical + Border.Thickness.Vertical + Padding.Thickness.Vertical;
return new Size (newWidth, newHeight);
}
bool IsValidAutoSize (out Size autoSize)
{
var rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
autoSize = new Size (rect.Size.Width - GetHotKeySpecifierLength (),
rect.Size.Height - GetHotKeySpecifierLength (false));
return !(ValidatePosDim && (!(Width is Dim.DimAbsolute) || !(Height is Dim.DimAbsolute)) ||
_frame.Size.Width != rect.Size.Width - GetHotKeySpecifierLength () ||
_frame.Size.Height != rect.Size.Height - GetHotKeySpecifierLength (false));
}
bool IsValidAutoSizeWidth (Dim width)
{
var rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
var dimValue = width.Anchor (0);
return !(ValidatePosDim && !(width is Dim.DimAbsolute) || dimValue != rect.Size.Width - GetHotKeySpecifierLength ());
}
bool IsValidAutoSizeHeight (Dim height)
{
var rect = TextFormatter.CalcRect (_frame.X, _frame.Y, TextFormatter.Text, TextDirection);
var dimValue = height.Anchor (0);
return !(ValidatePosDim && !(height is Dim.DimAbsolute) || dimValue != rect.Size.Height - GetHotKeySpecifierLength (false));
}
}