mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2026-01-01 16:59:35 +01:00
Merge branch 'v2_combining-tab-fix_3011' into v2_tabview-frame-fix_2882
This commit is contained in:
@@ -169,6 +169,15 @@ namespace Terminal.Gui {
|
|||||||
return StringExtensions.ToString (runes);
|
return StringExtensions.ToString (runes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static string ReplaceTABWithSpaces (string str, int tabWidth)
|
||||||
|
{
|
||||||
|
if (tabWidth == 0) {
|
||||||
|
return str.Replace ("\t", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.Replace ("\t", new string (' ', tabWidth));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Splits all newlines in the <paramref name="text"/> into a list
|
/// Splits all newlines in the <paramref name="text"/> into a list
|
||||||
/// and supports both CRLF and LF, preserving the ending newline.
|
/// and supports both CRLF and LF, preserving the ending newline.
|
||||||
@@ -368,7 +377,17 @@ namespace Terminal.Gui {
|
|||||||
if (end == start) {
|
if (end == start) {
|
||||||
end = start + width;
|
end = start + width;
|
||||||
}
|
}
|
||||||
lines.Add (StringExtensions.ToString (runes.GetRange (start, end - start)));
|
var zeroLength = 0;
|
||||||
|
for (int i = end; i < runes.Count - start; i++) {
|
||||||
|
var r = runes [i];
|
||||||
|
if (r.GetColumns () == 0) {
|
||||||
|
zeroLength++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines.Add (StringExtensions.ToString (runes.GetRange (start, end - start + zeroLength)));
|
||||||
|
end += zeroLength;
|
||||||
start = end;
|
start = end;
|
||||||
if (runes [end].Value == ' ') {
|
if (runes [end].Value == ' ') {
|
||||||
start++;
|
start++;
|
||||||
@@ -472,12 +491,13 @@ namespace Terminal.Gui {
|
|||||||
if (IsHorizontalDirection (textDirection)) {
|
if (IsHorizontalDirection (textDirection)) {
|
||||||
return StringExtensions.ToString (runes.GetRange (0, GetLengthThatFits (text, width)));
|
return StringExtensions.ToString (runes.GetRange (0, GetLengthThatFits (text, width)));
|
||||||
} else {
|
} else {
|
||||||
return StringExtensions.ToString (runes.GetRange (0, width));
|
var zeroLength = runes.Sum (r => r.GetColumns () == 0 ? 1 : 0);
|
||||||
|
return StringExtensions.ToString (runes.GetRange (0, width + zeroLength));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (justify) {
|
if (justify) {
|
||||||
return Justify (text, width, ' ', textDirection);
|
return Justify (text, width, ' ', textDirection);
|
||||||
} else if (IsHorizontalDirection (textDirection) && text.GetColumns () > width) {
|
} else if (IsHorizontalDirection (textDirection) && GetRuneWidth (text.GetColumns ()) > width) {
|
||||||
return StringExtensions.ToString (runes.GetRange (0, GetLengthThatFits (text, width)));
|
return StringExtensions.ToString (runes.GetRange (0, GetLengthThatFits (text, width)));
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
@@ -602,6 +622,7 @@ namespace Terminal.Gui {
|
|||||||
|
|
||||||
if (wordWrap == false) {
|
if (wordWrap == false) {
|
||||||
text = ReplaceCRLFWithSpace (text);
|
text = ReplaceCRLFWithSpace (text);
|
||||||
|
text = ReplaceTABWithSpaces (text, tabWidth);
|
||||||
lineResult.Add (ClipAndJustify (text, width, justify, textDirection));
|
lineResult.Add (ClipAndJustify (text, width, justify, textDirection));
|
||||||
return lineResult;
|
return lineResult;
|
||||||
}
|
}
|
||||||
@@ -623,7 +644,7 @@ namespace Terminal.Gui {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (var line in WordWrapText (StringExtensions.ToString (runes.GetRange (lp, runeCount - lp)), width, preserveTrailingSpaces, tabWidth, textDirection)) {
|
foreach (var line in WordWrapText (StringExtensions.ToString (runes.GetRange (lp, runeCount - lp)), width, preserveTrailingSpaces, tabWidth, textDirection)) {
|
||||||
lineResult.Add (ClipAndJustify (line, width, justify, textDirection));
|
lineResult.Add (ClipAndJustify (ReplaceTABWithSpaces (line, tabWidth), width, justify, textDirection));
|
||||||
}
|
}
|
||||||
|
|
||||||
return lineResult;
|
return lineResult;
|
||||||
@@ -654,7 +675,7 @@ namespace Terminal.Gui {
|
|||||||
var max = 0;
|
var max = 0;
|
||||||
result.ForEach (s => {
|
result.ForEach (s => {
|
||||||
var m = 0;
|
var m = 0;
|
||||||
s.ToRuneList ().ForEach (r => m += Math.Max (r.GetColumns (), 1));
|
s.ToRuneList ().ForEach (r => m += GetRuneWidth (r.GetColumns ()));
|
||||||
if (m > max) {
|
if (m > max) {
|
||||||
max = m;
|
max = m;
|
||||||
}
|
}
|
||||||
@@ -688,7 +709,7 @@ namespace Terminal.Gui {
|
|||||||
for (int i = (startIndex == -1 ? 0 : startIndex); i < (length == -1 ? lines.Count : startIndex + length); i++) {
|
for (int i = (startIndex == -1 ? 0 : startIndex); i < (length == -1 ? lines.Count : startIndex + length); i++) {
|
||||||
var runes = lines [i];
|
var runes = lines [i];
|
||||||
if (runes.Length > 0)
|
if (runes.Length > 0)
|
||||||
max += runes.EnumerateRunes ().Max (r => Math.Max (r.GetColumns (), 1));
|
max += runes.EnumerateRunes ().Max (r => GetRuneWidth (r.GetColumns ()));
|
||||||
}
|
}
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
@@ -706,7 +727,7 @@ namespace Terminal.Gui {
|
|||||||
var max = 0;
|
var max = 0;
|
||||||
var runes = text.ToRunes ();
|
var runes = text.ToRunes ();
|
||||||
for (int i = (startIndex == -1 ? 0 : startIndex); i < (length == -1 ? runes.Length : startIndex + length); i++) {
|
for (int i = (startIndex == -1 ? 0 : startIndex); i < (length == -1 ? runes.Length : startIndex + length); i++) {
|
||||||
max += Math.Max (runes [i].GetColumns (), 1);
|
max += GetRuneWidth (runes [i].GetColumns ());
|
||||||
}
|
}
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
@@ -734,7 +755,7 @@ namespace Terminal.Gui {
|
|||||||
var runesLength = 0;
|
var runesLength = 0;
|
||||||
var runeIdx = 0;
|
var runeIdx = 0;
|
||||||
for (; runeIdx < runes.Count; runeIdx++) {
|
for (; runeIdx < runes.Count; runeIdx++) {
|
||||||
var runeWidth = Math.Max (runes [runeIdx].GetColumns (), 1);
|
var runeWidth = GetRuneWidth (runes [runeIdx].GetColumns ());
|
||||||
if (runesLength + runeWidth > columns) {
|
if (runesLength + runeWidth > columns) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -743,6 +764,15 @@ namespace Terminal.Gui {
|
|||||||
return runeIdx;
|
return runeIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int GetRuneWidth (int runeWidth)
|
||||||
|
{
|
||||||
|
if (runeWidth < 0 || runeWidth > 0) {
|
||||||
|
return Math.Max (runeWidth, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return runeWidth;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the index position from the list based on the <paramref name="width"/>.
|
/// Gets the index position from the list based on the <paramref name="width"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -756,7 +786,7 @@ namespace Terminal.Gui {
|
|||||||
for (; lineIdx < lines.Count; lineIdx++) {
|
for (; lineIdx < lines.Count; lineIdx++) {
|
||||||
var runes = lines [lineIdx].ToRuneList ();
|
var runes = lines [lineIdx].ToRuneList ();
|
||||||
var maxRruneWidth = runes.Count > 0
|
var maxRruneWidth = runes.Count > 0
|
||||||
? runes.Max (r => Math.Max (r.GetColumns (), 1)) : 1;
|
? runes.Max (r => GetRuneWidth (r.GetColumns ())) : 1;
|
||||||
if (runesLength + maxRruneWidth > width) {
|
if (runesLength + maxRruneWidth > width) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -772,8 +802,9 @@ namespace Terminal.Gui {
|
|||||||
/// <param name="y">The y location of the rectangle</param>
|
/// <param name="y">The y location of the rectangle</param>
|
||||||
/// <param name="text">The text to measure</param>
|
/// <param name="text">The text to measure</param>
|
||||||
/// <param name="direction">The text direction.</param>
|
/// <param name="direction">The text direction.</param>
|
||||||
|
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Rect CalcRect (int x, int y, string text, TextDirection direction = TextDirection.LeftRight_TopBottom)
|
public static Rect CalcRect (int x, int y, string text, TextDirection direction = TextDirection.LeftRight_TopBottom, int tabWidth = 0)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty (text)) {
|
if (string.IsNullOrEmpty (text)) {
|
||||||
return new Rect (new Point (x, y), Size.Empty);
|
return new Rect (new Point (x, y), Size.Empty);
|
||||||
@@ -795,9 +826,16 @@ namespace Terminal.Gui {
|
|||||||
cols = 0;
|
cols = 0;
|
||||||
} else if (rune.Value != '\r') {
|
} else if (rune.Value != '\r') {
|
||||||
cols++;
|
cols++;
|
||||||
var rw = ((Rune)rune).GetColumns ();
|
var rw = 0;
|
||||||
if (rw > 0) {
|
if (rune.Value == '\t') {
|
||||||
rw--;
|
rw += tabWidth - 1;
|
||||||
|
} else {
|
||||||
|
rw = ((Rune)rune).GetColumns ();
|
||||||
|
if (rw > 0) {
|
||||||
|
rw--;
|
||||||
|
} else if (rw == 0) {
|
||||||
|
cols--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cols += rw;
|
cols += rw;
|
||||||
}
|
}
|
||||||
@@ -822,10 +860,18 @@ namespace Terminal.Gui {
|
|||||||
cw = 1;
|
cw = 1;
|
||||||
} else if (rune.Value != '\r') {
|
} else if (rune.Value != '\r') {
|
||||||
rows++;
|
rows++;
|
||||||
var rw = ((Rune)rune).GetColumns ();
|
var rw = 0;
|
||||||
if (cw < rw) {
|
if (rune.Value == '\t') {
|
||||||
cw = rw;
|
rw += tabWidth - 1;
|
||||||
vw++;
|
rows += rw;
|
||||||
|
} else {
|
||||||
|
rw = ((Rune)rune).GetColumns ();
|
||||||
|
if (rw == 0) {
|
||||||
|
rows--;
|
||||||
|
} else if (cw < rw) {
|
||||||
|
cw = rw;
|
||||||
|
vw++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -954,7 +1000,7 @@ namespace Terminal.Gui {
|
|||||||
#endregion // Static Members
|
#endregion // Static Members
|
||||||
|
|
||||||
List<string> _lines = new List<string> ();
|
List<string> _lines = new List<string> ();
|
||||||
string _text;
|
string _text = null;
|
||||||
TextAlignment _textAlignment;
|
TextAlignment _textAlignment;
|
||||||
VerticalTextAlignment _textVerticalAlignment;
|
VerticalTextAlignment _textVerticalAlignment;
|
||||||
TextDirection _textDirection;
|
TextDirection _textDirection;
|
||||||
@@ -973,13 +1019,17 @@ namespace Terminal.Gui {
|
|||||||
public virtual string Text {
|
public virtual string Text {
|
||||||
get => _text;
|
get => _text;
|
||||||
set {
|
set {
|
||||||
|
if (AutoSize || (_text == null && value != null && Size.IsEmpty)) {
|
||||||
|
Size = CalcRect (0, 0, value, _textDirection, TabWidth).Size;
|
||||||
|
}
|
||||||
|
|
||||||
_text = value;
|
_text = value;
|
||||||
|
|
||||||
if (_text != null && _text.GetRuneCount () > 0 && (Size.Width == 0 || Size.Height == 0 || Size.Width != _text.GetColumns ())) {
|
//if (_text != null && _text.GetRuneCount () > 0 && (Size.Width == 0 || Size.Height == 0 || Size.Width != _text.GetColumns ())) {
|
||||||
// Provide a default size (width = length of longest line, height = 1)
|
// // Provide a default size (width = length of longest line, height = 1)
|
||||||
// TODO: It might makes more sense for the default to be width = length of first line?
|
// // TODO: It might makes more sense for the default to be width = length of first line?
|
||||||
Size = new Size (TextFormatter.MaxWidth (Text, int.MaxValue), 1);
|
// Size = new Size (TextFormatter.MaxWidth (Text, int.MaxValue), 1);
|
||||||
}
|
//}
|
||||||
|
|
||||||
NeedsFormat = true;
|
NeedsFormat = true;
|
||||||
}
|
}
|
||||||
@@ -1194,7 +1244,7 @@ namespace Terminal.Gui {
|
|||||||
if (IsVerticalDirection (_textDirection)) {
|
if (IsVerticalDirection (_textDirection)) {
|
||||||
var colsWidth = GetSumMaxCharWidth (shown_text, 0, 1);
|
var colsWidth = GetSumMaxCharWidth (shown_text, 0, 1);
|
||||||
_lines = Format (shown_text, Size.Height, _textVerticalAlignment == VerticalTextAlignment.Justified, Size.Width > colsWidth,
|
_lines = Format (shown_text, Size.Height, _textVerticalAlignment == VerticalTextAlignment.Justified, Size.Width > colsWidth,
|
||||||
PreserveTrailingSpaces, 0, _textDirection);
|
PreserveTrailingSpaces, TabWidth, _textDirection);
|
||||||
if (!AutoSize) {
|
if (!AutoSize) {
|
||||||
colsWidth = GetMaxColsForWidth (_lines, Size.Width);
|
colsWidth = GetMaxColsForWidth (_lines, Size.Width);
|
||||||
if (_lines.Count > colsWidth) {
|
if (_lines.Count > colsWidth) {
|
||||||
@@ -1203,7 +1253,7 @@ namespace Terminal.Gui {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_lines = Format (shown_text, Size.Width, _textAlignment == TextAlignment.Justified, Size.Height > 1,
|
_lines = Format (shown_text, Size.Width, _textAlignment == TextAlignment.Justified, Size.Height > 1,
|
||||||
PreserveTrailingSpaces, 0, _textDirection);
|
PreserveTrailingSpaces, TabWidth, _textDirection);
|
||||||
if (!AutoSize && _lines.Count > Size.Height) {
|
if (!AutoSize && _lines.Count > Size.Height) {
|
||||||
_lines.RemoveRange (Size.Height, _lines.Count - Size.Height);
|
_lines.RemoveRange (Size.Height, _lines.Count - Size.Height);
|
||||||
}
|
}
|
||||||
@@ -1226,6 +1276,11 @@ namespace Terminal.Gui {
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public bool NeedsFormat { get; set; }
|
public bool NeedsFormat { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the number of columns used for a tab.
|
||||||
|
/// </summary>
|
||||||
|
public int TabWidth { get; set; } = 4;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Causes the <see cref="TextFormatter"/> to reformat the text.
|
/// Causes the <see cref="TextFormatter"/> to reformat the text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1375,31 +1430,64 @@ namespace Terminal.Gui {
|
|||||||
var start = isVertical ? bounds.Top : bounds.Left;
|
var start = isVertical ? bounds.Top : bounds.Left;
|
||||||
var size = isVertical ? bounds.Height : bounds.Width;
|
var size = isVertical ? bounds.Height : bounds.Width;
|
||||||
var current = start + colOffset;
|
var current = start + colOffset;
|
||||||
|
List<Point?> lastZeroWidthPos = null;
|
||||||
|
Rune rune = default;
|
||||||
|
Rune lastRuneUsed;
|
||||||
|
var zeroLengthCount = isVertical ? runes.Sum (r => r.GetColumns () == 0 ? 1 : 0) : 0;
|
||||||
|
|
||||||
for (var idx = (isVertical ? start - y : start - x) + colOffset; current < start + size; idx++) {
|
for (var idx = (isVertical ? start - y : start - x) + colOffset; current < start + size + zeroLengthCount; idx++) {
|
||||||
if (idx < 0 || x + current + colOffset < 0) {
|
lastRuneUsed = rune;
|
||||||
current++;
|
if (lastZeroWidthPos == null) {
|
||||||
continue;
|
if (idx < 0 || x + current + colOffset < 0) {
|
||||||
} else if (!fillRemaining && idx > runes.Length - 1) {
|
current++;
|
||||||
break;
|
continue;
|
||||||
|
} else if (!fillRemaining && idx > runes.Length - 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((!isVertical && current - start > maxBounds.Left + maxBounds.Width - bounds.X + colOffset)
|
||||||
|
|| (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y)) {
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((!isVertical && idx > maxBounds.Left + maxBounds.Width - bounds.X + colOffset)
|
//if ((!isVertical && idx > maxBounds.Left + maxBounds.Width - bounds.X + colOffset)
|
||||||
|| (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y))
|
// || (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y))
|
||||||
|
|
||||||
break;
|
// break;
|
||||||
|
|
||||||
var rune = (Rune)' ';
|
rune = (Rune)' ';
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
Application.Driver?.Move (x, current);
|
|
||||||
if (idx >= 0 && idx < runes.Length) {
|
if (idx >= 0 && idx < runes.Length) {
|
||||||
rune = runes [idx];
|
rune = runes [idx];
|
||||||
}
|
}
|
||||||
|
if (lastZeroWidthPos == null) {
|
||||||
|
Application.Driver?.Move (x, current);
|
||||||
|
} else {
|
||||||
|
var foundIdx = lastZeroWidthPos.IndexOf (p => p.Value.Y == current);
|
||||||
|
if (foundIdx > -1) {
|
||||||
|
if (rune.IsCombiningMark ()) {
|
||||||
|
lastZeroWidthPos [foundIdx] = (new Point (lastZeroWidthPos [foundIdx].Value.X + 1, current));
|
||||||
|
|
||||||
|
Application.Driver?.Move (lastZeroWidthPos [foundIdx].Value.X, current);
|
||||||
|
} else if (!rune.IsCombiningMark () && lastRuneUsed.IsCombiningMark ()) {
|
||||||
|
current++;
|
||||||
|
Application.Driver?.Move (x, current);
|
||||||
|
} else {
|
||||||
|
Application.Driver?.Move (x, current);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Application.Driver?.Move (x, current);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Application.Driver?.Move (current, y);
|
Application.Driver?.Move (current, y);
|
||||||
if (idx >= 0 && idx < runes.Length) {
|
if (idx >= 0 && idx < runes.Length) {
|
||||||
rune = runes [idx];
|
rune = runes [idx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var runeWidth = GetRuneWidth (rune.GetColumns ());
|
||||||
|
|
||||||
if (HotKeyPos > -1 && idx == HotKeyPos) {
|
if (HotKeyPos > -1 && idx == HotKeyPos) {
|
||||||
if ((isVertical && _textVerticalAlignment == VerticalTextAlignment.Justified) ||
|
if ((isVertical && _textVerticalAlignment == VerticalTextAlignment.Justified) ||
|
||||||
(!isVertical && _textAlignment == TextAlignment.Justified)) {
|
(!isVertical && _textAlignment == TextAlignment.Justified)) {
|
||||||
@@ -1409,12 +1497,27 @@ namespace Terminal.Gui {
|
|||||||
Application.Driver?.AddRune (rune);
|
Application.Driver?.AddRune (rune);
|
||||||
Application.Driver?.SetAttribute (normalColor);
|
Application.Driver?.SetAttribute (normalColor);
|
||||||
} else {
|
} else {
|
||||||
|
if (isVertical) {
|
||||||
|
if (runeWidth == 0) {
|
||||||
|
if (lastZeroWidthPos == null) {
|
||||||
|
lastZeroWidthPos = new List<Point?> ();
|
||||||
|
}
|
||||||
|
var foundIdx = lastZeroWidthPos.IndexOf (p => p.Value.Y == current);
|
||||||
|
if (foundIdx == -1) {
|
||||||
|
current--;
|
||||||
|
lastZeroWidthPos.Add ((new Point (x + 1, current)));
|
||||||
|
}
|
||||||
|
Application.Driver?.Move (x + 1, current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Application.Driver?.AddRune (rune);
|
Application.Driver?.AddRune (rune);
|
||||||
}
|
}
|
||||||
// BUGBUG: I think this is a bug. If rune is a combining mark current should not be incremented.
|
|
||||||
var runeWidth = rune.GetColumns (); //Math.Max (rune.GetColumns (), 1);
|
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
current++;
|
if (runeWidth > 0) {
|
||||||
|
current++;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
current += runeWidth;
|
current += runeWidth;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,13 +65,23 @@ namespace Terminal.Gui.TextTests {
|
|||||||
Assert.NotEmpty (tf.Lines);
|
Assert.NotEmpty (tf.Lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void TestSize_TextChange ()
|
[InlineData (TextDirection.LeftRight_TopBottom, false)]
|
||||||
|
[InlineData (TextDirection.TopBottom_LeftRight, true)]
|
||||||
|
public void TestSize_TextChange (TextDirection textDirection, bool autoSize)
|
||||||
{
|
{
|
||||||
var tf = new TextFormatter () { Text = "你" };
|
var tf = new TextFormatter () { Direction = textDirection, Text = "你", AutoSize = autoSize };
|
||||||
Assert.Equal (2, tf.Size.Width);
|
Assert.Equal (2, tf.Size.Width);
|
||||||
tf.Text = "你你";
|
tf.Text = "你你";
|
||||||
Assert.Equal (4, tf.Size.Width);
|
if (autoSize) {
|
||||||
|
if (textDirection == TextDirection.LeftRight_TopBottom) {
|
||||||
|
Assert.Equal (4, tf.Size.Width);
|
||||||
|
} else {
|
||||||
|
Assert.Equal (2, tf.Size.Width);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Assert.Equal (2, tf.Size.Width);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -620,21 +630,24 @@ namespace Terminal.Gui.TextTests {
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 51, 0, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" })]
|
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 51, 0, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" })]
|
||||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 50, -1, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัา", "ำ" })]
|
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 50, -1, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" })]
|
||||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 46, -5, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮ", "ฯะัาำ" })]
|
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 46, -5, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮ", "ฯะัาำ" })]
|
||||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 26, -25, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" })]
|
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 26, -25, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" })]
|
||||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 17, -34, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑ", "ฒณดตถทธนบปผฝพฟภมย", "รฤลฦวศษสหฬอฮฯะัาำ" })]
|
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 17, -34, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑ", "ฒณดตถทธนบปผฝพฟภมย", "รฤลฦวศษสหฬอฮฯะัาำ" })]
|
||||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 13, -38, new string [] { "กขฃคฅฆงจฉชซฌญ", "ฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦว", "ศษสหฬอฮฯะัาำ" })]
|
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 13, -38, new string [] { "กขฃคฅฆงจฉชซฌญ", "ฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦว", "ศษสหฬอฮฯะัาำ" })]
|
||||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 1, -50, new string [] { "ก", "ข", "ฃ", "ค", "ฅ", "ฆ", "ง", "จ", "ฉ", "ช", "ซ", "ฌ", "ญ", "ฎ", "ฏ", "ฐ", "ฑ", "ฒ", "ณ", "ด", "ต", "ถ", "ท", "ธ", "น", "บ", "ป", "ผ", "ฝ", "พ", "ฟ", "ภ", "ม", "ย", "ร", "ฤ", "ล", "ฦ", "ว", "ศ", "ษ", "ส", "ห", "ฬ", "อ", "ฮ", "ฯ", "ะ", "ั", "า", "ำ" })]
|
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 1, -50, new string [] { "ก", "ข", "ฃ", "ค", "ฅ", "ฆ", "ง", "จ", "ฉ", "ช", "ซ", "ฌ", "ญ", "ฎ", "ฏ", "ฐ", "ฑ", "ฒ", "ณ", "ด", "ต", "ถ", "ท", "ธ", "น", "บ", "ป", "ผ", "ฝ", "พ", "ฟ", "ภ", "ม", "ย", "ร", "ฤ", "ล", "ฦ", "ว", "ศ", "ษ", "ส", "ห", "ฬ", "อ", "ฮ", "ฯ", "ะั", "า", "ำ" })]
|
||||||
public void WordWrap_Unicode_SingleWordLine (string text, int maxWidth, int widthOffset, IEnumerable<string> resultLines)
|
public void WordWrap_Unicode_SingleWordLine (string text, int maxWidth, int widthOffset, IEnumerable<string> resultLines)
|
||||||
{
|
{
|
||||||
List<string> wrappedLines;
|
List<string> wrappedLines;
|
||||||
|
|
||||||
|
var zeroWidth = text.EnumerateRunes ().Where (r => r.GetColumns () == 0);
|
||||||
|
Assert.Single (zeroWidth);
|
||||||
|
Assert.Equal ('ั', zeroWidth.ElementAt (0).Value);
|
||||||
Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
|
Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
|
||||||
var expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
|
var expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
|
||||||
wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
|
wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
|
||||||
Assert.Equal (wrappedLines.Count, resultLines.Count ());
|
Assert.Equal (wrappedLines.Count, resultLines.Count ());
|
||||||
Assert.True (expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0));
|
Assert.True (expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount () + zeroWidth.Count () - 1 + widthOffset) : 0));
|
||||||
Assert.True (expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0));
|
Assert.True (expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0));
|
||||||
Assert.Equal (resultLines, wrappedLines);
|
Assert.Equal (resultLines, wrappedLines);
|
||||||
}
|
}
|
||||||
@@ -1418,5 +1431,127 @@ namespace Terminal.Gui.TextTests {
|
|||||||
Assert.Equal (expected, text [index].ToString ());
|
Assert.Equal (expected, text [index].ToString ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetLengthThatFits_With_Combining_Runes ()
|
||||||
|
{
|
||||||
|
var text = "Les Mise\u0328\u0301rables";
|
||||||
|
Assert.Equal (16, TextFormatter.GetLengthThatFits (text, 14));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetMaxColsForWidth_With_Combining_Runes ()
|
||||||
|
{
|
||||||
|
var text = new List<string> () { "Les Mis", "e\u0328\u0301", "rables" };
|
||||||
|
Assert.Equal (1, TextFormatter.GetMaxColsForWidth (text, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetSumMaxCharWidth_With_Combining_Runes ()
|
||||||
|
{
|
||||||
|
var text = "Les Mise\u0328\u0301rables";
|
||||||
|
Assert.Equal (1, TextFormatter.GetSumMaxCharWidth (text, 1, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetSumMaxCharWidth_List_With_Combining_Runes ()
|
||||||
|
{
|
||||||
|
var text = new List<string> () { "Les Mis", "e\u0328\u0301", "rables" };
|
||||||
|
Assert.Equal (1, TextFormatter.GetSumMaxCharWidth (text, 1, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData (14, 1, TextDirection.LeftRight_TopBottom)]
|
||||||
|
[InlineData (1, 14, TextDirection.TopBottom_LeftRight)]
|
||||||
|
public void CalcRect_With_Combining_Runes (int width, int height, TextDirection textDirection)
|
||||||
|
{
|
||||||
|
var text = "Les Mise\u0328\u0301rables";
|
||||||
|
Assert.Equal (new Rect (0, 0, width, height), TextFormatter.CalcRect (0, 0, text, textDirection));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, AutoInitShutdown]
|
||||||
|
[InlineData (14, 1, TextDirection.LeftRight_TopBottom, "Les Misęrables")]
|
||||||
|
[InlineData (1, 14, TextDirection.TopBottom_LeftRight, "L\ne\ns\n \nM\ni\ns\nę\nr\na\nb\nl\ne\ns")]
|
||||||
|
[InlineData (4, 4, TextDirection.TopBottom_LeftRight, @"
|
||||||
|
LMre
|
||||||
|
eias
|
||||||
|
ssb
|
||||||
|
ęl ")]
|
||||||
|
public void Draw_With_Combining_Runes (int width, int height, TextDirection textDirection, string expected)
|
||||||
|
{
|
||||||
|
var text = "Les Mise\u0328\u0301rables";
|
||||||
|
|
||||||
|
var tf = new TextFormatter ();
|
||||||
|
tf.Direction = textDirection;
|
||||||
|
tf.Text = text;
|
||||||
|
|
||||||
|
if (textDirection == TextDirection.LeftRight_TopBottom) {
|
||||||
|
Assert.Equal (new Size (width, height), tf.Size);
|
||||||
|
} else {
|
||||||
|
Assert.Equal (new Size (1, text.GetColumns ()), tf.Size);
|
||||||
|
tf.Size = new Size (width, height);
|
||||||
|
}
|
||||||
|
tf.Draw (new Rect (0, 0, width, height), new Attribute (ColorName.White, ColorName.Black), new Attribute (ColorName.Blue, ColorName.Black));
|
||||||
|
TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, AutoInitShutdown]
|
||||||
|
[InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
|
||||||
|
[InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
|
||||||
|
[InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")]
|
||||||
|
[InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")]
|
||||||
|
public void TabWith_PreserveTrailingSpaces_False (int width, int height, TextDirection textDirection, int tabWidth, string expected)
|
||||||
|
{
|
||||||
|
var text = "This is a \tTab";
|
||||||
|
var tf = new TextFormatter ();
|
||||||
|
tf.Direction = textDirection;
|
||||||
|
tf.TabWidth = tabWidth;
|
||||||
|
tf.Text = text;
|
||||||
|
|
||||||
|
Assert.False (tf.WordWrap);
|
||||||
|
Assert.False (tf.PreserveTrailingSpaces);
|
||||||
|
Assert.Equal (new Size (width, height), tf.Size);
|
||||||
|
tf.Draw (new Rect (0, 0, width, height), new Attribute (ColorName.White, ColorName.Black), new Attribute (ColorName.Blue, ColorName.Black));
|
||||||
|
TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, AutoInitShutdown]
|
||||||
|
[InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
|
||||||
|
[InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
|
||||||
|
[InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")]
|
||||||
|
[InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")]
|
||||||
|
public void TabWith_PreserveTrailingSpaces_True (int width, int height, TextDirection textDirection, int tabWidth, string expected)
|
||||||
|
{
|
||||||
|
var text = "This is a \tTab";
|
||||||
|
var tf = new TextFormatter ();
|
||||||
|
tf.Direction = textDirection;
|
||||||
|
tf.TabWidth = tabWidth;
|
||||||
|
tf.PreserveTrailingSpaces = true;
|
||||||
|
tf.Text = text;
|
||||||
|
|
||||||
|
Assert.False (tf.WordWrap);
|
||||||
|
Assert.Equal (new Size (width, height), tf.Size);
|
||||||
|
tf.Draw (new Rect (0, 0, width, height), new Attribute (ColorName.White, ColorName.Black), new Attribute (ColorName.Blue, ColorName.Black));
|
||||||
|
TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, AutoInitShutdown]
|
||||||
|
[InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
|
||||||
|
[InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
|
||||||
|
[InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")]
|
||||||
|
[InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")]
|
||||||
|
public void TabWith_WordWrap_True (int width, int height, TextDirection textDirection, int tabWidth, string expected)
|
||||||
|
{
|
||||||
|
var text = "This is a \tTab";
|
||||||
|
var tf = new TextFormatter ();
|
||||||
|
tf.Direction = textDirection;
|
||||||
|
tf.TabWidth = tabWidth;
|
||||||
|
tf.WordWrap = true;
|
||||||
|
tf.Text = text;
|
||||||
|
|
||||||
|
Assert.Equal (new Size (width, height), tf.Size);
|
||||||
|
tf.Draw (new Rect (0, 0, width, height), new Attribute (ColorName.White, ColorName.Black), new Attribute (ColorName.Blue, ColorName.Black));
|
||||||
|
TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user