Tig 1 éve
szülő
commit
c4de9fe4f4

+ 376 - 322
Terminal.Gui/Text/TextFormatter.cs

@@ -1,11 +1,10 @@
 #nullable enable
 using System.Diagnostics;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
 
 namespace Terminal.Gui;
 
 /// <summary>
-///     Provides text formatting. Supports <see cref="View.HotKey"/>s, horizontal alignment, vertical alignment,
+///     Provides text formatting. Supports <see cref="View.HotKey"/>s, horizontal and vertical alignment, text direction,
 ///     multiple lines, and word-based line wrap.
 /// </summary>
 public class TextFormatter
@@ -23,6 +22,10 @@ public class TextFormatter
     private Alignment _textVerticalAlignment = Alignment.Start;
     private bool _wordWrap = true;
 
+    private int? _width;
+
+    private int? _height;
+
     /// <summary>Get or sets the horizontal text alignment.</summary>
     /// <value>The text alignment.</value>
     public Alignment Alignment
@@ -33,11 +36,15 @@ public class TextFormatter
 
     /// <summary>Gets or sets whether the <see cref="Size"/> should be automatically changed to fit the <see cref="Text"/>.</summary>
     /// <remarks>
-    ///     <para>Used when <see cref="View"/> is using <see cref="Dim.Auto"/> to resize the view's <see cref="View.Viewport"/> to fit <see cref="Size"/>.</para>
+    ///     <para>
+    ///         Used when <see cref="View"/> is using <see cref="Dim.Auto"/> to resize the view's <see cref="View.Viewport"/>
+    ///         to fit <see cref="Size"/>.
+    ///     </para>
     ///     <para>
     ///         AutoSize is ignored if <see cref="Gui.Alignment.Fill"/> is used.
     ///     </para>
     /// </remarks>
+    [Obsolete ("AutoSize is deprecated, use Width and Height instead.")]
     public bool AutoSize
     {
         get => _autoSize;
@@ -57,46 +64,6 @@ public class TextFormatter
         }
     }
 
-    internal Size GetAutoSize ()
-    {
-        Size size = CalcRect (0, 0, Text, Direction, TabWidth).Size;
-
-        return size with
-        {
-            Width = size.Width - GetHotKeySpecifierLength (),
-            Height = size.Height - GetHotKeySpecifierLength (false)
-        };
-    }
-    /// <summary>
-    ///     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.
-    /// </remarks>
-    /// <param name="isWidth">
-    ///     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="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>
-    public int GetHotKeySpecifierLength (bool isWidth = true)
-    {
-        if (isWidth)
-        {
-            return TextFormatter.IsHorizontalDirection (Direction) && Text?.Contains ((char)HotKeySpecifier.Value) == true
-                       ? Math.Max (HotKeySpecifier.GetColumns (), 0)
-                       : 0;
-        }
-
-        return TextFormatter.IsVerticalDirection (Direction) && Text?.Contains ((char)HotKeySpecifier.Value) == true
-                   ? Math.Max (HotKeySpecifier.GetColumns (), 0)
-                   : 0;
-    }
-
     /// <summary>
     ///     Gets the cursor position of the <see cref="HotKey"/>. If the <see cref="HotKey"/> is defined, the cursor will
     ///     be positioned over it.
@@ -119,202 +86,6 @@ public class TextFormatter
         }
     }
 
-    /// <summary>
-    ///     Determines if the viewport width will be used or only the text width will be used,
-    ///     If <see langword="true"/> all the viewport area will be filled with whitespaces and the same background color
-    ///     showing a perfect rectangle.
-    /// </summary>
-    public bool FillRemaining { get; set; }
-
-    /// <summary>Gets or sets the hot key. Fires the <see cref="HotKeyChanged"/> event.</summary>
-    public Key HotKey
-    {
-        get => _hotKey;
-        internal set
-        {
-            if (_hotKey != value)
-            {
-                Key oldKey = _hotKey;
-                _hotKey = value;
-                HotKeyChanged?.Invoke (this, new KeyChangedEventArgs (oldKey, value));
-            }
-        }
-    }
-
-    /// <summary>The position in the text of the hot key. The hot key will be rendered using the hot color.</summary>
-    public int HotKeyPos
-    {
-        get => _hotKeyPos;
-        internal set => _hotKeyPos = value;
-    }
-
-    /// <summary>
-    ///     The specifier character for the hot key (e.g. '_'). Set to '\xffff' to disable hot key support for this View
-    ///     instance. The default is '\xffff'.
-    /// </summary>
-    public Rune HotKeySpecifier { get; set; } = (Rune)0xFFFF;
-
-    /// <summary>Gets or sets a value indicating whether multi line is allowed.</summary>
-    /// <remarks>Multi line is ignored if <see cref="WordWrap"/> is <see langword="true"/>.</remarks>
-    public bool MultiLine
-    {
-        get => _multiLine;
-        set => _multiLine = EnableNeedsFormat (value);
-    }
-
-    /// <summary>Gets or sets whether the <see cref="TextFormatter"/> needs to format the text.</summary>
-    /// <remarks>
-    ///     <para>If <see langword="false"/> when Draw is called, the Draw call will be faster.</para>
-    ///     <para>Used by <see cref="Draw"/></para>
-    ///     <para>Set to <see langword="true"/> when any of the properties of <see cref="TextFormatter"/> are set.</para>
-    ///     <para>Set to <see langword="false"/> when the text is formatted (if <see cref="GetLines"/> is accessed).</para>
-    /// </remarks>
-    public bool NeedsFormat { get; set; }
-
-    /// <summary>
-    ///     Gets or sets whether trailing spaces at the end of word-wrapped lines are preserved or not when
-    ///     <see cref="TextFormatter.WordWrap"/> is enabled. If <see langword="true"/> trailing spaces at the end of wrapped
-    ///     lines will be removed when <see cref="Text"/> is formatted for display. The default is <see langword="false"/>.
-    /// </summary>
-    public bool PreserveTrailingSpaces
-    {
-        get => _preserveTrailingSpaces;
-        set => _preserveTrailingSpaces = EnableNeedsFormat (value);
-    }
-
-    private int? _width;
-
-    public int? Width
-    {
-        get => _width;
-        set
-        {
-            if (_width == value)
-            {
-                return;
-            }
-
-            if (value < 0)
-            {
-                throw  new ArgumentOutOfRangeException (nameof (Width), value, @"Must be greater than or equal to 0.");
-            }
-
-            _width = value;
-            if (_width is null || _height is null)
-            {
-                return;
-            }
-            _width = EnableNeedsFormat (value);
-        }
-    }
-
-    private int? _height;
-
-    public int? Height
-    {
-        get => _height;
-        set
-        {
-            if (_height == value)
-            {
-                return;
-            }
-
-            if (value < 0)
-            {
-                throw new ArgumentOutOfRangeException (nameof (Width), value, @"Must be greater than or equal to 0.");
-            }
-
-            _height = value;
-            if (_width is null || _height is null)
-            {
-                return;
-            }
-            _height = EnableNeedsFormat (value);
-        }
-    }
-
-    /// <summary>Gets or sets the size <see cref="Text"/> will be constrained to when formatted.</summary>
-    /// <remarks>
-    ///     <para>
-    ///         Does not return the size of the formatted text but the size that will be used to constrain the text when
-    ///         formatted.
-    ///     </para>
-    ///     <para>When set, <see cref="NeedsFormat"/> is set to <see langword="true"/>.</para>
-    /// </remarks>
-    public Size? Size
-    {
-        get
-        {
-            if (_width is null || _height is null)
-            {
-                return null;
-            }
-
-            return new Size (_width.Value, _height.Value);
-        }
-        set
-        {
-            if (AutoSize)
-            {
-                Size size = EnableNeedsFormat (GetAutoSize ());
-                _width = size.Width;
-                _height = size.Height;
-            }
-            else
-            {
-                if (value is null)
-                {
-                    _width = null;
-                    _height = null;
-                }
-                else
-                {
-                    Size size = EnableNeedsFormat (value.Value);
-                    _width = size.Width;
-                    _height = size.Height;
-                }
-            }
-        }
-    }
-
-    /// <summary>Gets or sets the number of columns used for a tab.</summary>
-    public int TabWidth
-    {
-        get => _tabWidth;
-        set => _tabWidth = EnableNeedsFormat (value);
-    }
-
-    /// <summary>The text to be formatted. This string is never modified.</summary>
-    public virtual string Text
-    {
-        get => _text!;
-        set
-        {
-            _text = EnableNeedsFormat (value);
-
-            if (AutoSize)
-            {
-                Size = GetAutoSize (); ;
-            }
-        }
-    }
-
-    /// <summary>Gets or sets the vertical text-alignment.</summary>
-    /// <value>The text vertical alignment.</value>
-    public Alignment VerticalAlignment
-    {
-        get => _textVerticalAlignment;
-        set => _textVerticalAlignment = EnableNeedsFormat (value);
-    }
-
-    /// <summary>Gets or sets whether word wrap will be used to fit <see cref="Text"/> to <see cref="Size"/>.</summary>
-    public bool WordWrap
-    {
-        get => _wordWrap;
-        set => _wordWrap = EnableNeedsFormat (value);
-    }
-
     /// <summary>Draws the text held by <see cref="TextFormatter"/> to <see cref="ConsoleDriver"/> using the colors specified.</summary>
     /// <remarks>
     ///     Causes the text to be formatted (references <see cref="GetLines"/>). Sets <see cref="NeedsFormat"/> to
@@ -565,8 +336,12 @@ public class TextFormatter
                         break;
                     }
 
-                    if ((!isVertical && (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset || (idx < runes.Length && runes [idx].GetColumns () > screen.Width)))
-                        || (isVertical && ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y) || (idx < runes.Length && runes [idx].GetColumns () > screen.Width))))
+                    if ((!isVertical
+                         && (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset
+                             || (idx < runes.Length && runes [idx].GetColumns () > screen.Width)))
+                        || (isVertical
+                            && ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y)
+                                || (idx < runes.Length && runes [idx].GetColumns () > screen.Width))))
                     {
                         break;
                     }
@@ -659,7 +434,7 @@ public class TextFormatter
                         {
                             if (lastZeroWidthPos is null)
                             {
-                                lastZeroWidthPos = new List<Point?> ();
+                                lastZeroWidthPos = new ();
                             }
 
                             int foundIdx = lastZeroWidthPos.IndexOf (
@@ -704,6 +479,13 @@ public class TextFormatter
         }
     }
 
+    /// <summary>
+    ///     Determines if the viewport width will be used or only the text width will be used,
+    ///     If <see langword="true"/> all the viewport area will be filled with whitespaces and the same background color
+    ///     showing a perfect rectangle.
+    /// </summary>
+    public bool FillRemaining { get; set; }
+
     /// <summary>Returns the formatted text, constrained to <see cref="Size"/>.</summary>
     /// <remarks>
     ///     If <see cref="NeedsFormat"/> is <see langword="true"/>, causes a format, resetting <see cref="NeedsFormat"/>
@@ -725,6 +507,7 @@ public class TextFormatter
 
     /// <summary>Gets the size required to hold the formatted text, given the constraints placed by <see cref="Size"/>.</summary>
     /// <remarks>Causes a format, resetting <see cref="NeedsFormat"/> to <see langword="false"/>.</remarks>
+    /// <param name="constrainSize">If provided, will cause the text to be constrained to the provided size instead of <see cref="Width"/> and <see cref="Height"/>.</param>
     /// <returns>The size required to hold the formatted text.</returns>
     public Size FormatAndGetSize (Size? constrainSize = null)
     {
@@ -736,24 +519,29 @@ public class TextFormatter
         // HACK: This is a total hack to work around the fact that TextFormatter.Format does not match TextFormatter.Draw.
         int? prevWidth = _width;
         int? prevHeight = _height;
+
         if (constrainSize is { })
         {
             _width = constrainSize?.Width;
             _height = constrainSize?.Height;
         }
-        
+
         // HACK: Fill normally will fill the entire constraint size, but we need to know the actual size of the text.
         Alignment prevAlignment = Alignment;
+
         if (Alignment == Alignment.Fill)
         {
             Alignment = Alignment.Start;
         }
+
         Alignment prevVerticalAlignment = VerticalAlignment;
+
         if (VerticalAlignment == Alignment.Fill)
         {
             VerticalAlignment = Alignment.Start;
         }
 
+        // This calls Format
         List<string> lines = GetLines ();
 
         // Undo hacks
@@ -788,6 +576,36 @@ public class TextFormatter
         return new (width, height);
     }
 
+    /// <summary>
+    ///     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.
+    /// </remarks>
+    /// <param name="isWidth">
+    ///     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="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>
+    public int GetHotKeySpecifierLength (bool isWidth = true)
+    {
+        if (isWidth)
+        {
+            return IsHorizontalDirection (Direction) && Text?.Contains ((char)HotKeySpecifier.Value) == true
+                       ? Math.Max (HotKeySpecifier.GetColumns (), 0)
+                       : 0;
+        }
+
+        return IsVerticalDirection (Direction) && Text?.Contains ((char)HotKeySpecifier.Value) == true
+                   ? Math.Max (HotKeySpecifier.GetColumns (), 0)
+                   : 0;
+    }
+
     /// <summary>Gets a list of formatted lines, constrained to <see cref="Size"/>.</summary>
     /// <remarks>
     ///     <para>
@@ -805,7 +623,7 @@ public class TextFormatter
         // With this check, we protect against subclasses with overrides of Text
         if (string.IsNullOrEmpty (Text) || Width is null || Width == 0 || Height is null || Height == 0)
         {
-            _lines = new List<string> { string.Empty };
+            _lines = new() { string.Empty };
             NeedsFormat = false;
 
             return _lines;
@@ -864,7 +682,7 @@ public class TextFormatter
 
                 if (!AutoSize && _lines.Count > Height.Value)
                 {
-                    _lines.RemoveRange (Height.Value, _lines.Count -  Height.Value);
+                    _lines.RemoveRange (Height.Value, _lines.Count - Height.Value);
                 }
             }
 
@@ -874,9 +692,237 @@ public class TextFormatter
         return _lines;
     }
 
+    /// <summary>Gets or sets the height <see cref="Text"/> will be constrained to when formatted.</summary>
+    /// <remarks>
+    ///     <para>
+    ///         Does not return the height of the formatted text but the height that will be used to constrain the text when
+    ///         formatted.
+    ///     </para>
+    ///     <para>
+    ///         Use <see cref="FormatAndGetSize"/> to get the size of the formatted text.
+    ///     </para>
+    ///     <para>When set, <see cref="NeedsFormat"/> is set to <see langword="true"/>.</para>
+    /// </remarks>
+
+    public int? Height
+    {
+        get => _height;
+        set
+        {
+            if (_height == value)
+            {
+                return;
+            }
+
+            if (value < 0)
+            {
+                throw new ArgumentOutOfRangeException (nameof (Width), value, @"Must be greater than or equal to 0.");
+            }
+
+            _height = value;
+
+            if (_width is null || _height is null)
+            {
+                return;
+            }
+
+            _height = EnableNeedsFormat (value);
+        }
+    }
+
+    /// <summary>Gets or sets the hot key. Fires the <see cref="HotKeyChanged"/> event.</summary>
+    public Key HotKey
+    {
+        get => _hotKey;
+        internal set
+        {
+            if (_hotKey != value)
+            {
+                Key oldKey = _hotKey;
+                _hotKey = value;
+                HotKeyChanged?.Invoke (this, new (oldKey, value));
+            }
+        }
+    }
+
     /// <summary>Event invoked when the <see cref="HotKey"/> is changed.</summary>
     public event EventHandler<KeyChangedEventArgs>? HotKeyChanged;
 
+    /// <summary>The position in the text of the hot key. The hot key will be rendered using the hot color.</summary>
+    public int HotKeyPos
+    {
+        get => _hotKeyPos;
+        internal set => _hotKeyPos = value;
+    }
+
+    /// <summary>
+    ///     The specifier character for the hot key (e.g. '_'). Set to '\xffff' to disable hot key support for this View
+    ///     instance. The default is '\xffff'.
+    /// </summary>
+    public Rune HotKeySpecifier { get; set; } = (Rune)0xFFFF;
+
+    /// <summary>Gets or sets a value indicating whether multi line is allowed.</summary>
+    /// <remarks>Multi line is ignored if <see cref="WordWrap"/> is <see langword="true"/>.</remarks>
+    public bool MultiLine
+    {
+        get => _multiLine;
+        set => _multiLine = EnableNeedsFormat (value);
+    }
+
+    /// <summary>Gets or sets whether the <see cref="TextFormatter"/> needs to format the text.</summary>
+    /// <remarks>
+    ///     <para>If <see langword="false"/> when Draw is called, the Draw call will be faster.</para>
+    ///     <para>Used by <see cref="Draw"/></para>
+    ///     <para>Set to <see langword="true"/> when any of the properties of <see cref="TextFormatter"/> are set.</para>
+    ///     <para>Set to <see langword="false"/> when the text is formatted (if <see cref="GetLines"/> is accessed).</para>
+    /// </remarks>
+    public bool NeedsFormat { get; set; }
+
+    /// <summary>
+    ///     Gets or sets whether trailing spaces at the end of word-wrapped lines are preserved or not when
+    ///     <see cref="TextFormatter.WordWrap"/> is enabled. If <see langword="true"/> trailing spaces at the end of wrapped
+    ///     lines will be removed when <see cref="Text"/> is formatted for display. The default is <see langword="false"/>.
+    /// </summary>
+    public bool PreserveTrailingSpaces
+    {
+        get => _preserveTrailingSpaces;
+        set => _preserveTrailingSpaces = EnableNeedsFormat (value);
+    }
+
+    /// <summary>Gets or sets the width and height <see cref="Text"/> will be constrained to when formatted.</summary>
+    /// <remarks>
+    ///     <para>
+    ///         Does not return the size of the formatted text but the size that will be used to constrain the text when
+    ///         formatted.
+    ///     </para>
+    ///     <para>
+    ///         Use <see cref="FormatAndGetSize"/> to get the size of the formatted text.
+    ///     </para>
+    ///     <para>When set, <see cref="NeedsFormat"/> is set to <see langword="true"/>.</para>
+    /// </remarks>
+    public Size? Size
+    {
+        get
+        {
+            if (_width is null || _height is null)
+            {
+                return null;
+            }
+
+            return new Size (_width.Value, _height.Value);
+        }
+        set
+        {
+            if (AutoSize)
+            {
+                Size size = EnableNeedsFormat (GetAutoSize ());
+                _width = size.Width;
+                _height = size.Height;
+            }
+            else
+            {
+                if (value is null)
+                {
+                    _width = null;
+                    _height = null;
+                    EnableNeedsFormat (true);
+                }
+                else
+                {
+                    Size size = EnableNeedsFormat (value.Value);
+                    _width = size.Width;
+                    _height = size.Height;
+                }
+            }
+        }
+    }
+
+    /// <summary>Gets or sets the number of columns used for a tab.</summary>
+    public int TabWidth
+    {
+        get => _tabWidth;
+        set => _tabWidth = EnableNeedsFormat (value);
+    }
+
+    /// <summary>The text to be formatted. This string is never modified.</summary>
+    public string Text
+    {
+        get => _text!;
+        set
+        {
+            _text = EnableNeedsFormat (value);
+
+            if (AutoSize)
+            {
+                Size = GetAutoSize ();
+                ;
+            }
+        }
+    }
+
+    /// <summary>Gets or sets the vertical text-alignment.</summary>
+    /// <value>The text vertical alignment.</value>
+    public Alignment VerticalAlignment
+    {
+        get => _textVerticalAlignment;
+        set => _textVerticalAlignment = EnableNeedsFormat (value);
+    }
+
+    /// <summary>Gets or sets the width <see cref="Text"/> will be constrained to when formatted.</summary>
+    /// <remarks>
+    ///     <para>
+    ///         Does not return the width of the formatted text but the width that will be used to constrain the text when
+    ///         formatted.
+    ///     </para>
+    ///     <para>
+    ///         Use <see cref="FormatAndGetSize"/> to get the size of the formatted text.
+    ///     </para>
+    ///     <para>When set, <see cref="NeedsFormat"/> is set to <see langword="true"/>.</para>
+    /// </remarks>
+    public int? Width
+    {
+        get => _width;
+        set
+        {
+            if (_width == value)
+            {
+                return;
+            }
+
+            if (value < 0)
+            {
+                throw new ArgumentOutOfRangeException (nameof (Width), value, @"Must be greater than or equal to 0.");
+            }
+
+            _width = value;
+
+            if (_width is null || _height is null)
+            {
+                return;
+            }
+
+            _width = EnableNeedsFormat (value);
+        }
+    }
+
+    /// <summary>Gets or sets whether word wrap will be used to fit <see cref="Text"/> to <see cref="Size"/>.</summary>
+    public bool WordWrap
+    {
+        get => _wordWrap;
+        set => _wordWrap = EnableNeedsFormat (value);
+    }
+
+    private Size GetAutoSize ()
+    {
+        Size size = CalcRect (0, 0, Text, Direction, TabWidth).Size;
+
+        return size with
+        {
+            Width = size.Width - GetHotKeySpecifierLength (),
+            Height = size.Height - GetHotKeySpecifierLength (false)
+        };
+    }
+
     /// <summary>Sets <see cref="NeedsFormat"/> to <see langword="true"/> and returns the value.</summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="value"></param>
@@ -894,48 +940,48 @@ public class TextFormatter
     public static bool IsHorizontalDirection (TextDirection textDirection)
     {
         return textDirection switch
-        {
-            TextDirection.LeftRight_TopBottom => true,
-            TextDirection.LeftRight_BottomTop => true,
-            TextDirection.RightLeft_TopBottom => true,
-            TextDirection.RightLeft_BottomTop => true,
-            _ => false
-        };
+               {
+                   TextDirection.LeftRight_TopBottom => true,
+                   TextDirection.LeftRight_BottomTop => true,
+                   TextDirection.RightLeft_TopBottom => true,
+                   TextDirection.RightLeft_BottomTop => true,
+                   _ => false
+               };
     }
 
     /// <summary>Check if it is a vertical direction</summary>
     public static bool IsVerticalDirection (TextDirection textDirection)
     {
         return textDirection switch
-        {
-            TextDirection.TopBottom_LeftRight => true,
-            TextDirection.TopBottom_RightLeft => true,
-            TextDirection.BottomTop_LeftRight => true,
-            TextDirection.BottomTop_RightLeft => true,
-            _ => false
-        };
+               {
+                   TextDirection.TopBottom_LeftRight => true,
+                   TextDirection.TopBottom_RightLeft => true,
+                   TextDirection.BottomTop_LeftRight => true,
+                   TextDirection.BottomTop_RightLeft => true,
+                   _ => false
+               };
     }
 
     /// <summary>Check if it is Left to Right direction</summary>
     public static bool IsLeftToRight (TextDirection textDirection)
     {
         return textDirection switch
-        {
-            TextDirection.LeftRight_TopBottom => true,
-            TextDirection.LeftRight_BottomTop => true,
-            _ => false
-        };
+               {
+                   TextDirection.LeftRight_TopBottom => true,
+                   TextDirection.LeftRight_BottomTop => true,
+                   _ => false
+               };
     }
 
     /// <summary>Check if it is Top to Bottom direction</summary>
     public static bool IsTopToBottom (TextDirection textDirection)
     {
         return textDirection switch
-        {
-            TextDirection.TopBottom_LeftRight => true,
-            TextDirection.TopBottom_RightLeft => true,
-            _ => false
-        };
+               {
+                   TextDirection.TopBottom_LeftRight => true,
+                   TextDirection.TopBottom_RightLeft => true,
+                   _ => false
+               };
     }
 
     // TODO: Move to StringExtensions?
@@ -1023,7 +1069,7 @@ public class TextFormatter
             return str.Replace ("\t", "");
         }
 
-        return str.Replace ("\t", new string (' ', tabWidth));
+        return str.Replace ("\t", new (' ', tabWidth));
     }
 
     // TODO: Move to StringExtensions?
@@ -1114,7 +1160,7 @@ public class TextFormatter
         }
 
         // value is too wide
-        return new string (text.TakeWhile (c => (width -= ((Rune)c).GetColumns ()) >= 0).ToArray ());
+        return new (text.TakeWhile (c => (width -= ((Rune)c).GetColumns ()) >= 0).ToArray ());
     }
 
     /// <summary>Formats the provided text to fit within the width provided using word wrapping.</summary>
@@ -1327,21 +1373,21 @@ public class TextFormatter
                     case ' ':
                         return GetNextWhiteSpace (to + 1, cWidth, out incomplete, length);
                     case '\t':
-                        {
-                            length += tabWidth + 1;
-
-                            if (length == tabWidth && tabWidth > cWidth)
-                            {
-                                return to + 1;
-                            }
+                    {
+                        length += tabWidth + 1;
 
-                            if (length > cWidth && tabWidth > cWidth)
-                            {
-                                return to;
-                            }
+                        if (length == tabWidth && tabWidth > cWidth)
+                        {
+                            return to + 1;
+                        }
 
-                            return GetNextWhiteSpace (to + 1, cWidth, out incomplete, length);
+                        if (length > cWidth && tabWidth > cWidth)
+                        {
+                            return to;
                         }
+
+                        return GetNextWhiteSpace (to + 1, cWidth, out incomplete, length);
+                    }
                     default:
                         to++;
 
@@ -1350,11 +1396,11 @@ public class TextFormatter
             }
 
             return cLength switch
-            {
-                > 0 when to < runes.Count && runes [to].Value != ' ' && runes [to].Value != '\t' => from,
-                > 0 when to < runes.Count && (runes [to].Value == ' ' || runes [to].Value == '\t') => from,
-                _ => to
-            };
+                   {
+                       > 0 when to < runes.Count && runes [to].Value != ' ' && runes [to].Value != '\t' => from,
+                       > 0 when to < runes.Count && (runes [to].Value == ' ' || runes [to].Value == '\t') => from,
+                       _ => to
+                   };
         }
 
         if (start < text.GetRuneCount ())
@@ -1728,8 +1774,14 @@ public class TextFormatter
 
                 foreach (string line in lines)
                 {
-
-                    lineResult.Add (ClipAndJustify (PerformCorrectFormatDirection (textDirection, line), width, justify, textDirection, tabWidth, textFormatter));
+                    lineResult.Add (
+                                    ClipAndJustify (
+                                                    PerformCorrectFormatDirection (textDirection, line),
+                                                    width,
+                                                    justify,
+                                                    textDirection,
+                                                    tabWidth,
+                                                    textFormatter));
                 }
 
                 return PerformCorrectFormatDirection (textDirection, lineResult);
@@ -1793,13 +1845,13 @@ public class TextFormatter
     private static string PerformCorrectFormatDirection (TextDirection textDirection, string line)
     {
         return textDirection switch
-        {
-            TextDirection.RightLeft_BottomTop
-                or TextDirection.RightLeft_TopBottom
-                or TextDirection.BottomTop_LeftRight
-                or TextDirection.BottomTop_RightLeft => StringExtensions.ToString (line.EnumerateRunes ().Reverse ()),
-            _ => line
-        };
+               {
+                   TextDirection.RightLeft_BottomTop
+                       or TextDirection.RightLeft_TopBottom
+                       or TextDirection.BottomTop_LeftRight
+                       or TextDirection.BottomTop_RightLeft => StringExtensions.ToString (line.EnumerateRunes ().Reverse ()),
+                   _ => line
+               };
     }
 
     private static List<Rune> PerformCorrectFormatDirection (TextDirection textDirection, List<Rune> runes)
@@ -1810,25 +1862,13 @@ public class TextFormatter
     private static List<string> PerformCorrectFormatDirection (TextDirection textDirection, List<string> lines)
     {
         return textDirection switch
-        {
-            TextDirection.TopBottom_RightLeft
-                or TextDirection.LeftRight_BottomTop
-                or TextDirection.RightLeft_BottomTop
-                or TextDirection.BottomTop_RightLeft => lines.ToArray ().Reverse ().ToList (),
-            _ => lines
-        };
-    }
-
-    /// <summary>Returns the number of lines needed to render the specified text given the width.</summary>
-    /// <remarks>Calls <see cref="Format()"/>.</remarks>
-    /// <returns>Number of lines.</returns>
-    /// <param name="text">Text, may contain newlines.</param>
-    /// <param name="width">The minimum width for the text.</param>
-    public static int GetLineCount (string text, int width)
-    {
-        List<string> result = Format (text, width, false, true);
-
-        return result.Count;
+               {
+                   TextDirection.TopBottom_RightLeft
+                       or TextDirection.LeftRight_BottomTop
+                       or TextDirection.RightLeft_BottomTop
+                       or TextDirection.BottomTop_RightLeft => lines.ToArray ().Reverse ().ToList (),
+                   _ => lines
+               };
     }
 
     /// <summary>
@@ -1840,7 +1880,10 @@ public class TextFormatter
     /// </remarks>
     /// <param name="lines">The lines.</param>
     /// <param name="startLine">The line in the list to start with (any lines before will be ignored).</param>
-    /// <param name="linesCount">The number of lines to process (if less than <c>lines.Count</c>, any lines after will be ignored).</param>
+    /// <param name="linesCount">
+    ///     The number of lines to process (if less than <c>lines.Count</c>, any lines after will be
+    ///     ignored).
+    /// </param>
     /// <param name="tabWidth">The number of columns used for a tab.</param>
     /// <returns>The width required.</returns>
     public static int GetColumnsRequiredForVerticalText (
@@ -1857,11 +1900,13 @@ public class TextFormatter
              i++)
         {
             string runes = lines [i];
+
             if (runes.Length > 0)
             {
                 max += runes.EnumerateRunes ().Max (r => GetRuneWidth (r, tabWidth));
             }
         }
+
         return max;
     }
 
@@ -1961,8 +2006,15 @@ public class TextFormatter
         return runeIdx;
     }
 
-    private static int GetRuneWidth (string str, int tabWidth, TextDirection textDirection = TextDirection.LeftRight_TopBottom) { return GetRuneWidth (str.EnumerateRunes ().ToList (), tabWidth, textDirection); }
-    private static int GetRuneWidth (List<Rune> runes, int tabWidth, TextDirection textDirection = TextDirection.LeftRight_TopBottom) { return runes.Sum (r => GetRuneWidth (r, tabWidth, textDirection)); }
+    private static int GetRuneWidth (string str, int tabWidth, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
+    {
+        return GetRuneWidth (str.EnumerateRunes ().ToList (), tabWidth, textDirection);
+    }
+
+    private static int GetRuneWidth (List<Rune> runes, int tabWidth, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
+    {
+        return runes.Sum (r => GetRuneWidth (r, tabWidth, textDirection));
+    }
 
     private static int GetRuneWidth (Rune rune, int tabWidth, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
     {
@@ -2025,7 +2077,9 @@ public class TextFormatter
     /// <param name="direction">The text direction.</param>
     /// <param name="tabWidth">The number of columns used for a tab.</param>
     /// <returns></returns>
-    public static Rectangle CalcRect (
+    [Obsolete ("CalcRect is deprecated, FormatAndGetSize instead.")]
+
+    internal static Rectangle CalcRect (
         int x,
         int y,
         string text,
@@ -2273,7 +2327,7 @@ public class TextFormatter
 
         if (Rune.IsLetterOrDigit (runes [hotPos]))
         {
-            runes [hotPos] = new Rune ((uint)runes [hotPos].Value);
+            runes [hotPos] = new ((uint)runes [hotPos].Value);
         }
 
         return StringExtensions.ToString (runes);
@@ -2312,4 +2366,4 @@ public class TextFormatter
     }
 
     #endregion // Static Members
-}
+}

+ 9 - 58
Terminal.Gui/View/Layout/DimAuto.cs

@@ -70,22 +70,23 @@ public class DimAuto () : Dim
 
         if (Style.FastHasFlags (DimAutoStyle.Text))
         {
-
             if (dimension == Dimension.Width)
             {
                 if (us.TextFormatter.Width is null)
                 {
+                    // Set BOTH width and height (by setting Size). We do this because we will be called again, next
+                    // for Dimension.Height. We need to know the width to calculate the height.
                     us.TextFormatter.Size = us.TextFormatter.FormatAndGetSize (new (int.Min (autoMax, screenX4), screenX4));
                 }
-//                else
-                {
-                    textSize = us.TextFormatter.Width.Value;
-                }
+
+                textSize = us.TextFormatter.Width!.Value;
             }
             else
             {
                 if (us.TextFormatter.Height is null)
                 {
+                    // Set just the height. It is assumed that the width has already been set.
+                    // TODO: There may be cases where the width is not set. We may need to set it here.
                     textSize = us.TextFormatter.FormatAndGetSize (new (us.TextFormatter.Width ?? screenX4, int.Min (autoMax, screenX4))).Height;
                     us.TextFormatter.Height = textSize;
                 }
@@ -93,18 +94,6 @@ public class DimAuto () : Dim
                 {
                     textSize = us.TextFormatter.Height.Value;
                 }
-
-                //if (us.Width.Has (typeof(DimAuto), out var widthDim))
-                //{
-                //    DimAuto widthDimAuto = (DimAuto)widthDim;
-                //    textSize = us.TextFormatter.FormatAndGetSize (us.GetContentSize ()).Height;
-                //}
-                //else
-                //{
-                //    textSize = us.TextFormatter.FormatAndGetSize ().Height;
-                //}
-
-                //us.TextFormatter.Size = us.TextFormatter.Size with { Height = textSize };
             }
         }
 
@@ -254,41 +243,10 @@ public class DimAuto () : Dim
                 #endregion Centered
 
                 #region Percent
-                //// [ ] 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<View> 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 ();
-                //}
-
-                //viewsNeedingLayout.AddRange (dimPercentSubViews);
-
-                //int maxDimPercent = 0;
-
-                //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);
+                // [ ] DimPercent   - Dimension is dependent on `us.ContentSize`
+                // No need to do anything.
                 #endregion Percent
 
-
                 #region Aligned
                 // [ ] PosAlign     - Position is dependent on other views with `GroupId` AND `us.ContentSize`
                 int maxAlign = 0;
@@ -340,8 +298,6 @@ public class DimAuto () : Dim
                 maxCalculatedSize = int.Max (maxCalculatedSize, maxAlign);
                 #endregion Aligned
 
-
-
                 #region Anchored
                 // [x] PosAnchorEnd - Position is dependent on `us.ContentSize` AND `subview.Frame` 
                 List<View> anchoredSubViews;
@@ -463,6 +419,7 @@ public class DimAuto () : Dim
 
         int oppositeScreen = dimension == Dimension.Width ? Application.Screen.Height * 4 : Application.Screen.Width * 4;
 
+        // TODO: Double-check that we really do need to SetRelativeLayout on these views!
         foreach (var v in viewsNeedingLayout)
         {
             if (dimension == Dimension.Width)
@@ -490,12 +447,6 @@ public class DimAuto () : Dim
         return max;
     }
 
-    internal override bool ReferencesOtherViews ()
-    {
-        // BUGBUG: This is not correct. _contentSize may be null.
-        return false; //_style.HasFlag (DimAutoStyle.Content);
-    }
-
     /// <inheritdoc/>
     public override bool Equals (object? other)
     {

+ 0 - 19
Terminal.Gui/View/Layout/DimCombine.cs

@@ -61,23 +61,4 @@ public class DimCombine (AddOrSubtract add, Dim left, Dim right) : Dim
 
         return newDimension;
     }
-
-    /// <summary>
-    ///     Diagnostics API to determine if this Dim object references other views.
-    /// </summary>
-    /// <returns></returns>
-    internal override bool ReferencesOtherViews ()
-    {
-        if (Left!.ReferencesOtherViews ())
-        {
-            return true;
-        }
-
-        if (Right!.ReferencesOtherViews ())
-        {
-            return true;
-        }
-
-        return false;
-    }
 }