From 3c6e945d1be1a4c47e2cd3c7749840bb0cffe5ba Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 8 Nov 2025 01:42:54 +0000 Subject: [PATCH 01/32] Fixes #4382. StringExtensions.GetColumns method should only return the total text width and not the sum of all runes width --- Terminal.Gui/Text/StringExtensions.cs | 25 ++++++++++++++++++- .../UnitTestsParallelizable/Text/RuneTests.cs | 11 +++++--- .../Text/StringTests.cs | 17 +++++++++---- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/Terminal.Gui/Text/StringExtensions.cs b/Terminal.Gui/Text/StringExtensions.cs index a38bb57993..129164a991 100644 --- a/Terminal.Gui/Text/StringExtensions.cs +++ b/Terminal.Gui/Text/StringExtensions.cs @@ -1,5 +1,6 @@ #nullable enable using System.Buffers; +using System.Globalization; namespace Terminal.Gui.Text; @@ -55,7 +56,29 @@ public static (Rune Rune, int Size) DecodeRune (this string str, int start = 0, /// This is a Terminal.Gui extension method to to support TUI text manipulation. /// The string to measure. /// - public static int GetColumns (this string str) { return str is null ? 0 : str.EnumerateRunes ().Sum (r => Math.Max (r.GetColumns (), 0)); } + public static int GetColumns (this string str) + { + if (string.IsNullOrEmpty (str)) + { + return 0; + } + + var total = 0; + TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator (str); + + while (enumerator.MoveNext ()) + { + string element = enumerator.GetTextElement (); + + // Sum all runes' display widths within this grapheme cluster + int width = element + .EnumerateRunes () + .Max (r => Math.Max (r.GetColumns (), 0)); + total += width; + } + + return total; + } /// Gets the number of runes in the string. /// This is a Terminal.Gui extension method to to support TUI text manipulation. diff --git a/Tests/UnitTestsParallelizable/Text/RuneTests.cs b/Tests/UnitTestsParallelizable/Text/RuneTests.cs index d37a940168..34214d6ef8 100644 --- a/Tests/UnitTestsParallelizable/Text/RuneTests.cs +++ b/Tests/UnitTestsParallelizable/Text/RuneTests.cs @@ -7,7 +7,7 @@ namespace UnitTests_Parallelizable.TextTests; public class RuneTests { [Fact] - public void Cast_To_Char_Durrogate_Pair_Return_UTF16 () + public void Cast_To_Char_Surrogate_Pair_Return_UTF16 () { Assert.NotEqual ("𝔹", $"{new Rune (unchecked ((char)0x1d539))}"); Assert.Equal ("픹", $"{new Rune (unchecked ((char)0x1d539))}"); @@ -65,8 +65,11 @@ public void GetColumns_GetRuneCount () PrintTextElementCount ("\u0061\u0301", "á", 1, 2, 2, 1); PrintTextElementCount ("\u0061\u0301", "á", 1, 2, 2, 1); PrintTextElementCount ("\u0065\u0301", "é", 1, 2, 2, 1); - PrintTextElementCount ("\U0001f469\U0001f3fd\u200d\U0001f692", "👩🏽‍🚒", 6, 4, 7, 1); + PrintTextElementCount ("\U0001f469\U0001f3fd\u200d\U0001f692", "👩🏽‍🚒", 2, 4, 7, 1); PrintTextElementCount ("\ud801\udccf", "𐓏", 1, 1, 2, 1); + PrintTextElementCount ("\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", "👨‍👩‍👧‍👦", 2, 7, 11, 1); + PrintTextElementCount ("\U0001f469\u200d\U0001f692", "👩‍🚒", 2, 3, 5, 1); + PrintTextElementCount ("\u0068\u0069", "hi", 2, 2, 2, 2); } [Theory] @@ -84,8 +87,8 @@ public void GetColumns_GetRuneCount () 2, 1 )] // the letters 법 join to form the Korean word for "rice:" U+BC95 법 (read from top left to bottom right) - [InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467", "👨‍👩‍👧", 8, 6, 8)] // Man, Woman and Girl emoji. - [InlineData ("\u0915\u093f", "कि", 2, 2, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I + [InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467", "👨‍👩‍👧", 8, 2, 8)] // Man, Woman and Girl emoji. + [InlineData ("\u0915\u093f", "कि", 2, 1, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I [InlineData ( "\u0e4d\u0e32", "ํา", diff --git a/Tests/UnitTestsParallelizable/Text/StringTests.cs b/Tests/UnitTestsParallelizable/Text/StringTests.cs index c2dc802245..1c77e75644 100644 --- a/Tests/UnitTestsParallelizable/Text/StringTests.cs +++ b/Tests/UnitTestsParallelizable/Text/StringTests.cs @@ -33,11 +33,11 @@ public void TestGetColumns_Empty () [InlineData ("🙂", 2)] [InlineData ("a🙂", 3)] [InlineData ("🙂a", 3)] - [InlineData ("👨‍👩‍👦‍👦", 8)] - [InlineData ("👨‍👩‍👦‍👦🙂", 10)] - [InlineData ("👨‍👩‍👦‍👦🙂a", 11)] - [InlineData ("👨‍👩‍👦‍👦a🙂", 11)] - [InlineData ("👨‍👩‍👦‍👦👨‍👩‍👦‍👦", 16)] + [InlineData ("👨‍👩‍👦‍👦", 2)] + [InlineData ("👨‍👩‍👦‍👦🙂", 4)] + [InlineData ("👨‍👩‍👦‍👦🙂a", 5)] + [InlineData ("👨‍👩‍👦‍👦a🙂", 5)] + [InlineData ("👨‍👩‍👦‍👦👨‍👩‍👦‍👦", 4)] [InlineData ("山", 2)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71 [InlineData ("山🙂", 4)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71 //[InlineData ("\ufe20\ufe21", 2)] // Combining Ligature Left Half ︠ - U+fe20 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml @@ -57,4 +57,11 @@ public void TestGetColumns_SingleRune () var str = "a"; Assert.Equal (1, str.GetColumns ()); } + + [Fact] + public void TestGetColumns_Zero_Width () + { + var str = "\u200D"; + Assert.Equal (0, str.GetColumns ()); + } } From 3b3671488ad48e2d0a307508ad04661f447b8faf Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 8 Nov 2025 02:13:26 +0000 Subject: [PATCH 02/32] Trying to fix unit test error --- Tests/UnitTests/Views/HexViewTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/UnitTests/Views/HexViewTests.cs b/Tests/UnitTests/Views/HexViewTests.cs index aafd21aeb7..83d661f6fa 100644 --- a/Tests/UnitTests/Views/HexViewTests.cs +++ b/Tests/UnitTests/Views/HexViewTests.cs @@ -72,7 +72,6 @@ public void ReadOnly_Prevents_Edits () Application.Top.Dispose (); Application.ResetState (true); - } [Fact] @@ -321,6 +320,7 @@ public void KeyBindings_Test_Movement_LeftSide () [Fact] public void PositionChanged_Event () { + Application.Navigation = new ApplicationNavigation (); var hv = new HexView (LoadStream (null, out _)) { Width = 20, Height = 10 }; Application.Top = new Toplevel (); Application.Top.Add (hv); @@ -346,6 +346,7 @@ public void PositionChanged_Event () [Fact] public void Source_Sets_Address_To_Zero_If_Greater_Than_Source_Length () { + Application.Navigation = new ApplicationNavigation (); var hv = new HexView (LoadStream (null, out _)) { Width = 10, Height = 5 }; Application.Top = new Toplevel (); Application.Top.Add (hv); From 78cfe73d078657b15ad28e69c5d5a7234fb56e92 Mon Sep 17 00:00:00 2001 From: Tig Date: Wed, 12 Nov 2025 06:55:25 -0700 Subject: [PATCH 03/32] Update StringExtensions.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Terminal.Gui/Text/StringExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Terminal.Gui/Text/StringExtensions.cs b/Terminal.Gui/Text/StringExtensions.cs index 129164a991..e379cf3da6 100644 --- a/Terminal.Gui/Text/StringExtensions.cs +++ b/Terminal.Gui/Text/StringExtensions.cs @@ -70,7 +70,7 @@ public static int GetColumns (this string str) { string element = enumerator.GetTextElement (); - // Sum all runes' display widths within this grapheme cluster + // Get the maximum rune width within this grapheme cluster int width = element .EnumerateRunes () .Max (r => Math.Max (r.GetColumns (), 0)); From 87ea2af9bf1a79bf23d17f28b91a0977801ab572 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 12 Nov 2025 15:25:29 +0000 Subject: [PATCH 04/32] Resolving merge conflicts --- .../UICatalog/Scenarios/CombiningMarks.cs | 11 +- Examples/UICatalog/Scenarios/LineDrawing.cs | 2 +- Examples/UICatalog/Scenarios/Sliders.cs | 16 +- .../UICatalog/Scenarios/SyntaxHighlighting.cs | 11 +- Terminal.Gui/App/Application.cs | 18 +- Terminal.Gui/Drawing/Cell.cs | 141 +- Terminal.Gui/Drawing/GraphemeHelper.cs | 49 + Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs | 9 +- Terminal.Gui/Drivers/DriverImpl.cs | 6 +- Terminal.Gui/Drivers/IDriver.cs | 6 +- Terminal.Gui/Drivers/IOutputBuffer.cs | 8 +- Terminal.Gui/Drivers/OutputBase.cs | 27 +- Terminal.Gui/Drivers/OutputBufferImpl.cs | 209 +- Terminal.Gui/Text/StringExtensions.cs | 94 +- Terminal.Gui/Text/TextFormatter.cs | 328 +-- Terminal.Gui/ViewBase/Adornment/ShadowView.cs | 10 +- .../ViewBase/View.Drawing.Primitives.cs | 20 +- Terminal.Gui/ViewBase/View.Drawing.cs | 16 +- .../AutocompleteFilepathContext.cs | 26 +- .../Autocomplete/ISuggestionGenerator.cs | 4 +- .../Views/Autocomplete/PopupAutocomplete.cs | 2 +- .../SingleWordSuggestionGenerator.cs | 16 +- Terminal.Gui/Views/Slider/Slider.cs | 50 +- .../Views/TableView/TreeTableSource.cs | 8 +- Terminal.Gui/Views/TextInput/TextField.cs | 34 +- Terminal.Gui/Views/TextInput/TextModel.cs | 110 +- Terminal.Gui/Views/TextInput/TextView.cs | 55 +- Terminal.Gui/Views/TreeView/Branch.cs | 42 +- Tests/UnitTests/DriverAssert.cs | 31 +- Tests/UnitTests/Drivers/ClipRegionTests.cs | 16 +- Tests/UnitTests/View/Draw/ClipTests.cs | 8 +- Tests/UnitTests/View/TextTests.cs | 2 +- Tests/UnitTests/Views/LabelTests.cs | 14 +- Tests/UnitTests/Views/ListViewTests.cs | 2 +- Tests/UnitTests/Views/ProgressBarTests.cs | 2352 ++++++++--------- Tests/UnitTests/Views/TabViewTests.cs | 4 +- Tests/UnitTests/Views/TextFieldTests.cs | 5 +- Tests/UnitTests/Views/TextViewTests.cs | 13 +- Tests/UnitTests/Views/TreeViewTests.cs | 14 +- .../Drawing/CellTests.cs | 56 +- .../Drivers/AddRuneTests.cs | 36 +- .../Drivers/ContentsTests.cs | 10 +- .../Drivers/FakeDriverTests.cs | 2 +- .../UnitTestsParallelizable/Text/RuneTests.cs | 133 +- .../Text/StringTests.cs | 144 +- .../Text/TextFormatterDrawTests.cs | 25 + .../Text/TextFormatterTests.cs | 35 +- .../Views/TextFieldTests.cs | 6 +- .../Views/TextViewTests.cs | 64 +- 49 files changed, 2322 insertions(+), 1978 deletions(-) create mode 100644 Terminal.Gui/Drawing/GraphemeHelper.cs diff --git a/Examples/UICatalog/Scenarios/CombiningMarks.cs b/Examples/UICatalog/Scenarios/CombiningMarks.cs index 7d8437a230..3918083ec3 100644 --- a/Examples/UICatalog/Scenarios/CombiningMarks.cs +++ b/Examples/UICatalog/Scenarios/CombiningMarks.cs @@ -16,7 +16,8 @@ public override void Main () Application.Top!.SetNeedsDraw (); var i = -1; - top.AddStr ("Terminal.Gui only supports combining marks that normalize. See Issue #2616."); + top.Move (0, ++i); + top.AddStr ("Terminal.Gui supports all combining sequences that can be rendered as an unique grapheme."); top.Move (0, ++i); top.AddStr ("\u0301<- \"\\u0301\" using AddStr."); top.Move (0, ++i); @@ -38,7 +39,7 @@ public override void Main () top.AddRune ('\u0301'); top.AddRune ('\u0328'); top.AddRune (']'); - top.AddStr ("<- \"[a\\u0301\\u0301\\u0328]\" using AddRune for each."); + top.AddStr ("<- \"[a\\u0301\\u0301\\u0328]\" using AddRune for each. Avoid use AddRune for combining sequences because may result with empty blocks at end."); top.Move (0, ++i); top.AddStr ("[a\u0301\u0301\u0328]<- \"[a\\u0301\\u0301\\u0328]\" using AddStr."); top.Move (0, ++i); @@ -82,6 +83,12 @@ public override void Main () top.AddStr ("[\U0001F468\U0001F469\U0001F9D2]<- \"[\\U0001F468\\U0001F469\\U0001F9D2]\" using AddStr."); top.Move (0, ++i); top.AddStr ("[\U0001F468\u200D\U0001F469\u200D\U0001F9D2]<- \"[\\U0001F468\\u200D\\U0001F469\\u200D\\U0001F9D2]\" using AddStr."); + top.Move (0, ++i); + top.AddStr ("[\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466]<- \"[\\U0001F468\\u200D\\U0001F469\\u200D\\U0001F467\\u200D\\U0001F466]\" using AddStr."); + top.Move (0, ++i); + top.AddStr ("[\u0e32\u0e33]<- \"[\\u0e32\\u0e33]\" using AddStr."); + top.Move (0, ++i); + top.AddStr ("[\U0001F469\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468]<- \"[\\U0001F469\\u200D\\u2764\\uFE0F\\u200D\\U0001F48B\\u200D\\U0001F468]\" using AddStr."); }; Application.Run (top); diff --git a/Examples/UICatalog/Scenarios/LineDrawing.cs b/Examples/UICatalog/Scenarios/LineDrawing.cs index cbc8dd27bb..217aea27b1 100644 --- a/Examples/UICatalog/Scenarios/LineDrawing.cs +++ b/Examples/UICatalog/Scenarios/LineDrawing.cs @@ -284,7 +284,7 @@ protected override bool OnDrawingContent () SetCurrentAttribute (c.Value.Value.Attribute ?? GetAttributeForRole (VisualRole.Normal)); // TODO: #2616 - Support combining sequences that don't normalize - AddRune (c.Key.X, c.Key.Y, c.Value.Value.Rune); + AddStr (c.Key.X, c.Key.Y, c.Value.Value.Grapheme); } } } diff --git a/Examples/UICatalog/Scenarios/Sliders.cs b/Examples/UICatalog/Scenarios/Sliders.cs index fae50c015d..8d954d1005 100644 --- a/Examples/UICatalog/Scenarios/Sliders.cs +++ b/Examples/UICatalog/Scenarios/Sliders.cs @@ -86,17 +86,17 @@ public void MakeSliders (View v, List options) { if (single.Orientation == Orientation.Horizontal) { - single.Style.SpaceChar = new () { Rune = Glyphs.HLine }; - single.Style.OptionChar = new () { Rune = Glyphs.HLine }; + single.Style.SpaceChar = new () { Grapheme = Glyphs.HLine.ToString () }; + single.Style.OptionChar = new () { Grapheme = Glyphs.HLine.ToString () }; } else { - single.Style.SpaceChar = new () { Rune = Glyphs.VLine }; - single.Style.OptionChar = new () { Rune = Glyphs.VLine }; + single.Style.SpaceChar = new () { Grapheme = Glyphs.VLine.ToString () }; + single.Style.OptionChar = new () { Grapheme = Glyphs.VLine.ToString () }; } }; - single.Style.SetChar = new () { Rune = Glyphs.ContinuousMeterSegment }; - single.Style.DragChar = new () { Rune = Glyphs.ContinuousMeterSegment }; + single.Style.SetChar = new () { Grapheme = Glyphs.ContinuousMeterSegment.ToString () }; + single.Style.DragChar = new () { Grapheme = Glyphs.ContinuousMeterSegment.ToString () }; v.Add (single); @@ -257,7 +257,7 @@ public override void Main () { s.Orientation = Orientation.Horizontal; - s.Style.SpaceChar = new () { Rune = Glyphs.HLine }; + s.Style.SpaceChar = new () { Grapheme = Glyphs.HLine.ToString () }; if (prev == null) { @@ -275,7 +275,7 @@ public override void Main () { s.Orientation = Orientation.Vertical; - s.Style.SpaceChar = new () { Rune = Glyphs.VLine }; + s.Style.SpaceChar = new () { Grapheme = Glyphs.VLine.ToString () }; if (prev == null) { diff --git a/Examples/UICatalog/Scenarios/SyntaxHighlighting.cs b/Examples/UICatalog/Scenarios/SyntaxHighlighting.cs index 7b0c054628..ad9f934605 100644 --- a/Examples/UICatalog/Scenarios/SyntaxHighlighting.cs +++ b/Examples/UICatalog/Scenarios/SyntaxHighlighting.cs @@ -152,12 +152,12 @@ public override void Main () ), null, new ( - "_Load Rune Cells", + "_Load Text Cells", "", () => ApplyLoadCells () ), new ( - "_Save Rune Cells", + "_Save Text Cells", "", () => SaveCells () ), @@ -240,12 +240,9 @@ private void ApplyLoadCells () { string csName = color.Key; - foreach (Rune rune in csName.EnumerateRunes ()) - { - cells.Add (new () { Rune = rune, Attribute = color.Value.Normal }); - } + cells.AddRange (Cell.ToCellList (csName, color.Value.Normal)); - cells.Add (new () { Rune = (Rune)'\n', Attribute = color.Value.Focus }); + cells.Add (new () { Grapheme = "\n", Attribute = color.Value.Focus }); } if (File.Exists (_path)) diff --git a/Terminal.Gui/App/Application.cs b/Terminal.Gui/App/Application.cs index bd3ccf7a3f..92730e7118 100644 --- a/Terminal.Gui/App/Application.cs +++ b/Terminal.Gui/App/Application.cs @@ -89,26 +89,14 @@ public static string ToString (IDriver? driver) { for (var c = 0; c < driver.Cols; c++) { - Rune rune = contents [r, c].Rune; + string text = contents [r, c].Grapheme; - if (rune.DecodeSurrogatePair (out char []? sp)) - { - sb.Append (sp); - } - else - { - sb.Append ((char)rune.Value); - } + sb.Append (text); - if (rune.GetColumns () > 1) + if (text.GetColumns () > 1) { c++; } - - // See Issue #2616 - //foreach (var combMark in contents [r, c].CombiningMarks) { - // sb.Append ((char)combMark.Value); - //} } sb.AppendLine (); diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index e72a7837ee..c3f6130384 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -1,94 +1,103 @@ #nullable enable - namespace Terminal.Gui.Drawing; /// /// Represents a single row/column in a Terminal.Gui rendering surface (e.g. and /// ). /// -public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, Rune Rune = default) +public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, string Grapheme = "") { /// The attributes to use when drawing the Glyph. public Attribute? Attribute { get; set; } = Attribute; /// - /// Gets or sets a value indicating whether this has been modified since the + /// Gets or sets a value indicating whether this has been modified since the /// last time it was drawn. /// public bool IsDirty { get; set; } = IsDirty; - private Rune _rune = Rune; + private string _grapheme = Grapheme; - /// The character to display. If is , then is ignored. - public Rune Rune + /// + /// The single grapheme cluster to display from this cell. If is or + /// , then is ignored. + /// + public string Grapheme { - get => _rune; + readonly get => _grapheme; set { - _combiningMarks?.Clear (); - _rune = value; + if (GraphemeHelper.GetGraphemes(value).ToArray().Length > 1) + { + throw new InvalidOperationException ($"Only a single grapheme cluster is allowed per Cell in {nameof (Grapheme)}."); + } + + _grapheme = value; } } - private List? _combiningMarks; - /// - /// The combining marks for that when combined makes this Cell a combining sequence. If - /// empty, then is ignored. + /// The rune for or runes for that when combined makes this Cell a combining sequence. /// /// - /// Only valid in the rare case where is a combining sequence that could not be normalized to a - /// single Rune. + /// In the case where has more than one rune it is a combining sequence that is normalized to a + /// single Text which may occupies 1 or 2 columns. /// - internal IReadOnlyList CombiningMarks + public IReadOnlyList Runes => Grapheme.EnumerateRunes ().ToList (); + + /// + public override string ToString () { - // PERFORMANCE: Downside of the interface return type is that List struct enumerator cannot be utilized, i.e. enumerator is allocated. - // If enumeration is used heavily in the future then might be better to expose the List Enumerator directly via separate mechanism. - get + string visibleText = EscapeControlAndInvisible (Grapheme); + + return $"[\"{visibleText}\":{Attribute}]"; + } + + private static string EscapeControlAndInvisible (string text) + { + if (string.IsNullOrEmpty (text)) + { + return ""; + } + + var sb = new StringBuilder (); + + foreach (var rune in text.EnumerateRunes ()) { - // Avoid unnecessary list allocation. - if (_combiningMarks == null) + switch (rune.Value) { - return Array.Empty (); + case '\0': sb.Append ("␀"); break; + case '\t': sb.Append ("\\t"); break; + case '\r': sb.Append ("\\r"); break; + case '\n': sb.Append ("\\n"); break; + case '\f': sb.Append ("\\f"); break; + case '\v': sb.Append ("\\v"); break; + default: + if (char.IsControl ((char)rune.Value)) + { + // show as \uXXXX + sb.Append ($"\\u{rune.Value:X4}"); + } + else + { + sb.Append (rune.ToString ()); + } + break; } - return _combiningMarks; } - } - /// - /// Adds combining mark to the cell. - /// - /// The combining mark to add to the cell. - internal void AddCombiningMark (Rune combiningMark) - { - _combiningMarks ??= []; - _combiningMarks.Add (combiningMark); + return sb.ToString (); } - /// - /// Clears combining marks of the cell. - /// - internal void ClearCombiningMarks () - { - _combiningMarks?.Clear (); - } - - /// - public override string ToString () { return $"['{Rune}':{Attribute}]"; } - /// Converts the string into a . /// The string to convert. /// The to use. /// public static List ToCellList (string str, Attribute? attribute = null) { - List cells = new (); - - foreach (Rune rune in str.EnumerateRunes ()) - { - cells.Add (new () { Rune = rune, Attribute = attribute }); - } + List cells = []; + cells.AddRange (GraphemeHelper.GetGraphemes (str).Select (grapheme => new Cell { Grapheme = grapheme, Attribute = attribute })); return cells; } @@ -101,9 +110,7 @@ public static List ToCellList (string str, Attribute? attribute = null) /// A for each line. public static List> StringToLinesOfCells (string content, Attribute? attribute = null) { - List cells = content.EnumerateRunes () - .Select (x => new Cell { Rune = x, Attribute = attribute }) - .ToList (); + List cells = ToCellList (content, attribute); return SplitNewLines (cells); } @@ -113,14 +120,14 @@ public static List> StringToLinesOfCells (string content, Attribute? /// public static string ToString (IEnumerable cells) { - var str = string.Empty; + StringBuilder sb = new (); foreach (Cell cell in cells) { - str += cell.Rune.ToString (); + sb.Append (cell.Grapheme); } - return str; + return sb.ToString (); } /// Converts a generic collection into a string. @@ -148,26 +155,19 @@ public static string ToString (List> cellsList) internal static List StringToCells (string str, Attribute? attribute = null) { - List cells = []; - - foreach (Rune rune in str.ToRunes ()) - { - cells.Add (new () { Rune = rune, Attribute = attribute }); - } - - return cells; + return ToCellList (str, attribute); } - internal static List ToCells (IEnumerable runes, Attribute? attribute = null) + internal static List ToCells (IEnumerable strings, Attribute? attribute = null) { - List cells = new (); + StringBuilder sb = new (); - foreach (Rune rune in runes) + foreach (string str in strings) { - cells.Add (new () { Rune = rune, Attribute = attribute }); + sb.Append (str); } - return cells; + return ToCellList (sb.ToString (), attribute); } private static List> SplitNewLines (List cells) @@ -180,14 +180,15 @@ private static List> SplitNewLines (List cells) // ASCII code 10 = Line Feed. for (; i < cells.Count; i++) { - if (cells [i].Rune.Value == 13) + if (cells [i].Grapheme.Length == 1 && cells [i].Grapheme [0] == 13) { hasCR = true; continue; } - if (cells [i].Rune.Value == 10) + if ((cells [i].Grapheme.Length == 1 && cells [i].Grapheme [0] == 10) + || cells [i].Grapheme == "\r\n") { if (i - start > 0) { diff --git a/Terminal.Gui/Drawing/GraphemeHelper.cs b/Terminal.Gui/Drawing/GraphemeHelper.cs new file mode 100644 index 0000000000..4ae00148c7 --- /dev/null +++ b/Terminal.Gui/Drawing/GraphemeHelper.cs @@ -0,0 +1,49 @@ +using System.Globalization; + +namespace Terminal.Gui.Drawing; + +/// +/// Provides utility methods for enumerating Unicode grapheme clusters (user-perceived characters) +/// in a string. A grapheme cluster may consist of one or more values, +/// including combining marks or zero-width joiner (ZWJ) sequences such as emoji family groups. +/// +/// +/// +/// This helper uses to enumerate +/// text elements according to the Unicode Standard Annex #29 (UAX #29) rules for +/// extended grapheme clusters. +/// +/// +/// On legacy Windows consoles (e.g., cmd.exe, conhost.exe), complex grapheme +/// sequences such as ZWJ emoji or combining marks may not render correctly, even though +/// the underlying string data is valid. +/// +/// +/// For most accurate visual rendering, prefer modern terminals such as Windows Terminal +/// or Linux-based terminals with full Unicode and font support. +/// +/// +public static class GraphemeHelper +{ + /// + /// Enumerates extended grapheme clusters from a string. + /// Handles surrogate pairs, combining marks, and basic ZWJ sequences. + /// Safe for legacy consoles; memory representation is correct. + /// + public static IEnumerable GetGraphemes (string text) + { + if (string.IsNullOrEmpty (text)) + { + yield break; + } + + TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator (text); + + while (enumerator.MoveNext ()) + { + string element = enumerator.GetTextElement (); + + yield return element; + } + } +} diff --git a/Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs b/Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs index 637b61861e..80f87a8e92 100644 --- a/Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs +++ b/Terminal.Gui/Drawing/LineCanvas/LineCanvas.cs @@ -174,7 +174,7 @@ public void Clear () intersectionsBufferList.Clear (); foreach (var line in _lines) { - if (line.Intersects (x, y) is IntersectionDefinition intersect) + if (line.Intersects (x, y) is { } intersect) { intersectionsBufferList.Add (intersect); } @@ -218,9 +218,8 @@ public Dictionary GetMap (Rectangle inArea) for (int x = inArea.X; x < inArea.X + inArea.Width; x++) { IntersectionDefinition [] intersects = _lines - // ! nulls are filtered out by the next Where filter - .Select (l => l.Intersects (x, y)!) - .Where (i => i is not null) + .Select (l => l.Intersects (x, y)) + .OfType () // automatically filters nulls and casts .ToArray (); Rune? rune = GetRuneForIntersects (Application.Driver, intersects); @@ -414,7 +413,7 @@ private void ConfigurationManager_Applied (object? sender, ConfigurationManagerE if (rune.HasValue) { - cell.Rune = rune.Value; + cell.Grapheme = rune.ToString ()!; } cell.Attribute = GetAttributeForIntersects (intersects); diff --git a/Terminal.Gui/Drivers/DriverImpl.cs b/Terminal.Gui/Drivers/DriverImpl.cs index db19184590..583615a240 100644 --- a/Terminal.Gui/Drivers/DriverImpl.cs +++ b/Terminal.Gui/Drivers/DriverImpl.cs @@ -332,8 +332,8 @@ public virtual string GetVersionInfo () /// public bool IsRuneSupported (Rune rune) { return Rune.IsValid (rune.Value); } - /// Tests whether the specified coordinate are valid for drawing the specified Rune. - /// Used to determine if one or two columns are required. + /// Tests whether the specified coordinate are valid for drawing the specified Text. + /// Used to determine if one or two columns are required. /// The column. /// The row. /// @@ -341,7 +341,7 @@ public virtual string GetVersionInfo () /// . /// otherwise. /// - public bool IsValidLocation (Rune rune, int col, int row) { return OutputBuffer.IsValidLocation (rune, col, row); } + public bool IsValidLocation (string text, int col, int row) { return OutputBuffer.IsValidLocation (text, col, row); } /// /// Updates and to the specified column and row in diff --git a/Terminal.Gui/Drivers/IDriver.cs b/Terminal.Gui/Drivers/IDriver.cs index 32af99b05e..93e0bf62d9 100644 --- a/Terminal.Gui/Drivers/IDriver.cs +++ b/Terminal.Gui/Drivers/IDriver.cs @@ -125,8 +125,8 @@ public interface IDriver /// bool IsRuneSupported (Rune rune); - /// Tests whether the specified coordinate are valid for drawing the specified Rune. - /// Used to determine if one or two columns are required. + /// Tests whether the specified coordinate are valid for drawing the specified Text. + /// Used to determine if one or two columns are required. /// The column. /// The row. /// @@ -134,7 +134,7 @@ public interface IDriver /// . /// otherwise. /// - bool IsValidLocation (Rune rune, int col, int row); + bool IsValidLocation (string text, int col, int row); /// /// Updates and to the specified column and row in diff --git a/Terminal.Gui/Drivers/IOutputBuffer.cs b/Terminal.Gui/Drivers/IOutputBuffer.cs index 2b8991593f..8e2deba2c4 100644 --- a/Terminal.Gui/Drivers/IOutputBuffer.cs +++ b/Terminal.Gui/Drivers/IOutputBuffer.cs @@ -85,15 +85,15 @@ public interface IOutputBuffer void FillRect (Rectangle rect, char rune); /// - /// Tests whether the specified coordinate is valid for drawing the specified Rune. + /// Tests whether the specified coordinate is valid for drawing the specified Text. /// - /// Used to determine if one or two columns are required. + /// Used to determine if one or two columns are required. /// The column. /// The row. /// - /// True if the coordinate is valid for the Rune; false otherwise. + /// True if the coordinate is valid for the Text; false otherwise. /// - bool IsValidLocation (Rune rune, int col, int row); + bool IsValidLocation (string text, int col, int row); /// /// The first cell index on left of screen - basically always 0. diff --git a/Terminal.Gui/Drivers/OutputBase.cs b/Terminal.Gui/Drivers/OutputBase.cs index ac43dc93d3..e210564a84 100644 --- a/Terminal.Gui/Drivers/OutputBase.cs +++ b/Terminal.Gui/Drivers/OutputBase.cs @@ -32,9 +32,6 @@ public virtual void Write (IOutputBuffer buffer) CursorVisibility? savedVisibility = _cachedCursorVisibility; SetCursorVisibility (CursorVisibility.Invisible); - const int MAX_CHARS_PER_RUNE = 2; - Span runeBuffer = stackalloc char [MAX_CHARS_PER_RUNE]; - for (int row = top; row < rows; row++) { if (!SetCursorPositionImpl (0, row)) @@ -94,28 +91,8 @@ public virtual void Write (IOutputBuffer buffer) outputWidth++; - // Avoid Rune.ToString() by appending the rune chars. - Rune rune = buffer.Contents [row, col].Rune; - int runeCharsWritten = rune.EncodeToUtf16 (runeBuffer); - ReadOnlySpan runeChars = runeBuffer [..runeCharsWritten]; - output.Append (runeChars); - - if (buffer.Contents [row, col].CombiningMarks.Count > 0) - { - // AtlasEngine does not support NON-NORMALIZED combining marks in a way - // compatible with the driver architecture. Any CMs (except in the first col) - // are correctly combined with the base char, but are ALSO treated as 1 column - // width codepoints E.g. `echo "[e`u{0301}`u{0301}]"` will output `[é ]`. - // - // For now, we just ignore the list of CMs. - //foreach (var combMark in Contents [row, col].CombiningMarks) { - // output.Append (combMark); - } - else if (rune.IsSurrogatePair () && rune.GetColumns () < 2) - { - WriteToConsole (output, ref lastCol, row, ref outputWidth); - SetCursorPositionImpl (col - 1, row); - } + string text = buffer.Contents [row, col].Grapheme; + output.Append (text); buffer.Contents [row, col].IsDirty = false; } diff --git a/Terminal.Gui/Drivers/OutputBufferImpl.cs b/Terminal.Gui/Drivers/OutputBufferImpl.cs index ee2493d602..02237d23cd 100644 --- a/Terminal.Gui/Drivers/OutputBufferImpl.cs +++ b/Terminal.Gui/Drivers/OutputBufferImpl.cs @@ -66,7 +66,9 @@ public int Cols /// The topmost row in the terminal. public virtual int Top { get; set; } = 0; - /// + /// + /// Indicates which lines have been modified and need to be redrawn. + /// public bool [] DirtyLines { get; set; } = []; // QUESTION: When non-full screen apps are supported, will this represent the app size, or will that be in Application? @@ -113,85 +115,50 @@ public Region? Clip /// will be added instead. /// /// - /// Rune to add. - public void AddRune (Rune rune) - { - int runeWidth = -1; - bool validLocation = IsValidLocation (rune, Col, Row); + /// Text to add. + public void AddRune (Rune rune) { AddStr (rune.ToString ()); } - if (Contents is null) - { - return; - } - - Clip ??= new (Screen); - - Rectangle clipRect = Clip!.GetBounds (); + /// + /// Adds the specified to the display at the current cursor position. This method is a + /// convenience method that calls with the constructor. + /// + /// Character to add. + public void AddRune (char c) { AddRune (new Rune (c)); } - if (validLocation) + /// Adds the to the display at the cursor position. + /// + /// + /// When the method returns, will be incremented by the number of columns + /// required, unless the new column value is outside the or screen + /// dimensions defined by . + /// + /// If requires more columns than are available, the output will be clipped. + /// + /// String. + public void AddStr (string str) + { + foreach (string grapheme in GraphemeHelper.GetGraphemes (str)) { - rune = rune.MakePrintable (); - runeWidth = rune.GetColumns (); + string text = grapheme; - lock (Contents) - { - if (runeWidth == 0 && rune.IsCombiningMark ()) - { - // AtlasEngine does not support NON-NORMALIZED combining marks in a way - // compatible with the driver architecture. Any CMs (except in the first col) - // are correctly combined with the base char, but are ALSO treated as 1 column - // width codepoints E.g. `echo "[e`u{0301}`u{0301}]"` will output `[é ]`. - // - // Until this is addressed (see Issue #), we do our best by - // a) Attempting to normalize any CM with the base char to it's left - // b) Ignoring any CMs that don't normalize - if (Col > 0) - { - if (Contents [Row, Col - 1].CombiningMarks.Count > 0) - { - // Just add this mark to the list - Contents [Row, Col - 1].AddCombiningMark (rune); - - // Ignore. Don't move to next column (let the driver figure out what to do). - } - else - { - // Attempt to normalize the cell to our left combined with this mark - string combined = Contents [Row, Col - 1].Rune + rune.ToString (); + int textWidth = -1; + bool validLocation = IsValidLocation (text, Col, Row); - // Normalize to Form C (Canonical Composition) - string normalized = combined.Normalize (NormalizationForm.FormC); + if (Contents is null) + { + return; + } - if (normalized.Length == 1) - { - // It normalized! We can just set the Cell to the left with the - // normalized codepoint - Contents [Row, Col - 1].Rune = (Rune)normalized [0]; + Clip ??= new (Screen); - // Ignore. Don't move to next column because we're already there - } - else - { - // It didn't normalize. Add it to the Cell to left's CM list - Contents [Row, Col - 1].AddCombiningMark (rune); + Rectangle clipRect = Clip!.GetBounds (); - // Ignore. Don't move to next column (let the driver figure out what to do). - } - } + if (validLocation) + { + text = text.MakePrintable (); + textWidth = text.GetColumns (); - Contents [Row, Col - 1].Attribute = CurrentAttribute; - Contents [Row, Col - 1].IsDirty = true; - } - else - { - // Most drivers will render a combining mark at col 0 as the mark - Contents [Row, Col].Rune = rune; - Contents [Row, Col].Attribute = CurrentAttribute; - Contents [Row, Col].IsDirty = true; - Col++; - } - } - else + lock (Contents) { Contents [Row, Col].Attribute = CurrentAttribute; Contents [Row, Col].IsDirty = true; @@ -199,49 +166,45 @@ public void AddRune (Rune rune) if (Col > 0) { // Check if cell to left has a wide glyph - if (Contents [Row, Col - 1].Rune.GetColumns () > 1) + if (Contents [Row, Col - 1].Grapheme.GetColumns () > 1) { // Invalidate cell to left - Contents [Row, Col - 1].Rune = Rune.ReplacementChar; + Contents [Row, Col - 1].Grapheme = Rune.ReplacementChar.ToString (); Contents [Row, Col - 1].IsDirty = true; } } - if (runeWidth < 1) + if (textWidth is 0 or 1) { - Contents [Row, Col].Rune = Rune.ReplacementChar; - } - else if (runeWidth == 1) - { - Contents [Row, Col].Rune = rune; + Contents [Row, Col].Grapheme = text; if (Col < clipRect.Right - 1) { Contents [Row, Col + 1].IsDirty = true; } } - else if (runeWidth == 2) + else if (textWidth == 2) { if (!Clip.Contains (Col + 1, Row)) { // We're at the right edge of the clip, so we can't display a wide character. // TODO: Figure out if it is better to show a replacement character or ' ' - Contents [Row, Col].Rune = Rune.ReplacementChar; + Contents [Row, Col].Grapheme = Rune.ReplacementChar.ToString (); } else if (!Clip.Contains (Col, Row)) { // Our 1st column is outside the clip, so we can't display a wide character. - Contents [Row, Col + 1].Rune = Rune.ReplacementChar; + Contents [Row, Col + 1].Grapheme = Rune.ReplacementChar.ToString (); } else { - Contents [Row, Col].Rune = rune; + Contents [Row, Col].Grapheme = text; if (Col < clipRect.Right - 1) { // Invalidate cell to right so that it doesn't get drawn // TODO: Figure out if it is better to show a replacement character or ' ' - Contents [Row, Col + 1].Rune = Rune.ReplacementChar; + Contents [Row, Col + 1].Grapheme = Rune.ReplacementChar.ToString (); Contents [Row, Col + 1].IsDirty = true; } } @@ -249,67 +212,37 @@ public void AddRune (Rune rune) else { // This is a non-spacing character, so we don't need to do anything - Contents [Row, Col].Rune = (Rune)' '; + Contents [Row, Col].Grapheme = " "; Contents [Row, Col].IsDirty = false; } DirtyLines [Row] = true; } } - } - if (runeWidth is < 0 or > 0) - { Col++; - } - - if (runeWidth > 1) - { - Debug.Assert (runeWidth <= 2); - if (validLocation && Col < clipRect.Right) + if (textWidth > 1) { - lock (Contents!) + Debug.Assert (textWidth <= 2); + + if (validLocation && Col < clipRect.Right) { - // This is a double-width character, and we are not at the end of the line. - // Col now points to the second column of the character. Ensure it doesn't - // Get rendered. - Contents [Row, Col].IsDirty = false; - Contents [Row, Col].Attribute = CurrentAttribute; + lock (Contents!) + { + // This is a double-width character, and we are not at the end of the line. + // Col now points to the second column of the character. Ensure it doesn't + // Get rendered. + Contents [Row, Col].IsDirty = false; + Contents [Row, Col].Attribute = CurrentAttribute; - // TODO: Determine if we should wipe this out (for now now) - //Contents [Row, Col].Rune = (Rune)' '; + // TODO: Determine if we should wipe this out (for now now) + //Contents [Row, Col].Text = (Text)' '; + } } - } - - Col++; - } - } - - /// - /// Adds the specified to the display at the current cursor position. This method is a - /// convenience method that calls with the constructor. - /// - /// Character to add. - public void AddRune (char c) { AddRune (new Rune (c)); } - - /// Adds the to the display at the cursor position. - /// - /// - /// When the method returns, will be incremented by the number of columns - /// required, unless the new column value is outside of the or screen - /// dimensions defined by . - /// - /// If requires more columns than are available, the output will be clipped. - /// - /// String. - public void AddStr (string str) - { - List runes = str.EnumerateRunes ().ToList (); - for (var i = 0; i < runes.Count; i++) - { - AddRune (runes [i]); + Col++; + } } } @@ -332,7 +265,7 @@ public void ClearContents () { Contents [row, c] = new () { - Rune = (Rune)' ', + Grapheme = " ", Attribute = new Attribute (Color.White, Color.Black), IsDirty = true }; @@ -346,17 +279,17 @@ public void ClearContents () //ClearedContents?.Invoke (this, EventArgs.Empty); } - /// Tests whether the specified coordinate are valid for drawing the specified Rune. - /// Used to determine if one or two columns are required. + /// Tests whether the specified coordinate are valid for drawing the specified Text. + /// Used to determine if one or two columns are required. /// The column. /// The row. /// /// if the coordinate is outside the screen bounds or outside of . /// otherwise. /// - public bool IsValidLocation (Rune rune, int col, int row) + public bool IsValidLocation (string text, int col, int row) { - if (rune.GetColumns () < 2) + if (text.GetColumns () < 2) { return col >= 0 && row >= 0 && col < Cols && row < Rows && Clip!.Contains (col, row); } @@ -384,14 +317,14 @@ public void FillRect (Rectangle rect, Rune rune) { for (int c = rect.X; c < rect.X + rect.Width; c++) { - if (!IsValidLocation (rune, c, r)) + if (!IsValidLocation (rune.ToString (), c, r)) { continue; } Contents [r, c] = new () { - Rune = rune != default (Rune) ? rune : (Rune)' ', + Grapheme = rune != default (Rune) ? rune.ToString () : " ", Attribute = CurrentAttribute, IsDirty = true }; } diff --git a/Terminal.Gui/Text/StringExtensions.cs b/Terminal.Gui/Text/StringExtensions.cs index 129164a991..b9e72f95f7 100644 --- a/Terminal.Gui/Text/StringExtensions.cs +++ b/Terminal.Gui/Text/StringExtensions.cs @@ -1,6 +1,5 @@ #nullable enable using System.Buffers; -using System.Globalization; namespace Terminal.Gui.Text; @@ -55,8 +54,9 @@ public static (Rune Rune, int Size) DecodeRune (this string str, int start = 0, /// Gets the number of columns the string occupies in the terminal. /// This is a Terminal.Gui extension method to to support TUI text manipulation. /// The string to measure. + /// Indicates whether to ignore values ​​less than zero, such as control keys. /// - public static int GetColumns (this string str) + public static int GetColumns (this string str, bool ignoreLessThanZero = true) { if (string.IsNullOrEmpty (str)) { @@ -64,17 +64,31 @@ public static int GetColumns (this string str) } var total = 0; - TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator (str); - while (enumerator.MoveNext ()) + foreach (string grapheme in GraphemeHelper.GetGraphemes (str)) { - string element = enumerator.GetTextElement (); - // Sum all runes' display widths within this grapheme cluster - int width = element - .EnumerateRunes () - .Max (r => Math.Max (r.GetColumns (), 0)); - total += width; + int clusterWidth = 0; + + foreach (var rune in grapheme.EnumerateRunes ()) + { + int width = rune.GetColumns (); + + if (ignoreLessThanZero && width < 0) + { + width = 0; + } + + clusterWidth += width; + } + + // Clamp to realistic max display width + if (clusterWidth > 2) + { + clusterWidth = 2; + } + + total += clusterWidth; } return total; @@ -95,7 +109,7 @@ public static int GetColumns (this string str) /// A indicating if all elements of the are ASCII digits ( /// ) or not ( /// - public static bool IsAllAsciiDigits (this ReadOnlySpan stringSpan) { return stringSpan.ToString ().All (char.IsAsciiDigit); } + public static bool IsAllAsciiDigits (this ReadOnlySpan stringSpan) { return !stringSpan.IsEmpty && stringSpan.ToString ().All (char.IsAsciiDigit); } /// /// Determines if this of is composed entirely of ASCII @@ -106,7 +120,7 @@ public static int GetColumns (this string str) /// A indicating if all elements of the are ASCII digits ( /// ) or not ( /// - public static bool IsAllAsciiHexDigits (this ReadOnlySpan stringSpan) { return stringSpan.ToString ().All (char.IsAsciiHexDigit); } + public static bool IsAllAsciiHexDigits (this ReadOnlySpan stringSpan) { return !stringSpan.IsEmpty && stringSpan.ToString ().All (char.IsAsciiHexDigit); } /// Repeats the string times. /// This is a Terminal.Gui extension method to to support TUI text manipulation. @@ -207,4 +221,60 @@ public static string ToString (IEnumerable bytes, Encoding? encoding = nul return encoding.GetString (bytes.ToArray ()); } + + /// Converts a generic collection into a string. + /// The enumerable string to convert. + /// + public static string ToString (IEnumerable strings) { return string.Concat (strings); } + + /// Converts the string into a . + /// This is a Terminal.Gui extension method to to support TUI text manipulation. + /// The string to convert. + /// + public static List ToStringList (this string str) + { + List strings = []; + + foreach (string grapheme in GraphemeHelper.GetGraphemes (str)) + { + strings.Add (grapheme); + } + + return strings; + } + + /// Reports whether a string is a surrogate code point. + /// This is a Terminal.Gui extension method to to support TUI text manipulation. + /// The string to probe. + /// if the string is a surrogate code point; otherwise. + public static bool IsSurrogatePair (this string str) + { + if (str.Length != 2) + { + return false; + } + + Rune rune = Rune.GetRuneAt (str, 0); + + return rune.IsSurrogatePair (); + } + + /// + /// Ensures the text is not a control character and can be displayed by translating characters below 0x20 to + /// equivalent, printable, Unicode chars. + /// + /// This is a Terminal.Gui extension method to to support TUI text manipulation. + /// The text. + /// + public static string MakePrintable (this string str) + { + if (str.Length > 1) + { + return str; + } + + char ch = str [0]; + + return char.IsControl (ch) ? new ((char)(ch + 0x2400), 1) : str; + } } diff --git a/Terminal.Gui/Text/TextFormatter.cs b/Terminal.Gui/Text/TextFormatter.cs index 70636c018b..532f348008 100644 --- a/Terminal.Gui/Text/TextFormatter.cs +++ b/Terminal.Gui/Text/TextFormatter.cs @@ -126,9 +126,10 @@ public void Draw ( break; } - Rune [] runes = linesFormatted [line].ToRunes (); + string strings = linesFormatted [line]; + string[] graphemes = GraphemeHelper.GetGraphemes (strings).ToArray (); - // When text is justified, we lost left or right, so we use the direction to align. + // When text is justified, we lost left or right, so we use the direction to align. int x = 0, y = 0; @@ -143,7 +144,7 @@ public void Draw ( } else { - int runesWidth = StringExtensions.ToString (runes).GetColumns (); + int runesWidth = strings.GetColumns (); x = screen.Right - runesWidth; CursorPosition = screen.Width - runesWidth + (_hotKeyPos > -1 ? _hotKeyPos : 0); } @@ -199,7 +200,7 @@ public void Draw ( } else { - int runesWidth = StringExtensions.ToString (runes).GetColumns (); + int runesWidth = strings.GetColumns (); x = screen.Left + (screen.Width - runesWidth) / 2; CursorPosition = (screen.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0); @@ -217,7 +218,7 @@ public void Draw ( { if (isVertical) { - y = screen.Bottom - runes.Length; + y = screen.Bottom - graphemes.Length; } else { @@ -253,7 +254,7 @@ public void Draw ( { if (isVertical) { - int s = (screen.Height - runes.Length) / 2; + int s = (screen.Height - graphemes.Length) / 2; y = screen.Top + s; } else @@ -274,14 +275,14 @@ public void Draw ( int size = isVertical ? screen.Height : screen.Width; int current = start + colOffset; List lastZeroWidthPos = null!; - Rune rune = default; - int zeroLengthCount = isVertical ? runes.Sum (r => r.GetColumns () == 0 ? 1 : 0) : 0; + string text = default; + int zeroLengthCount = isVertical ? strings.EnumerateRunes ().Sum (r => r.GetColumns () == 0 ? 1 : 0) : 0; for (int idx = (isVertical ? start - y : start - x) + colOffset; current < start + size + zeroLengthCount; idx++) { - Rune lastRuneUsed = rune; + string lastTextUsed = text; if (lastZeroWidthPos is null) { @@ -295,17 +296,17 @@ public void Draw ( continue; } - if (!FillRemaining && idx > runes.Length - 1) + if (!FillRemaining && idx > graphemes.Length - 1) { break; } if ((!isVertical && (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset - || (idx < runes.Length && runes [idx].GetColumns () > screen.Width))) + || (idx < graphemes.Length && graphemes [idx].GetColumns () > screen.Width))) || (isVertical && ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y) - || (idx < runes.Length && runes [idx].GetColumns () > screen.Width)))) + || (idx < graphemes.Length && graphemes [idx].GetColumns () > screen.Width)))) { break; } @@ -316,13 +317,13 @@ public void Draw ( // break; - rune = (Rune)' '; + text = " "; if (isVertical) { - if (idx >= 0 && idx < runes.Length) + if (idx >= 0 && idx < graphemes.Length) { - rune = runes [idx]; + text = graphemes [idx]; } if (lastZeroWidthPos is null) @@ -338,7 +339,7 @@ public void Draw ( if (foundIdx > -1) { - if (rune.IsCombiningMark ()) + if (Rune.GetRuneAt (text, 0).IsCombiningMark ()) { lastZeroWidthPos [foundIdx] = new Point ( @@ -351,7 +352,7 @@ public void Draw ( current ); } - else if (!rune.IsCombiningMark () && lastRuneUsed.IsCombiningMark ()) + else if (!Rune.GetRuneAt (text, 0).IsCombiningMark () && Rune.GetRuneAt (lastTextUsed, 0).IsCombiningMark ()) { current++; driver?.Move (x, current); @@ -371,13 +372,13 @@ public void Draw ( { driver?.Move (current, y); - if (idx >= 0 && idx < runes.Length) + if (idx >= 0 && idx < graphemes.Length) { - rune = runes [idx]; + text = graphemes [idx]; } } - int runeWidth = GetRuneWidth (rune, TabWidth); + int runeWidth = GetTextWidth (text, TabWidth); if (HotKeyPos > -1 && idx == HotKeyPos) { @@ -387,7 +388,7 @@ public void Draw ( } driver?.SetAttribute (hotColor); - driver?.AddRune (rune); + driver?.AddStr (text); driver?.SetAttribute (normalColor); } else @@ -416,7 +417,7 @@ public void Draw ( } } - driver?.AddRune (rune); + driver?.AddStr (text); } if (isVertical) @@ -431,11 +432,11 @@ public void Draw ( current += runeWidth; } - int nextRuneWidth = idx + 1 > -1 && idx + 1 < runes.Length - ? runes [idx + 1].GetColumns () + int nextRuneWidth = idx + 1 > -1 && idx + 1 < graphemes.Length + ? graphemes [idx + 1].GetColumns () : 0; - if (!isVertical && idx + 1 < runes.Length && current + nextRuneWidth > start + size) + if (!isVertical && idx + 1 < graphemes.Length && current + nextRuneWidth > start + size) { break; } @@ -933,9 +934,10 @@ public Region GetDrawRegion (Rectangle screen, Rectangle maximum = default) break; } - Rune [] runes = linesFormatted [line].ToRunes (); + string strings = linesFormatted [line]; + string [] graphemes = GraphemeHelper.GetGraphemes (strings).ToArray (); - // When text is justified, we lost left or right, so we use the direction to align. + // When text is justified, we lost left or right, so we use the direction to align. int x = 0, y = 0; switch (Alignment) @@ -950,17 +952,17 @@ public Region GetDrawRegion (Rectangle screen, Rectangle maximum = default) } case Alignment.End: { - int runesWidth = StringExtensions.ToString (runes).GetColumns (); - x = screen.Right - runesWidth; + int stringsWidth = strings.GetColumns (); + x = screen.Right - stringsWidth; break; } case Alignment.Start when isVertical: { - int runesWidth = line > 0 - ? GetColumnsRequiredForVerticalText (linesFormatted, 0, line, TabWidth) - : 0; - x = screen.Left + runesWidth; + int stringsWidth = line > 0 + ? GetColumnsRequiredForVerticalText (linesFormatted, 0, line, TabWidth) + : 0; + x = screen.Left + stringsWidth; break; } @@ -970,7 +972,7 @@ public Region GetDrawRegion (Rectangle screen, Rectangle maximum = default) break; case Alignment.Fill when isVertical: { - int runesWidth = GetColumnsRequiredForVerticalText (linesFormatted, 0, linesFormatted.Count, TabWidth); + int stringsWidth = GetColumnsRequiredForVerticalText (linesFormatted, 0, linesFormatted.Count, TabWidth); int prevLineWidth = line > 0 ? GetColumnsRequiredForVerticalText (linesFormatted, line - 1, 1, TabWidth) : 0; int firstLineWidth = GetColumnsRequiredForVerticalText (linesFormatted, 0, 1, TabWidth); int lastLineWidth = GetColumnsRequiredForVerticalText (linesFormatted, linesFormatted.Count - 1, 1, TabWidth); @@ -979,7 +981,7 @@ public Region GetDrawRegion (Rectangle screen, Rectangle maximum = default) x = line == 0 ? screen.Left : line < linesFormatted.Count - 1 - ? screen.Width - runesWidth <= lastLineWidth ? screen.Left + prevLineWidth : screen.Left + line * interval + ? screen.Width - stringsWidth <= lastLineWidth ? screen.Left + prevLineWidth : screen.Left + line * interval : screen.Right - lastLineWidth; break; @@ -990,16 +992,16 @@ public Region GetDrawRegion (Rectangle screen, Rectangle maximum = default) break; case Alignment.Center when isVertical: { - int runesWidth = GetColumnsRequiredForVerticalText (linesFormatted, 0, linesFormatted.Count, TabWidth); + int stringsWidth = GetColumnsRequiredForVerticalText (linesFormatted, 0, linesFormatted.Count, TabWidth); int linesWidth = GetColumnsRequiredForVerticalText (linesFormatted, 0, line, TabWidth); - x = screen.Left + linesWidth + (screen.Width - runesWidth) / 2; + x = screen.Left + linesWidth + (screen.Width - stringsWidth) / 2; break; } case Alignment.Center: { - int runesWidth = StringExtensions.ToString (runes).GetColumns (); - x = screen.Left + (screen.Width - runesWidth) / 2; + int stringsWidth = strings.GetColumns (); + x = screen.Left + (screen.Width - stringsWidth) / 2; break; } @@ -1013,7 +1015,7 @@ public Region GetDrawRegion (Rectangle screen, Rectangle maximum = default) { // Vertical Alignment case Alignment.End when isVertical: - y = screen.Bottom - runes.Length; + y = screen.Bottom - graphemes.Length; break; case Alignment.End: @@ -1043,7 +1045,7 @@ public Region GetDrawRegion (Rectangle screen, Rectangle maximum = default) } case Alignment.Center when isVertical: { - int s = (screen.Height - runes.Length) / 2; + int s = (screen.Height - graphemes.Length) / 2; y = screen.Top + s; break; @@ -1065,7 +1067,7 @@ public Region GetDrawRegion (Rectangle screen, Rectangle maximum = default) int start = isVertical ? screen.Top : screen.Left; int size = isVertical ? screen.Height : screen.Width; int current = start + colOffset; - int zeroLengthCount = isVertical ? runes.Sum (r => r.GetColumns () == 0 ? 1 : 0) : 0; + int zeroLengthCount = isVertical ? strings.EnumerateRunes ().Sum (r => r.GetColumns () == 0 ? 1 : 0) : 0; int lineX = x, lineY = y, lineWidth = 0, lineHeight = 1; @@ -1083,23 +1085,23 @@ public Region GetDrawRegion (Rectangle screen, Rectangle maximum = default) continue; } - if (!FillRemaining && idx > runes.Length - 1) + if (!FillRemaining && idx > graphemes.Length - 1) { break; } if ((!isVertical && (current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset - || (idx < runes.Length && runes [idx].GetColumns () > screen.Width))) + || (idx < graphemes.Length && graphemes [idx].GetColumns () > screen.Width))) || (isVertical && ((current > start + size + zeroLengthCount && idx > maxScreen.Top + maxScreen.Height - screen.Y) - || (idx < runes.Length && runes [idx].GetColumns () > screen.Width)))) + || (idx < graphemes.Length && graphemes [idx].GetColumns () > screen.Width)))) { break; } - Rune rune = idx >= 0 && idx < runes.Length ? runes [idx] : (Rune)' '; - int runeWidth = GetRuneWidth (rune, TabWidth); + string text = idx >= 0 && idx < graphemes.Length ? graphemes [idx] : " "; + int runeWidth = GetStringWidth (text, TabWidth); if (isVertical) { @@ -1118,11 +1120,11 @@ public Region GetDrawRegion (Rectangle screen, Rectangle maximum = default) current += isVertical && runeWidth > 0 ? 1 : runeWidth; - int nextRuneWidth = idx + 1 > -1 && idx + 1 < runes.Length - ? runes [idx + 1].GetColumns () + int nextStringWidth = idx + 1 > -1 && idx + 1 < graphemes.Length + ? graphemes [idx + 1].GetColumns () : 0; - if (!isVertical && idx + 1 < runes.Length && current + nextRuneWidth > start + size) + if (!isVertical && idx + 1 < graphemes.Length && current + nextStringWidth > start + size) { break; } @@ -1335,33 +1337,34 @@ private static string ReplaceTABWithSpaces (string str, int tabWidth) /// A list of text without the newline characters. public static List SplitNewLine (string text) { - List runes = text.ToRuneList (); + List graphemes = GraphemeHelper.GetGraphemes (text).ToList (); List lines = new (); var start = 0; - for (var i = 0; i < runes.Count; i++) + for (var i = 0; i < graphemes.Count; i++) { int end = i; - switch (runes [i].Value) + switch (graphemes [i]) { - case '\n': - lines.Add (StringExtensions.ToString (runes.GetRange (start, end - start))); + case "\n": + case "\r\n": + lines.Add (StringExtensions.ToString (graphemes.GetRange (start, end - start))); i++; start = i; break; - case '\r': - if (i + 1 < runes.Count && runes [i + 1].Value == '\n') + case "\r": + if (i + 1 < graphemes.Count && graphemes [i + 1] == "\n") { - lines.Add (StringExtensions.ToString (runes.GetRange (start, end - start))); + lines.Add (StringExtensions.ToString (graphemes.GetRange (start, end - start))); i += 2; start = i; } else { - lines.Add (StringExtensions.ToString (runes.GetRange (start, end - start))); + lines.Add (StringExtensions.ToString (graphemes.GetRange (start, end - start))); i++; start = i; } @@ -1370,14 +1373,14 @@ public static List SplitNewLine (string text) } } - switch (runes.Count) + switch (graphemes.Count) { case > 0 when lines.Count == 0: - lines.Add (StringExtensions.ToString (runes)); + lines.Add (StringExtensions.ToString (graphemes)); break; - case > 0 when start < runes.Count: - lines.Add (StringExtensions.ToString (runes.GetRange (start, runes.Count - start))); + case > 0 when start < graphemes.Count: + lines.Add (StringExtensions.ToString (graphemes.GetRange (start, graphemes.Count - start))); break; default: @@ -1405,16 +1408,19 @@ public static string ClipOrPad (string text, int width) } // if value is not wide enough - if (text.EnumerateRunes ().Sum (c => c.GetColumns ()) < width) + string [] graphemes = GraphemeHelper.GetGraphemes (text).ToArray (); + int totalColumns = graphemes.Sum (s => s.GetColumns ()); + + if (totalColumns < width) { // pad it out with spaces to the given Alignment - int toPad = width - text.EnumerateRunes ().Sum (c => c.GetColumns ()); + int toPad = width - totalColumns; return text + new string (' ', toPad); } // value is too wide - return new (text.TakeWhile (c => (width -= ((Rune)c).GetColumns ()) >= 0).ToArray ()); + return string.Concat (graphemes.TakeWhile (t => (width -= t.GetColumns ()) >= 0)); } /// Formats the provided text to fit within the width provided using word wrapping. @@ -1455,18 +1461,18 @@ public static List WordWrapText ( return lines; } - List runes = StripCRLF (text).ToRuneList (); + List graphemes = GraphemeHelper.GetGraphemes (StripCRLF (text)).ToList (); int start = Math.Max ( - !runes.Contains ((Rune)' ') && textFormatter is { VerticalAlignment: Alignment.End } && IsVerticalDirection (textDirection) - ? runes.Count - width + !graphemes.Contains (" ") && textFormatter is { VerticalAlignment: Alignment.End } && IsVerticalDirection (textDirection) + ? graphemes.Count - width : 0, 0); int end; if (preserveTrailingSpaces) { - while ((end = start) < runes.Count) + while ((end = start) < graphemes.Count) { end = GetNextWhiteSpace (start, width, out bool incomplete); @@ -1477,7 +1483,7 @@ public static List WordWrapText ( break; } - lines.Add (StringExtensions.ToString (runes.GetRange (start, end - start))); + lines.Add (StringExtensions.ToString (graphemes.GetRange (start, end - start))); start = end; if (incomplete) @@ -1494,14 +1500,14 @@ public static List WordWrapText ( { while ((end = start + GetLengthThatFits ( - runes.GetRange (start, runes.Count - start), + string.Concat (graphemes.GetRange (start, graphemes.Count - start)), width, tabWidth, textDirection )) - < runes.Count) + < graphemes.Count) { - while (runes [end].Value != ' ' && end > start) + while (graphemes [end] != " " && end > start) { end--; } @@ -1510,22 +1516,22 @@ public static List WordWrapText ( { end = start + GetLengthThatFits ( - runes.GetRange (end, runes.Count - end), + string.Concat (graphemes.GetRange (end, graphemes.Count - end)), width, tabWidth, textDirection ); } - var str = StringExtensions.ToString (runes.GetRange (start, end - start)); + var str = StringExtensions.ToString (graphemes.GetRange (start, end - start)); int zeroLength = text.EnumerateRunes ().Sum (r => r.GetColumns () == 0 ? 1 : 0); - if (end > start && GetRuneWidth (str, tabWidth, textDirection) <= width + zeroLength) + if (end > start && GetTextWidth (str, tabWidth, textDirection) <= width + zeroLength) { lines.Add (str); start = end; - if (runes [end].Value == ' ') + if (graphemes [end] == " ") { start++; } @@ -1539,9 +1545,9 @@ public static List WordWrapText ( } else { - while ((end = start + width) < runes.Count) + while ((end = start + width) < graphemes.Count) { - while (runes [end].Value != ' ' && end > start) + while (graphemes [end] != " " && end > start) { end--; } @@ -1553,11 +1559,11 @@ public static List WordWrapText ( var zeroLength = 0; - for (int i = end; i < runes.Count - start; i++) + for (int i = end; i < graphemes.Count - start; i++) { - Rune r = runes [i]; + string s = graphemes [i]; - if (r.GetColumns () == 0) + if (s.GetColumns () == 0) { zeroLength++; } @@ -1569,7 +1575,7 @@ public static List WordWrapText ( lines.Add ( StringExtensions.ToString ( - runes.GetRange ( + graphemes.GetRange ( start, end - start + zeroLength ) @@ -1578,7 +1584,7 @@ public static List WordWrapText ( end += zeroLength; start = end; - if (runes [end].Value == ' ') + if (graphemes [end] == " ") { start++; } @@ -1592,13 +1598,13 @@ int GetNextWhiteSpace (int from, int cWidth, out bool incomplete, int cLength = int length = cLength; incomplete = false; - while (length < cWidth && to < runes.Count) + while (length < cWidth && to < graphemes.Count) { - Rune rune = runes [to]; + string grapheme = graphemes [to]; if (IsHorizontalDirection (textDirection)) { - length += rune.GetColumns (); + length += grapheme.GetColumns (false); } else { @@ -1607,7 +1613,7 @@ int GetNextWhiteSpace (int from, int cWidth, out bool incomplete, int cLength = if (length > cWidth) { - if (to >= runes.Count || (length > 1 && cWidth <= 1)) + if (to >= graphemes.Count || (length > 1 && cWidth <= 1)) { incomplete = true; } @@ -1615,15 +1621,15 @@ int GetNextWhiteSpace (int from, int cWidth, out bool incomplete, int cLength = return to; } - switch (rune.Value) + switch (grapheme) { - case ' ' when length == cWidth: + case " " when length == cWidth: return to + 1; - case ' ' when length > cWidth: + case " " when length > cWidth: return to; - case ' ': + case " ": return GetNextWhiteSpace (to + 1, cWidth, out incomplete, length); - case '\t': + case "\t": { length += tabWidth + 1; @@ -1648,8 +1654,8 @@ int GetNextWhiteSpace (int from, int cWidth, out bool incomplete, int cLength = return cLength switch { - > 0 when to < runes.Count && runes [to].Value != ' ' && runes [to].Value != '\t' => from, - > 0 when to < runes.Count && (runes [to].Value == ' ' || runes [to].Value == '\t') => from, + > 0 when to < graphemes.Count && graphemes [to] != " " && graphemes [to] != "\t" => from, + > 0 when to < graphemes.Count && (graphemes [to] == " " || graphemes [to] == "\t") => from, _ => to }; } @@ -1657,7 +1663,7 @@ int GetNextWhiteSpace (int from, int cWidth, out bool incomplete, int cLength = if (start < text.GetRuneCount ()) { string str = ReplaceTABWithSpaces ( - StringExtensions.ToString (runes.GetRange (start, runes.Count - start)), + StringExtensions.ToString (graphemes.GetRange (start, graphemes.Count - start)), tabWidth ); @@ -1721,42 +1727,42 @@ public static string ClipAndJustify ( } text = ReplaceTABWithSpaces (text, tabWidth); - List runes = text.ToRuneList (); - int zeroLength = runes.Sum (r => r.GetColumns () == 0 ? 1 : 0); + List graphemes = GraphemeHelper.GetGraphemes (text).ToList (); + int zeroLength = graphemes.Sum (s => s.EnumerateRunes ().Sum (r => r.GetColumns() == 0 ? 1 : 0)); - if (runes.Count - zeroLength > width) + if (graphemes.Count - zeroLength > width) { if (IsHorizontalDirection (textDirection)) { if (textFormatter is { Alignment: Alignment.End }) { - return GetRangeThatFits (runes, runes.Count - width, text, width, tabWidth, textDirection); + return GetRangeThatFits (graphemes, graphemes.Count - width, text, width, tabWidth, textDirection); } if (textFormatter is { Alignment: Alignment.Center }) { - return GetRangeThatFits (runes, Math.Max ((runes.Count - width - zeroLength) / 2, 0), text, width, tabWidth, textDirection); + return GetRangeThatFits (graphemes, Math.Max ((graphemes.Count - width - zeroLength) / 2, 0), text, width, tabWidth, textDirection); } - return GetRangeThatFits (runes, 0, text, width, tabWidth, textDirection); + return GetRangeThatFits (graphemes, 0, text, width, tabWidth, textDirection); } if (IsVerticalDirection (textDirection)) { if (textFormatter is { VerticalAlignment: Alignment.End }) { - return GetRangeThatFits (runes, runes.Count - width, text, width, tabWidth, textDirection); + return GetRangeThatFits (graphemes, graphemes.Count - width, text, width, tabWidth, textDirection); } if (textFormatter is { VerticalAlignment: Alignment.Center }) { - return GetRangeThatFits (runes, Math.Max ((runes.Count - width - zeroLength) / 2, 0), text, width, tabWidth, textDirection); + return GetRangeThatFits (graphemes, Math.Max ((graphemes.Count - width - zeroLength) / 2, 0), text, width, tabWidth, textDirection); } - return GetRangeThatFits (runes, 0, text, width, tabWidth, textDirection); + return GetRangeThatFits (graphemes, 0, text, width, tabWidth, textDirection); } - return StringExtensions.ToString (runes.GetRange (0, width + zeroLength)); + return StringExtensions.ToString (graphemes.GetRange (0, width + zeroLength)); } if (justify) @@ -1768,18 +1774,18 @@ public static string ClipAndJustify ( { if (textFormatter is { Alignment: Alignment.End }) { - if (GetRuneWidth (text, tabWidth, textDirection) > width) + if (GetTextWidth (text, tabWidth, textDirection) > width) { - return GetRangeThatFits (runes, runes.Count - width, text, width, tabWidth, textDirection); + return GetRangeThatFits (graphemes, graphemes.Count - width, text, width, tabWidth, textDirection); } } else if (textFormatter is { Alignment: Alignment.Center }) { - return GetRangeThatFits (runes, Math.Max ((runes.Count - width - zeroLength) / 2, 0), text, width, tabWidth, textDirection); + return GetRangeThatFits (graphemes, Math.Max ((graphemes.Count - width - zeroLength) / 2, 0), text, width, tabWidth, textDirection); } - else if (GetRuneWidth (text, tabWidth, textDirection) > width) + else if (GetTextWidth (text, tabWidth, textDirection) > width) { - return GetRangeThatFits (runes, 0, text, width, tabWidth, textDirection); + return GetRangeThatFits (graphemes, 0, text, width, tabWidth, textDirection); } } @@ -1787,28 +1793,28 @@ public static string ClipAndJustify ( { if (textFormatter is { VerticalAlignment: Alignment.End }) { - if (runes.Count - zeroLength > width) + if (graphemes.Count - zeroLength > width) { - return GetRangeThatFits (runes, runes.Count - width, text, width, tabWidth, textDirection); + return GetRangeThatFits (graphemes, graphemes.Count - width, text, width, tabWidth, textDirection); } } else if (textFormatter is { VerticalAlignment: Alignment.Center }) { - return GetRangeThatFits (runes, Math.Max ((runes.Count - width - zeroLength) / 2, 0), text, width, tabWidth, textDirection); + return GetRangeThatFits (graphemes, Math.Max ((graphemes.Count - width - zeroLength) / 2, 0), text, width, tabWidth, textDirection); } - else if (runes.Count - zeroLength > width) + else if (graphemes.Count - zeroLength > width) { - return GetRangeThatFits (runes, 0, text, width, tabWidth, textDirection); + return GetRangeThatFits (graphemes, 0, text, width, tabWidth, textDirection); } } return text; } - private static string GetRangeThatFits (List runes, int index, string text, int width, int tabWidth, TextDirection textDirection) + private static string GetRangeThatFits (List strings, int index, string text, int width, int tabWidth, TextDirection textDirection) { return StringExtensions.ToString ( - runes.GetRange ( + strings.GetRange ( Math.Max (index, 0), GetLengthThatFits (text, width, tabWidth, textDirection) ) @@ -1846,7 +1852,7 @@ public static string Justify ( if (IsHorizontalDirection (textDirection)) { - textCount = words.Sum (arg => GetRuneWidth (arg, tabWidth, textDirection)); + textCount = words.Sum (arg => GetTextWidth (arg, tabWidth, textDirection)); } else { @@ -2141,11 +2147,11 @@ public static int GetColumnsRequiredForVerticalText ( i < (linesCount == -1 ? lines.Count : startLine + linesCount); i++) { - string runes = lines [i]; + string strings = lines [i]; - if (runes.Length > 0) + if (strings.Length > 0) { - max += runes.EnumerateRunes ().Max (r => GetRuneWidth (r, tabWidth)); + max += strings.EnumerateRunes ().Max (r => GetRuneWidth (r, tabWidth)); } } @@ -2167,7 +2173,7 @@ public static int GetWidestLineLength (string text, int tabWidth = 0) { List result = SplitNewLine (text); - return result.Max (x => GetRuneWidth (x, tabWidth)); + return result.Max (x => GetTextWidth (x, tabWidth)); } /// @@ -2186,13 +2192,13 @@ public static int GetWidestLineLength (string text, int tabWidth = 0) public static int GetSumMaxCharWidth (string text, int startIndex = -1, int length = -1, int tabWidth = 0) { var max = 0; - Rune [] runes = text.ToRunes (); + string [] graphemes = GraphemeHelper.GetGraphemes (text).ToArray (); for (int i = startIndex == -1 ? 0 : startIndex; - i < (length == -1 ? runes.Length : startIndex + length); + i < (length == -1 ? graphemes.Length : startIndex + length); i++) { - max += GetRuneWidth (runes [i], tabWidth); + max += GetStringWidth (graphemes [i], tabWidth); } return max; @@ -2210,51 +2216,38 @@ public static int GetSumMaxCharWidth (string text, int startIndex = -1, int leng /// The index of the text that fit the width. public static int GetLengthThatFits (string text, int width, int tabWidth = 0, TextDirection textDirection = TextDirection.LeftRight_TopBottom) { - return GetLengthThatFits (text?.ToRuneList () ?? [], width, tabWidth, textDirection); - } - - /// Gets the number of the Runes in a list of Runes that will fit in . - /// - /// This API will return incorrect results if the text includes glyphs whose width is dependent on surrounding - /// glyphs (e.g. Arabic). - /// - /// The list of runes. - /// The width. - /// The width used for a tab. - /// The text direction. - /// The index of the last Rune in that fit in . - public static int GetLengthThatFits (List runes, int width, int tabWidth = 0, TextDirection textDirection = TextDirection.LeftRight_TopBottom) - { - if (runes is null || runes.Count == 0) + if (string.IsNullOrEmpty (text)) { return 0; } - var runesLength = 0; - var runeIdx = 0; + var textLength = 0; + var stringIdx = 0; - for (; runeIdx < runes.Count; runeIdx++) + foreach (string grapheme in GraphemeHelper.GetGraphemes (text)) { - int runeWidth = GetRuneWidth (runes [runeIdx], tabWidth, textDirection); + int textWidth = GetStringWidth (grapheme, tabWidth, textDirection); - if (runesLength + runeWidth > width) + if (textLength + textWidth > width) { break; } - runesLength += runeWidth; + textLength += textWidth; + stringIdx++; } - return runeIdx; + return stringIdx; } - private static int GetRuneWidth (string str, int tabWidth, TextDirection textDirection = TextDirection.LeftRight_TopBottom) + private static int GetTextWidth (string str, int tabWidth, TextDirection textDirection = TextDirection.LeftRight_TopBottom) { int runesWidth = 0; - foreach (Rune rune in str.EnumerateRunes ()) + foreach (string grapheme in GraphemeHelper.GetGraphemes (str)) { - runesWidth += GetRuneWidth (rune, tabWidth, textDirection); + runesWidth += GetStringWidth (grapheme, tabWidth, textDirection); } + return runesWidth; } @@ -2275,6 +2268,23 @@ private static int GetRuneWidth (Rune rune, int tabWidth, TextDirection textDire return runeWidth; } + private static int GetStringWidth (string str, int tabWidth, TextDirection textDirection = TextDirection.LeftRight_TopBottom) + { + int textWidth = IsHorizontalDirection (textDirection) ? str.GetColumns (false) : str.GetColumns () == 0 ? 0 : 1; + + if (str == "\t") + { + return tabWidth; + } + + if (textWidth is < 0 or > 0) + { + return Math.Max (textWidth, 1); + } + + return textWidth; + } + /// Gets the index position from the list based on the . /// /// This API will return incorrect results if the text includes glyphs whose width is dependent on surrounding @@ -2286,23 +2296,23 @@ private static int GetRuneWidth (Rune rune, int tabWidth, TextDirection textDire /// The index of the list that fit the width. public static int GetMaxColsForWidth (List lines, int width, int tabWidth = 0) { - var runesLength = 0; + var textLength = 0; var lineIdx = 0; for (; lineIdx < lines.Count; lineIdx++) { - List runes = lines [lineIdx].ToRuneList (); + string [] graphemes = GraphemeHelper.GetGraphemes (lines [lineIdx]).ToArray (); - int maxRruneWidth = runes.Count > 0 - ? runes.Max (r => GetRuneWidth (r, tabWidth)) + int maxTextWidth = graphemes.Length > 0 + ? graphemes.Max (r => GetStringWidth (r, tabWidth)) : 1; - if (runesLength + maxRruneWidth > width) + if (textLength + maxTextWidth > width) { break; } - runesLength += maxRruneWidth; + textLength += maxTextWidth; } return lineIdx; diff --git a/Terminal.Gui/ViewBase/Adornment/ShadowView.cs b/Terminal.Gui/ViewBase/Adornment/ShadowView.cs index 151aa149ca..b9c2e4274c 100644 --- a/Terminal.Gui/ViewBase/Adornment/ShadowView.cs +++ b/Terminal.Gui/ViewBase/Adornment/ShadowView.cs @@ -100,7 +100,7 @@ private void DrawHorizontalShadowTransparent (Rectangle viewport) if (c < ScreenContents?.GetLength (1) && r < ScreenContents?.GetLength (0)) { - AddRune (ScreenContents [r, c].Rune); + AddStr (ScreenContents [r, c].Grapheme); } } } @@ -134,7 +134,7 @@ private void DrawVerticalShadowTransparent (Rectangle viewport) if (ScreenContents is { } && screen.X < ScreenContents.GetLength (1) && r < ScreenContents.GetLength (0)) { - AddRune (ScreenContents [r, c].Rune); + AddStr (ScreenContents [r, c].Grapheme); } } } @@ -142,7 +142,7 @@ private void DrawVerticalShadowTransparent (Rectangle viewport) private Attribute GetAttributeUnderLocation (Point location) { - if (SuperView is not Adornment adornment + if (SuperView is not Adornment || location.X < 0 || location.X >= Application.Screen.Width || location.Y < 0 @@ -170,8 +170,8 @@ private Attribute GetAttributeUnderLocation (Point location) // use the Normal attribute from the View under the shadow. if (newAttribute.Background == Color.DarkGray) { - List currentViewsUnderMouse = View.GetViewsUnderLocation (location, ViewportSettingsFlags.Transparent); - View? underView = currentViewsUnderMouse!.LastOrDefault (); + List currentViewsUnderMouse = GetViewsUnderLocation (location, ViewportSettingsFlags.Transparent); + View? underView = currentViewsUnderMouse.LastOrDefault (); attr = underView?.GetAttributeForRole (VisualRole.Normal) ?? Attribute.Default; newAttribute = new ( diff --git a/Terminal.Gui/ViewBase/View.Drawing.Primitives.cs b/Terminal.Gui/ViewBase/View.Drawing.Primitives.cs index d9d9333ee2..c81ffdbd30 100644 --- a/Terminal.Gui/ViewBase/View.Drawing.Primitives.cs +++ b/Terminal.Gui/ViewBase/View.Drawing.Primitives.cs @@ -32,7 +32,6 @@ public void AddRune (Rune rune) Driver?.AddRune (rune); } - /// /// Adds the specified to the display at the current cursor position. This method is a /// convenience method that calls with the constructor. @@ -72,6 +71,25 @@ public void AddStr (string str) { Driver?.AddStr (str); } + + /// Draws the specified in the specified viewport-relative column and row of the View. + /// + /// If the provided coordinates are outside the visible content area, this method does nothing. + /// + /// + /// The top-left corner of the visible content area is ViewPort.Location. + /// + /// Column (viewport-relative). + /// Row (viewport-relative). + /// The Text. + public void AddStr (int col, int row, string str) + { + if (Move (col, row)) + { + Driver?.AddStr (str); + } + } + /// Utility function to draw strings that contain a hotkey. /// String to display, the hotkey specifier before a letter flags the next letter as the hotkey. /// Hot color. diff --git a/Terminal.Gui/ViewBase/View.Drawing.cs b/Terminal.Gui/ViewBase/View.Drawing.cs index 1f27561238..82c1dc421e 100644 --- a/Terminal.Gui/ViewBase/View.Drawing.cs +++ b/Terminal.Gui/ViewBase/View.Drawing.cs @@ -195,11 +195,11 @@ internal void DoDrawAdornments (Region? originalClip) else { // Set the clip to be just the thicknesses of the adornments - // TODO: Put this union logic in a method on View? + // TODO: Put this union logic in a method on View? Region? clipAdornments = Margin!.Thickness.AsRegion (Margin!.FrameToScreen ()); - clipAdornments?.Combine (Border!.Thickness.AsRegion (Border!.FrameToScreen ()), RegionOp.Union); - clipAdornments?.Combine (Padding!.Thickness.AsRegion (Padding!.FrameToScreen ()), RegionOp.Union); - clipAdornments?.Combine (originalClip, RegionOp.Intersect); + clipAdornments.Combine (Border!.Thickness.AsRegion (Border!.FrameToScreen ()), RegionOp.Union); + clipAdornments.Combine (Padding!.Thickness.AsRegion (Padding!.FrameToScreen ()), RegionOp.Union); + clipAdornments.Combine (originalClip, RegionOp.Intersect); SetClip (clipAdornments); } @@ -240,7 +240,7 @@ public void DrawAdornments () { // We do not attempt to draw Margin. It is drawn in a separate pass. - // Each of these renders lines to this View's LineCanvas + // Each of these renders lines to this View's LineCanvas // Those lines will be finally rendered in OnRenderLineCanvas if (Border is { } && Border.Thickness != Thickness.Empty) { @@ -446,7 +446,7 @@ public void DrawText (DrawContext? context = null) // Report the drawn area to the context context?.AddDrawnRegion (textRegion); - TextFormatter?.Draw ( + TextFormatter.Draw ( drawRect, HasFocus ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal), HasFocus ? GetAttributeForRole (VisualRole.HotFocus) : GetAttributeForRole (VisualRole.HotNormal), @@ -658,7 +658,7 @@ public void RenderLineCanvas () Driver.Move (p.Key.X, p.Key.Y); // TODO: #2616 - Support combining sequences that don't normalize - AddRune (p.Value.Value.Rune); + AddStr (p.Value.Value.Grapheme); } } @@ -685,7 +685,7 @@ private void DoDrawComplete (DrawContext? context) context!.ClipDrawnRegion (ViewportToScreen (Viewport)); // Exclude the drawn region from the clip - ExcludeFromClip (context!.GetDrawnRegion ()); + ExcludeFromClip (context.GetDrawnRegion ()); // Exclude the Border and Padding from the clip ExcludeFromClip (Border?.Thickness.AsRegion (Border.FrameToScreen ())); diff --git a/Terminal.Gui/Views/Autocomplete/AutocompleteFilepathContext.cs b/Terminal.Gui/Views/Autocomplete/AutocompleteFilepathContext.cs index 6c3e12c34d..d5d8240fa5 100644 --- a/Terminal.Gui/Views/Autocomplete/AutocompleteFilepathContext.cs +++ b/Terminal.Gui/Views/Autocomplete/AutocompleteFilepathContext.cs @@ -11,16 +11,16 @@ internal class AutocompleteFilepathContext (string currentLine, int cursorPositi internal class FilepathSuggestionGenerator : ISuggestionGenerator { - private FileDialogState state; + private FileDialogState _state; public IEnumerable GenerateSuggestions (AutocompleteContext context) { if (context is AutocompleteFilepathContext fileState) { - state = fileState.State; + _state = fileState.State; } - if (state is null) + if (_state is null) { return Enumerable.Empty (); } @@ -41,7 +41,7 @@ public IEnumerable GenerateSuggestions (AutocompleteContext context) return Enumerable.Empty (); } - if (term.Equals (state?.Directory?.Name)) + if (term.Equals (_state?.Directory?.Name)) { // Clear suggestions return Enumerable.Empty (); @@ -49,13 +49,13 @@ public IEnumerable GenerateSuggestions (AutocompleteContext context) bool isWindows = RuntimeInformation.IsOSPlatform (OSPlatform.Windows); - string [] suggestions = state.Children.Where (d => !d.IsParent) - .Select ( - e => e.FileSystemInfo is IDirectoryInfo d - ? d.Name + Path.DirectorySeparatorChar - : e.FileSystemInfo.Name - ) - .ToArray (); + string [] suggestions = _state!.Children.Where (d => !d.IsParent) + .Select ( + e => e.FileSystemInfo is IDirectoryInfo d + ? d.Name + Path.DirectorySeparatorChar + : e.FileSystemInfo.Name + ) + .ToArray (); string [] validSuggestions = suggestions .Where ( @@ -81,9 +81,9 @@ public IEnumerable GenerateSuggestions (AutocompleteContext context) .ToList (); } - public bool IsWordChar (Rune rune) + public bool IsWordChar (string text) { - if (rune.Value == '\n') + if (text == "\n") { return false; } diff --git a/Terminal.Gui/Views/Autocomplete/ISuggestionGenerator.cs b/Terminal.Gui/Views/Autocomplete/ISuggestionGenerator.cs index 08fd17c9a2..9f6c1dd45e 100644 --- a/Terminal.Gui/Views/Autocomplete/ISuggestionGenerator.cs +++ b/Terminal.Gui/Views/Autocomplete/ISuggestionGenerator.cs @@ -7,9 +7,9 @@ public interface ISuggestionGenerator IEnumerable GenerateSuggestions (AutocompleteContext context); /// - /// Returns if is a character that would continue autocomplete + /// Returns if is a character that would continue autocomplete /// suggesting. Returns if it is a 'breaking' character (i.e. terminating current word /// boundary) /// - bool IsWordChar (Rune rune); + bool IsWordChar (string text); } diff --git a/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs b/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs index 602a849d7f..dc912e4271 100644 --- a/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs +++ b/Terminal.Gui/Views/Autocomplete/PopupAutocomplete.cs @@ -189,7 +189,7 @@ public override bool OnMouseEvent (MouseEventArgs me, bool fromHost = false) /// trueif the key can be handled falseotherwise. public override bool ProcessKey (Key key) { - if (SuggestionGenerator.IsWordChar ((Rune)key)) + if (SuggestionGenerator.IsWordChar (key.AsRune.ToString ())) { Visible = true; _closed = false; diff --git a/Terminal.Gui/Views/Autocomplete/SingleWordSuggestionGenerator.cs b/Terminal.Gui/Views/Autocomplete/SingleWordSuggestionGenerator.cs index 1accda9222..77d11acdeb 100644 --- a/Terminal.Gui/Views/Autocomplete/SingleWordSuggestionGenerator.cs +++ b/Terminal.Gui/Views/Autocomplete/SingleWordSuggestionGenerator.cs @@ -17,10 +17,10 @@ public IEnumerable GenerateSuggestions (AutocompleteContext context) // if there is nothing to pick from if (AllSuggestions.Count == 0) { - return Enumerable.Empty (); + return []; } - List line = context.CurrentLine.Select (c => c.Rune).ToList (); + List line = context.CurrentLine.Select (c => c.Grapheme).ToList (); string currentWord = IdxToWord (line, context.CursorPosition, out int startIdx); context.CursorPosition = startIdx < 1 ? startIdx : Math.Min (startIdx + 1, line.Count); @@ -43,9 +43,13 @@ public IEnumerable GenerateSuggestions (AutocompleteContext context) /// Return true if the given symbol should be considered part of a word and can be contained in matches. Base /// behavior is to use /// - /// The rune. + /// The text. /// - public virtual bool IsWordChar (Rune rune) { return char.IsLetterOrDigit ((char)rune.Value); } + public virtual bool IsWordChar (string text) + { + return !string.IsNullOrEmpty (text) + && Rune.IsLetterOrDigit (text.EnumerateRunes ().First ()); + } /// /// @@ -64,7 +68,7 @@ public IEnumerable GenerateSuggestions (AutocompleteContext context) /// The start index of the word. /// /// - protected virtual string IdxToWord (List line, int idx, out int startIdx, int columnOffset = 0) + protected virtual string IdxToWord (List line, int idx, out int startIdx, int columnOffset = 0) { var sb = new StringBuilder (); startIdx = idx; @@ -93,7 +97,7 @@ protected virtual string IdxToWord (List line, int idx, out int startIdx, { if (IsWordChar (line [startIdx])) { - sb.Insert (0, (char)line [startIdx].Value); + sb.Insert (0, line [startIdx]); } else { diff --git a/Terminal.Gui/Views/Slider/Slider.cs b/Terminal.Gui/Views/Slider/Slider.cs index ff0308a560..e064e46d23 100644 --- a/Terminal.Gui/Views/Slider/Slider.cs +++ b/Terminal.Gui/Views/Slider/Slider.cs @@ -73,13 +73,13 @@ private void SetDefaultStyle () switch (_config._sliderOrientation) { case Orientation.Horizontal: - Style.SpaceChar = new () { Rune = Glyphs.HLine }; // '─' - Style.OptionChar = new () { Rune = Glyphs.BlackCircle }; // '┼●🗹□⏹' + Style.SpaceChar = new () { Grapheme = Glyphs.HLine.ToString () }; // '─' + Style.OptionChar = new () { Grapheme = Glyphs.BlackCircle.ToString () }; // '┼●🗹□⏹' break; case Orientation.Vertical: - Style.SpaceChar = new () { Rune = Glyphs.VLine }; - Style.OptionChar = new () { Rune = Glyphs.BlackCircle }; + Style.SpaceChar = new () { Grapheme = Glyphs.VLine.ToString () }; + Style.OptionChar = new () { Grapheme = Glyphs.BlackCircle.ToString () }; break; } @@ -104,12 +104,12 @@ private void SetDefaultStyle () */ _config._legendsOrientation = _config._sliderOrientation; - Style.EmptyChar = new () { Rune = new (' ') }; - Style.SetChar = new () { Rune = Glyphs.ContinuousMeterSegment }; // ■ - Style.RangeChar = new () { Rune = Glyphs.Stipple }; // ░ ▒ ▓ // Medium shade not blinking on curses. - Style.StartRangeChar = new () { Rune = Glyphs.ContinuousMeterSegment }; - Style.EndRangeChar = new () { Rune = Glyphs.ContinuousMeterSegment }; - Style.DragChar = new () { Rune = Glyphs.Diamond }; + Style.EmptyChar = new () { Grapheme = " " }; + Style.SetChar = new () { Grapheme = Glyphs.ContinuousMeterSegment.ToString () }; // ■ + Style.RangeChar = new () { Grapheme = Glyphs.Stipple.ToString () }; // ░ ▒ ▓ // Medium shade not blinking on curses. + Style.StartRangeChar = new () { Grapheme = Glyphs.ContinuousMeterSegment.ToString () }; + Style.EndRangeChar = new () { Grapheme = Glyphs.ContinuousMeterSegment.ToString () }; + Style.DragChar = new () { Grapheme = Glyphs.Diamond.ToString () }; // TODO: Support left & right (top/bottom) // First = '├', @@ -255,11 +255,11 @@ public void OnOrientationChanged (Orientation newOrientation) switch (_config._sliderOrientation) { case Orientation.Horizontal: - Style.SpaceChar = new () { Rune = Glyphs.HLine }; // '─' + Style.SpaceChar = new () { Grapheme = Glyphs.HLine.ToString () }; // '─' break; case Orientation.Vertical: - Style.SpaceChar = new () { Rune = Glyphs.VLine }; + Style.SpaceChar = new () { Grapheme = Glyphs.VLine.ToString () }; break; } @@ -798,7 +798,7 @@ protected override bool OnDrawingContent () if (_dragPosition.HasValue && _moveRenderPosition.HasValue) { - AddRune (_moveRenderPosition.Value.X, _moveRenderPosition.Value.Y, Style.DragChar.Rune); + AddStr (_moveRenderPosition.Value.X, _moveRenderPosition.Value.Y, Style.DragChar.Grapheme); } return true; @@ -874,11 +874,11 @@ private void DrawSlider () ? Style.RangeChar.Attribute ?? normalAttr : Style.SpaceChar.Attribute ?? normalAttr ); - Rune rune = isSet && _config._type == SliderType.LeftRange ? Style.RangeChar.Rune : Style.SpaceChar.Rune; + string text = isSet && _config._type == SliderType.LeftRange ? Style.RangeChar.Grapheme : Style.SpaceChar.Grapheme; for (var i = 0; i < _config._startSpacing; i++) { - MoveAndAdd (x, y, rune); + MoveAndAdd (x, y, text); if (isVertical) { @@ -896,7 +896,7 @@ private void DrawSlider () for (var i = 0; i < _config._startSpacing; i++) { - MoveAndAdd (x, y, Style.EmptyChar.Rune); + MoveAndAdd (x, y, Style.EmptyChar.Grapheme); if (isVertical) { @@ -950,25 +950,25 @@ private void DrawSlider () drawRange ? Style.RangeChar.Attribute ?? setAttr : Style.OptionChar.Attribute ?? normalAttr ); - Rune rune = drawRange ? Style.RangeChar.Rune : Style.OptionChar.Rune; + string text = drawRange ? Style.RangeChar.Grapheme : Style.OptionChar.Grapheme; if (isSet) { if (_setOptions [0] == i) { - rune = Style.StartRangeChar.Rune; + text = Style.StartRangeChar.Grapheme; } else if (_setOptions.Count > 1 && _setOptions [1] == i) { - rune = Style.EndRangeChar.Rune; + text = Style.EndRangeChar.Grapheme; } else if (_setOptions.Contains (i)) { - rune = Style.SetChar.Rune; + text = Style.SetChar.Grapheme; } } - MoveAndAdd (x, y, rune); + MoveAndAdd (x, y, text); if (isVertical) { @@ -991,7 +991,7 @@ private void DrawSlider () for (var s = 0; s < _config._cachedInnerSpacing; s++) { - MoveAndAdd (x, y, drawRange && isSet ? Style.RangeChar.Rune : Style.SpaceChar.Rune); + MoveAndAdd (x, y, drawRange && isSet ? Style.RangeChar.Grapheme : Style.SpaceChar.Grapheme); if (isVertical) { @@ -1016,11 +1016,11 @@ private void DrawSlider () ? Style.RangeChar.Attribute ?? normalAttr : Style.SpaceChar.Attribute ?? normalAttr ); - Rune rune = isSet && _config._type == SliderType.RightRange ? Style.RangeChar.Rune : Style.SpaceChar.Rune; + string text = isSet && _config._type == SliderType.RightRange ? Style.RangeChar.Grapheme : Style.SpaceChar.Grapheme; for (var i = 0; i < remaining; i++) { - MoveAndAdd (x, y, rune); + MoveAndAdd (x, y, text); if (isVertical) { @@ -1038,7 +1038,7 @@ private void DrawSlider () for (var i = 0; i < remaining; i++) { - MoveAndAdd (x, y, Style.EmptyChar.Rune); + MoveAndAdd (x, y, Style.EmptyChar.Grapheme); if (isVertical) { diff --git a/Terminal.Gui/Views/TableView/TreeTableSource.cs b/Terminal.Gui/Views/TableView/TreeTableSource.cs index 6cbb5b0a73..0a671f1f60 100644 --- a/Terminal.Gui/Views/TableView/TreeTableSource.cs +++ b/Terminal.Gui/Views/TableView/TreeTableSource.cs @@ -87,14 +87,14 @@ private string GetColumnZeroRepresentationFromTree (int row) { Branch branch = RowToBranch (row); - // Everything on line before the expansion run and branch text - Rune [] prefix = branch.GetLinePrefix ().ToArray (); - Rune expansion = branch.GetExpandableSymbol (); + // Everything on the line before the expansion run and branch text + string [] prefix = branch.GetLinePrefix ().ToArray (); + string expansion = branch.GetExpandableSymbol (); string lineBody = _tree.AspectGetter (branch.Model) ?? ""; var sb = new StringBuilder (); - foreach (Rune p in prefix) + foreach (string p in prefix) { sb.Append (p); } diff --git a/Terminal.Gui/Views/TextInput/TextField.cs b/Terminal.Gui/Views/TextInput/TextField.cs index bc2390fb62..808ae386dd 100644 --- a/Terminal.Gui/Views/TextInput/TextField.cs +++ b/Terminal.Gui/Views/TextInput/TextField.cs @@ -16,7 +16,7 @@ public class TextField : View, IDesignable private int _selectedStart; // -1 represents there is no text selection. private string _selectedText; private int _start; - private List _text; + private List _text; /// /// Initializes a new instance of the class. @@ -543,7 +543,7 @@ public string SelectedText ClearAllSelection (); // Note we use NewValue here; TextChanging subscribers may have changed it - _text = args.Result.EnumerateRunes ().ToList (); + _text = args.Result.ToStringList (); if (!Secret && !_historyText.IsFromHistory) { @@ -631,7 +631,7 @@ public virtual void Cut () } Clipboard.Contents = SelectedText; - List newText = DeleteSelectedText (); + List newText = DeleteSelectedText (); Text = StringExtensions.ToString (newText); Adjust (); } @@ -702,7 +702,7 @@ public virtual void DeleteCharLeft (bool usePreTextChangedCursorPos) } else { - List newText = DeleteSelectedText (); + List newText = DeleteSelectedText (); Text = StringExtensions.ToString (newText); Adjust (); } @@ -736,7 +736,7 @@ public virtual void DeleteCharRight () } else { - List newText = DeleteSelectedText (); + List newText = DeleteSelectedText (); Text = StringExtensions.ToString (newText); Adjust (); } @@ -945,8 +945,8 @@ protected override bool OnDrawingContent () for (int idx = p; idx < tcount; idx++) { - Rune rune = _text [idx]; - int cols = rune.GetColumns (); + string text = _text [idx]; + int cols = text.GetColumns (); if (!Enabled) { @@ -982,7 +982,7 @@ protected override bool OnDrawingContent () if (col + cols <= width) { - AddRune (Secret ? Glyphs.Dot : rune); + AddStr (Secret ? Glyphs.Dot.ToString () : text); } if (!TextModel.SetCol (ref col, width, cols)) @@ -1258,7 +1258,7 @@ private void CreateContextMenu () private void ContextMenu_KeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.Replace (e.OldKey.KeyCode, e.NewKey.KeyCode); } - private List DeleteSelectedText () + private List DeleteSelectedText () { SetSelectedStartSelectedLength (); int selStart = SelectedStart > -1 ? _start : _cursorPosition; @@ -1274,7 +1274,7 @@ private List DeleteSelectedText () ClearAllSelection (); _cursorPosition = selStart >= newText.GetRuneCount () ? newText.GetRuneCount () : selStart; - return newText.ToRuneList (); + return newText.ToStringList (); } private void GenerateSuggestions () @@ -1322,7 +1322,7 @@ private void InsertText (Key a, bool usePreTextChangedCursorPos) new (_cursorPosition, 0) ); - List newText = _text; + List newText = _text; if (SelectedLength > 0) { @@ -1343,7 +1343,7 @@ private void InsertText (Key a, bool usePreTextChangedCursorPos) if (_cursorPosition == newText.Count + 1) { - SetText (newText.Concat (kbstr).ToList ()); + SetText (newText.Concat (kbstr.Select (r => r.ToString ())).ToList ()); } else { @@ -1354,7 +1354,7 @@ private void InsertText (Key a, bool usePreTextChangedCursorPos) SetText ( newText.GetRange (0, _preTextChangedCursorPos) - .Concat (kbstr) + .Concat (kbstr.Select (r => r.ToString ())) .Concat ( newText.GetRange ( _preTextChangedCursorPos, @@ -1371,7 +1371,7 @@ private void InsertText (Key a, bool usePreTextChangedCursorPos) { SetText ( newText.GetRange (0, _preTextChangedCursorPos) - .Concat (kbstr) + .Concat (kbstr.Select (r => r.ToString ())) .Concat ( newText.GetRange ( Math.Min (_preTextChangedCursorPos + 1, newText.Count), @@ -1736,7 +1736,7 @@ private void RenderCaption () hotKeyAttribute); } - private void SetClipboard (IEnumerable text) + private void SetClipboard (IEnumerable text) { if (!Secret) { @@ -1762,8 +1762,8 @@ private void SetSelectedStartSelectedLength () } } - private void SetText (List newText) { Text = StringExtensions.ToString (newText); } - private void SetText (IEnumerable newText) { SetText (newText.ToList ()); } + private void SetText (List newText) { Text = StringExtensions.ToString (newText); } + private void SetText (IEnumerable newText) { SetText (newText.ToList ()); } private void ShowContextMenu (bool keyboard) { diff --git a/Terminal.Gui/Views/TextInput/TextModel.cs b/Terminal.Gui/Views/TextInput/TextModel.cs index 275c8a9083..be11b5e5e2 100644 --- a/Terminal.Gui/Views/TextInput/TextModel.cs +++ b/Terminal.Gui/Views/TextInput/TextModel.cs @@ -72,7 +72,7 @@ public int GetMaxVisibleLine (int first, int last, int tabWidth) for (int i = first; i < last; i++) { List line = GetLine (i); - int tabSum = line.Sum (c => c.Rune.Value == '\t' ? Math.Max (tabWidth - 1, 0) : 0); + int tabSum = line.Sum (c => c.Grapheme == "\t" ? Math.Max (tabWidth - 1, 0) : 0); int l = line.Count + tabSum; if (l > maxLength) @@ -223,7 +223,7 @@ public override string ToString () if (cell is { }) { - rune = cell.Value.Rune; + rune = Rune.GetRuneAt (cell.Value.Grapheme, 0); } else { @@ -300,10 +300,11 @@ void ProcMovePrev (ref int nCol, ref int nRow, Rune nRune) } List line = GetLine (nRow); + Rune firstRune = Rune.GetRuneAt (line [0].Grapheme, 0); if (nCol == 0 && nRow == fromRow - && (Rune.IsLetterOrDigit (line [0].Rune) || Rune.IsPunctuation (line [0].Rune) || Rune.IsSymbol (line [0].Rune))) + && (Rune.IsLetterOrDigit (firstRune) || Rune.IsPunctuation (firstRune) || Rune.IsSymbol (firstRune))) { return; } @@ -367,7 +368,7 @@ void ProcMovePrev (ref int nCol, ref int nRow, Rune nRune) try { - Rune rune = _lines [row].Count > 0 ? RuneAt (col, row)!.Value.Rune : default (Rune); + Rune rune = _lines [row].Count > 0 ? Rune.GetRuneAt (RuneAt (col, row)!.Value.Grapheme, 0) : default (Rune); RuneType runeType = GetRuneType (rune); int lastValidCol = IsSameRuneType (rune, runeType, useSameRuneType) && (Rune.IsLetterOrDigit (rune) || Rune.IsPunctuation (rune) || Rune.IsSymbol (rune)) @@ -426,10 +427,11 @@ void ProcMoveNext (ref int nCol, ref int nRow, Rune nRune) } List line = GetLine (nRow); + Rune firstRune = Rune.GetRuneAt (line [0].Grapheme, 0); if (nCol == line.Count && nRow == fromRow - && (Rune.IsLetterOrDigit (line [0].Rune) || Rune.IsPunctuation (line [0].Rune) || Rune.IsSymbol (line [0].Rune))) + && (Rune.IsLetterOrDigit (firstRune) || Rune.IsPunctuation (firstRune) || Rune.IsSymbol (firstRune))) { return; } @@ -476,10 +478,10 @@ void ProcMoveNext (ref int nCol, ref int nRow, Rune nRune) } if (startCol > 0 - && StringExtensions.ToString (line.GetRange (startCol, col - startCol).Select (c => c.Rune).ToList ()).Trim () == "" - && (col - startCol > 1 || (col - startCol > 0 && line [startCol - 1].Rune == (Rune)' '))) + && StringExtensions.ToString (line.GetRange (startCol, col - startCol).Select (c => c.Grapheme).ToList ()).Trim () == "" + && (col - startCol > 1 || (col - startCol > 0 && line [startCol - 1].Grapheme == " "))) { - while (startCol > 0 && line [startCol - 1].Rune == (Rune)' ') + while (startCol > 0 && line [startCol - 1].Grapheme == " ") { startCol--; } @@ -496,13 +498,13 @@ void ProcMoveNext (ref int nCol, ref int nRow, Rune nRune) if (selectWordOnly) { - List selRunes = line.GetRange (startCol, col - startCol).Select (c => c.Rune).ToList (); + List selText = line.GetRange (startCol, col - startCol).Select (c => c.Grapheme).ToList (); - if (StringExtensions.ToString (selRunes).Trim () != "") + if (StringExtensions.ToString (selText).Trim () != "") { - for (int i = selRunes.Count - 1; i > -1; i--) + for (int i = selText.Count - 1; i > -1; i--) { - if (selRunes [i] == (Rune)' ') + if (selText [i] == " ") { col--; } @@ -520,18 +522,18 @@ void ProcMoveNext (ref int nCol, ref int nRow, Rune nRune) internal static int CalculateLeftColumn (List t, int start, int end, int width, int tabWidth = 0) { - List runes = new (); + List strings = new (); foreach (Cell cell in t) { - runes.Add (cell.Rune); + strings.Add (cell.Grapheme); } - return CalculateLeftColumn (runes, start, end, width, tabWidth); + return CalculateLeftColumn (strings, start, end, width, tabWidth); } // Returns the left column in a range of the string. - internal static int CalculateLeftColumn (List t, int start, int end, int width, int tabWidth = 0) + internal static int CalculateLeftColumn (List t, int start, int end, int width, int tabWidth = 0) { if (t is null || t.Count == 0) { @@ -539,15 +541,15 @@ internal static int CalculateLeftColumn (List t, int start, int end, int w } var size = 0; - int tcount = end > t.Count - 1 ? t.Count - 1 : end; + int tCount = end > t.Count - 1 ? t.Count - 1 : end; var col = 0; - for (int i = tcount; i >= 0; i--) + for (int i = tCount; i >= 0; i--) { - Rune rune = t [i]; - size += rune.GetColumns (); + string text = t [i]; + size += text.GetColumns (false); - if (rune.Value == '\t') + if (text == "\t") { size += tabWidth + 1; } @@ -577,23 +579,23 @@ internal static (int size, int length) DisplaySize ( List t, int start = -1, int end = -1, - bool checkNextRune = true, + bool checkNextText = true, int tabWidth = 0 ) { - List runes = new (); + List strings = new (); foreach (Cell cell in t) { - runes.Add (cell.Rune); + strings.Add (cell.Grapheme); } - return DisplaySize (runes, start, end, checkNextRune, tabWidth); + return DisplaySize (strings, start, end, checkNextText, tabWidth); } // Returns the size and length in a range of the string. internal static (int size, int length) DisplaySize ( - List t, + List t, int start = -1, int end = -1, bool checkNextRune = true, @@ -608,35 +610,35 @@ internal static (int size, int length) DisplaySize ( var size = 0; var len = 0; - int tcount = end == -1 ? t.Count : + int tCount = end == -1 ? t.Count : end > t.Count ? t.Count : end; int i = start == -1 ? 0 : start; - for (; i < tcount; i++) + for (; i < tCount; i++) { - Rune rune = t [i]; - size += rune.GetColumns (); - len += rune.GetEncodingLength (Encoding.Unicode); + string text = t [i]; + size += text.GetColumns (false); + len += text.Length; - if (rune.Value == '\t') + if (text == "\t") { size += tabWidth + 1; len += tabWidth - 1; } - if (checkNextRune && i == tcount - 1 && t.Count > tcount && IsWideRune (t [i + 1], tabWidth, out int s, out int l)) + if (checkNextRune && i == tCount - 1 && t.Count > tCount && IsWideText (t [i + 1], tabWidth, out int s, out int l)) { size += s; len += l; } } - bool IsWideRune (Rune r, int tWidth, out int s, out int l) + bool IsWideText (string s1, int tWidth, out int s, out int l) { - s = r.GetColumns (); - l = r.GetEncodingLength (); + s = s1.GetColumns (); + l = Encoding.Unicode.GetByteCount (s1); - if (r.Value == '\t') + if (s1 == "\t") { s += tWidth + 1; l += tWidth - 1; @@ -745,17 +747,17 @@ internal Size GetDisplaySize () internal static int GetColFromX (List t, int start, int x, int tabWidth = 0) { - List runes = new (); + List strings = new (); foreach (Cell cell in t) { - runes.Add (cell.Rune); + strings.Add (cell.Grapheme); } - return GetColFromX (runes, start, x, tabWidth); + return GetColFromX (strings, start, x, tabWidth); } - internal static int GetColFromX (List t, int start, int x, int tabWidth = 0) + internal static int GetColFromX (List t, int start, int x, int tabWidth = 0) { if (x < 0) { @@ -767,10 +769,10 @@ internal static int GetColFromX (List t, int start, int x, int tabWidth = for (int i = start; i < t.Count; i++) { - Rune r = t [i]; - size += r.GetColumns (); + string s = t [i]; + size += s.GetColumns (); - if (r.Value == '\t') + if (s == "\t") { size += tabWidth + 1; } @@ -1056,18 +1058,21 @@ private bool MoveNext (ref int col, ref int row, out Rune rune, bool useSameRune if (col + 1 < line.Count) { col++; - rune = line [col].Rune; + rune = Rune.GetRuneAt (line [col].Grapheme, 0); + Rune prevRune = Rune.GetRuneAt (line [col - 1].Grapheme, 0); if (col + 1 == line.Count && !Rune.IsLetterOrDigit (rune) - && !Rune.IsWhiteSpace (line [col - 1].Rune) - && IsSameRuneType (line [col - 1].Rune, GetRuneType (rune), useSameRuneType)) + && !Rune.IsWhiteSpace (prevRune) + && IsSameRuneType (prevRune, GetRuneType (rune), useSameRuneType)) { col++; } + prevRune = Rune.GetRuneAt (line [col - 1].Grapheme, 0); + if (!Rune.IsWhiteSpace (rune) - && (Rune.IsWhiteSpace (line [col - 1].Rune) || !IsSameRuneType (line [col - 1].Rune, GetRuneType (rune), useSameRuneType))) + && (Rune.IsWhiteSpace (prevRune) || !IsSameRuneType (prevRune, GetRuneType (rune), useSameRuneType))) { return false; } @@ -1098,12 +1103,13 @@ private bool MovePrev (ref int col, ref int row, out Rune rune, bool useSameRune if (col > 0) { col--; - rune = line [col].Rune; + rune = Rune.GetRuneAt (line [col].Grapheme, 0); + Rune nextRune = Rune.GetRuneAt (line [col + 1].Grapheme, 0); if ((!Rune.IsWhiteSpace (rune) - && !Rune.IsWhiteSpace (line [col + 1].Rune) - && !IsSameRuneType (line [col + 1].Rune, GetRuneType (rune), useSameRuneType)) - || (Rune.IsWhiteSpace (rune) && !Rune.IsWhiteSpace (line [col + 1].Rune))) + && !Rune.IsWhiteSpace (nextRune) + && !IsSameRuneType (nextRune, GetRuneType (rune), useSameRuneType)) + || (Rune.IsWhiteSpace (rune) && !Rune.IsWhiteSpace (nextRune))) { return false; } diff --git a/Terminal.Gui/Views/TextInput/TextView.cs b/Terminal.Gui/Views/TextInput/TextView.cs index f9e2031d94..d4fb20b093 100644 --- a/Terminal.Gui/Views/TextInput/TextView.cs +++ b/Terminal.Gui/Views/TextInput/TextView.cs @@ -1487,7 +1487,7 @@ public void Load (Stream stream) } /// Loads the contents of the list into the . - /// Rune cells list to load the contents from. + /// Text cells list to load the contents from. public void Load (List cells) { SetWrapModel (); @@ -1807,8 +1807,8 @@ protected override bool OnDrawingContent () for (int idxCol = _leftColumn; idxCol < lineRuneCount; idxCol++) { - Rune rune = idxCol >= lineRuneCount ? (Rune)' ' : line [idxCol].Rune; - int cols = rune.GetColumns (); + string text = idxCol >= lineRuneCount ? " " : line [idxCol].Grapheme; + int cols = text.GetColumns (false); if (idxCol < line.Count && IsSelecting && PointInSelection (idxCol, idxRow)) { @@ -1827,7 +1827,7 @@ protected override bool OnDrawingContent () OnDrawNormalColor (line, idxCol, idxRow); } - if (rune.Value == '\t') + if (text == "\t") { cols += TabWidth + 1; @@ -1846,7 +1846,7 @@ protected override bool OnDrawingContent () } else { - AddRune (col, row, rune); + AddStr (col, row, text); // Ensures that cols less than 0 to be 1 because it will be converted to a printable rune cols = Math.Max (cols, 1); @@ -1857,7 +1857,7 @@ protected override bool OnDrawingContent () break; } - if (idxCol + 1 < lineRuneCount && col + line [idxCol + 1].Rune.GetColumns () > right) + if (idxCol + 1 < lineRuneCount && col + line [idxCol + 1].Grapheme.GetColumns () > right) { break; } @@ -2053,9 +2053,9 @@ public void Paste () break; } - int cols = line [idx].Rune.GetColumns (); + int cols = line [idx].Grapheme.GetColumns (); - if (line [idx].Rune.Value == '\t') + if (line [idx].Grapheme == "\t") { cols += TabWidth + 1; } @@ -2812,12 +2812,12 @@ internal string GetRegion ( cells = line.GetRange (startCol, endCol - startCol); cellsList.Add (cells); - return StringFromRunes (cells); + return StringFromCells (cells); } cells = line.GetRange (startCol, line.Count - startCol); cellsList.Add (cells); - string res = StringFromRunes (cells); + string res = StringFromCells (cells); for (int row = startRow + 1; row < maxRow; row++) { @@ -2827,14 +2827,14 @@ internal string GetRegion ( res = res + Environment.NewLine - + StringFromRunes (cells); + + StringFromCells (cells); } line = model is null ? _model.GetLine (maxRow) : model.GetLine (maxRow); cellsList.AddRange ([]); cells = line.GetRange (0, endCol); cellsList.Add (cells); - res = res + Environment.NewLine + StringFromRunes (cells); + res = res + Environment.NewLine + StringFromCells (cells); return res; } @@ -3114,7 +3114,7 @@ private bool InsertText (Key a, Attribute? attribute = null) { if (Used) { - Insert (new () { Rune = a.AsRune, Attribute = attribute }); + Insert (new () { Grapheme = a.AsRune.ToString (), Attribute = attribute }); CurrentColumn++; if (CurrentColumn >= _leftColumn + Viewport.Width) @@ -3125,7 +3125,7 @@ private bool InsertText (Key a, Attribute? attribute = null) } else { - Insert (new () { Rune = a.AsRune, Attribute = attribute }); + Insert (new () { Grapheme = a.AsRune.ToString (), Attribute = attribute }); CurrentColumn++; } } @@ -3213,7 +3213,7 @@ private void KillToEndOfLine () int restCount = currentLine.Count - CurrentColumn; List rest = currentLine.GetRange (CurrentColumn, restCount); var val = string.Empty; - val += StringFromRunes (rest); + val += StringFromCells (rest); if (_lastWasKill) { @@ -3319,7 +3319,7 @@ private void KillToLeftStart () int restCount = CurrentColumn; List rest = currentLine.GetRange (0, restCount); var val = string.Empty; - val += StringFromRunes (rest); + val += StringFromCells (rest); if (_lastWasKill) { @@ -3848,7 +3848,7 @@ private bool ProcessBackTab () List currentLine = GetCurrentLine (); - if (currentLine.Count > 0 && currentLine [CurrentColumn - 1].Rune.Value == '\t') + if (currentLine.Count > 0 && currentLine[CurrentColumn - 1].Grapheme == "\t") { _historyText.Add (new () { new (currentLine) }, CursorPosition); @@ -4606,29 +4606,28 @@ private void StopSelecting () _isButtonShift = false; } - private string StringFromRunes (List cells) + private string StringFromCells (List cells) { - if (cells is null) - { - throw new ArgumentNullException (nameof (cells)); - } + ArgumentNullException.ThrowIfNull (cells); var size = 0; - foreach (Cell cell in cells) { - size += cell.Rune.GetEncodingLength (); + string t = cell.Grapheme; + size += Encoding.Unicode.GetByteCount (t); } - var encoded = new byte [size]; + byte [] encoded = new byte [size]; var offset = 0; - foreach (Cell cell in cells) { - offset += cell.Rune.Encode (encoded, offset); + string t = cell.Grapheme; + int bytesWritten = Encoding.Unicode.GetBytes (t, 0, t.Length, encoded, offset); + offset += bytesWritten; } - return StringExtensions.ToString (encoded); + // decode using the same encoding and the bytes actually written + return Encoding.Unicode.GetString (encoded, 0, offset); } private void TextView_SuperViewChanged (object sender, SuperViewChangedEventArgs e) diff --git a/Terminal.Gui/Views/TreeView/Branch.cs b/Terminal.Gui/Views/TreeView/Branch.cs index 811824dbfc..f1c036075d 100644 --- a/Terminal.Gui/Views/TreeView/Branch.cs +++ b/Terminal.Gui/Views/TreeView/Branch.cs @@ -87,9 +87,9 @@ public virtual void Draw (int y, int availableWidth) isSelected ? _tree.HasFocus ? _tree.GetAttributeForRole (VisualRole.Focus) : _tree.GetAttributeForRole (VisualRole.HotNormal) : _tree.GetAttributeForRole (VisualRole.Normal); Attribute symbolColor = _tree.Style.HighlightModelTextOnly ? _tree.GetAttributeForRole (VisualRole.Normal) : textColor; - // Everything on line before the expansion run and branch text - Rune [] prefix = GetLinePrefix ().ToArray (); - Rune expansion = GetExpandableSymbol (); + // Everything on the line before the expansion run and branch text + string [] prefix = GetLinePrefix ().ToArray (); + string expansion = GetExpandableSymbol (); string lineBody = _tree.AspectGetter (Model) ?? ""; _tree.Move (0, y); @@ -99,7 +99,7 @@ public virtual void Draw (int y, int availableWidth) Attribute attr = symbolColor; // Draw the line prefix (all parallel lanes or whitespace and an expand/collapse/leaf symbol) - foreach (Rune r in prefix) + foreach (string s in prefix) { if (toSkip > 0) { @@ -107,8 +107,8 @@ public virtual void Draw (int y, int availableWidth) } else { - cells.Add (NewCell (attr, r)); - availableWidth -= r.GetColumns (); + cells.Add (NewCell (attr, s)); + availableWidth -= s.GetColumns (); } } @@ -212,7 +212,7 @@ public virtual void Draw (int y, int availableWidth) } attr = modelColor; - cells.AddRange (lineBody.Select (r => NewCell (attr, new (r)))); + cells.AddRange (lineBody.Select (c => NewCell (attr, c.ToString ()))); if (availableWidth > 0) { @@ -220,7 +220,7 @@ public virtual void Draw (int y, int availableWidth) cells.AddRange ( Enumerable.Repeat ( - NewCell (attr, new (' ')), + NewCell (attr, " "), availableWidth ) ); @@ -243,7 +243,7 @@ public virtual void Draw (int y, int availableWidth) foreach (Cell cell in cells) { _tree.SetAttribute ((Attribute)cell.Attribute!); - _tree.AddRune (cell.Rune); + _tree.AddStr (cell.Grapheme); } } @@ -288,21 +288,21 @@ private List> FetchChildren () /// object to indicate whether it or not (or it is a leaf). /// /// - public Rune GetExpandableSymbol () + public string GetExpandableSymbol () { Rune leafSymbol = _tree.Style.ShowBranchLines ? Glyphs.HLine : (Rune)' '; if (IsExpanded) { - return _tree.Style.CollapseableSymbol ?? leafSymbol; + return _tree.Style.CollapseableSymbol.ToString () ?? leafSymbol.ToString (); } if (CanExpand ()) { - return _tree.Style.ExpandableSymbol ?? leafSymbol; + return _tree.Style.ExpandableSymbol.ToString () ?? leafSymbol.ToString (); } - return leafSymbol; + return leafSymbol.ToString (); } /// @@ -409,14 +409,14 @@ internal void ExpandAll () /// any tree branches (if enabled). /// /// - internal IEnumerable GetLinePrefix () + internal IEnumerable GetLinePrefix () { // If not showing line branches or this is a root object. if (!_tree.Style.ShowBranchLines) { for (var i = 0; i < Depth; i++) { - yield return new (' '); + yield return new (" "); } yield break; @@ -427,23 +427,23 @@ internal IEnumerable GetLinePrefix () { if (cur.IsLast ()) { - yield return new (' '); + yield return new (" "); } else { - yield return Glyphs.VLine; + yield return Glyphs.VLine.ToString (); } - yield return new (' '); + yield return new (" "); } if (IsLast ()) { - yield return Glyphs.LLCorner; + yield return Glyphs.LLCorner.ToString (); } else { - yield return Glyphs.LeftTee; + yield return Glyphs.LeftTee.ToString (); } } @@ -531,5 +531,5 @@ private bool IsLast () return Parent.ChildBranches.LastOrDefault () == this; } - private static Cell NewCell (Attribute attr, Rune r) { return new () { Rune = r, Attribute = new (attr) }; } + private static Cell NewCell (Attribute attr, string s) { return new () { Grapheme = s, Attribute = new (attr) }; } } diff --git a/Tests/UnitTests/DriverAssert.cs b/Tests/UnitTests/DriverAssert.cs index a1abc36b59..bbef9143f5 100644 --- a/Tests/UnitTests/DriverAssert.cs +++ b/Tests/UnitTests/DriverAssert.cs @@ -196,7 +196,7 @@ public static Rectangle AssertDriverContentsWithFrameAre ( IDriver? driver = null ) { - List> lines = []; + List> lines = []; var sb = new StringBuilder (); driver ??= Application.Driver; @@ -209,13 +209,13 @@ public static Rectangle AssertDriverContentsWithFrameAre ( for (var rowIndex = 0; rowIndex < driver.Rows; rowIndex++) { - List runes = []; + List strings = []; for (var colIndex = 0; colIndex < driver.Cols; colIndex++) { - Rune runeAtCurrentLocation = contents! [rowIndex, colIndex].Rune; + string textAtCurrentLocation = contents! [rowIndex, colIndex].Grapheme; - if (runeAtCurrentLocation != _spaceRune) + if (textAtCurrentLocation != _spaceRune.ToString ()) { if (x == -1) { @@ -224,11 +224,11 @@ public static Rectangle AssertDriverContentsWithFrameAre ( for (var i = 0; i < colIndex; i++) { - runes.InsertRange (i, [_spaceRune]); + strings.InsertRange (i, [_spaceRune.ToString ()]); } } - if (runeAtCurrentLocation.GetColumns () > 1) + if (textAtCurrentLocation.GetColumns () > 1) { colIndex++; } @@ -243,18 +243,13 @@ public static Rectangle AssertDriverContentsWithFrameAre ( if (x > -1) { - runes.Add (runeAtCurrentLocation); + strings.Add (textAtCurrentLocation); } - - // See Issue #2616 - //foreach (var combMark in contents [r, c].CombiningMarks) { - // runes.Add (combMark); - //} } - if (runes.Count > 0) + if (strings.Count > 0) { - lines.Add (runes); + lines.Add (strings); } } @@ -268,13 +263,13 @@ public static Rectangle AssertDriverContentsWithFrameAre ( } // Remove trailing whitespace on each line - foreach (List row in lines) + foreach (List row in lines) { for (int c = row.Count - 1; c >= 0; c--) { - Rune rune = row [c]; + string text = row [c]; - if (rune != (Rune)' ' || row.Sum (x => x.GetColumns ()) == w) + if (text != " " || row.Sum (x => x.GetColumns ()) == w) { break; } @@ -283,7 +278,7 @@ public static Rectangle AssertDriverContentsWithFrameAre ( } } - // Convert Rune list to string + // Convert Text list to string for (var r = 0; r < lines.Count; r++) { var line = StringExtensions.ToString (lines [r]); diff --git a/Tests/UnitTests/Drivers/ClipRegionTests.cs b/Tests/UnitTests/Drivers/ClipRegionTests.cs index 8ede58565a..40c5aba9f8 100644 --- a/Tests/UnitTests/Drivers/ClipRegionTests.cs +++ b/Tests/UnitTests/Drivers/ClipRegionTests.cs @@ -16,24 +16,24 @@ public void AddRune_Is_Clipped () Application.Driver!.Move (0, 0); Application.Driver!.AddRune ('x'); - Assert.Equal ((Rune)'x', Application.Driver!.Contents! [0, 0].Rune); + Assert.Equal ("x", Application.Driver!.Contents! [0, 0].Grapheme); Application.Driver?.Move (5, 5); Application.Driver?.AddRune ('x'); - Assert.Equal ((Rune)'x', Application.Driver!.Contents [5, 5].Rune); + Assert.Equal ("x", Application.Driver!.Contents [5, 5].Grapheme); // Clear the contents Application.Driver?.FillRect (new Rectangle (0, 0, Application.Driver.Rows, Application.Driver.Cols), ' '); - Assert.Equal ((Rune)' ', Application.Driver?.Contents [0, 0].Rune); + Assert.Equal (" ", Application.Driver?.Contents [0, 0].Grapheme); // Setup the region with a single rectangle, fill screen with 'x' Application.Driver!.Clip = new (new Rectangle (5, 5, 5, 5)); Application.Driver.FillRect (new Rectangle (0, 0, Application.Driver.Rows, Application.Driver.Cols), 'x'); - Assert.Equal ((Rune)' ', Application.Driver?.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', Application.Driver?.Contents [4, 9].Rune); - Assert.Equal ((Rune)'x', Application.Driver?.Contents [5, 5].Rune); - Assert.Equal ((Rune)'x', Application.Driver?.Contents [9, 9].Rune); - Assert.Equal ((Rune)' ', Application.Driver?.Contents [10, 10].Rune); + Assert.Equal (" ", Application.Driver?.Contents [0, 0].Grapheme); + Assert.Equal (" ", Application.Driver?.Contents [4, 9].Grapheme); + Assert.Equal ("x", Application.Driver?.Contents [5, 5].Grapheme); + Assert.Equal ("x", Application.Driver?.Contents [9, 9].Grapheme); + Assert.Equal (" ", Application.Driver?.Contents [10, 10].Grapheme); Application.Shutdown (); } diff --git a/Tests/UnitTests/View/Draw/ClipTests.cs b/Tests/UnitTests/View/Draw/ClipTests.cs index 44b120d769..57f20199d7 100644 --- a/Tests/UnitTests/View/Draw/ClipTests.cs +++ b/Tests/UnitTests/View/Draw/ClipTests.cs @@ -47,17 +47,17 @@ public void AddRune_Is_Constrained_To_Viewport () view.Draw (); // Only valid location w/in Viewport is 0, 0 (view) - 2, 2 (screen) - Assert.Equal ((Rune)' ', Application.Driver?.Contents! [2, 2].Rune); + Assert.Equal (" ", Application.Driver?.Contents! [2, 2].Grapheme); // When we exit Draw, the view is excluded from the clip. So drawing at 0,0, is not valid and is clipped. view.AddRune (0, 0, Rune.ReplacementChar); - Assert.Equal ((Rune)' ', Application.Driver?.Contents! [2, 2].Rune); + Assert.Equal (" ", Application.Driver?.Contents! [2, 2].Grapheme); view.AddRune (-1, -1, Rune.ReplacementChar); - Assert.Equal ((Rune)'P', Application.Driver?.Contents! [1, 1].Rune); + Assert.Equal ("P", Application.Driver?.Contents! [1, 1].Grapheme); view.AddRune (1, 1, Rune.ReplacementChar); - Assert.Equal ((Rune)'P', Application.Driver?.Contents! [3, 3].Rune); + Assert.Equal ("P", Application.Driver?.Contents! [3, 3].Grapheme); } [Theory] diff --git a/Tests/UnitTests/View/TextTests.cs b/Tests/UnitTests/View/TextTests.cs index 3e552204c1..8e9a7beab8 100644 --- a/Tests/UnitTests/View/TextTests.cs +++ b/Tests/UnitTests/View/TextTests.cs @@ -701,7 +701,7 @@ string GetContents () for (var i = 0; i < 4; i++) { - text += Application.Driver?.Contents [0, i].Rune; + text += Application.Driver?.Contents [0, i].Grapheme; } return text; diff --git a/Tests/UnitTests/Views/LabelTests.cs b/Tests/UnitTests/Views/LabelTests.cs index 550abf360b..0ebc19d9ba 100644 --- a/Tests/UnitTests/Views/LabelTests.cs +++ b/Tests/UnitTests/Views/LabelTests.cs @@ -154,7 +154,7 @@ This TextFormatter (tf2) is rewritten. ", [AutoInitShutdown] public void Label_Draw_Horizontal_Simple_Runes () { - var label = new Label { Text = "Demo Simple Rune" }; + var label = new Label { Text = "Demo Simple Text" }; var top = new Toplevel (); top.Add (label); Application.Begin (top); @@ -163,7 +163,7 @@ public void Label_Draw_Horizontal_Simple_Runes () Assert.Equal (new (0, 0, 16, 1), label.Frame); var expected = @" -Demo Simple Rune +Demo Simple Text "; Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); @@ -173,9 +173,9 @@ Demo Simple Rune [Fact] [AutoInitShutdown] - public void Label_Draw_Vertical_Simple_Runes () + public void Label_Draw_Vertical_Simple_Text () { - var label = new Label { TextDirection = TextDirection.TopBottom_LeftRight, Text = "Demo Simple Rune" }; + var label = new Label { TextDirection = TextDirection.TopBottom_LeftRight, Text = "Demo Simple Text" }; var top = new Toplevel (); top.Add (label); Application.Begin (top); @@ -196,10 +196,10 @@ public void Label_Draw_Vertical_Simple_Runes () l e -R -u -n +T e +x +t "; Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); diff --git a/Tests/UnitTests/Views/ListViewTests.cs b/Tests/UnitTests/Views/ListViewTests.cs index d428277608..3995e61dde 100644 --- a/Tests/UnitTests/Views/ListViewTests.cs +++ b/Tests/UnitTests/Views/ListViewTests.cs @@ -362,7 +362,7 @@ string GetContents (int line) for (var i = 0; i < 7; i++) { - item += Application.Driver?.Contents [line, i].Rune; + item += Application.Driver?.Contents [line, i].Grapheme; } return item; diff --git a/Tests/UnitTests/Views/ProgressBarTests.cs b/Tests/UnitTests/Views/ProgressBarTests.cs index 26b70fe0ed..f78cbc4453 100644 --- a/Tests/UnitTests/Views/ProgressBarTests.cs +++ b/Tests/UnitTests/Views/ProgressBarTests.cs @@ -42,57 +42,57 @@ public void Fraction_Redraw () if (i == 0) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); } else if (i == 1) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); } else if (i == 2) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); } else if (i == 3) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); } else if (i == 4) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); } else if (i == 5) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); } } } @@ -180,687 +180,687 @@ public void Pulse_Redraw_BidirectionalMarquee_False () if (i == 0) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 1) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 2) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 3) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 4) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 5) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 6) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 7) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 8) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 9) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 10) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 11) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 12) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 13) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 14) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 15) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 16) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 17) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 18) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 19) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 20) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 21) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 22) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 23) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 24) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 25) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 26) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 27) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 28) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 29) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 30) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 31) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 32) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 33) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 34) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 35) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 36) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 37) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } } } @@ -885,687 +885,687 @@ public void Pulse_Redraw_BidirectionalMarquee_True_Default () if (i == 0) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 1) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 2) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 3) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 4) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 5) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 6) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 7) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 8) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 9) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 10) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 11) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 12) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 13) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 14) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 15) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 16) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 17) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 18) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 19) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 20) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 21) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 22) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 14].Grapheme); } else if (i == 23) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 24) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 25) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 26) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 27) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 28) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 29) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 30) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 31) { - Assert.Equal ((Rune)' ', driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (" ", driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 32) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 33) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 34) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 35) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 36) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } else if (i == 37) { - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 0].Rune); - Assert.Equal (Glyphs.BlocksMeterSegment, driver.Contents [0, 1].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 2].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 3].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 4].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 5].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 6].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 7].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 8].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 9].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 10].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 11].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 12].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 13].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 14].Rune); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 0].Grapheme); + Assert.Equal (Glyphs.BlocksMeterSegment.ToString (), driver.Contents [0, 1].Grapheme); + Assert.Equal (" ", driver.Contents [0, 2].Grapheme); + Assert.Equal (" ", driver.Contents [0, 3].Grapheme); + Assert.Equal (" ", driver.Contents [0, 4].Grapheme); + Assert.Equal (" ", driver.Contents [0, 5].Grapheme); + Assert.Equal (" ", driver.Contents [0, 6].Grapheme); + Assert.Equal (" ", driver.Contents [0, 7].Grapheme); + Assert.Equal (" ", driver.Contents [0, 8].Grapheme); + Assert.Equal (" ", driver.Contents [0, 9].Grapheme); + Assert.Equal (" ", driver.Contents [0, 10].Grapheme); + Assert.Equal (" ", driver.Contents [0, 11].Grapheme); + Assert.Equal (" ", driver.Contents [0, 12].Grapheme); + Assert.Equal (" ", driver.Contents [0, 13].Grapheme); + Assert.Equal (" ", driver.Contents [0, 14].Grapheme); } } } diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index 746f0307df..c338eb7ece 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -1055,7 +1055,7 @@ public void ShowTopLine_True_TabsOnBottom_False_With_Unicode () DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭──────────────╮ -│Les Misérables│ +│Les Misérables│ ◄ ╰───╮ │hi2 │ └──────────────────┘", @@ -1231,7 +1231,7 @@ public void ShowTopLine_True_TabsOnBottom_True_With_Unicode () ┌──────────────────┐ │hi2 │ ◄ ╭───╯ -│Les Misérables│ +│Les Misérables│ ╰──────────────╯ ", output ); diff --git a/Tests/UnitTests/Views/TextFieldTests.cs b/Tests/UnitTests/Views/TextFieldTests.cs index b4d27b93b0..8347ac6e3c 100644 --- a/Tests/UnitTests/Views/TextFieldTests.cs +++ b/Tests/UnitTests/Views/TextFieldTests.cs @@ -95,6 +95,7 @@ public void CaptionedTextField_DoesNotOverspillViewport_Unicode () Assert.Equal (11, caption.Length); Assert.Equal (10, caption.EnumerateRunes ().Sum (c => c.GetColumns ())); + Assert.Equal (10, caption.GetColumns ()); TextField tf = GetTextFieldsInView (); @@ -103,7 +104,7 @@ public void CaptionedTextField_DoesNotOverspillViewport_Unicode () Assert.False (tf.HasFocus); tf.Draw (); - DriverAssert.AssertDriverContentsAre ("Misérables", output); + DriverAssert.AssertDriverContentsAre ("Misérables", output); Application.Top.Dispose (); } @@ -1646,7 +1647,7 @@ public void Words_With_Accents_Incorrect_Order_Will_Result_With_Wrong_Accent_Pla DriverAssert.AssertDriverContentsWithFrameAre ( @" -Les Miśerables", +Les Miśerables", output ); } diff --git a/Tests/UnitTests/Views/TextViewTests.cs b/Tests/UnitTests/Views/TextViewTests.cs index d00000d2e0..6b4d9c7662 100644 --- a/Tests/UnitTests/Views/TextViewTests.cs +++ b/Tests/UnitTests/Views/TextViewTests.cs @@ -6916,7 +6916,7 @@ private int GetLeftCol (int start) { string [] lines = _textView.Text.Split (Environment.NewLine); - if (lines == null || lines.Length == 0) + if (lines is { Length: 0 }) { return 0; } @@ -7024,11 +7024,11 @@ public void CellEventArgs_WordWrap_True () List> text = [ Cell.ToCells ( - "This is the first line.".ToRunes () + "This is the first line.".ToStringList () ), Cell.ToCells ( - "This is the second line.".ToRunes () + "This is the second line.".ToStringList () ) ]; TextView tv = CreateTextView (); @@ -7091,12 +7091,9 @@ public void Cell_LoadCells_InheritsPreviousAttribute () { string csName = color.Key; - foreach (Rune rune in csName.EnumerateRunes ()) - { - cells.Add (new () { Rune = rune, Attribute = color.Value.Normal }); - } + cells.AddRange (Cell.ToCellList (csName, color.Value.Normal)); - cells.Add (new () { Rune = (Rune)'\n', Attribute = color.Value.Focus }); + cells.Add (new () { Grapheme = "\n", Attribute = color.Value.Focus }); } TextView tv = CreateTextView (); diff --git a/Tests/UnitTests/Views/TreeViewTests.cs b/Tests/UnitTests/Views/TreeViewTests.cs index 88b9cde4be..4a35ac84c3 100644 --- a/Tests/UnitTests/Views/TreeViewTests.cs +++ b/Tests/UnitTests/Views/TreeViewTests.cs @@ -975,10 +975,10 @@ public void TestTreeView_DrawLineEvent () Assert.All (eventArgs, ea => Assert.Equal (ea.Tree, tv)); Assert.All (eventArgs, ea => Assert.False (ea.Handled)); - Assert.Equal ("├-root one", eventArgs [0].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); - Assert.Equal ("│ ├─leaf 1", eventArgs [1].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); - Assert.Equal ("│ └─leaf 2", eventArgs [2].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); - Assert.Equal ("└─root two", eventArgs [3].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("├-root one", eventArgs [0].Cells.Aggregate ("", (s, n) => s += n.Grapheme).TrimEnd ()); + Assert.Equal ("│ ├─leaf 1", eventArgs [1].Cells.Aggregate ("", (s, n) => s += n.Grapheme).TrimEnd ()); + Assert.Equal ("│ └─leaf 2", eventArgs [2].Cells.Aggregate ("", (s, n) => s += n.Grapheme).TrimEnd ()); + Assert.Equal ("└─root two", eventArgs [3].Cells.Aggregate ("", (s, n) => s += n.Grapheme).TrimEnd ()); Assert.Equal (1, eventArgs [0].IndexOfExpandCollapseSymbol); Assert.Equal (3, eventArgs [1].IndexOfExpandCollapseSymbol); @@ -1088,9 +1088,9 @@ oot two Assert.All (eventArgs, ea => Assert.Equal (ea.Tree, tv)); Assert.All (eventArgs, ea => Assert.False (ea.Handled)); - Assert.Equal ("─leaf 1", eventArgs [0].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); - Assert.Equal ("─leaf 2", eventArgs [1].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); - Assert.Equal ("oot two", eventArgs [2].Cells.Aggregate ("", (s, n) => s += n.Rune).TrimEnd ()); + Assert.Equal ("─leaf 1", eventArgs [0].Cells.Aggregate ("", (s, n) => s += n.Grapheme).TrimEnd ()); + Assert.Equal ("─leaf 2", eventArgs [1].Cells.Aggregate ("", (s, n) => s += n.Grapheme).TrimEnd ()); + Assert.Equal ("oot two", eventArgs [2].Cells.Aggregate ("", (s, n) => s += n.Grapheme).TrimEnd ()); Assert.Equal (0, eventArgs [0].IndexOfExpandCollapseSymbol); Assert.Equal (0, eventArgs [1].IndexOfExpandCollapseSymbol); diff --git a/Tests/UnitTestsParallelizable/Drawing/CellTests.cs b/Tests/UnitTestsParallelizable/Drawing/CellTests.cs index 13a088a13f..3ee2dea542 100644 --- a/Tests/UnitTestsParallelizable/Drawing/CellTests.cs +++ b/Tests/UnitTestsParallelizable/Drawing/CellTests.cs @@ -1,17 +1,15 @@ -using System.Text; -using Xunit.Abstractions; +namespace UnitTests_Parallelizable.DrawingTests; -namespace UnitTests_Parallelizable.DrawingTests; - -public class CellTests () +public class CellTests { [Fact] public void Constructor_Defaults () { var c = new Cell (); Assert.True (c is { }); - Assert.Equal (0, c.Rune.Value); Assert.Null (c.Attribute); + Assert.False (c.IsDirty); + Assert.Null (c.Grapheme); } [Fact] @@ -21,32 +19,50 @@ public void Equals_False () var c2 = new Cell { - Rune = new ('a'), Attribute = new (Color.Red) + Grapheme = "a", Attribute = new (Color.Red) }; Assert.False (c1.Equals (c2)); Assert.False (c2.Equals (c1)); - c1.Rune = new ('a'); + c1.Grapheme = "a"; c1.Attribute = new (); - Assert.Equal (c1.Rune, c2.Rune); + Assert.Equal (c1.Grapheme, c2.Grapheme); Assert.False (c1.Equals (c2)); Assert.False (c2.Equals (c1)); } [Fact] - public void ToString_Override () + public void Set_Text_With_Invalid_Grapheme_Throws () { - var c1 = new Cell (); + Assert.Throws (() => new Cell { Grapheme = "ab" }); + Assert.Throws (() => new Cell { Grapheme = "\u0061\u0062" }); // ab + } - var c2 = new Cell - { - Rune = new ('a'), Attribute = new (Color.Red) - }; - Assert.Equal ("['\0':]", c1.ToString ()); + [Theory] + [MemberData (nameof (ToStringTestData))] + public void ToString_Override (string text, Attribute? attribute, string expected) + { + var c = new Cell (attribute, true, text); + string result = c.ToString (); - Assert.Equal ( - "['a':[Red,Red,None]]", - c2.ToString () - ); + Assert.Equal (expected, result); + } + + public static IEnumerable ToStringTestData () + { + yield return ["", null, "[\"\":]"]; + yield return ["a", null, "[\"a\":]"]; + yield return ["\t", null, "[\"\\t\":]"]; + yield return ["\r", null, "[\"\\r\":]"]; + yield return ["\n", null, "[\"\\n\":]"]; + yield return ["\r\n", null, "[\"\\r\\n\":]"]; + yield return ["\f", null, "[\"\\f\":]"]; + yield return ["\v", null, "[\"\\v\":]"]; + yield return ["\x1B", null, "[\"\\u001B\":]"]; + yield return ["\\", new Attribute (Color.Blue), "[\"\\\":[Blue,Blue,None]]"]; + yield return ["😀", null, "[\"😀\":]"]; + yield return ["👨‍👩‍👦‍👦", null, "[\"👨‍👩‍👦‍👦\":]"]; + yield return ["A", new Attribute (Color.Red) { Style = TextStyle.Blink }, "[\"A\":[Red,Red,Blink]]"]; + yield return ["\U0001F469\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468", null, "[\"👩‍❤️‍💋‍👨\":]"]; } } diff --git a/Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs b/Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs index 2dd2fb769a..aa0c3fb76d 100644 --- a/Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs +++ b/Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs @@ -19,7 +19,7 @@ public void AddRune () driver.Rows = 25; driver.Cols = 80; driver.AddRune (new Rune ('a')); - Assert.Equal ((Rune)'a', driver.Contents [0, 0].Rune); + Assert.Equal ("a", driver.Contents [0, 0].Grapheme); driver.End (); } @@ -29,28 +29,30 @@ public void AddRune_Accented_Letter_With_Three_Combining_Unicode_Chars () { IDriver driver = CreateFakeDriver (); - var expected = new Rune ('ắ'); + var expected = "ắ"; var text = "\u1eaf"; driver.AddStr (text); - Assert.Equal (expected, driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); + Assert.Equal (expected, driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); driver.ClearContents (); driver.Move (0, 0); + expected = "ắ"; text = "\u0103\u0301"; driver.AddStr (text); - Assert.Equal (expected, driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); + Assert.Equal (expected, driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); driver.ClearContents (); driver.Move (0, 0); + expected = "ắ"; text = "\u0061\u0306\u0301"; driver.AddStr (text); - Assert.Equal (expected, driver.Contents [0, 0].Rune); - Assert.Equal ((Rune)' ', driver.Contents [0, 1].Rune); + Assert.Equal (expected, driver.Contents [0, 0].Grapheme); + Assert.Equal (" ", driver.Contents [0, 1].Grapheme); // var s = "a\u0301\u0300\u0306"; @@ -86,7 +88,7 @@ public void AddRune_InvalidLocation_DoesNothing () { for (var row = 0; row < driver.Rows; row++) { - Assert.Equal ((Rune)' ', driver.Contents [row, col].Rune); + Assert.Equal (" ", driver.Contents [row, col].Grapheme); } } @@ -99,12 +101,12 @@ public void AddRune_MovesToNextColumn () IDriver driver = CreateFakeDriver (); driver.AddRune ('a'); - Assert.Equal ((Rune)'a', driver.Contents [0, 0].Rune); + Assert.Equal ("a", driver.Contents [0, 0].Grapheme); Assert.Equal (0, driver.Row); Assert.Equal (1, driver.Col); driver.AddRune ('b'); - Assert.Equal ((Rune)'b', driver.Contents [0, 1].Rune); + Assert.Equal ("b", driver.Contents [0, 1].Grapheme); Assert.Equal (0, driver.Row); Assert.Equal (2, driver.Col); @@ -116,7 +118,7 @@ public void AddRune_MovesToNextColumn () // Add a rune to the last column of the first row; should increment the row or col even though it's now invalid driver.AddRune ('c'); - Assert.Equal ((Rune)'c', driver.Contents [0, lastCol].Rune); + Assert.Equal ("c", driver.Contents [0, lastCol].Grapheme); Assert.Equal (lastCol + 1, driver.Col); // Add a rune; should succeed but do nothing as it's outside of Contents @@ -127,7 +129,7 @@ public void AddRune_MovesToNextColumn () { for (var row = 0; row < driver.Rows; row++) { - Assert.NotEqual ((Rune)'d', driver.Contents [row, col].Rune); + Assert.NotEqual ("d", driver.Contents [row, col].Grapheme); } } @@ -146,12 +148,12 @@ public void AddRune_MovesToNextColumn_Wide () Assert.Equal (2, rune.GetColumns ()); driver.AddRune (rune); - Assert.Equal (rune, driver.Contents [0, 0].Rune); + Assert.Equal (rune.ToString (), driver.Contents [0, 0].Grapheme); Assert.Equal (0, driver.Row); Assert.Equal (2, driver.Col); //driver.AddRune ('b'); - //Assert.Equal ((Rune)'b', driver.Contents [0, 1].Rune); + //Assert.Equal ((Text)'b', driver.Contents [0, 1].Text); //Assert.Equal (0, driver.Row); //Assert.Equal (2, driver.Col); @@ -163,7 +165,7 @@ public void AddRune_MovesToNextColumn_Wide () //// Add a rune to the last column of the first row; should increment the row or col even though it's now invalid //driver.AddRune ('c'); - //Assert.Equal ((Rune)'c', driver.Contents [0, lastCol].Rune); + //Assert.Equal ((Text)'c', driver.Contents [0, lastCol].Text); //Assert.Equal (lastCol + 1, driver.Col); //// Add a rune; should succeed but do nothing as it's outside of Contents @@ -171,7 +173,7 @@ public void AddRune_MovesToNextColumn_Wide () //Assert.Equal (lastCol + 2, driver.Col); //for (var col = 0; col < driver.Cols; col++) { // for (var row = 0; row < driver.Rows; row++) { - // Assert.NotEqual ((Rune)'d', driver.Contents [row, col].Rune); + // Assert.NotEqual ((Text)'d', driver.Contents [row, col].Text); // } //} diff --git a/Tests/UnitTestsParallelizable/Drivers/ContentsTests.cs b/Tests/UnitTestsParallelizable/Drivers/ContentsTests.cs index ceaccc042a..7068d828ef 100644 --- a/Tests/UnitTestsParallelizable/Drivers/ContentsTests.cs +++ b/Tests/UnitTestsParallelizable/Drivers/ContentsTests.cs @@ -27,7 +27,7 @@ public void AddStr_With_Combining_Characters () var acuteAccent = new Rune (0x0301); // Combining acute accent (é) string combined = "e" + acuteAccent; - var expected = "é"; + var expected = "é"; driver.AddStr (combined); DriverAssert.AssertDriverContentsAre (expected, output, driver); @@ -36,7 +36,7 @@ public void AddStr_With_Combining_Characters () // a + ogonek + acute = ( ą́ ) var oGonek = new Rune (0x0328); // Combining ogonek (a small hook or comma shape) combined = "a" + oGonek + acuteAccent; - expected = ("a" + oGonek).Normalize (NormalizationForm.FormC); // See Issue #2616 + expected = ("a" + oGonek + acuteAccent).Normalize (NormalizationForm.FormD); // See Issue #2616 driver.Move (0, 0); driver.AddStr (combined); @@ -44,7 +44,7 @@ public void AddStr_With_Combining_Characters () // e + ogonek + acute = ( ę́́ ) combined = "e" + oGonek + acuteAccent; - expected = ("e" + oGonek).Normalize (NormalizationForm.FormC); // See Issue #2616 + expected = ("e" + oGonek + acuteAccent).Normalize (NormalizationForm.FormD); // See Issue #2616 driver.Move (0, 0); driver.AddStr (combined); @@ -52,7 +52,7 @@ public void AddStr_With_Combining_Characters () // i + ogonek + acute = ( į́́́ ) combined = "i" + oGonek + acuteAccent; - expected = ("i" + oGonek).Normalize (NormalizationForm.FormC); // See Issue #2616 + expected = ("i" + oGonek + acuteAccent).Normalize (NormalizationForm.FormD); // See Issue #2616 driver.Move (0, 0); driver.AddStr (combined); @@ -60,7 +60,7 @@ public void AddStr_With_Combining_Characters () // u + ogonek + acute = ( ų́́́́ ) combined = "u" + oGonek + acuteAccent; - expected = ("u" + oGonek).Normalize (NormalizationForm.FormC); // See Issue #2616 + expected = ("u" + oGonek + acuteAccent).Normalize (NormalizationForm.FormD); // See Issue #2616 driver.Move (0, 0); driver.AddStr (combined); diff --git a/Tests/UnitTestsParallelizable/Drivers/FakeDriverTests.cs b/Tests/UnitTestsParallelizable/Drivers/FakeDriverTests.cs index 251a26344d..30ba27be9e 100644 --- a/Tests/UnitTestsParallelizable/Drivers/FakeDriverTests.cs +++ b/Tests/UnitTestsParallelizable/Drivers/FakeDriverTests.cs @@ -155,7 +155,7 @@ public void FakeDriver_Can_Fill_Rectangle () { for (int col = rect.X; col < rect.X + rect.Width; col++) { - Assert.Equal ((Rune)'X', driver.Contents [row, col].Rune); + Assert.Equal ("X", driver.Contents [row, col].Grapheme); } } } diff --git a/Tests/UnitTestsParallelizable/Text/RuneTests.cs b/Tests/UnitTestsParallelizable/Text/RuneTests.cs index 34214d6ef8..f5bc5ab675 100644 --- a/Tests/UnitTestsParallelizable/Text/RuneTests.cs +++ b/Tests/UnitTestsParallelizable/Text/RuneTests.cs @@ -88,7 +88,7 @@ public void GetColumns_GetRuneCount () 1 )] // the letters 법 join to form the Korean word for "rice:" U+BC95 법 (read from top left to bottom right) [InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467", "👨‍👩‍👧", 8, 2, 8)] // Man, Woman and Girl emoji. - [InlineData ("\u0915\u093f", "कि", 2, 1, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I + [InlineData ("\u0915\u093f", "कि", 2, 2, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I [InlineData ( "\u0e4d\u0e32", "ํา", @@ -365,6 +365,42 @@ public void Rune_ColumnWidth_Versus_String_ConsoleWidth (string text, int string [InlineData ('\ud801')] public void Rune_Exceptions_Integers (int code) { Assert.Throws (() => new Rune (code)); } + [Theory] + // Control characters (should be mapped to Control Pictures) + [InlineData ('\u0000', 0x2400)] // NULL → ␀ + [InlineData ('\u0009', 0x2409)] // TAB → ␉ + [InlineData ('\u000A', 0x240A)] // LF → ␊ + [InlineData ('\u000D', 0x240D)] // CR → ␍ + + // Printable characters (should remain unchanged) + [InlineData ('A', 'A')] + [InlineData (' ', ' ')] + [InlineData ('~', '~')] + public void MakePrintable_ReturnsExpected (char inputChar, int expectedCodePoint) + { + // Arrange + Rune input = new Rune (inputChar); + + // Act + Rune result = input.MakePrintable (); + + // Assert + Assert.Equal (expectedCodePoint, result.Value); + } + + [Fact] + public void MakePrintable_SupplementaryRune_RemainsUnchanged () + { + // Arrange: supplementary character outside BMP (not a control) + Rune input = new Rune (0x1F600); // 😀 grinning face emoji + + // Act + Rune result = input.MakePrintable (); + + // Assert + Assert.Equal (input.Value, result.Value); + } + [Theory] [InlineData (new [] { '\ud799', '\udc21' })] public void Rune_Exceptions_Utf16_Encode (char [] code) @@ -954,11 +990,9 @@ int txtElementCount Assert.Equal (runeCount, us.GetRuneCount ()); Assert.Equal (stringCount, s.Length); - TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator (s); - var textElementCount = 0; - while (enumerator.MoveNext ()) + foreach (string _ in GraphemeHelper.GetGraphemes (s)) { textElementCount++; // For versions prior to Net5.0 the StringInfo class might handle some grapheme clusters incorrectly. } @@ -1064,4 +1098,95 @@ private bool ProcessTestStringUseChar (string s) return true; } + + [Theory] + [InlineData (0x0041, new byte [] { 0x41 })] // 'A', ASCII + [InlineData (0x00E9, new byte [] { 0xC3, 0xA9 })] // 'é', 2-byte UTF-8 + [InlineData (0x20AC, new byte [] { 0xE2, 0x82, 0xAC })] // '€', 3-byte UTF-8 + [InlineData (0x1F600, new byte [] { 0xF0, 0x9F, 0x98, 0x80 })] // 😀 emoji, 4-byte UTF-8 + public void Encode_WritesExpectedBytes (int codePoint, byte [] expectedBytes) + { + // Arrange + Rune rune = new Rune (codePoint); + byte [] buffer = new byte [10]; // extra space + for (int i = 0; i < buffer.Length; i++) + { + buffer [i] = 0xFF; + } + + // Act + int written = rune.Encode (buffer); + + // Assert + Assert.Equal (expectedBytes.Length, written); + for (int i = 0; i < written; i++) + { + Assert.Equal (expectedBytes [i], buffer [i]); + } + } + + [Fact] + public void Encode_WithStartAndCount_WritesPartialBytes () + { + // Arrange: U+1F600 😀 (4 bytes) + Rune rune = new Rune (0x1F600); + byte [] buffer = new byte [10]; + for (int i = 0; i < buffer.Length; i++) + { + buffer [i] = 0xFF; + } + + // Act: write starting at index 2, limit count to 2 bytes + int written = rune.Encode (buffer, start: 2, count: 2); + + // Assert + Assert.Equal (2, written); + // Original UTF-8 bytes: F0 9F 98 80 + Assert.Equal (0xF0, buffer [2]); + Assert.Equal (0x9F, buffer [3]); + // Remaining buffer untouched + Assert.Equal (0xFF, buffer [0]); + Assert.Equal (0xFF, buffer [1]); + Assert.Equal (0xFF, buffer [4]); + } + + [Fact] + public void Encode_WithCountGreaterThanRuneBytes_WritesAllBytes () + { + // Arrange: é → C3 A9 + Rune rune = new Rune ('é'); + byte [] buffer = new byte [10]; + for (int i = 0; i < buffer.Length; i++) + { + buffer [i] = 0xFF; + } + + // Act: count larger than needed + int written = rune.Encode (buffer, start: 1, count: 10); + + // Assert + Assert.Equal (2, written); + Assert.Equal (0xC3, buffer [1]); + Assert.Equal (0xA9, buffer [2]); + Assert.Equal (0xFF, buffer [3]); // next byte untouched + } + + [Fact] + public void Encode_ZeroCount_WritesNothing () + { + Rune rune = new Rune ('A'); + byte [] buffer = new byte [5]; + for (int i = 0; i < buffer.Length; i++) + { + buffer [i] = 0xFF; + } + + int written = rune.Encode (buffer, start: 0, count: 0); + + Assert.Equal (0, written); + foreach (var b in buffer) + { + Assert.Equal (0xFF, b); // buffer untouched + } + } } diff --git a/Tests/UnitTestsParallelizable/Text/StringTests.cs b/Tests/UnitTestsParallelizable/Text/StringTests.cs index 1c77e75644..37c945ac54 100644 --- a/Tests/UnitTestsParallelizable/Text/StringTests.cs +++ b/Tests/UnitTestsParallelizable/Text/StringTests.cs @@ -30,19 +30,24 @@ public void TestGetColumns_Empty () // Test known wide codepoints [Theory] - [InlineData ("🙂", 2)] - [InlineData ("a🙂", 3)] - [InlineData ("🙂a", 3)] - [InlineData ("👨‍👩‍👦‍👦", 2)] - [InlineData ("👨‍👩‍👦‍👦🙂", 4)] - [InlineData ("👨‍👩‍👦‍👦🙂a", 5)] - [InlineData ("👨‍👩‍👦‍👦a🙂", 5)] - [InlineData ("👨‍👩‍👦‍👦👨‍👩‍👦‍👦", 4)] - [InlineData ("山", 2)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71 - [InlineData ("山🙂", 4)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71 + [InlineData ("🙂", 1, 2)] + [InlineData ("a🙂", 2, 3)] + [InlineData ("🙂a", 2, 3)] + [InlineData ("👨‍👩‍👦‍👦", 1, 2)] + [InlineData ("👨‍👩‍👦‍👦🙂", 2, 4)] + [InlineData ("👨‍👩‍👦‍👦🙂a", 3, 5)] + [InlineData ("👨‍👩‍👦‍👦a🙂", 3, 5)] + [InlineData ("👨‍👩‍👦‍👦👨‍👩‍👦‍👦", 2, 4)] + [InlineData ("าำ", 1, 2)] // า U+0E32 - THAI CHARACTER SARA AA with ำ U+0E33 - THAI CHARACTER SARA AM + [InlineData ("山", 1, 2)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71 + [InlineData ("山🙂", 2, 4)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71 //[InlineData ("\ufe20\ufe21", 2)] // Combining Ligature Left Half ︠ - U+fe20 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml // // Combining Ligature Right Half - U+fe21 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml - public void TestGetColumns_MultiRune_WideBMP (string str, int expected) { Assert.Equal (expected, str.GetColumns ()); } + public void TestGetColumns_MultiRune_WideBMP_Graphemes (string str, int graphemesCount, int expected) + { + Assert.Equal (graphemesCount, GraphemeHelper.GetGraphemes (str).ToArray ().Length); + Assert.Equal (expected, str.GetColumns ()); + } [Fact] public void TestGetColumns_Null () @@ -64,4 +69,121 @@ public void TestGetColumns_Zero_Width () var str = "\u200D"; Assert.Equal (0, str.GetColumns ()); } + + public class ReadOnlySpanExtensionsTests + { + [Theory] + [InlineData ("12345", true)] // all ASCII digits + [InlineData ("0", true)] // single ASCII digit + [InlineData ("", false)] // empty span + [InlineData ("12a45", false)] // contains a letter + [InlineData ("123", false)] // full-width Unicode digits (not ASCII) + [InlineData ("12 34", false)] // contains space + [InlineData ("١٢٣", false)] // Arabic-Indic digits + public void IsAllAsciiDigits_WorksAsExpected (string input, bool expected) + { + // Arrange + ReadOnlySpan span = input.AsSpan (); + + // Act + bool result = span.IsAllAsciiDigits (); + + // Assert + Assert.Equal (expected, result); + } + } + + [Theory] + [InlineData ("0", true)] + [InlineData ("9", true)] + [InlineData ("A", true)] + [InlineData ("F", true)] + [InlineData ("a", true)] + [InlineData ("f", true)] + [InlineData ("123ABC", true)] + [InlineData ("abcdef", true)] + [InlineData ("G", false)] // 'G' not hex + [InlineData ("Z9", false)] // 'Z' not hex + [InlineData ("12 34", false)] // space not hex + [InlineData ("", false)] // empty string + [InlineData ("123", false)] // full-width digits, not ASCII + [InlineData ("0xFF", false)] // includes 'x' + public void IsAllAsciiHexDigits_ReturnsExpected (string input, bool expected) + { + // Arrange + ReadOnlySpan span = input.AsSpan (); + + // Act + bool result = span.IsAllAsciiHexDigits (); + + // Assert + Assert.Equal (expected, result); + } + + [Theory] + [MemberData (nameof (GetStringConcatCases))] + public void ToString_ReturnsExpected (IEnumerable input, string expected) + { + // Act + string result = StringExtensions.ToString (input); + + // Assert + Assert.Equal (expected, result); + } + + public static IEnumerable GetStringConcatCases () + { + yield return [new string [] { }, string.Empty]; // Empty sequence + yield return [new [] { "" }, string.Empty]; // Single empty string + yield return [new [] { "A" }, "A"]; // Single element + yield return [new [] { "A", "B" }, "AB"]; // Simple concatenation + yield return [new [] { "Hello", " ", "World" }, "Hello World"]; // Multiple parts + yield return [new [] { "123", "456", "789" }, "123456789"]; // Numeric strings + yield return [new [] { "👩‍", "🧒" }, "👩‍🧒"]; // Grapheme sequence + yield return [new [] { "α", "β", "γ" }, "αβγ"]; // Unicode letters + yield return [new [] { "A", null, "B" }, "AB"]; // Null ignored by string.Concat + } + + [Theory] + [InlineData ("", false)] // Empty string + [InlineData ("A", false)] // Single BMP character + [InlineData ("AB", false)] // Two BMP chars, not a surrogate pair + [InlineData ("👩", true)] // Single emoji surrogate pair (U+1F469) + [InlineData ("🧒", true)] // Another emoji surrogate pair (U+1F9D2) + [InlineData ("𐍈", true)] // Gothic letter hwair (U+10348) + [InlineData ("A👩", false)] // One BMP + one surrogate half + [InlineData ("👩‍", false)] // Surrogate pair + ZWJ (length != 2) + public void IsSurrogatePair_ReturnsExpected (string input, bool expected) + { + // Act + bool result = input.IsSurrogatePair (); + + // Assert + Assert.Equal (expected, result); + } + + [Theory] + // Control characters (should be replaced with the "Control Pictures" block) + [InlineData ("\u0000", "\u2400")] // NULL → ␀ + [InlineData ("\u0009", "\u2409")] // TAB → ␉ + [InlineData ("\u000A", "\u240A")] // LF → ␊ + [InlineData ("\u000D", "\u240D")] // CR → ␍ + + // Printable characters (should remain unchanged) + [InlineData ("A", "A")] + [InlineData (" ", " ")] + [InlineData ("~", "~")] + + // Multi-character string (should return unchanged) + [InlineData ("AB", "AB")] + [InlineData ("Hello", "Hello")] + [InlineData ("\u0009A", "\u0009A")] // includes a control char, but length > 1 + public void MakePrintable_ReturnsExpected (string input, string expected) + { + // Act + string result = input.MakePrintable (); + + // Assert + Assert.Equal (expected, result); + } } diff --git a/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs b/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs index 0c142f08b2..af2946170c 100644 --- a/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs +++ b/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs @@ -665,5 +665,30 @@ string expectedDraw DriverAssert.AssertDriverContentsWithFrameAre (expectedDraw, output, driver); } + [Theory] + [InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", 2, 1, TextDirection.LeftRight_TopBottom, "👨‍👩‍👧‍👦")] + [InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", 2, 1, TextDirection.TopBottom_LeftRight, "👨‍👩‍👧‍👦")] + public void Draw_Emojis_With_Zero_Width_Joiner ( + string text, + int width, + int height, + TextDirection direction, + string expectedDraw + ) + { + TextFormatter tf = new () + { + Direction = direction, + ConstrainToSize = new (width, height), + Text = text, + WordWrap = false + }; + Assert.Equal (width, text.GetColumns ()); + + tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default); + + DriverAssert.AssertDriverContentsWithFrameAre (expectedDraw, output); + } + #endregion } diff --git a/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs b/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs index 88e95fd7f2..91b3cbdd1a 100644 --- a/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs +++ b/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs @@ -792,19 +792,16 @@ public void GetColumnsRequiredForVerticalText_List_With_Combining_Runes () [MemberData (nameof (CMGlyphs))] public void GetLengthThatFits_List_Simple_And_Wide_Runes (string text, int columns, int expectedLength) { - List runes = text.ToRuneList (); - Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns)); + Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns)); } [Theory] [InlineData ("test", 3, 3)] [InlineData ("test", 4, 4)] [InlineData ("test", 10, 4)] - public void GetLengthThatFits_Runelist (string text, int columns, int expectedLength) + public void GetLengthThatFits_For_String (string text, int columns, int expectedLength) { - List runes = text.ToRuneList (); - - Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns)); + Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns)); } [Theory] @@ -833,7 +830,7 @@ public void GetLengthThatFits_String (string text, int columns, int expectedLeng public void GetLengthThatFits_With_Combining_Runes () { var text = "Les Mise\u0328\u0301rables"; - Assert.Equal (16, TextFormatter.GetLengthThatFits (text, 14)); + Assert.Equal (14, TextFormatter.GetLengthThatFits (text, 14)); } [Fact] @@ -2761,8 +2758,7 @@ IEnumerable resultLines "ฮ", "ฯ", "ะั", - "า", - "ำ" + "าำ" } )] public void WordWrap_Unicode_SingleWordLine ( @@ -2797,7 +2793,17 @@ IEnumerable resultLines Assert.True ( expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) ); - Assert.Equal (resultLines, wrappedLines); + + if (maxWidth == 1) + { + List newResultLines = resultLines.ToList (); + newResultLines [^1] = ""; + Assert.Equal (newResultLines, wrappedLines); + } + else + { + Assert.Equal (resultLines, wrappedLines); + } } /// WordWrap strips CRLF @@ -3074,8 +3080,8 @@ public void Draw_Horizontal_Right (string text, int width, string expectedText) } [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 (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, @@ -3084,7 +3090,7 @@ public void Draw_Horizontal_Right (string text, int width, string expectedText) LMre eias ssb - ęl " + ę́l " )] public void Draw_With_Combining_Runes (int width, int height, TextDirection textDirection, string expected) { @@ -3111,7 +3117,6 @@ public void Draw_With_Combining_Runes (int width, int height, TextDirection text 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")] @@ -3189,7 +3194,6 @@ string expected 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")] @@ -3227,5 +3231,4 @@ string expected driver.End (); } - } diff --git a/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs b/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs index a5362f46fc..ab436722a4 100644 --- a/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs +++ b/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs @@ -591,7 +591,7 @@ public void Accented_Letter_With_Three_Combining_Unicode_Chars () DriverAssert.AssertDriverContentsWithFrameAre ( @" -ắ", +ắ", output, driver ); @@ -602,7 +602,7 @@ public void Accented_Letter_With_Three_Combining_Unicode_Chars () DriverAssert.AssertDriverContentsWithFrameAre ( @" -ắ", +ắ", output, driver ); @@ -626,7 +626,7 @@ string GetContents () for (var i = 0; i < 16; i++) { - item += driver.Contents [0, i]!.Rune; + item += driver.Contents [0, i]!.Grapheme; } return item; diff --git a/Tests/UnitTestsParallelizable/Views/TextViewTests.cs b/Tests/UnitTestsParallelizable/Views/TextViewTests.cs index 8ea0df071f..a9b940bea4 100644 --- a/Tests/UnitTestsParallelizable/Views/TextViewTests.cs +++ b/Tests/UnitTestsParallelizable/Views/TextViewTests.cs @@ -1444,23 +1444,23 @@ void AssertRedGreenAttribute () public void Internal_Tests () { var txt = "This is a text."; - List txtRunes = Cell.StringToCells (txt); - Assert.Equal (txt.Length, txtRunes.Count); - Assert.Equal ('T', txtRunes [0].Rune.Value); - Assert.Equal ('h', txtRunes [1].Rune.Value); - Assert.Equal ('i', txtRunes [2].Rune.Value); - Assert.Equal ('s', txtRunes [3].Rune.Value); - Assert.Equal (' ', txtRunes [4].Rune.Value); - Assert.Equal ('i', txtRunes [5].Rune.Value); - Assert.Equal ('s', txtRunes [6].Rune.Value); - Assert.Equal (' ', txtRunes [7].Rune.Value); - Assert.Equal ('a', txtRunes [8].Rune.Value); - Assert.Equal (' ', txtRunes [9].Rune.Value); - Assert.Equal ('t', txtRunes [10].Rune.Value); - Assert.Equal ('e', txtRunes [11].Rune.Value); - Assert.Equal ('x', txtRunes [12].Rune.Value); - Assert.Equal ('t', txtRunes [13].Rune.Value); - Assert.Equal ('.', txtRunes [^1].Rune.Value); + List txtStrings = Cell.StringToCells (txt); + Assert.Equal (txt.Length, txtStrings.Count); + Assert.Equal ("T", txtStrings [0].Grapheme); + Assert.Equal ("h", txtStrings [1].Grapheme); + Assert.Equal ("i", txtStrings [2].Grapheme); + Assert.Equal ("s", txtStrings [3].Grapheme); + Assert.Equal (" ", txtStrings [4].Grapheme); + Assert.Equal ("i", txtStrings [5].Grapheme); + Assert.Equal ("s", txtStrings [6].Grapheme); + Assert.Equal (" ", txtStrings [7].Grapheme); + Assert.Equal ("a", txtStrings [8].Grapheme); + Assert.Equal (" ", txtStrings [9].Grapheme); + Assert.Equal ("t", txtStrings [10].Grapheme); + Assert.Equal ("e", txtStrings [11].Grapheme); + Assert.Equal ("x", txtStrings [12].Grapheme); + Assert.Equal ("t", txtStrings [13].Grapheme); + Assert.Equal (".", txtStrings [^1].Grapheme); var col = 0; Assert.True (TextModel.SetCol (ref col, 80, 79)); @@ -1469,19 +1469,19 @@ public void Internal_Tests () var start = 0; var x = 8; - Assert.Equal (8, TextModel.GetColFromX (txtRunes, start, x)); - Assert.Equal ('a', txtRunes [start + x].Rune.Value); + Assert.Equal (8, TextModel.GetColFromX (txtStrings, start, x)); + Assert.Equal ("a", txtStrings [start + x].Grapheme); start = 1; x = 7; - Assert.Equal (7, TextModel.GetColFromX (txtRunes, start, x)); - Assert.Equal ('a', txtRunes [start + x].Rune.Value); + Assert.Equal (7, TextModel.GetColFromX (txtStrings, start, x)); + Assert.Equal ("a", txtStrings [start + x].Grapheme); - Assert.Equal ((15, 15), TextModel.DisplaySize (txtRunes)); - Assert.Equal ((6, 6), TextModel.DisplaySize (txtRunes, 1, 7)); + Assert.Equal ((15, 15), TextModel.DisplaySize (txtStrings)); + Assert.Equal ((6, 6), TextModel.DisplaySize (txtStrings, 1, 7)); - Assert.Equal (0, TextModel.CalculateLeftColumn (txtRunes, 0, 7, 8)); - Assert.Equal (1, TextModel.CalculateLeftColumn (txtRunes, 0, 8, 8)); - Assert.Equal (2, TextModel.CalculateLeftColumn (txtRunes, 0, 9, 8)); + Assert.Equal (0, TextModel.CalculateLeftColumn (txtStrings, 0, 7, 8)); + Assert.Equal (1, TextModel.CalculateLeftColumn (txtStrings, 0, 8, 8)); + Assert.Equal (2, TextModel.CalculateLeftColumn (txtStrings, 0, 9, 8)); var tm = new TextModel (); tm.AddLine (0, Cell.StringToCells ("This is first line.")); @@ -2050,9 +2050,9 @@ public void Equals_True () Assert.True (c1.Equals (c2)); Assert.True (c2.Equals (c1)); - c1.Rune = new ('a'); + c1.Grapheme = new ("a"); c1.Attribute = new (); - c2.Rune = new ('a'); + c2.Grapheme = new ("a"); c2.Attribute = new (); Assert.True (c1.Equals (c2)); Assert.True (c2.Equals (c1)); @@ -2063,10 +2063,10 @@ public void Cell_LoadCells_Without_Scheme_Is_Never_Null () { List cells = new () { - new () { Rune = new ('T') }, - new () { Rune = new ('e') }, - new () { Rune = new ('s') }, - new () { Rune = new ('t') } + new () { Grapheme = new ("T") }, + new () { Grapheme = new ("e") }, + new () { Grapheme = new ("s") }, + new () { Grapheme = new ("t") } }; TextView tv = CreateTextView (); var top = new Toplevel (); From 37c971851019cafce9c5477b700131a03804a9b7 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 12 Nov 2025 15:40:05 +0000 Subject: [PATCH 05/32] Prevents Runes throwing if Grapheme is null --- Terminal.Gui/Drawing/Cell.cs | 2 +- .../Drawing/CellTests.cs | 23 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index c3f6130384..96670e2961 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -44,7 +44,7 @@ public string Grapheme /// In the case where has more than one rune it is a combining sequence that is normalized to a /// single Text which may occupies 1 or 2 columns. /// - public IReadOnlyList Runes => Grapheme.EnumerateRunes ().ToList (); + public IReadOnlyList Runes => string.IsNullOrEmpty (Grapheme) ? [] : Grapheme.EnumerateRunes ().ToList (); /// public override string ToString () diff --git a/Tests/UnitTestsParallelizable/Drawing/CellTests.cs b/Tests/UnitTestsParallelizable/Drawing/CellTests.cs index 3ee2dea542..c3bc4786ac 100644 --- a/Tests/UnitTestsParallelizable/Drawing/CellTests.cs +++ b/Tests/UnitTestsParallelizable/Drawing/CellTests.cs @@ -1,4 +1,6 @@ -namespace UnitTests_Parallelizable.DrawingTests; +using System.Text; + +namespace UnitTests_Parallelizable.DrawingTests; public class CellTests { @@ -7,11 +9,30 @@ public void Constructor_Defaults () { var c = new Cell (); Assert.True (c is { }); + Assert.Empty (c.Runes); Assert.Null (c.Attribute); Assert.False (c.IsDirty); Assert.Null (c.Grapheme); } + [Theory] + [InlineData (null, new uint [] { })] + [InlineData ("", new uint [] { })] + [InlineData ("a", new uint [] { 0x0061 })] + [InlineData ("👩‍❤️‍💋‍👨", new uint [] { 0x1F469, 0x200D, 0x2764, 0xFE0F, 0x200D, 0x1F48B, 0x200D, 0x1F468 })] + public void Runes_From_Grapheme (string grapheme, uint [] expected) + { + // Arrange + var c = new Cell { Grapheme = grapheme }; + + // Act + Rune [] runes = expected.Select (u => new Rune (u)).ToArray (); + + // Assert + Assert.Equal (grapheme, c.Grapheme); + Assert.Equal (runes, c.Runes); + } + [Fact] public void Equals_False () { From 178e1e7039fcdc1c4a6119fccf25546695a0b6e1 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 12 Nov 2025 16:49:08 +0000 Subject: [PATCH 06/32] Add unit test to prove that null and empty string doesn't not throws anything. --- Tests/UnitTestsParallelizable/Text/StringTests.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Tests/UnitTestsParallelizable/Text/StringTests.cs b/Tests/UnitTestsParallelizable/Text/StringTests.cs index 1c77e75644..80c52b96ea 100644 --- a/Tests/UnitTestsParallelizable/Text/StringTests.cs +++ b/Tests/UnitTestsParallelizable/Text/StringTests.cs @@ -64,4 +64,19 @@ public void TestGetColumns_Zero_Width () var str = "\u200D"; Assert.Equal (0, str.GetColumns ()); } + + [Theory] + [InlineData (null)] + [InlineData ("")] + public void TestGetColumns_Does_Not_Throws_With_Null_And_Empty_String (string? text) + { + if (text is null) + { + Assert.Equal (0, StringExtensions.GetColumns (text!)); + } + else + { + Assert.Equal (0, text.GetColumns ()); + } + } } From 9b08101fa69a0e93a2642aea8e35d01c3e4df784 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 12 Nov 2025 19:22:45 +0000 Subject: [PATCH 07/32] Fix unit test failure --- .../UnitTestsParallelizable/Text/TextFormatterDrawTests.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs b/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs index af2946170c..34db80c7eb 100644 --- a/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs +++ b/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs @@ -676,6 +676,8 @@ public void Draw_Emojis_With_Zero_Width_Joiner ( string expectedDraw ) { + IDriver driver = CreateFakeDriver (); + TextFormatter tf = new () { Direction = direction, @@ -685,9 +687,9 @@ string expectedDraw }; Assert.Equal (width, text.GetColumns ()); - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default); + tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default, driver: driver); - DriverAssert.AssertDriverContentsWithFrameAre (expectedDraw, output); + DriverAssert.AssertDriverContentsWithFrameAre (expectedDraw, output, driver); } #endregion From 8d6f1628b25aabbb162c533895d33147c98cb8ce Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 12 Nov 2025 21:55:05 +0000 Subject: [PATCH 08/32] Fix IsValidLocation for wide graphemes --- Terminal.Gui/Drivers/OutputBufferImpl.cs | 7 +-- .../Drivers/DriverTests.cs | 46 ++++++++++++++++++- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/Terminal.Gui/Drivers/OutputBufferImpl.cs b/Terminal.Gui/Drivers/OutputBufferImpl.cs index 02237d23cd..ee573789b7 100644 --- a/Terminal.Gui/Drivers/OutputBufferImpl.cs +++ b/Terminal.Gui/Drivers/OutputBufferImpl.cs @@ -289,12 +289,9 @@ public void ClearContents () /// public bool IsValidLocation (string text, int col, int row) { - if (text.GetColumns () < 2) - { - return col >= 0 && row >= 0 && col < Cols && row < Rows && Clip!.Contains (col, row); - } + int textWidth = text.GetColumns (); - return Clip!.Contains (col, row) || Clip!.Contains (col + 1, row); + return col >= 0 && row >= 0 && col + textWidth <= Cols && row < Rows && Clip!.Contains (col, row); } /// diff --git a/Tests/UnitTestsParallelizable/Drivers/DriverTests.cs b/Tests/UnitTestsParallelizable/Drivers/DriverTests.cs index 9f87d80d09..ee47555073 100644 --- a/Tests/UnitTestsParallelizable/Drivers/DriverTests.cs +++ b/Tests/UnitTestsParallelizable/Drivers/DriverTests.cs @@ -1,6 +1,50 @@ using UnitTests; +using Xunit.Abstractions; namespace UnitTests_Parallelizable.DriverTests; public class DriverTests : FakeDriverBase -{ } +{ + [Theory] + [InlineData (null, true)] + [InlineData ("", true)] + [InlineData ("a", true)] + [InlineData ("👩‍❤️‍💋‍👨", false)] + public void IsValidLocation (string text, bool positive) + { + IDriver driver = CreateFakeDriver (); + driver.SetScreenSize (10, 10); + + // positive + Assert.True (driver.IsValidLocation (text, 0, 0)); + Assert.True (driver.IsValidLocation (text, 1, 1)); + Assert.Equal (positive, driver.IsValidLocation (text, driver.Cols - 1, driver.Rows - 1)); + + // negative + Assert.False (driver.IsValidLocation (text, -1, 0)); + Assert.False (driver.IsValidLocation (text, 0, -1)); + Assert.False (driver.IsValidLocation (text, -1, -1)); + Assert.False (driver.IsValidLocation (text, driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (text, driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (text, driver.Cols, driver.Rows)); + + // Define a clip rectangle + driver.Clip = new (new Rectangle (5, 5, 5, 5)); + + // positive + Assert.True (driver.IsValidLocation (text, 5, 5)); + Assert.Equal (positive, driver.IsValidLocation (text, 9, 9)); + + // negative + Assert.False (driver.IsValidLocation (text, 4, 5)); + Assert.False (driver.IsValidLocation (text, 5, 4)); + Assert.False (driver.IsValidLocation (text, 10, 9)); + Assert.False (driver.IsValidLocation (text, 9, 10)); + Assert.False (driver.IsValidLocation (text, -1, 0)); + Assert.False (driver.IsValidLocation (text, 0, -1)); + Assert.False (driver.IsValidLocation (text, -1, -1)); + Assert.False (driver.IsValidLocation (text, driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (text, driver.Cols, driver.Rows - 1)); + Assert.False (driver.IsValidLocation (text, driver.Cols, driver.Rows)); + } +} From 53b5cc2fd76c24984e66ad14a3806a6836103a33 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 13:32:49 +0000 Subject: [PATCH 09/32] Add more combining --- Examples/UICatalog/Scenarios/CombiningMarks.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Examples/UICatalog/Scenarios/CombiningMarks.cs b/Examples/UICatalog/Scenarios/CombiningMarks.cs index 3918083ec3..c1bd73ecf2 100644 --- a/Examples/UICatalog/Scenarios/CombiningMarks.cs +++ b/Examples/UICatalog/Scenarios/CombiningMarks.cs @@ -89,6 +89,10 @@ public override void Main () top.AddStr ("[\u0e32\u0e33]<- \"[\\u0e32\\u0e33]\" using AddStr."); top.Move (0, ++i); top.AddStr ("[\U0001F469\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468]<- \"[\\U0001F469\\u200D\\u2764\\uFE0F\\u200D\\U0001F48B\\u200D\\U0001F468]\" using AddStr."); + top.Move (0, ++i); + top.AddStr ("[\u0061\uFE20\u0065\uFE21]<- \"[\\u0061\\uFE20\\u0065\\uFE21]\" using AddStr."); + top.Move (0, ++i); + top.AddStr ("[\u1100\uD7B0]<- \"[\\u1100\\uD7B0]\" using AddStr."); }; Application.Run (top); From 0fad7031d935e2f9140d2e467e436e9364c7967b Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 13:38:11 +0000 Subject: [PATCH 10/32] Prevent set invalid graphemes --- Terminal.Gui/Drawing/Cell.cs | 24 ++++- .../Drawing/CellTests.cs | 91 +++++++++++++++++++ 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index 96670e2961..ff5610551b 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -30,10 +30,30 @@ public string Grapheme { if (GraphemeHelper.GetGraphemes(value).ToArray().Length > 1) { - throw new InvalidOperationException ($"Only a single grapheme cluster is allowed per Cell in {nameof (Grapheme)}."); + throw new InvalidOperationException ($"Only a single {nameof (Grapheme)} cluster is allowed per Cell."); } - _grapheme = value; + if (!string.IsNullOrEmpty (value) && value.Length == 1 && char.IsSurrogate (value [0])) + { + throw new ArgumentException ($"Only valid Unicode scalar values are allowed in a single {nameof (Grapheme)} cluster."); + } + + try + { + if (!string.IsNullOrEmpty (value) && !value.IsNormalized (NormalizationForm.FormC)) + { + _grapheme = value.Normalize (NormalizationForm.FormC); + } + else + { + _grapheme = value; + } + } + catch (ArgumentException) + { + // leave text unnormalized + _grapheme = value; + } } } diff --git a/Tests/UnitTestsParallelizable/Drawing/CellTests.cs b/Tests/UnitTestsParallelizable/Drawing/CellTests.cs index c3bc4786ac..b635d2bad1 100644 --- a/Tests/UnitTestsParallelizable/Drawing/CellTests.cs +++ b/Tests/UnitTestsParallelizable/Drawing/CellTests.cs @@ -20,6 +20,9 @@ public void Constructor_Defaults () [InlineData ("", new uint [] { })] [InlineData ("a", new uint [] { 0x0061 })] [InlineData ("👩‍❤️‍💋‍👨", new uint [] { 0x1F469, 0x200D, 0x2764, 0xFE0F, 0x200D, 0x1F48B, 0x200D, 0x1F468 })] + [InlineData ("æ", new uint [] { 0x00E6 })] + [InlineData ("a︠", new uint [] { 0x0061, 0xFE20 })] + [InlineData ("e︡", new uint [] { 0x0065, 0xFE21 })] public void Runes_From_Grapheme (string grapheme, uint [] expected) { // Arrange @@ -86,4 +89,92 @@ public static IEnumerable ToStringTestData () yield return ["A", new Attribute (Color.Red) { Style = TextStyle.Blink }, "[\"A\":[Red,Red,Blink]]"]; yield return ["\U0001F469\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468", null, "[\"👩‍❤️‍💋‍👨\":]"]; } + + [Fact] + public void Graphemes_Decomposed_Normalize () + { + Cell c1 = new () + { + // 'e' + '◌́' COMBINING ACUTE ACCENT (U+0301) + Grapheme = "e\u0301" // visually "é" + }; + + Cell c2 = new () + { + // NFC single code point (U+00E9) + Grapheme = "é" + }; + + // Validation + Assert.Equal ("é", c1.Grapheme); // Proper normalized grapheme + Assert.Equal (c1.Grapheme, c2.Grapheme); + Assert.Equal (c1.Runes.Count, c2.Runes.Count); + Assert.Equal (new (0x00E9), c2.Runes [0]); + } + + [Fact] + public void Cell_IsDirty_Flag_Works () + { + var c = new Cell (); + Assert.False (c.IsDirty); + c.IsDirty = true; + Assert.True (c.IsDirty); + c.IsDirty = false; + Assert.False (c.IsDirty); + } + + [Theory] + [InlineData ("\uFDD0", false)] + [InlineData ("\uFDEF", false)] + [InlineData ("\uFFFE", true)] + [InlineData ("\uFFFF", false)] + [InlineData ("\U0001FFFE", false)] + [InlineData ("\U0001FFFF", false)] + [InlineData ("\U0010FFFE", false)] + [InlineData ("\U0010FFFF", false)] + public void IsNormalized_ArgumentException (string text, bool throws) + { + try + { + bool normalized = text.IsNormalized (NormalizationForm.FormC); + + Assert.True (normalized); + Assert.False (throws); + } + catch (ArgumentException) + { + Assert.True (throws); + } + + Cell c = new () { Grapheme = text }; + } + + [Fact] + public void Surrogate_Normalize_Throws_And_Cell_Setter_Throws () + { + // Create the lone high surrogate at runtime (safe) + string s = new string ((char)0xD800, 1); + + // Confirm the runtime string actually contains the surrogate + Assert.Equal (0xD800, s [0]); + + // Normalize should throw + Assert.Throws (() => s.Normalize (NormalizationForm.FormC)); + + // And if your Grapheme setter normalizes, assignment should throw as well + Assert.Throws (() => new Cell () { Grapheme = s }); + + // Create the lone low surrogate at runtime (safe) + s = new string ((char)0xDC00, 1); + + // Confirm the runtime string actually contains the surrogate + Assert.Equal (0xDC00, s [0]); + + // Normalize should throw + Assert.Throws (() => s.Normalize (NormalizationForm.FormC)); + + // And if your Grapheme setter normalizes, assignment should throw as well + Assert.Throws (() => new Cell () { Grapheme = s }); + } + } From 4ae48d00f3ae90321f114e4d0557bd812a9bc715 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 13:58:40 +0000 Subject: [PATCH 11/32] Fix unit tests --- Tests/UnitTests/Views/TextFieldTests.cs | 4 ++-- Tests/UnitTestsParallelizable/Views/TextFieldTests.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/UnitTests/Views/TextFieldTests.cs b/Tests/UnitTests/Views/TextFieldTests.cs index 8347ac6e3c..c37b10ed2b 100644 --- a/Tests/UnitTests/Views/TextFieldTests.cs +++ b/Tests/UnitTests/Views/TextFieldTests.cs @@ -104,7 +104,7 @@ public void CaptionedTextField_DoesNotOverspillViewport_Unicode () Assert.False (tf.HasFocus); tf.Draw (); - DriverAssert.AssertDriverContentsAre ("Misérables", output); + DriverAssert.AssertDriverContentsAre ("Misérables", output); Application.Top.Dispose (); } @@ -1647,7 +1647,7 @@ public void Words_With_Accents_Incorrect_Order_Will_Result_With_Wrong_Accent_Pla DriverAssert.AssertDriverContentsWithFrameAre ( @" -Les Miśerables", +Les Miśerables", output ); } diff --git a/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs b/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs index ab436722a4..9a1bd2dfac 100644 --- a/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs +++ b/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs @@ -591,7 +591,7 @@ public void Accented_Letter_With_Three_Combining_Unicode_Chars () DriverAssert.AssertDriverContentsWithFrameAre ( @" -ắ", +ắ", output, driver ); @@ -602,7 +602,7 @@ public void Accented_Letter_With_Three_Combining_Unicode_Chars () DriverAssert.AssertDriverContentsWithFrameAre ( @" -ắ", +ắ", output, driver ); From 1e8a37d13106214b48a41434363ddd0c14944683 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 14:39:34 +0000 Subject: [PATCH 12/32] Grapheme doesn't support invalid code points like lone surrogates --- Tests/UnitTests/View/Draw/ClipTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/UnitTests/View/Draw/ClipTests.cs b/Tests/UnitTests/View/Draw/ClipTests.cs index 57f20199d7..474ebb03b9 100644 --- a/Tests/UnitTests/View/Draw/ClipTests.cs +++ b/Tests/UnitTests/View/Draw/ClipTests.cs @@ -226,7 +226,7 @@ public void Clipping_Wide_Runes () // 01 2345678901234 56 78 90 12 34 56 // │� |0123456989│� ン ラ イ ン で す 。 expectedOutput = """ - │�│0123456789│�ンラインです。 + │�│0123456789│ ンラインです。 """; DriverAssert.AssertDriverContentsWithFrameAre (expectedOutput, _output); From 5974e81dbfe2aff53ce7319ba47dffc2a424e5d8 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 18:21:03 +0000 Subject: [PATCH 13/32] Fixes more unit tests --- Tests/UnitTests/View/Draw/DrawTests.cs | 18 +++++++++--------- Tests/UnitTests/Views/TabViewTests.cs | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Tests/UnitTests/View/Draw/DrawTests.cs b/Tests/UnitTests/View/Draw/DrawTests.cs index 2526c2178f..2c731ac3ca 100644 --- a/Tests/UnitTests/View/Draw/DrawTests.cs +++ b/Tests/UnitTests/View/Draw/DrawTests.cs @@ -14,19 +14,19 @@ public class DrawTests (ITestOutputHelper output) [Trait ("Category", "Unicode")] public void CJK_Compatibility_Ideographs_ConsoleWidth_ColumnWidth_Equal_Two () { - const string us = "\U0000f900"; + const string s = "\U0000f900"; var r = (Rune)0xf900; - Assert.Equal ("豈", us); + Assert.Equal ("豈", s); Assert.Equal ("豈", r.ToString ()); - Assert.Equal (us, r.ToString ()); + Assert.Equal (s, r.ToString ()); - Assert.Equal (2, us.GetColumns ()); + Assert.Equal (2, s.GetColumns ()); Assert.Equal (2, r.GetColumns ()); - var win = new Window { Title = us }; + var win = new Window { Title = s }; var view = new View { Text = r.ToString (), Height = Dim.Fill (), Width = Dim.Fill () }; - var tf = new TextField { Text = us, Y = 1, Width = 3 }; + var tf = new TextField { Text = s, Y = 1, Width = 3 }; win.Add (view, tf); Toplevel top = new (); top.Add (win); @@ -36,9 +36,9 @@ public void CJK_Compatibility_Ideographs_ConsoleWidth_ColumnWidth_Equal_Two () const string expectedOutput = """ - ┌┤豈├────┐ - │豈 │ - │豈 │ + ┌┤豈├────┐ + │豈 │ + │豈 │ └────────┘ """; DriverAssert.AssertDriverContentsWithFrameAre (expectedOutput, output); diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index c338eb7ece..746f0307df 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -1055,7 +1055,7 @@ public void ShowTopLine_True_TabsOnBottom_False_With_Unicode () DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭──────────────╮ -│Les Misérables│ +│Les Misérables│ ◄ ╰───╮ │hi2 │ └──────────────────┘", @@ -1231,7 +1231,7 @@ public void ShowTopLine_True_TabsOnBottom_True_With_Unicode () ┌──────────────────┐ │hi2 │ ◄ ╭───╯ -│Les Misérables│ +│Les Misérables│ ╰──────────────╯ ", output ); From 410bcbe4f59536dcf1acf3a57f05fdfabc68188e Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 18:33:31 +0000 Subject: [PATCH 14/32] Fix unit test --- Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs b/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs index 91b3cbdd1a..f9e72f4dd8 100644 --- a/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs +++ b/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs @@ -3080,8 +3080,8 @@ public void Draw_Horizontal_Right (string text, int width, string expectedText) } [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 (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, @@ -3090,7 +3090,7 @@ public void Draw_Horizontal_Right (string text, int width, string expectedText) LMre eias ssb - ę́l " + ę́l " )] public void Draw_With_Combining_Runes (int width, int height, TextDirection textDirection, string expected) { From 5e87af7307c093648e5569fee397a790a27ea879 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 19:10:34 +0000 Subject: [PATCH 15/32] Seems all test are fixed now --- Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs | 4 ++-- Tests/UnitTestsParallelizable/Drivers/ContentsTests.cs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs b/Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs index aa0c3fb76d..4b42057911 100644 --- a/Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs +++ b/Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs @@ -39,7 +39,7 @@ public void AddRune_Accented_Letter_With_Three_Combining_Unicode_Chars () driver.ClearContents (); driver.Move (0, 0); - expected = "ắ"; + expected = "ắ"; text = "\u0103\u0301"; driver.AddStr (text); Assert.Equal (expected, driver.Contents [0, 0].Grapheme); @@ -48,7 +48,7 @@ public void AddRune_Accented_Letter_With_Three_Combining_Unicode_Chars () driver.ClearContents (); driver.Move (0, 0); - expected = "ắ"; + expected = "ắ"; text = "\u0061\u0306\u0301"; driver.AddStr (text); Assert.Equal (expected, driver.Contents [0, 0].Grapheme); diff --git a/Tests/UnitTestsParallelizable/Drivers/ContentsTests.cs b/Tests/UnitTestsParallelizable/Drivers/ContentsTests.cs index 7068d828ef..b900ad2065 100644 --- a/Tests/UnitTestsParallelizable/Drivers/ContentsTests.cs +++ b/Tests/UnitTestsParallelizable/Drivers/ContentsTests.cs @@ -27,7 +27,7 @@ public void AddStr_With_Combining_Characters () var acuteAccent = new Rune (0x0301); // Combining acute accent (é) string combined = "e" + acuteAccent; - var expected = "é"; + var expected = "é"; driver.AddStr (combined); DriverAssert.AssertDriverContentsAre (expected, output, driver); @@ -36,7 +36,7 @@ public void AddStr_With_Combining_Characters () // a + ogonek + acute = ( ą́ ) var oGonek = new Rune (0x0328); // Combining ogonek (a small hook or comma shape) combined = "a" + oGonek + acuteAccent; - expected = ("a" + oGonek + acuteAccent).Normalize (NormalizationForm.FormD); // See Issue #2616 + expected = ("a" + oGonek + acuteAccent).Normalize (NormalizationForm.FormC); // See Issue #2616 driver.Move (0, 0); driver.AddStr (combined); @@ -44,7 +44,7 @@ public void AddStr_With_Combining_Characters () // e + ogonek + acute = ( ę́́ ) combined = "e" + oGonek + acuteAccent; - expected = ("e" + oGonek + acuteAccent).Normalize (NormalizationForm.FormD); // See Issue #2616 + expected = ("e" + oGonek + acuteAccent).Normalize (NormalizationForm.FormC); // See Issue #2616 driver.Move (0, 0); driver.AddStr (combined); @@ -52,7 +52,7 @@ public void AddStr_With_Combining_Characters () // i + ogonek + acute = ( į́́́ ) combined = "i" + oGonek + acuteAccent; - expected = ("i" + oGonek + acuteAccent).Normalize (NormalizationForm.FormD); // See Issue #2616 + expected = ("i" + oGonek + acuteAccent).Normalize (NormalizationForm.FormC); // See Issue #2616 driver.Move (0, 0); driver.AddStr (combined); @@ -60,7 +60,7 @@ public void AddStr_With_Combining_Characters () // u + ogonek + acute = ( ų́́́́ ) combined = "u" + oGonek + acuteAccent; - expected = ("u" + oGonek + acuteAccent).Normalize (NormalizationForm.FormD); // See Issue #2616 + expected = ("u" + oGonek + acuteAccent).Normalize (NormalizationForm.FormC); // See Issue #2616 driver.Move (0, 0); driver.AddStr (combined); From ff538e31e77ecae1b04f9f32e397ba89b3606571 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 20:53:08 +0000 Subject: [PATCH 16/32] Adjust CharMap scenario with graphemes --- Terminal.Gui/Views/CharMap/CharMap.cs | 81 +++++++++------------------ 1 file changed, 26 insertions(+), 55 deletions(-) diff --git a/Terminal.Gui/Views/CharMap/CharMap.cs b/Terminal.Gui/Views/CharMap/CharMap.cs index 02308ac1e0..c74d840293 100644 --- a/Terminal.Gui/Views/CharMap/CharMap.cs +++ b/Terminal.Gui/Views/CharMap/CharMap.cs @@ -148,10 +148,8 @@ private void RebuildVisibleRows () break; } - var rune = new Rune (cp); - Span utf16 = new char [2]; - rune.EncodeToUtf16 (utf16); - UnicodeCategory cat = CharUnicodeInfo.GetUnicodeCategory (utf16 [0]); + string grapheme = new Rune (cp).ToString (); + UnicodeCategory cat = CharUnicodeInfo.GetUnicodeCategory (cp); if (cat == ShowUnicodeCategory.Value) { anyVisible = true; @@ -685,7 +683,7 @@ protected override bool OnDrawingContent () // Don't render out-of-range scalars if (scalar > MAX_CODE_POINT) { - AddRune (' '); + AddStr (" "); if (visibleRow == selectedRowIndex && col == selectedCol) { SetAttributeForRole (VisualRole.Normal); @@ -693,22 +691,20 @@ protected override bool OnDrawingContent () continue; } - var rune = (Rune)'?'; + string grapheme = "?"; if (Rune.IsValid (scalar)) { - rune = new (scalar); + grapheme = new Rune (scalar).ToString (); } - int width = rune.GetColumns (); + int width = grapheme.GetColumns (); // Compute visibility based on ShowUnicodeCategory bool isVisible = Rune.IsValid (scalar); if (isVisible && ShowUnicodeCategory.HasValue) { - Span filterUtf16 = new char [2]; - rune.EncodeToUtf16 (filterUtf16); - UnicodeCategory cat = CharUnicodeInfo.GetUnicodeCategory (filterUtf16 [0]); + UnicodeCategory cat = CharUnicodeInfo.GetUnicodeCategory (scalar); isVisible = cat == ShowUnicodeCategory.Value; } @@ -717,11 +713,11 @@ protected override bool OnDrawingContent () // Glyph row if (isVisible) { - RenderRune (rune, width); + RenderGrapheme (grapheme, width, scalar); } else { - AddRune (' '); + AddStr (" "); } } else @@ -736,7 +732,7 @@ protected override bool OnDrawingContent () } else { - AddRune (' '); + AddStr (" "); } } @@ -750,21 +746,18 @@ protected override bool OnDrawingContent () return true; - void RenderRune (Rune rune, int width) + void RenderGrapheme (string grapheme, int width, int scalar) { // Get the UnicodeCategory - Span utf16 = new char [2]; - int charCount = rune.EncodeToUtf16 (utf16); - // Get the bidi class for the first code unit // For most bidi characters, the first code unit is sufficient - UnicodeCategory category = CharUnicodeInfo.GetUnicodeCategory (utf16 [0]); + UnicodeCategory category = CharUnicodeInfo.GetUnicodeCategory (scalar); switch (category) { case UnicodeCategory.OtherNotAssigned: SetAttributeForRole (VisualRole.Highlight); - AddRune (Rune.ReplacementChar); + AddStr (Rune.ReplacementChar.ToString ()); SetAttributeForRole (VisualRole.Normal); break; @@ -773,7 +766,7 @@ void RenderRune (Rune rune, int width) // These report width of 0 and don't render on their own. case UnicodeCategory.Format: SetAttributeForRole (VisualRole.Highlight); - AddRune ('F'); + AddStr ("F"); SetAttributeForRole (VisualRole.Normal); break; @@ -786,38 +779,8 @@ void RenderRune (Rune rune, int width) case UnicodeCategory.EnclosingMark: if (width > 0) { - AddRune (rune); - } - else - { - if (rune.IsCombiningMark ()) - { - // This is a hack to work around the fact that combining marks - // a) can't be rendered on their own - // b) that don't normalize are not properly supported in - // any known terminal (esp Windows/AtlasEngine). - // See Issue #2616 - var sb = new StringBuilder (); - sb.Append ('a'); - sb.Append (rune); - - // Try normalizing after combining with 'a'. If it normalizes, at least - // it'll show on the 'a'. If not, just show the replacement char. - string normal = sb.ToString ().Normalize (NormalizationForm.FormC); - - if (normal.Length == 1) - { - AddRune ((Rune)normal [0]); - } - else - { - SetAttributeForRole (VisualRole.Highlight); - AddRune ('M'); - SetAttributeForRole (VisualRole.Normal); - } - } + AddStr (grapheme); } - break; // These report width of 0, but render as 1 @@ -825,20 +788,28 @@ void RenderRune (Rune rune, int width) case UnicodeCategory.LineSeparator: case UnicodeCategory.ParagraphSeparator: case UnicodeCategory.Surrogate: - AddRune (rune); + AddStr (grapheme); break; + case UnicodeCategory.OtherLetter: + AddStr (grapheme); + if (width == 0) + { + AddStr (" "); + } + + break; default: // Draw the rune if (width > 0) { - AddRune (rune); + AddStr (grapheme); } else { - throw new InvalidOperationException ($"The Rune \"{rune}\" (U+{rune.Value:x6}) has zero width and no special-case UnicodeCategory logic applies."); + throw new InvalidOperationException ($"The Rune \"{grapheme}\" (U+{Rune.GetRuneAt (grapheme, 0).Value:x6}) has zero width and no special-case UnicodeCategory logic applies."); } break; From 6afe4c85ae77062441de78c4ac5c97e701f1e01e Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 20:54:24 +0000 Subject: [PATCH 17/32] Upgrade Wcwidth to version 4.0.0 --- Directory.Packages.props | 2 +- .../UnitTestsParallelizable/Text/RuneTests.cs | 6 +- .../Text/StringTests.cs | 98 ++++++++++--------- 3 files changed, 57 insertions(+), 49 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d62a1d2982..2fdb4633e7 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,7 +18,7 @@ - + diff --git a/Tests/UnitTestsParallelizable/Text/RuneTests.cs b/Tests/UnitTestsParallelizable/Text/RuneTests.cs index f5bc5ab675..4e03e8048b 100644 --- a/Tests/UnitTestsParallelizable/Text/RuneTests.cs +++ b/Tests/UnitTestsParallelizable/Text/RuneTests.cs @@ -88,7 +88,7 @@ public void GetColumns_GetRuneCount () 1 )] // the letters 법 join to form the Korean word for "rice:" U+BC95 법 (read from top left to bottom right) [InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467", "👨‍👩‍👧", 8, 2, 8)] // Man, Woman and Girl emoji. - [InlineData ("\u0915\u093f", "कि", 2, 2, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I + //[InlineData ("\u0915\u093f", "कि", 2, 2, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I [InlineData ( "\u0e4d\u0e32", "ํา", @@ -213,7 +213,7 @@ public void GetColumns_Utf8_Encode (byte [] code, string str, int columns, int s [InlineData ( '\u1161', "ᅡ", - 1, + 0, 1, 3 )] // ᅡ Hangul Jungseong A - Unicode Hangul Jamo for join with column width equal to 0 alone. @@ -231,7 +231,7 @@ public void GetColumns_Utf8_Encode (byte [] code, string str, int columns, int s )] // ䷀Hexagram For The Creative Heaven - U+4dc0 - https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml // See https://github.com/microsoft/terminal/issues/19389 - [InlineData ('\ud7b0', "ힰ", 1, 1, 3)] // ힰ ┤Hangul Jungseong O-Yeo - ힰ U+d7b0')] + [InlineData ('\ud7b0', "ힰ", 0, 1, 3)] // ힰ ┤Hangul Jungseong O-Yeo - ힰ U+d7b0')] [InlineData ('\uf61e', "", 1, 1, 3)] // Private Use Area [InlineData ('\u23f0', "⏰", 2, 1, 3)] // Alarm Clock - ⏰ U+23f0 [InlineData ('\u1100', "ᄀ", 2, 1, 3)] // ᄀ Hangul Choseong Kiyeok diff --git a/Tests/UnitTestsParallelizable/Text/StringTests.cs b/Tests/UnitTestsParallelizable/Text/StringTests.cs index efbc2e4060..a3c4e52bab 100644 --- a/Tests/UnitTestsParallelizable/Text/StringTests.cs +++ b/Tests/UnitTestsParallelizable/Text/StringTests.cs @@ -4,6 +4,13 @@ public class StringTests { + [Fact] + public void TestGetColumns_Null () + { + string? str = null; + Assert.Equal (0, str!.GetColumns ()); + } + [Fact] public void TestGetColumns_Empty () { @@ -11,6 +18,20 @@ public void TestGetColumns_Empty () Assert.Equal (0, str.GetColumns ()); } + [Fact] + public void TestGetColumns_SingleRune () + { + var str = "a"; + Assert.Equal (1, str.GetColumns ()); + } + + [Fact] + public void TestGetColumns_Zero_Width () + { + var str = "\u200D"; + Assert.Equal (0, str.GetColumns ()); + } + [Theory] [InlineData ("a", 1)] [InlineData ("á", 1)] @@ -30,44 +51,37 @@ public void TestGetColumns_Empty () // Test known wide codepoints [Theory] - [InlineData ("🙂", 1, 2)] - [InlineData ("a🙂", 2, 3)] - [InlineData ("🙂a", 2, 3)] - [InlineData ("👨‍👩‍👦‍👦", 1, 2)] - [InlineData ("👨‍👩‍👦‍👦🙂", 2, 4)] - [InlineData ("👨‍👩‍👦‍👦🙂a", 3, 5)] - [InlineData ("👨‍👩‍👦‍👦a🙂", 3, 5)] - [InlineData ("👨‍👩‍👦‍👦👨‍👩‍👦‍👦", 2, 4)] - [InlineData ("าำ", 1, 2)] // า U+0E32 - THAI CHARACTER SARA AA with ำ U+0E33 - THAI CHARACTER SARA AM - [InlineData ("山", 1, 2)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71 - [InlineData ("山🙂", 2, 4)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71 - //[InlineData ("\ufe20\ufe21", 2)] // Combining Ligature Left Half ︠ - U+fe20 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml - // // Combining Ligature Right Half - U+fe21 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml - public void TestGetColumns_MultiRune_WideBMP_Graphemes (string str, int graphemesCount, int expected) - { - Assert.Equal (graphemesCount, GraphemeHelper.GetGraphemes (str).ToArray ().Length); - Assert.Equal (expected, str.GetColumns ()); - } - - [Fact] - public void TestGetColumns_Null () - { - string? str = null; - Assert.Equal (0, str!.GetColumns ()); - } - - [Fact] - public void TestGetColumns_SingleRune () + [InlineData ("🙂", 2, 1, 2)] + [InlineData ("a🙂", 3, 2, 3)] + [InlineData ("🙂a", 3, 2, 3)] + [InlineData ("👨‍👩‍👦‍👦", 8, 1, 2)] + [InlineData ("👨‍👩‍👦‍👦🙂", 10, 2, 4)] + [InlineData ("👨‍👩‍👦‍👦🙂a", 11, 3, 5)] + [InlineData ("👨‍👩‍👦‍👦a🙂", 11, 3, 5)] + [InlineData ("👨‍👩‍👦‍👦👨‍👩‍👦‍👦", 16, 2, 4)] + [InlineData ("าำ", 2, 1, 2)] // า U+0E32 - THAI CHARACTER SARA AA with ำ U+0E33 - THAI CHARACTER SARA AM + [InlineData ("山", 2, 1, 2)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71 + [InlineData ("山🙂", 4, 2, 4)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71 + [InlineData ("a\ufe20e\ufe21", 2, 2, 2)] // Combining Ligature Left Half ︠ - U+fe20 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml + // Combining Ligature Right Half - U+fe21 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml + //[InlineData ("क", 1, 1, 1)] // क U+0915 Devanagari Letter Ka + //[InlineData ("ि", 1, 1, 1)] // U+093F Devanagari Vowel Sign I ि (i-kar). + //[InlineData ("कि", 2, 1, 2)] // "कि" is U+0915 for the base consonant "क" with U+093F for the vowel sign "ि" (i-kar). + [InlineData ("ᄀ", 2, 1, 2)] // ᄀ U+1100 HANGUL CHOSEONG KIYEOK (consonant) + [InlineData ("ᅡ", 0, 1, 0)] // ᅡ U+1161 HANGUL JUNGSEONG A (vowel) + [InlineData ("가", 2, 1, 2)] // ᄀ U+1100 HANGUL CHOSEONG KIYEOK (consonant) with ᅡ U+1161 HANGUL JUNGSEONG A (vowel) + [InlineData ("ᄒ", 2, 1, 2)] // ᄒ U+1112 Hangul Choseong Hieuh + [InlineData ("ᅵ", 0, 1, 0)] // ᅵ U+1175 Hangul Jungseong I + [InlineData ("ᇂ", 0, 1, 0)] // ᇂ U+11C2 Hangul Jongseong Hieuh + [InlineData ("힣", 2, 1, 2)] // ᄒ (choseong h) + ᅵ (jungseong i) + ᇂ (jongseong h) + [InlineData ("ힰ", 0, 1, 0)] // U+D7B0 ힰ Hangul Jungseong O-Yeo + [InlineData ("ᄀힰ", 2, 1, 2)] // ᄀ U+1100 HANGUL CHOSEONG KIYEOK (consonant) with U+D7B0 ힰ Hangul Jungseong O-Yeo + //[InlineData ("षि", 2, 1, 2)] // U+0937 ष DEVANAGARI LETTER SSA with U+093F ि COMBINING DEVANAGARI VOWEL SIGN I + public void TestGetColumns_MultiRune_WideBMP_Graphemes (string str, int expectedRunesWidth, int expectedGraphemesCount, int expectedWidth) { - var str = "a"; - Assert.Equal (1, str.GetColumns ()); - } - - [Fact] - public void TestGetColumns_Zero_Width () - { - var str = "\u200D"; - Assert.Equal (0, str.GetColumns ()); + Assert.Equal (expectedRunesWidth, str.EnumerateRunes ().Sum (r => r.GetColumns ())); + Assert.Equal (expectedGraphemesCount, GraphemeHelper.GetGraphemes (str).ToArray ().Length); + Assert.Equal (expectedWidth, str.GetColumns ()); } [Theory] @@ -75,14 +89,8 @@ public void TestGetColumns_Zero_Width () [InlineData ("")] public void TestGetColumns_Does_Not_Throws_With_Null_And_Empty_String (string? text) { - if (text is null) - { - Assert.Equal (0, StringExtensions.GetColumns (text!)); - } - else - { - Assert.Equal (0, text.GetColumns ()); - } + // ReSharper disable once InvokeAsExtensionMethod + Assert.Equal (0, StringExtensions.GetColumns (text!)); } public class ReadOnlySpanExtensionsTests From f3e4a2dacf9e35fba3fffef6d73e5d38761f13fd Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 21:31:52 +0000 Subject: [PATCH 18/32] Reformat --- Terminal.Gui/Views/CharMap/CharMap.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Terminal.Gui/Views/CharMap/CharMap.cs b/Terminal.Gui/Views/CharMap/CharMap.cs index c74d840293..0750c8fe98 100644 --- a/Terminal.Gui/Views/CharMap/CharMap.cs +++ b/Terminal.Gui/Views/CharMap/CharMap.cs @@ -781,6 +781,7 @@ void RenderGrapheme (string grapheme, int width, int scalar) { AddStr (grapheme); } + break; // These report width of 0, but render as 1 From c9b46b796ad206f2dd310fa680163b9f7de878f2 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 21:54:29 +0000 Subject: [PATCH 19/32] Trying fix CheckDefaultState assertion --- Tests/UnitTestsParallelizable/TestSetup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/UnitTestsParallelizable/TestSetup.cs b/Tests/UnitTestsParallelizable/TestSetup.cs index 2a6402c119..5379905ba9 100644 --- a/Tests/UnitTestsParallelizable/TestSetup.cs +++ b/Tests/UnitTestsParallelizable/TestSetup.cs @@ -24,8 +24,8 @@ public void Dispose () // Reset application state just in case a test changed something. // TODO: Add an Assert to ensure none of the state of Application changed. // TODO: Add an Assert to ensure none of the state of ConfigurationManager changed. - CheckDefaultState (); Application.ResetState (true); + CheckDefaultState (); } // IMPORTANT: Ensure this matches the code in Init_ResetState_Resets_Properties From 1060ac9b632c109dee785fa83057959575875ca2 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 22:13:22 +0000 Subject: [PATCH 20/32] Revert "Trying fix CheckDefaultState assertion" This reverts commit c9b46b796ad206f2dd310fa680163b9f7de878f2. --- Tests/UnitTestsParallelizable/TestSetup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/UnitTestsParallelizable/TestSetup.cs b/Tests/UnitTestsParallelizable/TestSetup.cs index 5379905ba9..2a6402c119 100644 --- a/Tests/UnitTestsParallelizable/TestSetup.cs +++ b/Tests/UnitTestsParallelizable/TestSetup.cs @@ -24,8 +24,8 @@ public void Dispose () // Reset application state just in case a test changed something. // TODO: Add an Assert to ensure none of the state of Application changed. // TODO: Add an Assert to ensure none of the state of ConfigurationManager changed. - Application.ResetState (true); CheckDefaultState (); + Application.ResetState (true); } // IMPORTANT: Ensure this matches the code in Init_ResetState_Resets_Properties From ff34a1486631c2793e1bdd0455f5972eabb81387 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 22:15:07 +0000 Subject: [PATCH 21/32] Forgot to include driver.End in the test --- Tests/UnitTestsParallelizable/Drivers/DriverTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tests/UnitTestsParallelizable/Drivers/DriverTests.cs b/Tests/UnitTestsParallelizable/Drivers/DriverTests.cs index ee47555073..511c7e12f5 100644 --- a/Tests/UnitTestsParallelizable/Drivers/DriverTests.cs +++ b/Tests/UnitTestsParallelizable/Drivers/DriverTests.cs @@ -46,5 +46,7 @@ public void IsValidLocation (string text, bool positive) Assert.False (driver.IsValidLocation (text, driver.Cols, driver.Rows - 1)); Assert.False (driver.IsValidLocation (text, driver.Cols, driver.Rows - 1)); Assert.False (driver.IsValidLocation (text, driver.Cols, driver.Rows)); + + driver.End (); } } From b6072bb1c8522c472634a6dc2fb2e85b31816352 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 14 Nov 2025 22:19:49 +0000 Subject: [PATCH 22/32] Reapply "Trying fix CheckDefaultState assertion" This reverts commit 1060ac9b632c109dee785fa83057959575875ca2. --- Tests/UnitTestsParallelizable/TestSetup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/UnitTestsParallelizable/TestSetup.cs b/Tests/UnitTestsParallelizable/TestSetup.cs index 2a6402c119..5379905ba9 100644 --- a/Tests/UnitTestsParallelizable/TestSetup.cs +++ b/Tests/UnitTestsParallelizable/TestSetup.cs @@ -24,8 +24,8 @@ public void Dispose () // Reset application state just in case a test changed something. // TODO: Add an Assert to ensure none of the state of Application changed. // TODO: Add an Assert to ensure none of the state of ConfigurationManager changed. - CheckDefaultState (); Application.ResetState (true); + CheckDefaultState (); } // IMPORTANT: Ensure this matches the code in Init_ResetState_Resets_Properties From ffd78b6924d292ac0091719191a0b132b061b9bd Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 19 Nov 2025 22:48:35 +0000 Subject: [PATCH 23/32] Remove ToString --- Terminal.Gui/App/Application.cs | 52 --------------------------------- 1 file changed, 52 deletions(-) diff --git a/Terminal.Gui/App/Application.cs b/Terminal.Gui/App/Application.cs index 2e425816ec..7213e16be2 100644 --- a/Terminal.Gui/App/Application.cs +++ b/Terminal.Gui/App/Application.cs @@ -53,58 +53,6 @@ public static partial class Application /// public const ushort DefaultMaximumIterationsPerSecond = 25; - /// - /// Gets a string representation of the Application as rendered by . - /// - /// A string representation of the Application - public new static string ToString () - { - IDriver? driver = Driver; - - if (driver is null) - { - return string.Empty; - } - - return ToString (driver); - } - - /// - /// Gets a string representation of the Application rendered by the provided . - /// - /// The driver to use to render the contents. - /// A string representation of the Application - public static string ToString (IDriver? driver) - { - if (driver is null) - { - return string.Empty; - } - - var sb = new StringBuilder (); - - Cell [,] contents = driver?.Contents!; - - for (var r = 0; r < driver!.Rows; r++) - { - for (var c = 0; c < driver.Cols; c++) - { - string text = contents [r, c].Grapheme; - - sb.Append (text); - - if (text.GetColumns () > 1) - { - c++; - } - } - - sb.AppendLine (); - } - - return sb.ToString (); - } - /// Gets all cultures supported by the application without the invariant language. public static List? SupportedCultures { get; private set; } = GetSupportedCultures (); From 1f95fbedc6a436146249d83c6f50ce27b67d9f0c Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 19 Nov 2025 23:45:05 +0000 Subject: [PATCH 24/32] Fix merge errors --- .../Text/TextFormatterDrawTests.cs | 2 +- .../View/Draw/ViewClearViewportTests.cs | 10 +++++----- .../View/Draw/ViewDrawTextAndLineCanvasTests.cs | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs b/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs index 70adc06871..df2f044263 100644 --- a/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs +++ b/Tests/UnitTestsParallelizable/Text/TextFormatterDrawTests.cs @@ -679,7 +679,7 @@ string expectedDraw }; Assert.Equal (width, text.GetColumns ()); - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default, driver: driver); + tf.Draw (driver, new (0, 0, width, height), Attribute.Default, Attribute.Default); DriverAssert.AssertDriverContentsWithFrameAre (expectedDraw, output, driver); } diff --git a/Tests/UnitTestsParallelizable/View/Draw/ViewClearViewportTests.cs b/Tests/UnitTestsParallelizable/View/Draw/ViewClearViewportTests.cs index 1ef96e203b..58ad1bf9c2 100644 --- a/Tests/UnitTestsParallelizable/View/Draw/ViewClearViewportTests.cs +++ b/Tests/UnitTestsParallelizable/View/Draw/ViewClearViewportTests.cs @@ -36,7 +36,7 @@ public void ClearViewport_FillsViewportArea () { for (int x = viewportScreen.X; x < viewportScreen.X + viewportScreen.Width; x++) { - Assert.Equal (new Rune (' '), driver.Contents [y, x].Rune); + Assert.Equal (" ", driver.Contents [y, x].Grapheme); } } } @@ -75,7 +75,7 @@ public void ClearViewport_WithClearContentOnly_LimitsToVisibleContent () { for (int x = toClear.X; x < toClear.X + toClear.Width; x++) { - Assert.Equal (new Rune (' '), driver.Contents [y, x].Rune); + Assert.Equal (" ", driver.Contents [y, x].Grapheme); } } } @@ -154,7 +154,7 @@ public void ClearViewport_WithTransparentFlag_DoesNotClear () { for (int x = viewportScreen.X; x < viewportScreen.X + viewportScreen.Width; x++) { - Assert.Equal (new Rune ('X'), driver.Contents [y, x].Rune); + Assert.Equal ("X", driver.Contents [y, x].Grapheme); } } } @@ -309,7 +309,7 @@ public void ClearViewport_WithScrolledViewport_ClearsCorrectArea () { for (int x = viewportScreen.X; x < viewportScreen.X + viewportScreen.Width; x++) { - Assert.Equal (new Rune (' '), driver.Contents [y, x].Rune); + Assert.Equal (" ", driver.Contents [y, x].Grapheme); } } } @@ -353,7 +353,7 @@ public void ClearViewport_WithClearContentOnly_AndScrolledViewport_ClearsOnlyVis { for (int x = toClear.X; x < toClear.X + toClear.Width; x++) { - Assert.Equal (new Rune (' '), driver.Contents [y, x].Rune); + Assert.Equal (" ", driver.Contents[y, x].Grapheme); } } } diff --git a/Tests/UnitTestsParallelizable/View/Draw/ViewDrawTextAndLineCanvasTests.cs b/Tests/UnitTestsParallelizable/View/Draw/ViewDrawTextAndLineCanvasTests.cs index 5f2af20550..6a4c330e2d 100644 --- a/Tests/UnitTestsParallelizable/View/Draw/ViewDrawTextAndLineCanvasTests.cs +++ b/Tests/UnitTestsParallelizable/View/Draw/ViewDrawTextAndLineCanvasTests.cs @@ -80,10 +80,10 @@ public void DrawText_DrawsTextToDriver () // Text should appear at the content location Point screenPos = view.ContentToScreen (Point.Empty); - Assert.Equal ('T', (char)driver.Contents [screenPos.Y, screenPos.X].Rune.Value); - Assert.Equal ('e', (char)driver.Contents [screenPos.Y, screenPos.X + 1].Rune.Value); - Assert.Equal ('s', (char)driver.Contents [screenPos.Y, screenPos.X + 2].Rune.Value); - Assert.Equal ('t', (char)driver.Contents [screenPos.Y, screenPos.X + 3].Rune.Value); + Assert.Equal ("T", driver.Contents [screenPos.Y, screenPos.X].Grapheme); + Assert.Equal ("e", driver.Contents [screenPos.Y, screenPos.X + 1].Grapheme); + Assert.Equal ("s", driver.Contents [screenPos.Y, screenPos.X + 2].Grapheme); + Assert.Equal ("t", driver.Contents [screenPos.Y, screenPos.X + 3].Grapheme); } [Fact] @@ -273,7 +273,7 @@ public void RenderLineCanvas_DrawsLines () // Verify the line was drawn (check for horizontal line character) for (int i = 0; i < 5; i++) { - Assert.NotEqual (new Rune (' '), driver.Contents [screenPos.Y, screenPos.X + i].Rune); + Assert.NotEqual (" ", driver.Contents [screenPos.Y, screenPos.X + i].Grapheme); } } @@ -409,7 +409,7 @@ public void OnRenderingLineCanvas_CanPreventRendering () bool lineRendered = true; for (int i = 0; i < 5; i++) { - if (driver.Contents [screenPos.Y, screenPos.X + i].Rune.Value == ' ') + if (driver.Contents [screenPos.Y, screenPos.X + i].Grapheme == " ") { lineRendered = false; break; From f496e45b264f540731d45325407d6b62bae6392c Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 20 Nov 2025 14:37:31 +0000 Subject: [PATCH 25/32] Change to conditional expression --- Terminal.Gui/Drawing/Cell.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index 8dda59974b..cbe4119b0d 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -39,14 +39,9 @@ public string Grapheme try { - if (!string.IsNullOrEmpty (value) && !value.IsNormalized (NormalizationForm.FormC)) - { - _grapheme = value.Normalize (NormalizationForm.FormC); - } - else - { - _grapheme = value; - } + _grapheme = !string.IsNullOrEmpty (value) && !value.IsNormalized (NormalizationForm.FormC) + ? value.Normalize (NormalizationForm.FormC) + : value; } catch (ArgumentException) { From 78272c8666054ce44ce0453d9e9250886bb0609c Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 20 Nov 2025 14:58:40 +0000 Subject: [PATCH 26/32] Assertion to prove that no exception throws during cell initialization. --- Tests/UnitTestsParallelizable/Drawing/CellTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/UnitTestsParallelizable/Drawing/CellTests.cs b/Tests/UnitTestsParallelizable/Drawing/CellTests.cs index b635d2bad1..b51ab37a92 100644 --- a/Tests/UnitTestsParallelizable/Drawing/CellTests.cs +++ b/Tests/UnitTestsParallelizable/Drawing/CellTests.cs @@ -146,7 +146,7 @@ public void IsNormalized_ArgumentException (string text, bool throws) Assert.True (throws); } - Cell c = new () { Grapheme = text }; + Assert.Null (Record.Exception (() => new Cell { Grapheme = text })); } [Fact] From 78be9ac9a40335ffe3c8b0a1e52134050259cd4c Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 20 Nov 2025 15:26:37 +0000 Subject: [PATCH 27/32] Remove unnecessary assignment --- Terminal.Gui/Views/CharMap/CharMap.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Terminal.Gui/Views/CharMap/CharMap.cs b/Terminal.Gui/Views/CharMap/CharMap.cs index 170725a0e7..370c50c6b4 100644 --- a/Terminal.Gui/Views/CharMap/CharMap.cs +++ b/Terminal.Gui/Views/CharMap/CharMap.cs @@ -147,7 +147,6 @@ private void RebuildVisibleRows () break; } - string grapheme = new Rune (cp).ToString (); UnicodeCategory cat = CharUnicodeInfo.GetUnicodeCategory (cp); if (cat == ShowUnicodeCategory.Value) { From 666d21b5b4c2018c4070f0ba3a66acef4bdc6f2d Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 20 Nov 2025 16:07:49 +0000 Subject: [PATCH 28/32] Remove assignment to end --- Terminal.Gui/Text/TextFormatter.cs | 2 +- .../Text/TextFormatterTests.cs | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Terminal.Gui/Text/TextFormatter.cs b/Terminal.Gui/Text/TextFormatter.cs index d2eabf3a99..ecd1be4599 100644 --- a/Terminal.Gui/Text/TextFormatter.cs +++ b/Terminal.Gui/Text/TextFormatter.cs @@ -1468,7 +1468,7 @@ public static List WordWrapText ( if (preserveTrailingSpaces) { - while ((end = start) < graphemes.Count) + while (start < graphemes.Count) { end = GetNextWhiteSpace (start, width, out bool incomplete); diff --git a/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs b/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs index 0c55d6da9b..0ec0122928 100644 --- a/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs +++ b/Tests/UnitTestsParallelizable/Text/TextFormatterTests.cs @@ -831,6 +831,7 @@ public void GetLengthThatFits_With_Combining_Runes () { var text = "Les Mise\u0328\u0301rables"; Assert.Equal (14, TextFormatter.GetLengthThatFits (text, 14)); + Assert.Equal ("Les Misę́rables", text); } [Fact] @@ -838,14 +839,18 @@ public void GetMaxColsForWidth_With_Combining_Runes () { List text = new () { "Les Mis", "e\u0328\u0301", "rables" }; Assert.Equal (1, TextFormatter.GetMaxColsForWidth (text, 1)); + Assert.Equal ("Les Mis", text [0]); + Assert.Equal ("ę́", text [1]); + Assert.Equal ("rables", text [^1]); } - //[Fact] - //public void GetWidestLineLength_With_Combining_Runes () - //{ - // var text = "Les Mise\u0328\u0301rables"; - // Assert.Equal (1, TextFormatter.GetWidestLineLength (text, 1, 1)); - //} + [Fact] + public void GetWidestLineLength_With_Combining_Runes () + { + var text = "Les Mise\u0328\u0301rables"; + Assert.Equal (14, TextFormatter.GetWidestLineLength (text, 1)); + Assert.Equal ("Les Misę́rables", text); + } [Fact] public void Internal_Tests () @@ -2448,6 +2453,7 @@ IEnumerable resultLines Assert.Equal (expected, breakLines); // Double space Complex example - this is how VS 2022 does it + // which I think is not correct. //text = "A sentence has words. "; //breakLines = ""; //wrappedLines = TextFormatter.WordWrapText (text, width, preserveTrailingSpaces: true); From 395fd92aeb813c933fa0bcc127479a3f99ac995b Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 20 Nov 2025 16:26:15 +0000 Subject: [PATCH 29/32] Replace string concatenation with 'StringBuilder'. --- Tests/UnitTestsParallelizable/Views/TextFieldTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs b/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs index 9a1bd2dfac..eff2c41b2a 100644 --- a/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs +++ b/Tests/UnitTestsParallelizable/Views/TextFieldTests.cs @@ -622,14 +622,14 @@ public void Adjust_First () string GetContents () { - var item = ""; + var sb = new StringBuilder (); for (var i = 0; i < 16; i++) { - item += driver.Contents [0, i]!.Grapheme; + sb.Append (driver.Contents! [0, i]!.Grapheme); } - return item; + return sb.ToString (); } } } From 09fd042fc75ca42f70a3e39ae34e055a367cb38b Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 20 Nov 2025 16:42:30 +0000 Subject: [PATCH 30/32] Replace more string concatenation with 'StringBuilder' --- Tests/UnitTests/View/TextTests.cs | 9 +++++---- Tests/UnitTestsParallelizable/Views/ListViewTests.cs | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Tests/UnitTests/View/TextTests.cs b/Tests/UnitTests/View/TextTests.cs index 5bf43d1ea0..dba01c151c 100644 --- a/Tests/UnitTests/View/TextTests.cs +++ b/Tests/UnitTests/View/TextTests.cs @@ -1,4 +1,5 @@ -using UnitTests; +using System.Text; +using UnitTests; using Xunit.Abstractions; namespace UnitTests.ViewTests; @@ -699,14 +700,14 @@ public void Excess_Text_Is_Erased_When_The_Width_Is_Reduced () string GetContents () { - var text = ""; + var sb = new StringBuilder (); for (var i = 0; i < 4; i++) { - text += Application.Driver?.Contents [0, i].Grapheme; + sb.Append (Application.Driver?.Contents! [0, i].Grapheme); } - return text; + return sb.ToString (); } Application.End (rs); diff --git a/Tests/UnitTestsParallelizable/Views/ListViewTests.cs b/Tests/UnitTestsParallelizable/Views/ListViewTests.cs index bca9f43f16..50e60cc2e0 100644 --- a/Tests/UnitTestsParallelizable/Views/ListViewTests.cs +++ b/Tests/UnitTestsParallelizable/Views/ListViewTests.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.ObjectModel; using System.Collections.Specialized; +using System.Text; using Moq; using Terminal.Gui; using UnitTests; @@ -1278,14 +1279,14 @@ public void EnsureSelectedItemVisible_Top () string GetContents (int line) { - var item = ""; + var sb = new StringBuilder (); for (var i = 0; i < 7; i++) { - item += app.Driver?.Contents [line, i].Grapheme; + sb.Append ((app.Driver?.Contents!) [line, i].Grapheme); } - return item; + return sb.ToString (); } top.Dispose (); From 8fdec09fa36f3f514abbec46aaa99f12bcb4727d Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 20 Nov 2025 16:51:26 +0000 Subject: [PATCH 31/32] Remove redundant call to 'ToString' because Rune cast to a String object. --- Terminal.Gui/Drawing/Cell.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index cbe4119b0d..f7da577ad0 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -95,7 +95,7 @@ private static string EscapeControlAndInvisible (string text) } else { - sb.Append (rune.ToString ()); + sb.Append (rune); } break; } From d94196160cf3cb0d68d490f5265e7e12c21ba3ea Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 20 Nov 2025 17:11:18 +0000 Subject: [PATCH 32/32] Replace foreach loop with Sum linq --- Terminal.Gui/Text/StringExtensions.cs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Terminal.Gui/Text/StringExtensions.cs b/Terminal.Gui/Text/StringExtensions.cs index 3fc4176583..59f433b60d 100644 --- a/Terminal.Gui/Text/StringExtensions.cs +++ b/Terminal.Gui/Text/StringExtensions.cs @@ -1,5 +1,4 @@ using System.Buffers; -using System.Globalization; namespace Terminal.Gui.Text; @@ -68,19 +67,13 @@ public static int GetColumns (this string str, bool ignoreLessThanZero = true) foreach (string grapheme in GraphemeHelper.GetGraphemes (str)) { // Get the maximum rune width within this grapheme cluster - int clusterWidth = 0; + int clusterWidth = grapheme.EnumerateRunes () + .Sum (r => + { + int w = r.GetColumns (); - foreach (var rune in grapheme.EnumerateRunes ()) - { - int width = rune.GetColumns (); - - if (ignoreLessThanZero && width < 0) - { - width = 0; - } - - clusterWidth += width; - } + return ignoreLessThanZero && w < 0 ? 0 : w; + }); // Clamp to realistic max display width if (clusterWidth > 2)