Browse Source

Refactored TextFormatter.Size setting logic

Tig Kindel 1 year ago
parent
commit
304a6c914e
3 changed files with 159 additions and 100 deletions
  1. 58 24
      Terminal.Gui/Text/TextFormatter.cs
  2. 21 60
      Terminal.Gui/View/Layout/ViewLayout.cs
  3. 80 16
      Terminal.Gui/View/ViewText.cs

+ 58 - 24
Terminal.Gui/Text/TextFormatter.cs

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

+ 21 - 60
Terminal.Gui/View/Layout/ViewLayout.cs

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

+ 80 - 16
Terminal.Gui/View/ViewText.cs

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