mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2026-01-01 16:59:35 +01:00
* Add horizontal and vertical support for combining glyphs. * Fix text and auto size behavior. * Add TabWidth property. * Add unit test for WordWrap. * Add MultiLine property and improve more code. * Fix word wrap on MessageBox. * Fix label unit test. * Rename to GetTextFormatterSizeNeededForTextAndHotKey * Proves that TextFormatter.Size not must to have the same View.Bounds.Size. * Fix fails unit tests. * Updates AutoSize document. * Updates MultiLine document. * Removes Application dependency from the TextFormatter class. * Fix Draw XML comment.
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
@@ -169,6 +168,15 @@ namespace Terminal.Gui {
|
||||
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>
|
||||
/// Splits all newlines in the <paramref name="text"/> into a list
|
||||
/// and supports both CRLF and LF, preserving the ending newline.
|
||||
@@ -342,13 +350,13 @@ namespace Terminal.Gui {
|
||||
// }
|
||||
//}
|
||||
|
||||
while ((end = start + Math.Max (GetLengthThatFits (runes.GetRange (start, runes.Count - start), width), 1)) < runes.Count) {
|
||||
while ((end = start + GetLengthThatFits (runes.GetRange (start, runes.Count - start), width, tabWidth)) < runes.Count) {
|
||||
while (runes [end].Value != ' ' && end > start)
|
||||
end--;
|
||||
if (end == start)
|
||||
end = start + GetLengthThatFits (runes.GetRange (end, runes.Count - end), width);
|
||||
end = start + GetLengthThatFits (runes.GetRange (end, runes.Count - end), width, tabWidth);
|
||||
var str = StringExtensions.ToString (runes.GetRange (start, end - start));
|
||||
if (end > start && str.GetColumns () <= width) {
|
||||
if (end > start && GetRuneWidth (str, tabWidth) <= width) {
|
||||
lines.Add (str);
|
||||
start = end;
|
||||
if (runes [end].Value == ' ') {
|
||||
@@ -368,7 +376,17 @@ namespace Terminal.Gui {
|
||||
if (end == start) {
|
||||
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;
|
||||
if (runes [end].Value == ' ') {
|
||||
start++;
|
||||
@@ -427,7 +445,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
if (start < text.GetRuneCount ()) {
|
||||
var str = StringExtensions.ToString (runes.GetRange (start, runes.Count - start));
|
||||
var str = ReplaceTABWithSpaces (StringExtensions.ToString (runes.GetRange (start, runes.Count - start)), tabWidth);
|
||||
if (IsVerticalDirection (textDirection) || preserveTrailingSpaces || (!preserveTrailingSpaces && str.GetColumns () <= width)) {
|
||||
lines.Add (str);
|
||||
}
|
||||
@@ -443,10 +461,11 @@ namespace Terminal.Gui {
|
||||
/// <param name="width">The number of columns to clip the text to. Text longer than <paramref name="width"/> will be clipped.</param>
|
||||
/// <param name="talign">Alignment.</param>
|
||||
/// <param name="textDirection">The text direction.</param>
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
/// <returns>Justified and clipped text.</returns>
|
||||
public static string ClipAndJustify (string text, int width, TextAlignment talign, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
|
||||
public static string ClipAndJustify (string text, int width, TextAlignment talign, TextDirection textDirection = TextDirection.LeftRight_TopBottom, int tabWidth = 0)
|
||||
{
|
||||
return ClipAndJustify (text, width, talign == TextAlignment.Justified, textDirection);
|
||||
return ClipAndJustify (text, width, talign == TextAlignment.Justified, textDirection, tabWidth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -456,8 +475,9 @@ namespace Terminal.Gui {
|
||||
/// <param name="width">The number of columns to clip the text to. Text longer than <paramref name="width"/> will be clipped.</param>
|
||||
/// <param name="justify">Justify.</param>
|
||||
/// <param name="textDirection">The text direction.</param>
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
/// <returns>Justified and clipped text.</returns>
|
||||
public static string ClipAndJustify (string text, int width, bool justify, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
|
||||
public static string ClipAndJustify (string text, int width, bool justify, TextDirection textDirection = TextDirection.LeftRight_TopBottom, int tabWidth = 0)
|
||||
{
|
||||
if (width < 0) {
|
||||
throw new ArgumentOutOfRangeException ("Width cannot be negative.");
|
||||
@@ -466,19 +486,21 @@ namespace Terminal.Gui {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = ReplaceTABWithSpaces (text, tabWidth);
|
||||
var runes = text.ToRuneList ();
|
||||
int slen = runes.Count;
|
||||
if (slen > width) {
|
||||
if (IsHorizontalDirection (textDirection)) {
|
||||
return StringExtensions.ToString (runes.GetRange (0, GetLengthThatFits (text, width)));
|
||||
return StringExtensions.ToString (runes.GetRange (0, GetLengthThatFits (text, width, tabWidth)));
|
||||
} 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 {
|
||||
if (justify) {
|
||||
return Justify (text, width, ' ', textDirection);
|
||||
} else if (IsHorizontalDirection (textDirection) && text.GetColumns () > width) {
|
||||
return StringExtensions.ToString (runes.GetRange (0, GetLengthThatFits (text, width)));
|
||||
return Justify (text, width, ' ', textDirection, tabWidth);
|
||||
} else if (IsHorizontalDirection (textDirection) && GetRuneWidth (text, tabWidth) > width) {
|
||||
return StringExtensions.ToString (runes.GetRange (0, GetLengthThatFits (text, width, tabWidth)));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
@@ -492,8 +514,9 @@ namespace Terminal.Gui {
|
||||
/// <param name="width"></param>
|
||||
/// <param name="spaceChar">Character to replace whitespace and pad with. For debugging purposes.</param>
|
||||
/// <param name="textDirection">The text direction.</param>
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
/// <returns>The justified text.</returns>
|
||||
public static string Justify (string text, int width, char spaceChar = ' ', TextDirection textDirection = TextDirection.LeftRight_TopBottom)
|
||||
public static string Justify (string text, int width, char spaceChar = ' ', TextDirection textDirection = TextDirection.LeftRight_TopBottom, int tabWidth = 0)
|
||||
{
|
||||
if (width < 0) {
|
||||
throw new ArgumentOutOfRangeException ("Width cannot be negative.");
|
||||
@@ -502,10 +525,11 @@ namespace Terminal.Gui {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = ReplaceTABWithSpaces (text, tabWidth);
|
||||
var words = text.Split (' ');
|
||||
int textCount;
|
||||
if (IsHorizontalDirection (textDirection)) {
|
||||
textCount = words.Sum (arg => arg.GetColumns ());
|
||||
textCount = words.Sum (arg => GetRuneWidth (arg, tabWidth));
|
||||
} else {
|
||||
textCount = words.Sum (arg => arg.GetRuneCount ());
|
||||
}
|
||||
@@ -532,7 +556,7 @@ namespace Terminal.Gui {
|
||||
return s.ToString ();
|
||||
}
|
||||
|
||||
static char [] whitespace = new char [] { ' ', '\t' };
|
||||
//static char [] whitespace = new char [] { ' ', '\t' };
|
||||
|
||||
/// <summary>
|
||||
/// Reformats text into lines, applying text alignment and optionally wrapping text to new lines on word boundaries.
|
||||
@@ -546,6 +570,7 @@ namespace Terminal.Gui {
|
||||
/// If <see langword="false"/>, trailing spaces at the end of wrapped lines will be trimmed.</param>
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
/// <param name="textDirection">The text direction.</param>
|
||||
/// <param name="multiLine">If <see langword="true"/> new lines are allowed.</param>
|
||||
/// <returns>A list of word wrapped lines.</returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
@@ -558,9 +583,9 @@ namespace Terminal.Gui {
|
||||
/// If <paramref name="width"/> is int.MaxValue, the text will be formatted to the maximum width possible.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static List<string> Format (string text, int width, TextAlignment talign, bool wordWrap, bool preserveTrailingSpaces = false, int tabWidth = 0, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
|
||||
public static List<string> Format (string text, int width, TextAlignment talign, bool wordWrap, bool preserveTrailingSpaces = false, int tabWidth = 0, TextDirection textDirection = TextDirection.LeftRight_TopBottom, bool multiLine = false)
|
||||
{
|
||||
return Format (text, width, talign == TextAlignment.Justified, wordWrap, preserveTrailingSpaces, tabWidth, textDirection);
|
||||
return Format (text, width, talign == TextAlignment.Justified, wordWrap, preserveTrailingSpaces, tabWidth, textDirection, multiLine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -575,6 +600,7 @@ namespace Terminal.Gui {
|
||||
/// If <see langword="false"/>, trailing spaces at the end of wrapped lines will be trimmed.</param>
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
/// <param name="textDirection">The text direction.</param>
|
||||
/// <param name="multiLine">If <see langword="true"/> new lines are allowed.</param>
|
||||
/// <returns>A list of word wrapped lines.</returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
@@ -588,7 +614,7 @@ namespace Terminal.Gui {
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static List<string> Format (string text, int width, bool justify, bool wordWrap,
|
||||
bool preserveTrailingSpaces = false, int tabWidth = 0, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
|
||||
bool preserveTrailingSpaces = false, int tabWidth = 0, TextDirection textDirection = TextDirection.LeftRight_TopBottom, bool multiLine = false)
|
||||
{
|
||||
if (width < 0) {
|
||||
throw new ArgumentOutOfRangeException ("width cannot be negative");
|
||||
@@ -600,10 +626,27 @@ namespace Terminal.Gui {
|
||||
return lineResult;
|
||||
}
|
||||
|
||||
if (wordWrap == false) {
|
||||
text = ReplaceCRLFWithSpace (text);
|
||||
lineResult.Add (ClipAndJustify (text, width, justify, textDirection));
|
||||
return lineResult;
|
||||
if (!wordWrap) {
|
||||
text = ReplaceTABWithSpaces (text, tabWidth);
|
||||
if (multiLine) {
|
||||
string [] lines = null;
|
||||
if (text.Contains ("\r\n")) {
|
||||
lines = text.Split ("\r\n");
|
||||
} else if (text.Contains ('\n')) {
|
||||
lines = text.Split ('\n');
|
||||
}
|
||||
if (lines == null) {
|
||||
lines = new [] { text };
|
||||
}
|
||||
foreach (var line in lines) {
|
||||
lineResult.Add (ClipAndJustify (line, width, justify, textDirection, tabWidth));
|
||||
}
|
||||
return lineResult;
|
||||
} else {
|
||||
text = ReplaceCRLFWithSpace (text);
|
||||
lineResult.Add (ClipAndJustify (text, width, justify, textDirection, tabWidth));
|
||||
return lineResult;
|
||||
}
|
||||
}
|
||||
|
||||
var runes = StripCRLF (text, true).ToRuneList ();
|
||||
@@ -614,7 +657,7 @@ namespace Terminal.Gui {
|
||||
if (c.Value == '\n') {
|
||||
var wrappedLines = WordWrapText (StringExtensions.ToString (runes.GetRange (lp, i - lp)), width, preserveTrailingSpaces, tabWidth, textDirection);
|
||||
foreach (var line in wrappedLines) {
|
||||
lineResult.Add (ClipAndJustify (line, width, justify, textDirection));
|
||||
lineResult.Add (ClipAndJustify (line, width, justify, textDirection, tabWidth));
|
||||
}
|
||||
if (wrappedLines.Count == 0) {
|
||||
lineResult.Add (string.Empty);
|
||||
@@ -623,7 +666,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
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 (line, width, justify, textDirection, tabWidth));
|
||||
}
|
||||
|
||||
return lineResult;
|
||||
@@ -648,13 +691,14 @@ namespace Terminal.Gui {
|
||||
/// <returns>Width of the longest line after formatting the text constrained by <paramref name="maxColumns"/>.</returns>
|
||||
/// <param name="text">Text, may contain newlines.</param>
|
||||
/// <param name="maxColumns">The number of columns to constrain the text to for formatting.</param>
|
||||
public static int MaxWidth (string text, int maxColumns)
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
public static int MaxWidth (string text, int maxColumns, int tabWidth = 0)
|
||||
{
|
||||
var result = TextFormatter.Format (text: text, width: maxColumns, justify: false, wordWrap: true);
|
||||
var max = 0;
|
||||
result.ForEach (s => {
|
||||
var m = 0;
|
||||
s.ToRuneList ().ForEach (r => m += Math.Max (r.GetColumns (), 1));
|
||||
s.ToRuneList ().ForEach (r => m += GetRuneWidth (r, tabWidth));
|
||||
if (m > max) {
|
||||
max = m;
|
||||
}
|
||||
@@ -667,11 +711,12 @@ namespace Terminal.Gui {
|
||||
/// <paramref name="text"/> if it contains newlines.
|
||||
/// </summary>
|
||||
/// <param name="text">Text, may contain newlines.</param>
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
/// <returns>The length of the longest line.</returns>
|
||||
public static int MaxWidthLine (string text)
|
||||
public static int MaxWidthLine (string text, int tabWidth = 0)
|
||||
{
|
||||
var result = TextFormatter.SplitNewLine (text);
|
||||
return result.Max (x => x.GetColumns ());
|
||||
return result.Max (x => GetRuneWidth (x, tabWidth));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -681,14 +726,15 @@ namespace Terminal.Gui {
|
||||
/// <param name="lines">The lines.</param>
|
||||
/// <param name="startIndex">The start index.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
/// <returns>The maximum characters width.</returns>
|
||||
public static int GetSumMaxCharWidth (List<string> lines, int startIndex = -1, int length = -1)
|
||||
public static int GetSumMaxCharWidth (List<string> lines, int startIndex = -1, int length = -1, int tabWidth = 0)
|
||||
{
|
||||
var max = 0;
|
||||
for (int i = (startIndex == -1 ? 0 : startIndex); i < (length == -1 ? lines.Count : startIndex + length); i++) {
|
||||
var runes = lines [i];
|
||||
if (runes.Length > 0)
|
||||
max += runes.EnumerateRunes ().Max (r => Math.Max (r.GetColumns (), 1));
|
||||
max += runes.EnumerateRunes ().Max (r => GetRuneWidth (r, tabWidth));
|
||||
}
|
||||
return max;
|
||||
}
|
||||
@@ -700,13 +746,14 @@ namespace Terminal.Gui {
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="startIndex">The start index.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
/// <returns>The maximum characters width.</returns>
|
||||
public static int GetSumMaxCharWidth (string text, int startIndex = -1, int length = -1)
|
||||
public static int GetSumMaxCharWidth (string text, int startIndex = -1, int length = -1, int tabWidth = 0)
|
||||
{
|
||||
var max = 0;
|
||||
var runes = text.ToRunes ();
|
||||
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], tabWidth);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
@@ -716,16 +763,18 @@ namespace Terminal.Gui {
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="columns">The width.</param>
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
/// <returns>The index of the text that fit the width.</returns>
|
||||
public static int GetLengthThatFits (string text, int columns) => GetLengthThatFits (text?.ToRuneList (), columns);
|
||||
public static int GetLengthThatFits (string text, int columns, int tabWidth = 0) => GetLengthThatFits (text?.ToRuneList (), columns, tabWidth);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of the Runes in a list of Runes that will fit in <paramref name="columns"/>.
|
||||
/// </summary>
|
||||
/// <param name="runes">The list of runes.</param>
|
||||
/// <param name="columns">The width.</param>
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
/// <returns>The index of the last Rune in <paramref name="runes"/> that fit in <paramref name="columns"/>.</returns>
|
||||
public static int GetLengthThatFits (List<Rune> runes, int columns)
|
||||
public static int GetLengthThatFits (List<Rune> runes, int columns, int tabWidth = 0)
|
||||
{
|
||||
if (runes == null || runes.Count == 0) {
|
||||
return 0;
|
||||
@@ -734,7 +783,7 @@ namespace Terminal.Gui {
|
||||
var runesLength = 0;
|
||||
var runeIdx = 0;
|
||||
for (; runeIdx < runes.Count; runeIdx++) {
|
||||
var runeWidth = Math.Max (runes [runeIdx].GetColumns (), 1);
|
||||
var runeWidth = GetRuneWidth (runes [runeIdx], tabWidth);
|
||||
if (runesLength + runeWidth > columns) {
|
||||
break;
|
||||
}
|
||||
@@ -743,20 +792,44 @@ namespace Terminal.Gui {
|
||||
return runeIdx;
|
||||
}
|
||||
|
||||
private static int GetRuneWidth (string str, int tabWidth)
|
||||
{
|
||||
return GetRuneWidth (str.EnumerateRunes ().ToList (), tabWidth);
|
||||
}
|
||||
|
||||
private static int GetRuneWidth (List<Rune> runes, int tabWidth)
|
||||
{
|
||||
return runes.Sum (r => GetRuneWidth (r, tabWidth));
|
||||
}
|
||||
|
||||
private static int GetRuneWidth (Rune rune, int tabWidth)
|
||||
{
|
||||
var runeWidth = rune.GetColumns ();
|
||||
if (rune.Value == '\t') {
|
||||
return tabWidth;
|
||||
}
|
||||
if (runeWidth < 0 || runeWidth > 0) {
|
||||
return Math.Max (runeWidth, 1);
|
||||
}
|
||||
|
||||
return runeWidth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index position from the list based on the <paramref name="width"/>.
|
||||
/// </summary>
|
||||
/// <param name="lines">The lines.</param>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
/// <returns>The index of the list that fit the width.</returns>
|
||||
public static int GetMaxColsForWidth (List<string> lines, int width)
|
||||
public static int GetMaxColsForWidth (List<string> lines, int width, int tabWidth = 0)
|
||||
{
|
||||
var runesLength = 0;
|
||||
var lineIdx = 0;
|
||||
for (; lineIdx < lines.Count; lineIdx++) {
|
||||
var runes = lines [lineIdx].ToRuneList ();
|
||||
var maxRruneWidth = runes.Count > 0
|
||||
? runes.Max (r => Math.Max (r.GetColumns (), 1)) : 1;
|
||||
? runes.Max (r => GetRuneWidth (r, tabWidth)) : 1;
|
||||
if (runesLength + maxRruneWidth > width) {
|
||||
break;
|
||||
}
|
||||
@@ -772,8 +845,9 @@ namespace Terminal.Gui {
|
||||
/// <param name="y">The y location of the rectangle</param>
|
||||
/// <param name="text">The text to measure</param>
|
||||
/// <param name="direction">The text direction.</param>
|
||||
/// <param name="tabWidth">The number of columns used for a tab.</param>
|
||||
/// <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)) {
|
||||
return new Rect (new Point (x, y), Size.Empty);
|
||||
@@ -795,9 +869,16 @@ namespace Terminal.Gui {
|
||||
cols = 0;
|
||||
} else if (rune.Value != '\r') {
|
||||
cols++;
|
||||
var rw = ((Rune)rune).GetColumns ();
|
||||
if (rw > 0) {
|
||||
rw--;
|
||||
var rw = 0;
|
||||
if (rune.Value == '\t') {
|
||||
rw += tabWidth - 1;
|
||||
} else {
|
||||
rw = ((Rune)rune).GetColumns ();
|
||||
if (rw > 0) {
|
||||
rw--;
|
||||
} else if (rw == 0) {
|
||||
cols--;
|
||||
}
|
||||
}
|
||||
cols += rw;
|
||||
}
|
||||
@@ -822,10 +903,18 @@ namespace Terminal.Gui {
|
||||
cw = 1;
|
||||
} else if (rune.Value != '\r') {
|
||||
rows++;
|
||||
var rw = ((Rune)rune).GetColumns ();
|
||||
if (cw < rw) {
|
||||
cw = rw;
|
||||
vw++;
|
||||
var rw = 0;
|
||||
if (rune.Value == '\t') {
|
||||
rw += tabWidth - 1;
|
||||
rows += rw;
|
||||
} else {
|
||||
rw = ((Rune)rune).GetColumns ();
|
||||
if (rw == 0) {
|
||||
rows--;
|
||||
} else if (cw < rw) {
|
||||
cw = rw;
|
||||
vw++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -954,13 +1043,18 @@ namespace Terminal.Gui {
|
||||
#endregion // Static Members
|
||||
|
||||
List<string> _lines = new List<string> ();
|
||||
string _text;
|
||||
string _text = null;
|
||||
TextAlignment _textAlignment;
|
||||
VerticalTextAlignment _textVerticalAlignment;
|
||||
TextDirection _textDirection;
|
||||
Key _hotKey;
|
||||
int _hotKeyPos = -1;
|
||||
Size _size;
|
||||
private bool _autoSize;
|
||||
private bool _preserveTrailingSpaces;
|
||||
private int _tabWidth = 4;
|
||||
private bool _wordWrap = true;
|
||||
private bool _multiLine;
|
||||
|
||||
/// <summary>
|
||||
/// Event invoked when the <see cref="HotKey"/> is changed.
|
||||
@@ -973,15 +1067,18 @@ namespace Terminal.Gui {
|
||||
public virtual string Text {
|
||||
get => _text;
|
||||
set {
|
||||
_text = value;
|
||||
var textWasNull = _text == null && value != null;
|
||||
_text = EnableNeedsFormat (value);
|
||||
|
||||
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)
|
||||
// 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);
|
||||
if ((AutoSize && Alignment != TextAlignment.Justified && VerticalAlignment != VerticalTextAlignment.Justified) || (textWasNull && Size.IsEmpty)) {
|
||||
Size = CalcRect (0, 0, _text, _textDirection, TabWidth).Size;
|
||||
}
|
||||
|
||||
NeedsFormat = true;
|
||||
//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)
|
||||
// // 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);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -991,7 +1088,18 @@ namespace Terminal.Gui {
|
||||
/// <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.
|
||||
/// </summary>
|
||||
public bool AutoSize { get; set; }
|
||||
/// <remarks>
|
||||
/// Auto size is ignored if the <see cref="TextAlignment.Justified"/> and <see cref="VerticalTextAlignment.Justified"/> are used.
|
||||
/// </remarks>
|
||||
public bool AutoSize {
|
||||
get => _autoSize;
|
||||
set {
|
||||
_autoSize = EnableNeedsFormat (value);
|
||||
if (_autoSize && Alignment != TextAlignment.Justified && VerticalAlignment != VerticalTextAlignment.Justified) {
|
||||
Size = CalcRect (0, 0, Text, _textDirection, TabWidth).Size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether trailing spaces at the end of word-wrapped lines are preserved
|
||||
@@ -999,7 +1107,10 @@ namespace Terminal.Gui {
|
||||
/// 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; set; }
|
||||
public bool PreserveTrailingSpaces {
|
||||
get => _preserveTrailingSpaces;
|
||||
set => _preserveTrailingSpaces = EnableNeedsFormat (value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Controls the horizontal text-alignment property.
|
||||
@@ -1007,10 +1118,7 @@ namespace Terminal.Gui {
|
||||
/// <value>The text alignment.</value>
|
||||
public TextAlignment Alignment {
|
||||
get => _textAlignment;
|
||||
set {
|
||||
_textAlignment = value;
|
||||
NeedsFormat = true;
|
||||
}
|
||||
set => _textAlignment = EnableNeedsFormat (value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1019,10 +1127,7 @@ namespace Terminal.Gui {
|
||||
/// <value>The text vertical alignment.</value>
|
||||
public VerticalTextAlignment VerticalAlignment {
|
||||
get => _textVerticalAlignment;
|
||||
set {
|
||||
_textVerticalAlignment = value;
|
||||
NeedsFormat = true;
|
||||
}
|
||||
set => _textVerticalAlignment = EnableNeedsFormat (value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1031,10 +1136,7 @@ namespace Terminal.Gui {
|
||||
/// <value>The text vertical alignment.</value>
|
||||
public TextDirection Direction {
|
||||
get => _textDirection;
|
||||
set {
|
||||
_textDirection = value;
|
||||
NeedsFormat = true;
|
||||
}
|
||||
set => _textDirection = EnableNeedsFormat (value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1097,11 +1199,13 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This is not implemented!
|
||||
/// <summary>
|
||||
///
|
||||
/// Allows word wrap the to fit the available container width.
|
||||
/// </summary>
|
||||
public bool WordWrap { get; set; } = false;
|
||||
public bool WordWrap {
|
||||
get => _wordWrap;
|
||||
set => _wordWrap = EnableNeedsFormat (value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the area the text will be constrained to when formatted.
|
||||
@@ -1110,12 +1214,13 @@ namespace Terminal.Gui {
|
||||
/// Does not return the size the formatted text; just the value that was set.
|
||||
/// </remarks>
|
||||
public Size Size {
|
||||
get {
|
||||
return _size;
|
||||
}
|
||||
get => _size;
|
||||
set {
|
||||
_size = value;
|
||||
NeedsFormat = true;
|
||||
if (AutoSize && Alignment != TextAlignment.Justified && VerticalAlignment != VerticalTextAlignment.Justified) {
|
||||
_size = EnableNeedsFormat (CalcRect (0, 0, Text, _textDirection, TabWidth).Size);
|
||||
} else {
|
||||
_size = EnableNeedsFormat (value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1127,7 +1232,7 @@ namespace Terminal.Gui {
|
||||
/// <summary>
|
||||
/// The position in the text of the hotkey. The hotkey will be rendered using the hot color.
|
||||
/// </summary>
|
||||
public int HotKeyPos { get => _hotKeyPos; set => _hotKeyPos = value; }
|
||||
public int HotKeyPos { get => _hotKeyPos; internal set => _hotKeyPos = value; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hotkey. Will be an upper case letter or digit.
|
||||
@@ -1146,7 +1251,7 @@ namespace Terminal.Gui {
|
||||
/// <summary>
|
||||
/// Gets the cursor position from <see cref="HotKey"/>. If the <see cref="HotKey"/> is defined, the cursor will be positioned over it.
|
||||
/// </summary>
|
||||
public int CursorPosition { get; set; }
|
||||
public int CursorPosition { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size required to hold the formatted text, given the constraints placed by <see cref="Size"/>.
|
||||
@@ -1169,7 +1274,7 @@ namespace Terminal.Gui {
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Upon a 'get' of this property, if the text needs to be formatted (if <see cref="NeedsFormat"/> is <c>true</c>)
|
||||
/// <see cref="Format(string, int, bool, bool, bool, int, TextDirection)"/> will be called internally.
|
||||
/// <see cref="Format(string, int, bool, bool, bool, int, TextDirection, bool)"/> will be called internally.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public List<string> Lines {
|
||||
@@ -1192,18 +1297,18 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
if (IsVerticalDirection (_textDirection)) {
|
||||
var colsWidth = GetSumMaxCharWidth (shown_text, 0, 1);
|
||||
_lines = Format (shown_text, Size.Height, _textVerticalAlignment == VerticalTextAlignment.Justified, Size.Width > colsWidth,
|
||||
PreserveTrailingSpaces, 0, _textDirection);
|
||||
var colsWidth = GetSumMaxCharWidth (shown_text, 0, 1, TabWidth);
|
||||
_lines = Format (shown_text, Size.Height, VerticalAlignment == VerticalTextAlignment.Justified, Size.Width > colsWidth && WordWrap,
|
||||
PreserveTrailingSpaces, TabWidth, Direction, MultiLine);
|
||||
if (!AutoSize) {
|
||||
colsWidth = GetMaxColsForWidth (_lines, Size.Width);
|
||||
colsWidth = GetMaxColsForWidth (_lines, Size.Width, TabWidth);
|
||||
if (_lines.Count > colsWidth) {
|
||||
_lines.RemoveRange (colsWidth, _lines.Count - colsWidth);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_lines = Format (shown_text, Size.Width, _textAlignment == TextAlignment.Justified, Size.Height > 1,
|
||||
PreserveTrailingSpaces, 0, _textDirection);
|
||||
_lines = Format (shown_text, Size.Width, Alignment == TextAlignment.Justified, Size.Height > 1 && WordWrap,
|
||||
PreserveTrailingSpaces, TabWidth, Direction, MultiLine);
|
||||
if (!AutoSize && _lines.Count > Size.Height) {
|
||||
_lines.RemoveRange (Size.Height, _lines.Count - Size.Height);
|
||||
}
|
||||
@@ -1216,7 +1321,7 @@ 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)"/> is called.
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
@@ -1226,6 +1331,33 @@ namespace Terminal.Gui {
|
||||
/// </remarks>
|
||||
public bool NeedsFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of columns used for a tab.
|
||||
/// </summary>
|
||||
public int TabWidth {
|
||||
get => _tabWidth;
|
||||
set => _tabWidth = EnableNeedsFormat (value);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
|
||||
private T EnableNeedsFormat<T> (T value)
|
||||
{
|
||||
NeedsFormat = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Causes the <see cref="TextFormatter"/> to reformat the text.
|
||||
/// </summary>
|
||||
@@ -1235,27 +1367,31 @@ namespace Terminal.Gui {
|
||||
var sb = new StringBuilder ();
|
||||
// Lines_get causes a Format
|
||||
foreach (var line in Lines) {
|
||||
sb.AppendLine (line.ToString ());
|
||||
sb.AppendLine (line);
|
||||
}
|
||||
return sb.ToString ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws the text held by <see cref="TextFormatter"/> to <see cref="Application.Driver"/> using the colors specified.
|
||||
/// Draws the text held by <see cref="TextFormatter"/> to <see cref="ConsoleDriver"/> using the colors specified.
|
||||
/// </summary>
|
||||
/// <param name="bounds">Specifies the screen-relative location and maximum size for drawing the text.</param>
|
||||
/// <param name="normalColor">The color to use for all text except the hotkey</param>
|
||||
/// <param name="hotColor">The color to use to draw the hotkey</param>
|
||||
/// <param name="containerBounds">Specifies the screen-relative location and maximum container size.</param>
|
||||
/// <param name="fillRemaining">Determines if the bounds width will be used (default) or only the text width will be used.</param>
|
||||
public void Draw (Rect bounds, Attribute normalColor, Attribute hotColor, Rect containerBounds = default, bool fillRemaining = true)
|
||||
/// <param name="driver">The console driver currently used by the application.</param>
|
||||
public void Draw (Rect bounds, Attribute normalColor, Attribute hotColor, Rect containerBounds = default, bool fillRemaining = true, ConsoleDriver driver = null)
|
||||
{
|
||||
// With this check, we protect against subclasses with overrides of Text (like Button)
|
||||
if (string.IsNullOrEmpty (_text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Application.Driver?.SetAttribute (normalColor);
|
||||
if (driver == null) {
|
||||
driver = Application.Driver;
|
||||
}
|
||||
driver?.SetAttribute (normalColor);
|
||||
|
||||
// Use "Lines" to ensure a Format (don't use "lines"))
|
||||
|
||||
@@ -1271,7 +1407,7 @@ namespace Terminal.Gui {
|
||||
|
||||
var isVertical = IsVerticalDirection (_textDirection);
|
||||
var maxBounds = bounds;
|
||||
if (Application.Driver != null) {
|
||||
if (driver != null) {
|
||||
maxBounds = containerBounds == default
|
||||
? bounds
|
||||
: new Rect (Math.Max (containerBounds.X, bounds.X),
|
||||
@@ -1316,7 +1452,7 @@ namespace Terminal.Gui {
|
||||
// Horizontal Alignment
|
||||
if (_textAlignment == TextAlignment.Right || (_textAlignment == TextAlignment.Justified && !IsLeftToRight (_textDirection))) {
|
||||
if (isVertical) {
|
||||
var runesWidth = GetSumMaxCharWidth (Lines, line);
|
||||
var runesWidth = GetSumMaxCharWidth (Lines, line, TabWidth);
|
||||
x = bounds.Right - runesWidth;
|
||||
CursorPosition = bounds.Width - runesWidth + (_hotKeyPos > -1 ? _hotKeyPos : 0);
|
||||
} else {
|
||||
@@ -1326,7 +1462,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
} else if (_textAlignment == TextAlignment.Left || _textAlignment == TextAlignment.Justified) {
|
||||
if (isVertical) {
|
||||
var runesWidth = line > 0 ? GetSumMaxCharWidth (Lines, 0, line) : 0;
|
||||
var runesWidth = line > 0 ? GetSumMaxCharWidth (Lines, 0, line, TabWidth) : 0;
|
||||
x = bounds.Left + runesWidth;
|
||||
} else {
|
||||
x = bounds.Left;
|
||||
@@ -1334,7 +1470,7 @@ namespace Terminal.Gui {
|
||||
CursorPosition = _hotKeyPos > -1 ? _hotKeyPos : 0;
|
||||
} else if (_textAlignment == TextAlignment.Centered) {
|
||||
if (isVertical) {
|
||||
var runesWidth = GetSumMaxCharWidth (Lines, line);
|
||||
var runesWidth = GetSumMaxCharWidth (Lines, line, TabWidth);
|
||||
x = bounds.Left + line + ((bounds.Width - runesWidth) / 2);
|
||||
CursorPosition = (bounds.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0);
|
||||
} else {
|
||||
@@ -1375,46 +1511,94 @@ namespace Terminal.Gui {
|
||||
var start = isVertical ? bounds.Top : bounds.Left;
|
||||
var size = isVertical ? bounds.Height : bounds.Width;
|
||||
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++) {
|
||||
if (idx < 0 || x + current + colOffset < 0) {
|
||||
current++;
|
||||
continue;
|
||||
} else if (!fillRemaining && idx > runes.Length - 1) {
|
||||
break;
|
||||
for (var idx = (isVertical ? start - y : start - x) + colOffset; current < start + size + zeroLengthCount; idx++) {
|
||||
lastRuneUsed = rune;
|
||||
if (lastZeroWidthPos == null) {
|
||||
if (idx < 0 || x + current + colOffset < 0) {
|
||||
current++;
|
||||
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)
|
||||
|| (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y))
|
||||
//if ((!isVertical && idx > maxBounds.Left + maxBounds.Width - bounds.X + colOffset)
|
||||
// || (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y))
|
||||
|
||||
break;
|
||||
// break;
|
||||
|
||||
var rune = (Rune)' ';
|
||||
rune = (Rune)' ';
|
||||
if (isVertical) {
|
||||
Application.Driver?.Move (x, current);
|
||||
if (idx >= 0 && idx < runes.Length) {
|
||||
rune = runes [idx];
|
||||
}
|
||||
if (lastZeroWidthPos == null) {
|
||||
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));
|
||||
|
||||
driver?.Move (lastZeroWidthPos [foundIdx].Value.X, current);
|
||||
} else if (!rune.IsCombiningMark () && lastRuneUsed.IsCombiningMark ()) {
|
||||
current++;
|
||||
driver?.Move (x, current);
|
||||
} else {
|
||||
driver?.Move (x, current);
|
||||
}
|
||||
} else {
|
||||
driver?.Move (x, current);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Application.Driver?.Move (current, y);
|
||||
driver?.Move (current, y);
|
||||
if (idx >= 0 && idx < runes.Length) {
|
||||
rune = runes [idx];
|
||||
}
|
||||
}
|
||||
|
||||
var runeWidth = GetRuneWidth (rune, TabWidth);
|
||||
|
||||
if (HotKeyPos > -1 && idx == HotKeyPos) {
|
||||
if ((isVertical && _textVerticalAlignment == VerticalTextAlignment.Justified) ||
|
||||
(!isVertical && _textAlignment == TextAlignment.Justified)) {
|
||||
CursorPosition = idx - start;
|
||||
}
|
||||
Application.Driver?.SetAttribute (hotColor);
|
||||
Application.Driver?.AddRune (rune);
|
||||
Application.Driver?.SetAttribute (normalColor);
|
||||
driver?.SetAttribute (hotColor);
|
||||
driver?.AddRune (rune);
|
||||
driver?.SetAttribute (normalColor);
|
||||
} else {
|
||||
Application.Driver?.AddRune (rune);
|
||||
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)));
|
||||
}
|
||||
driver?.Move (x + 1, current);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
current++;
|
||||
if (runeWidth > 0) {
|
||||
current++;
|
||||
}
|
||||
} else {
|
||||
current += runeWidth;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Terminal.Gui {
|
||||
_frame = new Rect (value.X, value.Y, Math.Max (value.Width, 0), Math.Max (value.Height, 0));
|
||||
if (IsInitialized || LayoutStyle == LayoutStyle.Absolute) {
|
||||
LayoutFrames ();
|
||||
TextFormatter.Size = GetSizeNeededForTextAndHotKey ();
|
||||
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
|
||||
SetNeedsLayout ();
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
@@ -213,9 +213,11 @@ namespace Terminal.Gui {
|
||||
#if DEBUG
|
||||
if (LayoutStyle == LayoutStyle.Computed && !IsInitialized) {
|
||||
Debug.WriteLine ($"WARNING: Bounds is being accessed before the View has been initialized. This is likely a bug. View: {this}");
|
||||
Debug.WriteLine ($"The Frame is set before the View has been initialized. So it isn't a bug.Is by design.");
|
||||
}
|
||||
#endif // DEBUG
|
||||
var frameRelativeBounds = Padding?.Thickness.GetInside (Padding.Frame) ?? new Rect (default, Frame.Size);
|
||||
//var frameRelativeBounds = Padding?.Thickness.GetInside (Padding.Frame) ?? new Rect (default, Frame.Size);
|
||||
var frameRelativeBounds = FrameGetInsideBounds ();
|
||||
return new Rect (default, frameRelativeBounds.Size);
|
||||
}
|
||||
set {
|
||||
@@ -229,6 +231,16 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
|
||||
private Rect FrameGetInsideBounds ()
|
||||
{
|
||||
if (Margin == null || Border == null || Padding == null) {
|
||||
return new Rect (default, Frame.Size);
|
||||
}
|
||||
var width = Math.Max (0, Frame.Size.Width - Margin.Thickness.Horizontal - Border.Thickness.Horizontal - Padding.Thickness.Horizontal);
|
||||
var height = Math.Max (0, Frame.Size.Height - Margin.Thickness.Vertical - Border.Thickness.Vertical - Padding.Thickness.Vertical);
|
||||
return new Rect (Point.Empty, new Size (width, height));
|
||||
}
|
||||
|
||||
// Diagnostics to highlight when X or Y is read before the view has been initialized
|
||||
private Pos VerifyIsIntialized (Pos pos)
|
||||
{
|
||||
@@ -460,7 +472,7 @@ namespace Terminal.Gui {
|
||||
if (IsInitialized || LayoutStyle == LayoutStyle.Absolute) {
|
||||
SetMinWidthHeight ();
|
||||
LayoutFrames ();
|
||||
TextFormatter.Size = GetSizeNeededForTextAndHotKey ();
|
||||
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
|
||||
SetNeedsLayout ();
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
@@ -657,7 +669,7 @@ namespace Terminal.Gui {
|
||||
Frame = r;
|
||||
// BUGBUG: Why is this AFTER setting Frame? Seems duplicative.
|
||||
if (!SetMinWidthHeight ()) {
|
||||
TextFormatter.Size = GetSizeNeededForTextAndHotKey ();
|
||||
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -878,7 +890,7 @@ namespace Terminal.Gui {
|
||||
var oldBounds = Bounds;
|
||||
OnLayoutStarted (new LayoutEventArgs () { OldBounds = oldBounds });
|
||||
|
||||
TextFormatter.Size = GetSizeNeededForTextAndHotKey ();
|
||||
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
|
||||
|
||||
// Sort out the dependencies of the X, Y, Width, Height properties
|
||||
var nodes = new HashSet<View> ();
|
||||
@@ -958,7 +970,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
// BUGBUG: This call may be redundant
|
||||
TextFormatter.Size = GetSizeNeededForTextAndHotKey ();
|
||||
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
|
||||
return aSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace Terminal.Gui {
|
||||
} else {
|
||||
SetMinWidthHeight ();
|
||||
}
|
||||
TextFormatter.Size = GetSizeNeededForTextAndHotKey ();
|
||||
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace Terminal.Gui {
|
||||
} else {
|
||||
return TextFormatter.IsVerticalDirection (TextDirection) &&
|
||||
TextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
|
||||
? Math.Max (HotKeySpecifier.GetColumns(), 0) : 0;
|
||||
? Math.Max (HotKeySpecifier.GetColumns (), 0) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace Terminal.Gui {
|
||||
/// Gets the dimensions required for <see cref="Text"/> accounting for a <see cref="Terminal.Gui.TextFormatter.HotKeySpecifier"/> .
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Size GetSizeNeededForTextAndHotKey ()
|
||||
public Size GetTextFormatterSizeNeededForTextAndHotKey ()
|
||||
{
|
||||
if (string.IsNullOrEmpty (TextFormatter.Text)) {
|
||||
|
||||
@@ -188,9 +188,8 @@ namespace Terminal.Gui {
|
||||
|
||||
// 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 (_frame.Size.Width + GetHotKeySpecifierLength (),
|
||||
_frame.Size.Height + GetHotKeySpecifierLength (false));
|
||||
return new Size (Bounds.Size.Width + GetHotKeySpecifierLength (),
|
||||
Bounds.Size.Height + GetHotKeySpecifierLength (false));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.Text;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Terminal.Gui;
|
||||
using static Terminal.Gui.ConfigurationManager;
|
||||
|
||||
namespace Terminal.Gui {
|
||||
@@ -262,7 +260,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Dialog d;
|
||||
d = new Dialog (buttonList.ToArray ()) {
|
||||
Title = title,
|
||||
@@ -273,12 +271,12 @@ namespace Terminal.Gui {
|
||||
|
||||
if (width != 0) {
|
||||
d.Width = width;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (height != 0) {
|
||||
d.Height = height;
|
||||
}
|
||||
|
||||
|
||||
if (useErrorColors) {
|
||||
d.ColorScheme = Colors.Error;
|
||||
} else {
|
||||
@@ -286,7 +284,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
var messageLabel = new Label () {
|
||||
AutoSize = false,
|
||||
AutoSize = wrapMessage ? false : true,
|
||||
Text = message,
|
||||
TextAlignment = TextAlignment.Centered,
|
||||
X = 0,
|
||||
@@ -294,16 +292,19 @@ namespace Terminal.Gui {
|
||||
Width = Dim.Fill (0),
|
||||
Height = Dim.Fill (1)
|
||||
};
|
||||
messageLabel.TextFormatter.WordWrap = wrapMessage; // BUGBUG: This does nothing as it's not implemented by TextFormatter!
|
||||
messageLabel.TextFormatter.WordWrap = wrapMessage;
|
||||
messageLabel.TextFormatter.MultiLine = wrapMessage ? false : true;
|
||||
d.Add (messageLabel);
|
||||
|
||||
|
||||
d.Loaded += (s, e) => {
|
||||
if (width != 0 || height != 0) {
|
||||
return;
|
||||
}
|
||||
// TODO: replace with Dim.Fit when implemented
|
||||
var maxBounds = d.SuperView?.Bounds ?? Application.Top.Bounds;
|
||||
messageLabel.TextFormatter.Size = new Size (maxBounds.Size.Width - d.GetFramesThickness ().Horizontal, maxBounds.Size.Height - d.GetFramesThickness ().Vertical);
|
||||
if (wrapMessage) {
|
||||
messageLabel.TextFormatter.Size = new Size (maxBounds.Size.Width - d.GetFramesThickness ().Horizontal, maxBounds.Size.Height - d.GetFramesThickness ().Vertical);
|
||||
}
|
||||
var msg = messageLabel.TextFormatter.Format ();
|
||||
var messageSize = messageLabel.TextFormatter.GetFormattedSize ();
|
||||
|
||||
@@ -314,7 +315,8 @@ namespace Terminal.Gui {
|
||||
d.Width = newWidth;
|
||||
}
|
||||
// Ensure height fits the text + vspace + buttons
|
||||
d.Height = Math.Max (height, messageSize.Height + 2 + d.GetFramesThickness ().Vertical);
|
||||
var lastLine = messageLabel.TextFormatter.Lines [^1];
|
||||
d.Height = Math.Max (height, messageSize.Height + (lastLine.EndsWith ("\r\n") || lastLine.EndsWith ('\n') ? 1 : 2) + d.GetFramesThickness ().Vertical);
|
||||
d.SetRelativeLayout (d.SuperView?.Frame ?? Application.Top.Frame);
|
||||
};
|
||||
|
||||
|
||||
@@ -439,40 +439,40 @@ namespace Terminal.Gui.DialogTests {
|
||||
iterations++;
|
||||
|
||||
if (iterations == 0) {
|
||||
MessageBox.Query (string.Empty, new string ('f', 50), defaultButton: 0, wrapMessage: true, "btn");
|
||||
MessageBox.Query (string.Empty, new string ('f', 50), defaultButton: 0, wrapMessage: false, "btn");
|
||||
|
||||
Application.RequestStop ();
|
||||
} else if (iterations == 1) {
|
||||
Application.Refresh ();
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (@$"
|
||||
╔══════════════════╗
|
||||
║┌────────────────┐║
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ ff │║
|
||||
║│ │║
|
||||
║│ {btn} │║
|
||||
║└────────────────┘║
|
||||
║ ║
|
||||
────────────────────
|
||||
ffffffffffffffffffff
|
||||
|
||||
⟦► btn ◄⟧
|
||||
────────────────────
|
||||
║ ║
|
||||
║ ║
|
||||
╚══════════════════╝", output);
|
||||
|
||||
Application.RequestStop ();
|
||||
|
||||
// Really long text
|
||||
MessageBox.Query (string.Empty, new string ('f', 500), defaultButton: 0, wrapMessage: true, "btn");
|
||||
MessageBox.Query (string.Empty, new string ('f', 500), defaultButton: 0, wrapMessage: false, "btn");
|
||||
} else if (iterations == 2) {
|
||||
Application.Refresh ();
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (@$"
|
||||
╔┌────────────────┐╗
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ {btn} │║
|
||||
╚└────────────────┘╝", output);
|
||||
╔══════════════════╗
|
||||
║ ║
|
||||
────────────────────
|
||||
ffffffffffffffffffff
|
||||
|
||||
⟦► btn ◄⟧
|
||||
────────────────────
|
||||
║ ║
|
||||
║ ║
|
||||
╚══════════════════╝", output);
|
||||
|
||||
Application.RequestStop ();
|
||||
}
|
||||
@@ -505,14 +505,14 @@ namespace Terminal.Gui.DialogTests {
|
||||
Application.Refresh ();
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (@$"
|
||||
╔══════════════════╗
|
||||
║ ┌──────────────┐ ║
|
||||
║ │ff ff ff ff ff│ ║
|
||||
║ │ff ff ff ff ff│ ║
|
||||
║ │ff ff ff ff ff│ ║
|
||||
║ │ ff ff │ ║
|
||||
║ │ │ ║
|
||||
║ │ {btn} │ ║
|
||||
║ └──────────────┘ ║
|
||||
║ ║
|
||||
────────────────────
|
||||
ff ff ff ff ff ff ff
|
||||
|
||||
⟦► btn ◄⟧
|
||||
────────────────────
|
||||
║ ║
|
||||
║ ║
|
||||
╚══════════════════╝", output);
|
||||
Application.RequestStop ();
|
||||
|
||||
@@ -521,16 +521,16 @@ namespace Terminal.Gui.DialogTests {
|
||||
} else if (iterations == 2) {
|
||||
Application.Refresh ();
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (@$"
|
||||
╔┌────────────────┐╗
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ffffffffffffffff│║
|
||||
║│ {btn} │║
|
||||
╚└────────────────┘╝", output);
|
||||
╔══════════════════╗
|
||||
║ ║
|
||||
────────────────────
|
||||
ffffffffffffffffffff
|
||||
|
||||
⟦► btn ◄⟧
|
||||
────────────────────
|
||||
║ ║
|
||||
║ ║
|
||||
╚══════════════════╝", output);
|
||||
Application.RequestStop ();
|
||||
}
|
||||
};
|
||||
@@ -539,15 +539,15 @@ namespace Terminal.Gui.DialogTests {
|
||||
}
|
||||
|
||||
[Theory, AutoInitShutdown]
|
||||
[InlineData (" ", true)]
|
||||
[InlineData (" ", false)]
|
||||
[InlineData ("", true)]
|
||||
[InlineData ("", false)]
|
||||
[InlineData ("\n", true)]
|
||||
[InlineData ("\n", false)]
|
||||
[InlineData (" \n", true)]
|
||||
[InlineData (" \n", false)]
|
||||
public void Message_Empty_Or_A_NewLline_WrapMessagge_True_Or_False (string message, bool wrapMessage)
|
||||
[InlineData (" ", true, 1)]
|
||||
[InlineData (" ", false, 1)]
|
||||
[InlineData ("", true, 1)]
|
||||
[InlineData ("", false, 1)]
|
||||
[InlineData ("\n", true, 1)]
|
||||
[InlineData ("\n", false, 1)]
|
||||
[InlineData (" \n", true, 1)]
|
||||
[InlineData (" \n", false, 2)]
|
||||
public void Message_Empty_Or_A_NewLline_WrapMessagge_True_Or_False (string message, bool wrapMessage, int linesLength)
|
||||
{
|
||||
var iterations = -1;
|
||||
Application.Begin (Application.Top);
|
||||
@@ -561,12 +561,22 @@ namespace Terminal.Gui.DialogTests {
|
||||
Application.RequestStop ();
|
||||
} else if (iterations == 1) {
|
||||
Application.Refresh ();
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (@$"
|
||||
if (linesLength == 1) {
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (@$"
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ │
|
||||
│ {CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} ok {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket} │
|
||||
└──────────────────────────────────────────────┘", output);
|
||||
} else {
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (@$"
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ {CM.Glyphs.LeftBracket}{CM.Glyphs.LeftDefaultIndicator} ok {CM.Glyphs.RightDefaultIndicator}{CM.Glyphs.RightBracket} │
|
||||
└──────────────────────────────────────────────┘", output);
|
||||
}
|
||||
Application.RequestStop ();
|
||||
}
|
||||
};
|
||||
@@ -574,4 +584,4 @@ namespace Terminal.Gui.DialogTests {
|
||||
Application.Run ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Terminal.Gui;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
@@ -65,13 +64,130 @@ namespace Terminal.Gui.TextTests {
|
||||
Assert.NotEmpty (tf.Lines);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestSize_TextChange ()
|
||||
[Theory]
|
||||
[InlineData (TextDirection.LeftRight_TopBottom, false)]
|
||||
[InlineData (TextDirection.LeftRight_TopBottom, true)]
|
||||
[InlineData (TextDirection.TopBottom_LeftRight, 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 (1, tf.Size.Height);
|
||||
tf.Text = "你你";
|
||||
Assert.Equal (4, tf.Size.Width);
|
||||
if (autoSize) {
|
||||
if (textDirection == TextDirection.LeftRight_TopBottom) {
|
||||
Assert.Equal (4, tf.Size.Width);
|
||||
Assert.Equal (1, tf.Size.Height);
|
||||
} else {
|
||||
Assert.Equal (2, tf.Size.Width);
|
||||
Assert.Equal (2, tf.Size.Height);
|
||||
}
|
||||
} else {
|
||||
Assert.Equal (2, tf.Size.Width);
|
||||
Assert.Equal (1, tf.Size.Height);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (TextDirection.LeftRight_TopBottom)]
|
||||
[InlineData (TextDirection.TopBottom_LeftRight)]
|
||||
public void TestSize_AutoSizeChange (TextDirection textDirection)
|
||||
{
|
||||
var tf = new TextFormatter () { Direction = textDirection, Text = "你你" };
|
||||
if (textDirection == TextDirection.LeftRight_TopBottom) {
|
||||
Assert.Equal (4, tf.Size.Width);
|
||||
Assert.Equal (1, tf.Size.Height);
|
||||
} else {
|
||||
Assert.Equal (2, tf.Size.Width);
|
||||
Assert.Equal (2, tf.Size.Height);
|
||||
}
|
||||
Assert.False (tf.AutoSize);
|
||||
|
||||
tf.Size = new Size (1, 1);
|
||||
Assert.Equal (1, tf.Size.Width);
|
||||
Assert.Equal (1, tf.Size.Height);
|
||||
tf.AutoSize = true;
|
||||
if (textDirection == TextDirection.LeftRight_TopBottom) {
|
||||
Assert.Equal (4, tf.Size.Width);
|
||||
Assert.Equal (1, tf.Size.Height);
|
||||
} else {
|
||||
Assert.Equal (2, tf.Size.Width);
|
||||
Assert.Equal (2, tf.Size.Height);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (TextDirection.LeftRight_TopBottom, false)]
|
||||
[InlineData (TextDirection.LeftRight_TopBottom, true)]
|
||||
[InlineData (TextDirection.TopBottom_LeftRight, false)]
|
||||
[InlineData (TextDirection.TopBottom_LeftRight, true)]
|
||||
public void TestSize_SizeChange_AutoSize_True_Or_False (TextDirection textDirection, bool autoSize)
|
||||
{
|
||||
var tf = new TextFormatter () { Direction = textDirection, Text = "你你", AutoSize = autoSize };
|
||||
if (textDirection == TextDirection.LeftRight_TopBottom) {
|
||||
Assert.Equal (4, tf.Size.Width);
|
||||
Assert.Equal (1, tf.Size.Height);
|
||||
} else {
|
||||
Assert.Equal (2, tf.Size.Width);
|
||||
Assert.Equal (2, tf.Size.Height);
|
||||
}
|
||||
|
||||
tf.Size = new Size (1, 1);
|
||||
if (autoSize) {
|
||||
if (textDirection == TextDirection.LeftRight_TopBottom) {
|
||||
Assert.Equal (4, tf.Size.Width);
|
||||
Assert.Equal (1, tf.Size.Height);
|
||||
} else {
|
||||
Assert.Equal (2, tf.Size.Width);
|
||||
Assert.Equal (2, tf.Size.Height);
|
||||
}
|
||||
} else {
|
||||
Assert.Equal (1, tf.Size.Width);
|
||||
Assert.Equal (1, tf.Size.Height);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (TextAlignment.Left, false)]
|
||||
[InlineData (TextAlignment.Centered, true)]
|
||||
[InlineData (TextAlignment.Right, false)]
|
||||
[InlineData (TextAlignment.Justified, true)]
|
||||
public void TestSize_SizeChange_AutoSize_True_Or_False_Horizontal (TextAlignment textAlignment, bool autoSize)
|
||||
{
|
||||
var tf = new TextFormatter () { Direction = TextDirection.LeftRight_TopBottom, Text = "你你", Alignment = textAlignment, AutoSize = autoSize };
|
||||
Assert.Equal (4, tf.Size.Width);
|
||||
Assert.Equal (1, tf.Size.Height);
|
||||
|
||||
tf.Size = new Size (1, 1);
|
||||
if (autoSize && textAlignment != TextAlignment.Justified) {
|
||||
Assert.Equal (4, tf.Size.Width);
|
||||
Assert.Equal (1, tf.Size.Height);
|
||||
} else {
|
||||
Assert.Equal (1, tf.Size.Width);
|
||||
Assert.Equal (1, tf.Size.Height);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (VerticalTextAlignment.Top, false)]
|
||||
[InlineData (VerticalTextAlignment.Middle, true)]
|
||||
[InlineData (VerticalTextAlignment.Bottom, false)]
|
||||
[InlineData (VerticalTextAlignment.Justified, true)]
|
||||
public void TestSize_SizeChange_AutoSize_True_Or_False_Vertical (VerticalTextAlignment textAlignment, bool autoSize)
|
||||
{
|
||||
var tf = new TextFormatter () { Direction = TextDirection.TopBottom_LeftRight, Text = "你你", VerticalAlignment = textAlignment, AutoSize = autoSize };
|
||||
Assert.Equal (2, tf.Size.Width);
|
||||
Assert.Equal (2, tf.Size.Height);
|
||||
|
||||
tf.Size = new Size (1, 1);
|
||||
if (autoSize && textAlignment != VerticalTextAlignment.Justified) {
|
||||
Assert.Equal (2, tf.Size.Width);
|
||||
Assert.Equal (2, tf.Size.Height);
|
||||
} else {
|
||||
Assert.Equal (1, tf.Size.Width);
|
||||
Assert.Equal (1, tf.Size.Height);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -361,8 +477,8 @@ namespace Terminal.Gui.TextTests {
|
||||
[InlineData ("A sentence has words.", "A sentence has words.", int.MaxValue)] // should fit
|
||||
[InlineData ("A sentence has words.", "A sentence has words", 20)] // Should not fit
|
||||
[InlineData ("A sentence has words.", "A sentence", 10)] // Should not fit
|
||||
[InlineData ("A\tsentence\thas\twords.", "A\tsentence\thas\twords.", int.MaxValue)]
|
||||
[InlineData ("A\tsentence\thas\twords.", "A\tsentence", 10)]
|
||||
[InlineData ("A\tsentence\thas\twords.", "A sentence has words.", int.MaxValue)]
|
||||
[InlineData ("A\tsentence\thas\twords.", "A sentence", 10)]
|
||||
[InlineData ("line1\nline2\nline3long!", "line1\nline2\nline3long!", int.MaxValue)]
|
||||
[InlineData ("line1\nline2\nline3long!", "line1\nline", 10)]
|
||||
[InlineData (" ~ s gui.cs master ↑10", " ~ s ", 10)] // Unicode
|
||||
@@ -372,10 +488,12 @@ namespace Terminal.Gui.TextTests {
|
||||
public void ClipAndJustify_Valid_Left (string text, string justifiedText, int maxWidth)
|
||||
{
|
||||
var align = TextAlignment.Left;
|
||||
var textDirection = TextDirection.LeftRight_BottomTop;
|
||||
var tabWidth = 1;
|
||||
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align));
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth));
|
||||
var expectedClippedWidth = Math.Min (justifiedText.GetRuneCount (), maxWidth);
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align));
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth));
|
||||
Assert.True (justifiedText.GetRuneCount () <= maxWidth);
|
||||
Assert.True (justifiedText.GetColumns () <= maxWidth);
|
||||
Assert.Equal (expectedClippedWidth, justifiedText.GetRuneCount ());
|
||||
@@ -393,8 +511,8 @@ namespace Terminal.Gui.TextTests {
|
||||
[InlineData ("A sentence has words.", "A sentence has words.", int.MaxValue)] // should fit
|
||||
[InlineData ("A sentence has words.", "A sentence has words", 20)] // Should not fit
|
||||
[InlineData ("A sentence has words.", "A sentence", 10)] // Should not fit
|
||||
[InlineData ("A\tsentence\thas\twords.", "A\tsentence\thas\twords.", int.MaxValue)]
|
||||
[InlineData ("A\tsentence\thas\twords.", "A\tsentence", 10)]
|
||||
[InlineData ("A\tsentence\thas\twords.", "A sentence has words.", int.MaxValue)]
|
||||
[InlineData ("A\tsentence\thas\twords.", "A sentence", 10)]
|
||||
[InlineData ("line1\nline2\nline3long!", "line1\nline2\nline3long!", int.MaxValue)]
|
||||
[InlineData ("line1\nline2\nline3long!", "line1\nline", 10)]
|
||||
[InlineData (" ~ s gui.cs master ↑10", " ~ s ", 10)] // Unicode
|
||||
@@ -404,10 +522,12 @@ namespace Terminal.Gui.TextTests {
|
||||
public void ClipAndJustify_Valid_Right (string text, string justifiedText, int maxWidth)
|
||||
{
|
||||
var align = TextAlignment.Right;
|
||||
var textDirection = TextDirection.LeftRight_BottomTop;
|
||||
var tabWidth = 1;
|
||||
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align));
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth));
|
||||
var expectedClippedWidth = Math.Min (justifiedText.GetRuneCount (), maxWidth);
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align));
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth));
|
||||
Assert.True (justifiedText.GetRuneCount () <= maxWidth);
|
||||
Assert.True (justifiedText.GetColumns () <= maxWidth);
|
||||
Assert.Equal (expectedClippedWidth, justifiedText.GetRuneCount ());
|
||||
@@ -425,8 +545,8 @@ namespace Terminal.Gui.TextTests {
|
||||
[InlineData ("A sentence has words.", "A sentence has words.", int.MaxValue)] // should fit
|
||||
[InlineData ("A sentence has words.", "A sentence has words", 20)] // Should not fit
|
||||
[InlineData ("A sentence has words.", "A sentence", 10)] // Should not fit
|
||||
[InlineData ("A\tsentence\thas\twords.", "A\tsentence\thas\twords.", int.MaxValue)]
|
||||
[InlineData ("A\tsentence\thas\twords.", "A\tsentence", 10)]
|
||||
[InlineData ("A\tsentence\thas\twords.", "A sentence has words.", int.MaxValue)]
|
||||
[InlineData ("A\tsentence\thas\twords.", "A sentence", 10)]
|
||||
[InlineData ("line1\nline2\nline3long!", "line1\nline2\nline3long!", int.MaxValue)]
|
||||
[InlineData ("line1\nline2\nline3long!", "line1\nline", 10)]
|
||||
[InlineData (" ~ s gui.cs master ↑10", " ~ s ", 10)] // Unicode
|
||||
@@ -436,10 +556,12 @@ namespace Terminal.Gui.TextTests {
|
||||
public void ClipAndJustify_Valid_Centered (string text, string justifiedText, int maxWidth)
|
||||
{
|
||||
var align = TextAlignment.Centered;
|
||||
var textDirection = TextDirection.LeftRight_TopBottom;
|
||||
var tabWidth = 1;
|
||||
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align));
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth));
|
||||
var expectedClippedWidth = Math.Min (justifiedText.GetRuneCount (), maxWidth);
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align));
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth));
|
||||
Assert.True (justifiedText.GetRuneCount () <= maxWidth);
|
||||
Assert.True (justifiedText.GetColumns () <= maxWidth);
|
||||
Assert.Equal (expectedClippedWidth, justifiedText.GetRuneCount ());
|
||||
@@ -451,15 +573,16 @@ namespace Terminal.Gui.TextTests {
|
||||
[Theory]
|
||||
[InlineData ("test", "", 0)]
|
||||
[InlineData ("test", "te", 2)]
|
||||
[InlineData ("test", "test", int.MaxValue)]
|
||||
[InlineData ("test", "test", int.MaxValue)] // This doesn't throw because it only create a word with length 1
|
||||
[InlineData ("A sentence has words.", "A sentence has words.", 22)] // should fit
|
||||
[InlineData ("A sentence has words.", "A sentence has words.", 21)] // should fit
|
||||
[InlineData ("A sentence has words.", "A sentence has words.", 500)] // should fit
|
||||
[InlineData ("A sentence has words.", "A sentence has words", 20)] // Should not fit
|
||||
[InlineData ("A sentence has words.", "A sentence", 10)] // Should not fit
|
||||
[InlineData ("A\tsentence\thas\twords.", "A\tsentence\thas\twords.", int.MaxValue)]
|
||||
[InlineData ("A\tsentence\thas\twords.", "A\tsentence", 10)]
|
||||
[InlineData ("line1\nline2\nline3long!", "line1\nline2\nline3long!", int.MaxValue)]
|
||||
// Now throw System.OutOfMemoryException. See https://stackoverflow.com/questions/20672920/maxcapacity-of-stringbuilder
|
||||
//[InlineData ("A\tsentence\thas\twords.", "A sentence has words.", int.MaxValue)]
|
||||
[InlineData ("A\tsentence\thas\twords.", "A sentence", 10)]
|
||||
[InlineData ("line1\nline2\nline3long!", "line1\nline2\nline3long!", int.MaxValue)] // This doesn't throw because it only create a line with length 1
|
||||
[InlineData ("line1\nline2\nline3long!", "line1\nline", 10)]
|
||||
[InlineData (" ~ s gui.cs master ↑10", " ~ s ", 10)] // Unicode
|
||||
[InlineData ("Ð ÑÐ", "Ð ÑÐ", 5)] // should fit
|
||||
@@ -468,10 +591,12 @@ namespace Terminal.Gui.TextTests {
|
||||
public void ClipAndJustify_Valid_Justified (string text, string justifiedText, int maxWidth)
|
||||
{
|
||||
var align = TextAlignment.Justified;
|
||||
var textDirection = TextDirection.LeftRight_TopBottom;
|
||||
var tabWidth = 1;
|
||||
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align));
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth));
|
||||
var expectedClippedWidth = Math.Min (justifiedText.GetRuneCount (), maxWidth);
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align));
|
||||
Assert.Equal (justifiedText, TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth));
|
||||
Assert.True (justifiedText.GetRuneCount () <= maxWidth);
|
||||
Assert.True (justifiedText.GetColumns () <= maxWidth);
|
||||
Assert.Equal (expectedClippedWidth, justifiedText.GetRuneCount ());
|
||||
@@ -620,21 +745,24 @@ namespace Terminal.Gui.TextTests {
|
||||
|
||||
[Theory]
|
||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 51, 0, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" })]
|
||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 50, -1, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัา", "ำ" })]
|
||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 50, -1, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" })]
|
||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 46, -5, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮ", "ฯะัาำ" })]
|
||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 26, -25, new string [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" })]
|
||||
[InlineData ("กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", 17, -34, 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)
|
||||
{
|
||||
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);
|
||||
var expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
|
||||
wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
|
||||
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.Equal (resultLines, wrappedLines);
|
||||
}
|
||||
@@ -793,7 +921,7 @@ namespace Terminal.Gui.TextTests {
|
||||
[InlineData ("文に は言葉 があり ます。", 14, 0, new string [] { "文に は言葉", "があり ます。" })]
|
||||
[InlineData ("文に は言葉 があり ます。", 3, -11, new string [] { "文", "に", "は", "言", "葉", "が", "あ", "り", "ま", "す", "。" })]
|
||||
[InlineData ("文に は言葉 があり ます。", 2, -12, new string [] { "文", "に", "は", "言", "葉", "が", "あ", "り", "ま", "す", "。" })]
|
||||
[InlineData ("文に は言葉 があり ます。", 1, -13, new string [] { })]
|
||||
[InlineData ("文に は言葉 があり ます。", 1, -13, new string [] { " ", " ", " " })] // Just Spaces; should result in a single space for each line
|
||||
public void WordWrap_PreserveTrailingSpaces_False_Wide_Runes (string text, int maxWidth, int widthOffset, IEnumerable<string> resultLines)
|
||||
{
|
||||
List<string> wrappedLines;
|
||||
@@ -1071,7 +1199,7 @@ namespace Terminal.Gui.TextTests {
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 29, -1, TextAlignment.Left, false, 1, false, 1)]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 30, 0, TextAlignment.Left, false, 1, false)]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 31, 1, TextAlignment.Left, false, 1, false)]
|
||||
public void Reformat_NoWordrap_NewLines (string text, int maxWidth, int widthOffset, TextAlignment textAlignment, bool wrap, int linesCount, bool stringEmpty, int clipWidthOffset = 0)
|
||||
public void Reformat_NoWordrap_NewLines_MultiLine_False (string text, int maxWidth, int widthOffset, TextAlignment textAlignment, bool wrap, int linesCount, bool stringEmpty, int clipWidthOffset = 0)
|
||||
{
|
||||
Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
|
||||
var expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth) + clipWidthOffset;
|
||||
@@ -1092,6 +1220,64 @@ namespace Terminal.Gui.TextTests {
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData ("A sentence has words.\nLine 2.", 0, -29, TextAlignment.Left, false, 1, true, new string [] { "" })]
|
||||
[InlineData ("A sentence has words.\nLine 2.", 1, -28, TextAlignment.Left, false, 2, false, new string [] { "A", "L" })]
|
||||
[InlineData ("A sentence has words.\nLine 2.", 5, -24, TextAlignment.Left, false, 2, false, new string [] { "A sen", "Line " })]
|
||||
[InlineData ("A sentence has words.\nLine 2.", 28, -1, TextAlignment.Left, false, 2, false, new string [] { "A sentence has words.", "Line 2." })]
|
||||
//// no clip
|
||||
[InlineData ("A sentence has words.\nLine 2.", 29, 0, TextAlignment.Left, false, 2, false, new string [] { "A sentence has words.", "Line 2." })]
|
||||
[InlineData ("A sentence has words.\nLine 2.", 30, 1, TextAlignment.Left, false, 2, false, new string [] { "A sentence has words.", "Line 2." })]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 0, -30, TextAlignment.Left, false, 1, true, new string [] { "" })]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 1, -29, TextAlignment.Left, false, 2, false, new string [] { "A", "L" })]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 5, -25, TextAlignment.Left, false, 2, false, new string [] { "A sen", "Line " })]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 29, -1, TextAlignment.Left, false, 2, false, new string [] { "A sentence has words.", "Line 2." })]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 30, 0, TextAlignment.Left, false, 2, false, new string [] { "A sentence has words.", "Line 2." })]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 31, 1, TextAlignment.Left, false, 2, false, new string [] { "A sentence has words.", "Line 2." })]
|
||||
public void Reformat_NoWordrap_NewLines_MultiLine_True (string text, int maxWidth, int widthOffset, TextAlignment textAlignment, bool wrap, int linesCount, bool stringEmpty, IEnumerable<string> resultLines)
|
||||
{
|
||||
Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
|
||||
var list = TextFormatter.Format (text, maxWidth, textAlignment, wrap, false, 0, TextDirection.LeftRight_TopBottom, true);
|
||||
Assert.NotEmpty (list);
|
||||
Assert.True (list.Count == linesCount);
|
||||
if (stringEmpty) {
|
||||
Assert.Equal (string.Empty, list [0]);
|
||||
} else {
|
||||
Assert.NotEqual (string.Empty, list [0]);
|
||||
}
|
||||
|
||||
Assert.Equal (list, resultLines);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData ("A sentence has words.\nLine 2.", 0, -29, TextAlignment.Left, false, 1, true, new string [] { "" })]
|
||||
[InlineData ("A sentence has words.\nLine 2.", 1, -28, TextAlignment.Left, false, 2, false, new string [] { "A", "L" })]
|
||||
[InlineData ("A sentence has words.\nLine 2.", 5, -24, TextAlignment.Left, false, 2, false, new string [] { "A sen", "Line " })]
|
||||
[InlineData ("A sentence has words.\nLine 2.", 28, -1, TextAlignment.Left, false, 2, false, new string [] { "A sentence has words.", "Line 2." })]
|
||||
//// no clip
|
||||
[InlineData ("A sentence has words.\nLine 2.", 29, 0, TextAlignment.Left, false, 2, false, new string [] { "A sentence has words.", "Line 2." })]
|
||||
[InlineData ("A sentence has words.\nLine 2.", 30, 1, TextAlignment.Left, false, 2, false, new string [] { "A sentence has words.", "Line 2." })]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 0, -30, TextAlignment.Left, false, 1, true, new string [] { "" })]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 1, -29, TextAlignment.Left, false, 2, false, new string [] { "A", "L" })]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 5, -25, TextAlignment.Left, false, 2, false, new string [] { "A sen", "Line " })]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 29, -1, TextAlignment.Left, false, 2, false, new string [] { "A sentence has words.", "Line 2." })]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 30, 0, TextAlignment.Left, false, 2, false, new string [] { "A sentence has words.", "Line 2." })]
|
||||
[InlineData ("A sentence has words.\r\nLine 2.", 31, 1, TextAlignment.Left, false, 2, false, new string [] { "A sentence has words.", "Line 2." })]
|
||||
public void Reformat_NoWordrap_NewLines_MultiLine_True_Vertical (string text, int maxWidth, int widthOffset, TextAlignment textAlignment, bool wrap, int linesCount, bool stringEmpty, IEnumerable<string> resultLines)
|
||||
{
|
||||
Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
|
||||
var list = TextFormatter.Format (text, maxWidth, textAlignment, wrap, false, 0, TextDirection.TopBottom_LeftRight, true);
|
||||
Assert.NotEmpty (list);
|
||||
Assert.True (list.Count == linesCount);
|
||||
if (stringEmpty) {
|
||||
Assert.Equal (string.Empty, list [0]);
|
||||
} else {
|
||||
Assert.NotEqual (string.Empty, list [0]);
|
||||
}
|
||||
|
||||
Assert.Equal (list, resultLines);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// Even # of spaces
|
||||
// 0123456789
|
||||
@@ -1418,5 +1604,149 @@ namespace Terminal.Gui.TextTests {
|
||||
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]
|
||||
[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 driver = new FakeDriver ();
|
||||
driver.Init ();
|
||||
|
||||
var text = "Les Mise\u0328\u0301rables";
|
||||
|
||||
var tf = new TextFormatter ();
|
||||
tf.Direction = textDirection;
|
||||
tf.Text = text;
|
||||
|
||||
Assert.True (tf.WordWrap);
|
||||
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), default, true, driver);
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver);
|
||||
|
||||
driver.End ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[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 driver = new FakeDriver ();
|
||||
driver.Init ();
|
||||
|
||||
var text = "This is a \tTab";
|
||||
var tf = new TextFormatter ();
|
||||
tf.Direction = textDirection;
|
||||
tf.TabWidth = tabWidth;
|
||||
tf.Text = text;
|
||||
|
||||
Assert.True (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), default, true, driver);
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver);
|
||||
|
||||
driver.End ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[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 driver = new FakeDriver ();
|
||||
driver.Init ();
|
||||
|
||||
var text = "This is a \tTab";
|
||||
var tf = new TextFormatter ();
|
||||
tf.Direction = textDirection;
|
||||
tf.TabWidth = tabWidth;
|
||||
tf.PreserveTrailingSpaces = true;
|
||||
tf.Text = text;
|
||||
|
||||
Assert.True (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), default, true, driver);
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver);
|
||||
|
||||
driver.End ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[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 driver = new FakeDriver ();
|
||||
driver.Init ();
|
||||
|
||||
var text = "This is a \tTab";
|
||||
var tf = new TextFormatter ();
|
||||
tf.Direction = textDirection;
|
||||
tf.TabWidth = tabWidth;
|
||||
tf.WordWrap = true;
|
||||
tf.Text = text;
|
||||
|
||||
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), default, true, driver);
|
||||
TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver);
|
||||
|
||||
driver.End ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,9 +357,9 @@ namespace Terminal.Gui.ViewTests {
|
||||
win.Add (label);
|
||||
Application.Top.Add (win);
|
||||
|
||||
// Text is empty so height=0
|
||||
// Text is empty but height=1 by default, see Label view
|
||||
Assert.False (label.AutoSize);
|
||||
Assert.Equal ("(0,0,0,0)", label.Bounds.ToString ());
|
||||
Assert.Equal ("(0,0,0,1)", label.Bounds.ToString ());
|
||||
|
||||
label.Text = "New text\nNew line";
|
||||
Application.Top.LayoutSubviews ();
|
||||
@@ -385,10 +385,11 @@ namespace Terminal.Gui.ViewTests {
|
||||
|
||||
Assert.True (label.IsAdded);
|
||||
|
||||
// Text is empty so height=0
|
||||
// Text is empty but height=1 by default, see Label view
|
||||
Assert.True (label.AutoSize);
|
||||
// BUGBUG: LayoutSubviews has not been called, so this test is not really valid (pos/dim are indeterminate, not 0)
|
||||
Assert.Equal ("(0,0,0,0)", label.Bounds.ToString ());
|
||||
// Not really a bug because View call OnResizeNeeded method on the SetInitialProperties method
|
||||
Assert.Equal ("(0,0,0,1)", label.Bounds.ToString ());
|
||||
|
||||
label.Text = "First line\nSecond line";
|
||||
Application.Top.LayoutSubviews ();
|
||||
@@ -420,9 +421,9 @@ namespace Terminal.Gui.ViewTests {
|
||||
win.Add (label);
|
||||
Application.Top.Add (win);
|
||||
|
||||
// Text is empty so height=0
|
||||
// Text is empty but height=1 by default, see Label view
|
||||
Assert.True (label.AutoSize);
|
||||
Assert.Equal ("(0,0,0,0)", label.Bounds.ToString ());
|
||||
Assert.Equal ("(0,0,0,1)", label.Bounds.ToString ());
|
||||
|
||||
var rs = Application.Begin (Application.Top);
|
||||
|
||||
|
||||
@@ -446,8 +446,10 @@ Test
|
||||
Assert.Equal (new Rect (0, 0, width + 2, height + 2), pos);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
public void Label_WordWrap_PreserveTrailingSpaces_Horizontal_With_Wide_Runes ()
|
||||
[Theory, AutoInitShutdown]
|
||||
[InlineData (false)]
|
||||
[InlineData (true)]
|
||||
public void Label_WordWrap_PreserveTrailingSpaces_Horizontal_With_Wide_Runes (bool autoSize)
|
||||
{
|
||||
var text = "文に は言葉 があり ます。";
|
||||
var width = 6;
|
||||
@@ -455,17 +457,29 @@ Test
|
||||
var wrappedLines = TextFormatter.WordWrapText (text, width, true);
|
||||
var breakLines = "";
|
||||
foreach (var line in wrappedLines) breakLines += $"{line}{Environment.NewLine}";
|
||||
var label = new Label (breakLines) { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
var label = new Label (breakLines) { Width = Dim.Fill (), Height = Dim.Fill (), AutoSize = autoSize };
|
||||
var frame = new FrameView () { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
frame.Add (label);
|
||||
Application.Top.Add (frame);
|
||||
Application.Begin (Application.Top);
|
||||
|
||||
Assert.True (label.AutoSize == autoSize);
|
||||
Assert.Equal (new Rect (0, 0, 78, 23), label.Frame);
|
||||
if (autoSize) {
|
||||
// The size of the wrappedLines [1]
|
||||
Assert.Equal (new Size (width, height - 2), label.TextFormatter.Size);
|
||||
} else {
|
||||
Assert.Equal (new Size (78, 23), label.TextFormatter.Size);
|
||||
}
|
||||
((FakeDriver)Application.Driver).SetBufferSize (width + 2, height + 2);
|
||||
|
||||
Assert.True (label.AutoSize);
|
||||
Assert.Equal (new Rect (0, 0, width, height), label.Frame);
|
||||
Assert.Equal (new Size (width, height), label.TextFormatter.Size);
|
||||
if (autoSize) {
|
||||
Assert.Equal (new Size (width, height - 2), label.TextFormatter.Size);
|
||||
} else {
|
||||
Assert.Equal (new Size (width, height), label.TextFormatter.Size);
|
||||
}
|
||||
Assert.Equal (new Rect (0, 0, width + 2, height + 2), frame.Frame);
|
||||
|
||||
var expected = @"
|
||||
@@ -670,15 +684,17 @@ e
|
||||
Assert.Equal (new Rect (0, 0, 2, 7), pos);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
public void Label_Draw_Horizontal_Simple_TextAlignments ()
|
||||
[Theory, AutoInitShutdown]
|
||||
[InlineData (true)]
|
||||
[InlineData (false)]
|
||||
public void Label_Draw_Horizontal_Simple_TextAlignments (bool autoSize)
|
||||
{
|
||||
var text = "Hello World";
|
||||
var width = 20;
|
||||
var lblLeft = new Label (text) { Width = width };
|
||||
var lblCenter = new Label (text) { Y = 1, Width = width, TextAlignment = TextAlignment.Centered };
|
||||
var lblRight = new Label (text) { Y = 2, Width = width, TextAlignment = TextAlignment.Right };
|
||||
var lblJust = new Label (text) { Y = 3, Width = width, TextAlignment = TextAlignment.Justified };
|
||||
var lblLeft = new Label (text) { Width = width, AutoSize = autoSize };
|
||||
var lblCenter = new Label (text) { Y = 1, Width = width, TextAlignment = TextAlignment.Centered, AutoSize = autoSize };
|
||||
var lblRight = new Label (text) { Y = 2, Width = width, TextAlignment = TextAlignment.Right, AutoSize = autoSize };
|
||||
var lblJust = new Label (text) { Y = 3, Width = width, TextAlignment = TextAlignment.Justified, AutoSize = autoSize };
|
||||
var frame = new FrameView () { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
frame.Add (lblLeft, lblCenter, lblRight, lblJust);
|
||||
@@ -686,14 +702,28 @@ e
|
||||
Application.Begin (Application.Top);
|
||||
((FakeDriver)Application.Driver).SetBufferSize (width + 2, 6);
|
||||
|
||||
Assert.True (lblLeft.AutoSize);
|
||||
Assert.True (lblCenter.AutoSize);
|
||||
Assert.True (lblRight.AutoSize);
|
||||
Assert.True (lblJust.AutoSize);
|
||||
Assert.True (lblLeft.AutoSize == autoSize);
|
||||
Assert.True (lblCenter.AutoSize == autoSize);
|
||||
Assert.True (lblRight.AutoSize == autoSize);
|
||||
Assert.True (lblJust.AutoSize == autoSize);
|
||||
Assert.True (lblLeft.TextFormatter.AutoSize == autoSize);
|
||||
Assert.True (lblCenter.TextFormatter.AutoSize == autoSize);
|
||||
Assert.True (lblRight.TextFormatter.AutoSize == autoSize);
|
||||
Assert.True (lblJust.TextFormatter.AutoSize == autoSize);
|
||||
Assert.Equal (new Rect (0, 0, width, 1), lblLeft.Frame);
|
||||
Assert.Equal (new Rect (0, 1, width, 1), lblCenter.Frame);
|
||||
Assert.Equal (new Rect (0, 2, width, 1), lblRight.Frame);
|
||||
Assert.Equal (new Rect (0, 3, width, 1), lblJust.Frame);
|
||||
if (autoSize) {
|
||||
Assert.Equal (new Size (11, 1), lblLeft.TextFormatter.Size);
|
||||
Assert.Equal (new Size (11, 1), lblCenter.TextFormatter.Size);
|
||||
Assert.Equal (new Size (11, 1), lblRight.TextFormatter.Size);
|
||||
} else {
|
||||
Assert.Equal (new Size (width, 1), lblLeft.TextFormatter.Size);
|
||||
Assert.Equal (new Size (width, 1), lblCenter.TextFormatter.Size);
|
||||
Assert.Equal (new Size (width, 1), lblRight.TextFormatter.Size);
|
||||
}
|
||||
Assert.Equal (new Size (width, 1), lblJust.TextFormatter.Size);
|
||||
Assert.Equal (new Rect (0, 0, width + 2, 6), frame.Frame);
|
||||
|
||||
var expected = @"
|
||||
@@ -709,15 +739,17 @@ e
|
||||
Assert.Equal (new Rect (0, 0, width + 2, 6), pos);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
public void Label_Draw_Vertical_Simple_TextAlignments ()
|
||||
[Theory, AutoInitShutdown]
|
||||
[InlineData (true)]
|
||||
[InlineData (false)]
|
||||
public void Label_Draw_Vertical_Simple_TextAlignments (bool autoSize)
|
||||
{
|
||||
var text = "Hello World";
|
||||
var height = 20;
|
||||
var lblLeft = new Label (text, direction: TextDirection.TopBottom_LeftRight) { Height = height };
|
||||
var lblCenter = new Label (text, direction: TextDirection.TopBottom_LeftRight) { X = 2, Height = height, VerticalTextAlignment = VerticalTextAlignment.Middle };
|
||||
var lblRight = new Label (text, direction: TextDirection.TopBottom_LeftRight) { X = 4, Height = height, VerticalTextAlignment = VerticalTextAlignment.Bottom };
|
||||
var lblJust = new Label (text, direction: TextDirection.TopBottom_LeftRight) { X = 6, Height = height, VerticalTextAlignment = VerticalTextAlignment.Justified };
|
||||
var lblLeft = new Label (text, direction: TextDirection.TopBottom_LeftRight, autoSize) { Height = height };
|
||||
var lblCenter = new Label (text, direction: TextDirection.TopBottom_LeftRight, autoSize) { X = 2, Height = height, VerticalTextAlignment = VerticalTextAlignment.Middle };
|
||||
var lblRight = new Label (text, direction: TextDirection.TopBottom_LeftRight, autoSize) { X = 4, Height = height, VerticalTextAlignment = VerticalTextAlignment.Bottom };
|
||||
var lblJust = new Label (text, direction: TextDirection.TopBottom_LeftRight, autoSize) { X = 6, Height = height, VerticalTextAlignment = VerticalTextAlignment.Justified };
|
||||
var frame = new FrameView () { Width = Dim.Fill (), Height = Dim.Fill () };
|
||||
|
||||
frame.Add (lblLeft, lblCenter, lblRight, lblJust);
|
||||
@@ -725,14 +757,28 @@ e
|
||||
Application.Begin (Application.Top);
|
||||
((FakeDriver)Application.Driver).SetBufferSize (9, height + 2);
|
||||
|
||||
Assert.True (lblLeft.AutoSize);
|
||||
Assert.True (lblCenter.AutoSize);
|
||||
Assert.True (lblRight.AutoSize);
|
||||
Assert.True (lblJust.AutoSize);
|
||||
Assert.True (lblLeft.AutoSize == autoSize);
|
||||
Assert.True (lblCenter.AutoSize == autoSize);
|
||||
Assert.True (lblRight.AutoSize == autoSize);
|
||||
Assert.True (lblJust.AutoSize == autoSize);
|
||||
Assert.True (lblLeft.TextFormatter.AutoSize == autoSize);
|
||||
Assert.True (lblCenter.TextFormatter.AutoSize == autoSize);
|
||||
Assert.True (lblRight.TextFormatter.AutoSize == autoSize);
|
||||
Assert.True (lblJust.TextFormatter.AutoSize == autoSize);
|
||||
Assert.Equal (new Rect (0, 0, 1, height), lblLeft.Frame);
|
||||
Assert.Equal (new Rect (2, 0, 1, height), lblCenter.Frame);
|
||||
Assert.Equal (new Rect (4, 0, 1, height), lblRight.Frame);
|
||||
Assert.Equal (new Rect (6, 0, 1, height), lblJust.Frame);
|
||||
if (autoSize) {
|
||||
Assert.Equal (new Size (1, 11), lblLeft.TextFormatter.Size);
|
||||
Assert.Equal (new Size (1, 11), lblCenter.TextFormatter.Size);
|
||||
Assert.Equal (new Size (1, 11), lblRight.TextFormatter.Size);
|
||||
} else {
|
||||
Assert.Equal (new Size (1, height), lblLeft.TextFormatter.Size);
|
||||
Assert.Equal (new Size (1, height), lblCenter.TextFormatter.Size);
|
||||
Assert.Equal (new Size (1, height), lblRight.TextFormatter.Size);
|
||||
}
|
||||
Assert.Equal (new Size (1, height), lblJust.TextFormatter.Size);
|
||||
Assert.Equal (new Rect (0, 0, 9, height + 2), frame.Frame);
|
||||
|
||||
var expected = @"
|
||||
|
||||
Reference in New Issue
Block a user