Renamed API to simplify and make more clear. Fixed Text formatting vertical/right aligned text. Fixed crash in scenario

This commit is contained in:
Tig
2024-04-18 14:16:55 -06:00
parent c1cac7290c
commit 00ac1bbf7a
3 changed files with 83 additions and 70 deletions

View File

@@ -287,13 +287,13 @@ public class TextFormatter
Rune [] runes = linesFormatted [line].ToRunes ();
runes = Direction switch
{
TextDirection.RightLeft_BottomTop => runes.Reverse ().ToArray (),
TextDirection.RightLeft_TopBottom => runes.Reverse ().ToArray (),
TextDirection.BottomTop_LeftRight => runes.Reverse ().ToArray (),
TextDirection.BottomTop_RightLeft => runes.Reverse ().ToArray (),
_ => runes
};
{
TextDirection.RightLeft_BottomTop => runes.Reverse ().ToArray (),
TextDirection.RightLeft_TopBottom => runes.Reverse ().ToArray (),
TextDirection.BottomTop_LeftRight => runes.Reverse ().ToArray (),
TextDirection.BottomTop_RightLeft => runes.Reverse ().ToArray (),
_ => runes
};
// When text is justified, we lost left or right, so we use the direction to align.
@@ -304,7 +304,7 @@ public class TextFormatter
{
if (isVertical)
{
int runesWidth = GetWidestLineLength (linesFormatted, line, TabWidth);
int runesWidth = GetColumnsRequiredForVerticalText (linesFormatted, tabWidth: TabWidth);
x = screen.Right - runesWidth;
CursorPosition = screen.Width - runesWidth + (_hotKeyPos > -1 ? _hotKeyPos : 0);
}
@@ -320,7 +320,7 @@ public class TextFormatter
if (isVertical)
{
int runesWidth = line > 0
? GetWidestLineLength (linesFormatted, 0, line, TabWidth)
? GetColumnsRequiredForVerticalText (linesFormatted, TabWidth)
: 0;
x = screen.Left + runesWidth;
}
@@ -335,7 +335,7 @@ public class TextFormatter
{
if (isVertical)
{
int runesWidth = GetWidestLineLength (linesFormatted, line, TabWidth);
int runesWidth = GetColumnsRequiredForVerticalText (linesFormatted, TabWidth);
x = screen.Left + line + (screen.Width - runesWidth) / 2;
CursorPosition = (screen.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0);
@@ -700,48 +700,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?
@@ -1122,21 +1122,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++;
@@ -1145,11 +1145,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 ())
@@ -1580,29 +1580,28 @@ public class TextFormatter
}
/// <summary>
/// Returns the number of columns in the widest line in the list based on the <paramref name="startIndex"/> and
/// the <paramref name="length"/>.
/// Returns the number of columns required to render <paramref name="lines"/> oriented vertically.
/// </summary>
/// <remarks>
/// This API will return incorrect results if the text includes glyphs who's width is dependent on surrounding
/// glyphs (e.g. Arabic).
/// </remarks>
/// <param name="lines">The lines.</param>
/// <param name="startIndex">The start index.</param>
/// <param name="length">The length.</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="tabWidth">The number of columns used for a tab.</param>
/// <returns>The maximum characters width.</returns>
public static int GetWidestLineLength (
/// <returns>The width required.</returns>
public static int GetColumnsRequiredForVerticalText (
List<string> lines,
int startIndex = -1,
int length = -1,
int startLine = -1,
int linesCount = -1,
int tabWidth = 0
)
{
var max = 0;
for (int i = startIndex == -1 ? 0 : startIndex;
i < (length == -1 ? lines.Count : startIndex + length);
for (int i = startLine == -1 ? 0 : startLine;
i < (linesCount == -1 ? lines.Count : startLine + linesCount);
i++)
{
string runes = lines [i];

View File

@@ -334,7 +334,7 @@ public partial class View
switch (TextFormatter.IsVerticalDirection (TextDirection))
{
case true:
int colWidth = TextFormatter.GetWidestLineLength (new List<string> { TextFormatter.Text }, 0, 1);
int colWidth = TextFormatter.GetColumnsRequiredForVerticalText (new List<string> { TextFormatter.Text }, 0, 1);
// TODO: v2 - This uses frame.Width; it should only use Viewport
if (_frame.Width < colWidth

View File

@@ -1,5 +1,6 @@
using System.Text;
using Xunit.Abstractions;
using static Terminal.Gui.SpinnerStyle;
// Alias Console to MockConsole so we don't accidentally use Console
@@ -961,30 +962,43 @@ ssb
Assert.Equal (1, TextFormatter.GetMaxColsForWidth (text, 1));
}
[Theory]
[InlineData (new [] { "0123456789" }, 1)]
[InlineData (new [] { "Hello World" }, 1)]
[InlineData (new [] { "Hello", "World" }, 2)]
[InlineData (new [] { "こんにちは", "世界" }, 4)]
public void GetColumnsRequiredForVerticalText_List_GetsWidth (IEnumerable<string> text, int expectedWidth)
{
Assert.Equal (expectedWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList ()));
}
[Theory]
[InlineData (new [] { "Hello World" }, 1, 0, 1, 1)]
[InlineData (new [] { "Hello", "World" }, 2, 1, 1, 1)]
[InlineData (new [] { "こんにちは", "世界" }, 4, 1, 1, 2)]
public void GetWidestLineLength_List_Simple_And_Wide_Runes (
public void GetColumnsRequiredForVerticalText_List_Simple_And_Wide_Runes (
IEnumerable<string> text,
int width,
int expectedWidth,
int index,
int length,
int indexWidth
int expectedIndexWidth
)
{
Assert.Equal (width, TextFormatter.GetWidestLineLength (text.ToList ()));
Assert.Equal (indexWidth, TextFormatter.GetWidestLineLength (text.ToList (), index, length));
Assert.Equal (expectedWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList ()));
Assert.Equal (expectedIndexWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList (), index, length));
}
[Fact]
public void GetWidestLineLength_List_With_Combining_Runes ()
public void GetColumnsRequiredForVerticalText_List_With_Combining_Runes ()
{
List<string> text = new () { "Les Mis", "e\u0328\u0301", "rables" };
Assert.Equal (1, TextFormatter.GetWidestLineLength (text, 1, 1));
Assert.Equal (1, TextFormatter.GetColumnsRequiredForVerticalText (text, 1, 1));
}
[Fact]
public void GetWidestLineLength_With_Combining_Runes ()
public void GetColumnsRequiredForVerticalText_With_Combining_Runes ()
{
var text = "Les Mise\u0328\u0301rables";
Assert.Equal (1, TextFormatter.GetWidestLineLength (text, 1, 1));