From 0436019728975e93e24bc342cf3667b783012462 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 24 Oct 2023 22:38:53 +0100 Subject: [PATCH 01/16] Fixes #2616. Support combining sequences that don't normalize --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 6 +++- UnitTests/ConsoleDrivers/ContentsTests.cs | 36 ++++++++++---------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 042a10bb24..60396f5ee3 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -179,7 +179,11 @@ public void AddRune (Rune rune) Contents [Row, Col - 1].Attribute = CurrentAttribute; Contents [Row, Col - 1].IsDirty = true; - //Col--; + if (normalized.Length > 1) { + Contents [Row, Col].Runes = new List { (Rune)normalized [1] }; ; + Contents [Row, Col].Attribute = CurrentAttribute; + Contents [Row, Col].IsDirty = true; + } } else { Contents [Row, Col].Attribute = CurrentAttribute; Contents [Row, Col].IsDirty = true; diff --git a/UnitTests/ConsoleDrivers/ContentsTests.cs b/UnitTests/ConsoleDrivers/ContentsTests.cs index 2bd0a10b45..f0aec48e8a 100644 --- a/UnitTests/ConsoleDrivers/ContentsTests.cs +++ b/UnitTests/ConsoleDrivers/ContentsTests.cs @@ -17,40 +17,40 @@ public ContentsTests (ITestOutputHelper output) this.output = output; } - [Theory] + [Theory, AutoInitShutdown] [InlineData (typeof (FakeDriver))] //[InlineData (typeof (NetDriver))] //[InlineData (typeof (CursesDriver))] //[InlineData (typeof (WindowsDriver))] public void AddStr_With_Combining_Characters (Type driverType) { - var driver = (ConsoleDriver)Activator.CreateInstance (driverType); - Application.Init (driver); - // driver.Init (null); + Application.Init (); var acuteaccent = new System.Text.Rune (0x0301); // Combining acute accent (é) var combined = "e" + acuteaccent; var expected = "é"; - driver.AddStr (combined); - TestHelpers.AssertDriverContentsWithFrameAre (expected, output); - -#if false // Disabled Until #2616 is fixed + Application.Driver.AddStr (combined); + TestHelpers.AssertDriverContentsAre (expected, output); // 3 char combine - // a + ogonek + acute = ( ǫ́ ) + // a + ogonek + acute = ( ą́ ) var ogonek = new System.Text.Rune (0x0328); // Combining ogonek (a small hook or comma shape) combined = "a" + ogonek + acuteaccent; - expected = "ǫ́"; + expected = "ą́"; - driver.Move (0, 0); - driver.AddStr (combined); - TestHelpers.AssertDriverContentsWithFrameAre (expected, output); + Application.Driver.Move (0, 0); + Application.Driver.AddStr (combined); + TestHelpers.AssertDriverContentsAre (expected, output); -#endif - - // Shutdown must be called to safely clean up Application if Init has been called - Application.Shutdown (); + // o + ogonek + acute = ( ǫ́ ) + ogonek = new System.Text.Rune (0x0328); // Combining ogonek (a small hook or comma shape) + combined = "o" + ogonek + acuteaccent; + expected = "ǫ́"; + + Application.Driver.Move (0, 0); + Application.Driver.AddStr (combined); + TestHelpers.AssertDriverContentsAre (expected, output); } [Theory] @@ -91,7 +91,7 @@ public void Move_Bad_Coordinates (Type driverType) } // TODO: Add these unit tests - + // AddRune moves correctly // AddRune with wide characters are handled correctly From 475a89eb04d1c5b6716c41569a90599a1472383c Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 25 Oct 2023 09:06:47 -0600 Subject: [PATCH 02/16] Decouples Application from ConsoleDriver in TestHelpers --- UnitTests/ConsoleDrivers/ContentsTests.cs | 27 ++++++++--------- UnitTests/FileServices/FileDialogTests.cs | 4 +-- UnitTests/TestHelpers.cs | 35 ++++++++++++++++------- UnitTests/View/DrawTests.cs | 6 ++-- UnitTests/View/ViewTests.cs | 6 ++-- UnitTests/Views/ComboBoxTests.cs | 12 ++++---- UnitTests/Views/MenuTests.cs | 8 +++--- UnitTests/Views/RuneCellTests.cs | 8 +++--- UnitTests/Views/TableViewTests.cs | 20 ++++++------- UnitTests/Views/TreeViewTests.cs | 4 +-- 10 files changed, 71 insertions(+), 59 deletions(-) diff --git a/UnitTests/ConsoleDrivers/ContentsTests.cs b/UnitTests/ConsoleDrivers/ContentsTests.cs index f0aec48e8a..89d72927a1 100644 --- a/UnitTests/ConsoleDrivers/ContentsTests.cs +++ b/UnitTests/ConsoleDrivers/ContentsTests.cs @@ -17,21 +17,22 @@ public ContentsTests (ITestOutputHelper output) this.output = output; } - [Theory, AutoInitShutdown] + [Theory] [InlineData (typeof (FakeDriver))] //[InlineData (typeof (NetDriver))] //[InlineData (typeof (CursesDriver))] //[InlineData (typeof (WindowsDriver))] public void AddStr_With_Combining_Characters (Type driverType) { - Application.Init (); - + var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + driver.Init (); + var acuteaccent = new System.Text.Rune (0x0301); // Combining acute accent (é) var combined = "e" + acuteaccent; var expected = "é"; - Application.Driver.AddStr (combined); - TestHelpers.AssertDriverContentsAre (expected, output); + driver.AddStr (combined); + TestHelpers.AssertDriverContentsAre (expected, output, driver); // 3 char combine // a + ogonek + acute = ( ą́ ) @@ -39,18 +40,18 @@ public void AddStr_With_Combining_Characters (Type driverType) combined = "a" + ogonek + acuteaccent; expected = "ą́"; - Application.Driver.Move (0, 0); - Application.Driver.AddStr (combined); - TestHelpers.AssertDriverContentsAre (expected, output); + driver.Move (0, 0); + driver.AddStr (combined); + TestHelpers.AssertDriverContentsAre (expected, output, driver); // o + ogonek + acute = ( ǫ́ ) ogonek = new System.Text.Rune (0x0328); // Combining ogonek (a small hook or comma shape) combined = "o" + ogonek + acuteaccent; expected = "ǫ́"; - Application.Driver.Move (0, 0); - Application.Driver.AddStr (combined); - TestHelpers.AssertDriverContentsAre (expected, output); + driver.Move (0, 0); + driver.AddStr (combined); + TestHelpers.AssertDriverContentsAre (expected, output, driver); } [Theory] @@ -61,7 +62,6 @@ public void AddStr_With_Combining_Characters (Type driverType) public void Move_Bad_Coordinates (Type driverType) { var driver = (ConsoleDriver)Activator.CreateInstance (driverType); - Application.Init (driver); Assert.Equal (0, driver.Col); Assert.Equal (0, driver.Row); @@ -85,9 +85,6 @@ public void Move_Bad_Coordinates (Type driverType) driver.Move (500, 500); Assert.Equal (500, driver.Col); Assert.Equal (500, driver.Row); - - // Shutdown must be called to safely clean up Application if Init has been called - Application.Shutdown (); } // TODO: Add these unit tests diff --git a/UnitTests/FileServices/FileDialogTests.cs b/UnitTests/FileServices/FileDialogTests.cs index fb1b28b3fa..e0a6fcf2cf 100644 --- a/UnitTests/FileServices/FileDialogTests.cs +++ b/UnitTests/FileServices/FileDialogTests.cs @@ -401,7 +401,7 @@ public void TestDirectoryContents_Linux () │{CM.Glyphs.LeftBracket} ►► {CM.Glyphs.RightBracket} Enter Search {CM.Glyphs.LeftBracket} Ok {CM.Glyphs.RightBracket} {CM.Glyphs.LeftBracket} Cancel {CM.Glyphs.RightBracket} │ └──────────────────────────────────────────────────────────────────┘ "; - TestHelpers.AssertDriverContentsAre (expected, output, true); + TestHelpers.AssertDriverContentsAre (expected, output, ignoreLeadingWhitespace: true); } [Fact, AutoInitShutdown] @@ -437,7 +437,7 @@ public void TestDirectoryContents_Windows () │{CM.Glyphs.LeftBracket} ►► {CM.Glyphs.RightBracket} Enter Search {CM.Glyphs.LeftBracket} Ok {CM.Glyphs.RightBracket} {CM.Glyphs.LeftBracket} Cancel {CM.Glyphs.RightBracket} │ └──────────────────────────────────────────────────────────────────┘ "; - TestHelpers.AssertDriverContentsAre (expected, output, true); + TestHelpers.AssertDriverContentsAre (expected, output, ignoreLeadingWhitespace: true); } diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index ed5798e873..ed21d07cfb 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -147,12 +147,19 @@ partial class TestHelpers { private static partial Regex LeadingWhitespaceRegEx (); #pragma warning disable xUnit1013 // Public method should be marked as test - public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelper output, bool ignoreLeadingWhitespace = false) + /// + /// Asserts that the driver contents match the expected contents, optionally ignoring any trailing whitespace. + /// + /// + /// + /// The ConsoleDriver to use. If null will be used. + /// + public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelper output, ConsoleDriver driver = null, bool ignoreLeadingWhitespace = false) { #pragma warning restore xUnit1013 // Public method should be marked as test var sb = new StringBuilder (); - var driver = (FakeDriver)Application.Driver; + driver ??= Application.Driver; var contents = driver.Contents; @@ -198,11 +205,18 @@ public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelp Assert.Equal (expectedLook, actualLook); } - public static Rect AssertDriverContentsWithFrameAre (string expectedLook, ITestOutputHelper output) + /// + /// Asserts that the driver contents are equal to the expected look, and that the cursor is at the expected position. + /// + /// + /// + /// The ConsoleDriver to use. If null will be used. + /// + public static Rect AssertDriverContentsWithFrameAre (string expectedLook, ITestOutputHelper output, ConsoleDriver driver = null) { var lines = new List> (); var sb = new StringBuilder (); - var driver = Application.Driver; + driver ??= Application.Driver; var x = -1; var y = -1; var w = -1; @@ -290,16 +304,17 @@ public static Rect AssertDriverContentsWithFrameAre (string expectedLook, ITestO /// test method will verify those colors were used in the row/col of the console during rendering /// /// Numbers between 0 and 9 for each row/col of the console. Must be valid indexes of + /// The ConsoleDriver to use. If null will be used. /// - public static void AssertDriverColorsAre (string expectedLook, params Attribute [] expectedColors) + public static void AssertDriverColorsAre (string expectedLook, ConsoleDriver driver = null, params Attribute [] expectedColors) { #pragma warning restore xUnit1013 // Public method should be marked as test if (expectedColors.Length > 10) throw new ArgumentException ("This method only works for UIs that use at most 10 colors"); expectedLook = expectedLook.Trim (); - var driver = (FakeDriver)Application.Driver; - + driver ??= Application.Driver; + var contents = driver.Contents; var r = 0; @@ -331,11 +346,11 @@ public static void AssertDriverColorsAre (string expectedLook, params Attribute /// If one or more of the expected colors are not used then the failure will output both /// the colors that were found to be used and which of your expectations was not met. /// + /// if null uses /// - internal static void AssertDriverUsedColors (params Attribute [] expectedColors) + internal static void AssertDriverUsedColors (ConsoleDriver driver = null, params Attribute [] expectedColors) { - var driver = (FakeDriver)Application.Driver; - + driver ??= Application.Driver; var contents = driver.Contents; var toFind = expectedColors.ToList (); diff --git a/UnitTests/View/DrawTests.cs b/UnitTests/View/DrawTests.cs index 6a5ea02432..e744b361df 100644 --- a/UnitTests/View/DrawTests.cs +++ b/UnitTests/View/DrawTests.cs @@ -58,7 +58,7 @@ public void Non_Bmp_ConsoleWidth_ColumnWidth_Equal_Two () 0020000000 0000000000 0111000000 -0000000000", expectedColors); +0000000000", driver: Application.Driver, expectedColors); } [Fact, AutoInitShutdown] @@ -106,7 +106,7 @@ public void CJK_Compatibility_Ideographs_ConsoleWidth_ColumnWidth_Equal_Two () 0022000000 0000000000 0111000000 -0000000000", expectedColors); +0000000000", driver: Application.Driver, expectedColors); } [Fact, AutoInitShutdown] @@ -147,7 +147,7 @@ public void Colors_On_TextAlignment_Right_And_Bottom () 0 0 0 -0", new Attribute [] { Colors.Base.Normal }); +0", driver: Application.Driver, new Attribute [] { Colors.Base.Normal }); } [Fact, AutoInitShutdown] diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs index e2d90d8825..e420a95728 100644 --- a/UnitTests/View/ViewTests.cs +++ b/UnitTests/View/ViewTests.cs @@ -942,11 +942,11 @@ public void Clear_Does_Not_Spillover_Its_Parent (bool label) if (label) { TestHelpers.AssertDriverColorsAre (@" 111111111111111111110 -111111111111111111110", attributes); +111111111111111111110", driver: Application.Driver, attributes); } else { TestHelpers.AssertDriverColorsAre (@" 222222222222222222220 -111111111111111111110", attributes); +111111111111111111110", driver: Application.Driver, attributes); } if (label) { @@ -958,7 +958,7 @@ public void Clear_Does_Not_Spillover_Its_Parent (bool label) Application.Refresh (); TestHelpers.AssertDriverColorsAre (@" 222222222222222222220 -111111111111111111110", attributes); +111111111111111111110", driver: Application.Driver, attributes); } Application.End (runState); } diff --git a/UnitTests/Views/ComboBoxTests.cs b/UnitTests/Views/ComboBoxTests.cs index a7b2951a95..92ea05b784 100644 --- a/UnitTests/Views/ComboBoxTests.cs +++ b/UnitTests/Views/ComboBoxTests.cs @@ -836,7 +836,7 @@ cb.Subviews [1].GetNormalColor () 000000 222222 222222 -222222", attributes); +222222", driver: Application.Driver, attributes); Assert.True (cb.Subviews [1].ProcessKey (new KeyEvent (Key.CursorDown, new KeyModifiers ()))); Assert.Equal ("", selected); @@ -848,7 +848,7 @@ cb.Subviews [1].GetNormalColor () 000000 222222 000002 -222222", attributes); +222222", driver: Application.Driver, attributes); Assert.True (cb.Subviews [1].ProcessKey (new KeyEvent (Key.CursorDown, new KeyModifiers ()))); Assert.Equal ("", selected); @@ -860,7 +860,7 @@ cb.Subviews [1].GetNormalColor () 000000 222222 222222 -000002", attributes); +000002", driver: Application.Driver, attributes); Assert.True (cb.Subviews [1].ProcessKey (new KeyEvent (Key.Enter, new KeyModifiers ()))); Assert.Equal ("Three", selected); @@ -878,7 +878,7 @@ cb.Subviews [1].GetNormalColor () 000000 222222 222222 -000002", attributes); +000002", driver: Application.Driver, attributes); Assert.True (cb.Subviews [1].ProcessKey (new KeyEvent (Key.CursorUp, new KeyModifiers ()))); Assert.Equal ("Three", selected); @@ -890,7 +890,7 @@ cb.Subviews [1].GetNormalColor () 000000 222222 000002 -111112", attributes); +111112", driver: Application.Driver, attributes); Assert.True (cb.Subviews [1].ProcessKey (new KeyEvent (Key.CursorUp, new KeyModifiers ()))); Assert.Equal ("Three", selected); @@ -902,7 +902,7 @@ cb.Subviews [1].GetNormalColor () 000000 000002 222222 -111112", attributes); +111112", driver: Application.Driver, attributes); Assert.True (cb.ProcessKey (new KeyEvent (Key.F4, new KeyModifiers ()))); Assert.Equal ("Three", selected); diff --git a/UnitTests/Views/MenuTests.cs b/UnitTests/Views/MenuTests.cs index 0211fe075f..16d7a47c7d 100644 --- a/UnitTests/Views/MenuTests.cs +++ b/UnitTests/Views/MenuTests.cs @@ -1589,7 +1589,7 @@ public void Disabled_MenuItem_Is_Never_Selected () }; TestHelpers.AssertDriverColorsAre (@" -00000000000000", attributes); +00000000000000", driver: Application.Driver, attributes); Assert.True (menu.MouseEvent (new MouseEvent { X = 0, @@ -1605,7 +1605,7 @@ public void Disabled_MenuItem_Is_Never_Selected () 02222222222220 00000000000000 00000000000000 -00000000000000", attributes); +00000000000000", driver: Application.Driver, attributes); Assert.True (top.Subviews [1].MouseEvent (new MouseEvent { X = 0, @@ -1621,7 +1621,7 @@ public void Disabled_MenuItem_Is_Never_Selected () 02222222222220 00000000000000 00000000000000 -00000000000000", attributes); +00000000000000", driver: Application.Driver, attributes); Assert.True (top.Subviews [1].MouseEvent (new MouseEvent { X = 0, @@ -1637,7 +1637,7 @@ public void Disabled_MenuItem_Is_Never_Selected () 02222222222220 00000000000000 00000000000000 -00000000000000", attributes); +00000000000000", driver: Application.Driver, attributes); } [Fact, AutoInitShutdown] diff --git a/UnitTests/Views/RuneCellTests.cs b/UnitTests/Views/RuneCellTests.cs index 83ae3c2a11..952e2ae25c 100644 --- a/UnitTests/Views/RuneCellTests.cs +++ b/UnitTests/Views/RuneCellTests.cs @@ -116,12 +116,12 @@ public void RuneCell_LoadRuneCells_InheritsPreviousColorScheme () 2222220000 3333000000 4444400000"; - TestHelpers.AssertDriverColorsAre (expectedColor, attributes); + TestHelpers.AssertDriverColorsAre (expectedColor, driver: Application.Driver, attributes); tv.WordWrap = true; Application.Refresh (); TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); - TestHelpers.AssertDriverColorsAre (expectedColor, attributes); + TestHelpers.AssertDriverColorsAre (expectedColor, driver: Application.Driver, attributes); tv.CursorPosition = new Point (6, 2); tv.SelectionStartColumn = 0; @@ -149,7 +149,7 @@ public void RuneCell_LoadRuneCells_InheritsPreviousColorScheme () 4444444444 4444000000 4444444440"; - TestHelpers.AssertDriverColorsAre (expectedColor, attributes); + TestHelpers.AssertDriverColorsAre (expectedColor, driver: Application.Driver, attributes); tv.Undo (); tv.CursorPosition = new Point (0, 3); @@ -180,7 +180,7 @@ public void RuneCell_LoadRuneCells_InheritsPreviousColorScheme () 4444000000 4444440000 4440000000"; - TestHelpers.AssertDriverColorsAre (expectedColor, attributes); + TestHelpers.AssertDriverColorsAre (expectedColor, driver: Application.Driver, attributes); Application.End (rs); } diff --git a/UnitTests/Views/TableViewTests.cs b/UnitTests/Views/TableViewTests.cs index 1ff9ea0c54..4f5d605e7b 100644 --- a/UnitTests/Views/TableViewTests.cs +++ b/UnitTests/Views/TableViewTests.cs @@ -836,7 +836,7 @@ public void TableView_ColorTests_FocusedOrNot (bool focused) 01000 "; - TestHelpers.AssertDriverColorsAre (expectedColors, new Attribute [] { + TestHelpers.AssertDriverColorsAre (expectedColors, driver: Application.Driver, new Attribute [] { // 0 tv.ColorScheme.Normal, // 1 @@ -883,7 +883,7 @@ public void TableView_ColorTests_InvertSelectedCellFirstCharacter (bool focused) var invertFocus = new Attribute (tv.ColorScheme.Focus.Background, tv.ColorScheme.Focus.Foreground); var invertHotNormal = new Attribute (tv.ColorScheme.HotNormal.Background, tv.ColorScheme.HotNormal.Foreground); - TestHelpers.AssertDriverColorsAre (expectedColors, new Attribute [] { + TestHelpers.AssertDriverColorsAre (expectedColors, driver: Application.Driver, new Attribute [] { // 0 tv.ColorScheme.Normal, // 1 @@ -938,7 +938,7 @@ public void TableView_ColorsTest_RowColorGetter (bool focused) 21222 "; - TestHelpers.AssertDriverColorsAre (expectedColors, new Attribute [] { + TestHelpers.AssertDriverColorsAre (expectedColors, driver: Application.Driver, new Attribute [] { // 0 tv.ColorScheme.Normal, // 1 @@ -971,7 +971,7 @@ public void TableView_ColorsTest_RowColorGetter (bool focused) // now we only see 2 colors used (the selected cell color and Normal // rowHighlight should no longer be used because the delegate returned null // (now that the cell value is 5 - which does not match the conditional) - TestHelpers.AssertDriverColorsAre (expectedColors, new Attribute [] { + TestHelpers.AssertDriverColorsAre (expectedColors, driver: Application.Driver, new Attribute [] { // 0 tv.ColorScheme.Normal, // 1 @@ -1030,7 +1030,7 @@ public void TableView_ColorsTest_ColorGetter (bool focused) 01020 "; - TestHelpers.AssertDriverColorsAre (expectedColors, new Attribute [] { + TestHelpers.AssertDriverColorsAre (expectedColors, driver: Application.Driver, new Attribute [] { // 0 tv.ColorScheme.Normal, // 1 @@ -1063,7 +1063,7 @@ public void TableView_ColorsTest_ColorGetter (bool focused) // now we only see 2 colors used (the selected cell color and Normal // cellHighlight should no longer be used because the delegate returned null // (now that the cell value is 5 - which does not match the conditional) - TestHelpers.AssertDriverColorsAre (expectedColors, new Attribute [] { + TestHelpers.AssertDriverColorsAre (expectedColors, driver: Application.Driver, new Attribute [] { // 0 tv.ColorScheme.Normal, // 1 @@ -2113,7 +2113,7 @@ public void CellEventsBackgroundFill () 00000000000000000000 01111101101111111110 "; - TestHelpers.AssertDriverColorsAre (expected, new Attribute [] { tv.ColorScheme.Normal, color }); + TestHelpers.AssertDriverColorsAre (expected, driver: Application.Driver, new Attribute [] { tv.ColorScheme.Normal, color }); } @@ -2233,7 +2233,7 @@ public void TestFullRowSelect_SelectionColorStopsAtTableEdge_WithCellLines () 0111110 0000000"; - TestHelpers.AssertDriverColorsAre (expected, normal, focus); + TestHelpers.AssertDriverColorsAre (expected, driver: Application.Driver, normal, focus); } [Fact, AutoInitShutdown] @@ -2290,7 +2290,7 @@ public void TestFullRowSelect_AlwaysUseNormalColorForVerticalCellLines () 0101010 0000000"; - TestHelpers.AssertDriverColorsAre (expected, normal, focus); + TestHelpers.AssertDriverColorsAre (expected, driver: Application.Driver, normal, focus); } [Fact, AutoInitShutdown] @@ -2785,7 +2785,7 @@ 1 2 3 000000 111111"; - TestHelpers.AssertDriverColorsAre (expected, normal, focus); + TestHelpers.AssertDriverColorsAre (expected, driver: Application.Driver, normal, focus); } public static DataTableSource BuildTable (int cols, int rows) diff --git a/UnitTests/Views/TreeViewTests.cs b/UnitTests/Views/TreeViewTests.cs index d6fa9ade29..4ec9830771 100644 --- a/UnitTests/Views/TreeViewTests.cs +++ b/UnitTests/Views/TreeViewTests.cs @@ -870,7 +870,7 @@ public void TestTreeViewColor () 00000000 0000000000 000000 -", +", driver: Application.Driver, new [] { tv.ColorScheme.Normal, pink }); var pinkScheme = new ColorScheme { @@ -900,7 +900,7 @@ public void TestTreeViewColor () 00001111 0000000000 001111 -", +", driver: Application.Driver, new [] { tv.ColorScheme.Normal, pink }); } From 860ceb1118b6d7e72aadf5a9cf744a54544b2631 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 25 Oct 2023 09:26:54 -0600 Subject: [PATCH 03/16] Updates driver tests to match new arch --- .../CursesDriver/CursesDriver.cs | 4 +- .../ConsoleDrivers/ConsoleDriverTests.cs | 186 ++++++++---------- UnitTests/ConsoleDrivers/ContentsTests.cs | 15 +- 3 files changed, 95 insertions(+), 110 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 036c201783..fd22d3956e 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -15,8 +15,8 @@ namespace Terminal.Gui; /// internal class CursesDriver : ConsoleDriver { - public override int Cols => Curses.Cols; - public override int Rows => Curses.Lines; + //public override int Cols => Curses.Cols; + //public override int Rows => Curses.Lines; CursorVisibility? _initialCursorVisibility = null; CursorVisibility? _currentCursorVisibility = null; diff --git a/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs b/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs index f36a550b13..55a97c129c 100644 --- a/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs +++ b/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs @@ -20,53 +20,34 @@ public ConsoleDriverTests (ITestOutputHelper output) [Theory] [InlineData (typeof (FakeDriver))] - //[InlineData (typeof (NetDriver))] - //[InlineData (typeof (CursesDriver))] - //[InlineData (typeof (WindowsDriver))] + [InlineData (typeof (NetDriver))] + [InlineData (typeof (CursesDriver))] + [InlineData (typeof (WindowsDriver))] public void Init_Inits (Type driverType) { var driver = (ConsoleDriver)Activator.CreateInstance (driverType); - Application.Init (driver); - driver.Init (); - - Assert.Equal (80, Console.BufferWidth); - Assert.Equal (25, Console.BufferHeight); + var ml = driver.Init (); + Assert.NotNull (ml); + Assert.NotNull (driver.Clipboard); + Console.ForegroundColor = ConsoleColor.Red; + Assert.Equal (ConsoleColor.Red, Console.ForegroundColor); + Console.BackgroundColor = ConsoleColor.Green; + Assert.Equal (ConsoleColor.Green, Console.BackgroundColor); - // MockDriver is always 80x25 - Assert.Equal (Console.BufferWidth, driver.Cols); - Assert.Equal (Console.BufferHeight, driver.Rows); driver.End (); - - // Shutdown must be called to safely clean up Application if Init has been called - Application.Shutdown (); } [Theory] [InlineData (typeof (FakeDriver))] - //[InlineData (typeof (NetDriver))] - //[InlineData (typeof (CursesDriver))] - //[InlineData (typeof (WindowsDriver))] + [InlineData (typeof (NetDriver))] + [InlineData (typeof (CursesDriver))] + [InlineData (typeof (WindowsDriver))] public void End_Cleans_Up (Type driverType) { var driver = (ConsoleDriver)Activator.CreateInstance (driverType); - Application.Init (driver); driver.Init (); - - Console.ForegroundColor = ConsoleColor.Red; - Assert.Equal (ConsoleColor.Red, Console.ForegroundColor); - - Console.BackgroundColor = ConsoleColor.Green; - Assert.Equal (ConsoleColor.Green, Console.BackgroundColor); - driver.Move (2, 3); - driver.End (); - Assert.Equal (0, Console.CursorLeft); - Assert.Equal (0, Console.CursorTop); - Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor); - Assert.Equal (ConsoleColor.Black, Console.BackgroundColor); - - // Shutdown must be called to safely clean up Application if Init has been called - Application.Shutdown (); + } [Theory] @@ -199,88 +180,89 @@ public void FakeDriver_MockKeyPresses (Type driverType) [Theory] [InlineData (typeof (FakeDriver))] + [InlineData (typeof (NetDriver))] + [InlineData (typeof (CursesDriver))] + [InlineData (typeof (WindowsDriver))] public void TerminalResized_Simulation (Type driverType) { - var driver = (FakeDriver)Activator.CreateInstance (driverType); - Application.Init (driver); + var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + driver?.Init (); + driver.Cols = 80; + driver.Rows = 25; + var wasTerminalResized = false; - Application.SizeChanging += (s, e) => { + driver.SizeChanged += (s, e) => { wasTerminalResized = true; Assert.Equal (120, e.Size.Width); Assert.Equal (40, e.Size.Height); }; - Assert.Equal (80, Console.BufferWidth); - Assert.Equal (25, Console.BufferHeight); - - // MockDriver is by default 80x25 - Assert.Equal (Console.BufferWidth, driver.Cols); - Assert.Equal (Console.BufferHeight, driver.Rows); + Assert.Equal (80, driver.Cols); + Assert.Equal (25, driver.Rows); Assert.False (wasTerminalResized); - // MockDriver will now be sets to 120x40 - driver.SetBufferSize (120, 40); - Assert.Equal (120, Application.Driver.Cols); - Assert.Equal (40, Application.Driver.Rows); + driver.Cols = 120; + driver.Rows = 40; + driver.OnSizeChanged (new SizeChangedEventArgs(new Size(driver.Cols, driver.Rows))); + Assert.Equal (120, driver.Cols); + Assert.Equal (40, driver.Rows); Assert.True (wasTerminalResized); - - - Application.Shutdown (); + driver.End (); } // Disabled due to test error - Change Task.Delay to an await -// [Fact, AutoInitShutdown] -// public void Write_Do_Not_Change_On_ProcessKey () -// { -// var win = new Window (); -// Application.Begin (win); -// ((FakeDriver)Application.Driver).SetBufferSize (20, 8); - -// System.Threading.Tasks.Task.Run (() => { -// System.Threading.Tasks.Task.Delay (500).Wait (); -// Application.Invoke (() => { -// var lbl = new Label ("Hello World") { X = Pos.Center () }; -// var dlg = new Dialog (); -// dlg.Add (lbl); -// Application.Begin (dlg); - -// var expected = @" -//┌──────────────────┐ -//│┌───────────────┐ │ -//││ Hello World │ │ -//││ │ │ -//││ │ │ -//││ │ │ -//│└───────────────┘ │ -//└──────────────────┘ -//"; - -// var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); -// Assert.Equal (new Rect (0, 0, 20, 8), pos); - -// Assert.True (dlg.ProcessKey (new KeyEvent (Key.Tab, new KeyModifiers ()))); -// dlg.Draw (); - -// expected = @" -//┌──────────────────┐ -//│┌───────────────┐ │ -//││ Hello World │ │ -//││ │ │ -//││ │ │ -//││ │ │ -//│└───────────────┘ │ -//└──────────────────┘ -//"; - -// pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); -// Assert.Equal (new Rect (0, 0, 20, 8), pos); - -// win.RequestStop (); -// }); -// }); - -// Application.Run (win); -// Application.Shutdown (); -// } + // [Fact, AutoInitShutdown] + // public void Write_Do_Not_Change_On_ProcessKey () + // { + // var win = new Window (); + // Application.Begin (win); + // ((FakeDriver)Application.Driver).SetBufferSize (20, 8); + + // System.Threading.Tasks.Task.Run (() => { + // System.Threading.Tasks.Task.Delay (500).Wait (); + // Application.Invoke (() => { + // var lbl = new Label ("Hello World") { X = Pos.Center () }; + // var dlg = new Dialog (); + // dlg.Add (lbl); + // Application.Begin (dlg); + + // var expected = @" + //┌──────────────────┐ + //│┌───────────────┐ │ + //││ Hello World │ │ + //││ │ │ + //││ │ │ + //││ │ │ + //│└───────────────┘ │ + //└──────────────────┘ + //"; + + // var pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); + // Assert.Equal (new Rect (0, 0, 20, 8), pos); + + // Assert.True (dlg.ProcessKey (new KeyEvent (Key.Tab, new KeyModifiers ()))); + // dlg.Draw (); + + // expected = @" + //┌──────────────────┐ + //│┌───────────────┐ │ + //││ Hello World │ │ + //││ │ │ + //││ │ │ + //││ │ │ + //│└───────────────┘ │ + //└──────────────────┘ + //"; + + // pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output); + // Assert.Equal (new Rect (0, 0, 20, 8), pos); + + // win.RequestStop (); + // }); + // }); + + // Application.Run (win); + // Application.Shutdown (); + // } } } diff --git a/UnitTests/ConsoleDrivers/ContentsTests.cs b/UnitTests/ConsoleDrivers/ContentsTests.cs index 89d72927a1..337be747ac 100644 --- a/UnitTests/ConsoleDrivers/ContentsTests.cs +++ b/UnitTests/ConsoleDrivers/ContentsTests.cs @@ -14,14 +14,15 @@ public class ContentsTests { public ContentsTests (ITestOutputHelper output) { + ConsoleDriver.RunningUnitTests = true; this.output = output; } [Theory] [InlineData (typeof (FakeDriver))] - //[InlineData (typeof (NetDriver))] - //[InlineData (typeof (CursesDriver))] - //[InlineData (typeof (WindowsDriver))] + [InlineData (typeof (NetDriver))] + //[InlineData (typeof (CursesDriver))] // TODO: Uncomment when #2796 and #2615 are fixed + //[InlineData (typeof (WindowsDriver))] // TODO: Uncomment when #2610 is fixed public void AddStr_With_Combining_Characters (Type driverType) { var driver = (ConsoleDriver)Activator.CreateInstance (driverType); @@ -52,13 +53,14 @@ public void AddStr_With_Combining_Characters (Type driverType) driver.Move (0, 0); driver.AddStr (combined); TestHelpers.AssertDriverContentsAre (expected, output, driver); + driver.End (); } [Theory] [InlineData (typeof (FakeDriver))] - //[InlineData (typeof (NetDriver))] - //[InlineData (typeof (CursesDriver))] - //[InlineData (typeof (WindowsDriver))] + [InlineData (typeof (NetDriver))] + [InlineData (typeof (CursesDriver))] + [InlineData (typeof (WindowsDriver))] public void Move_Bad_Coordinates (Type driverType) { var driver = (ConsoleDriver)Activator.CreateInstance (driverType); @@ -85,6 +87,7 @@ public void Move_Bad_Coordinates (Type driverType) driver.Move (500, 500); Assert.Equal (500, driver.Col); Assert.Equal (500, driver.Row); + driver.End (); } // TODO: Add these unit tests From 5e3edcef09fdef05e742234fcb0cb59493c6a1cb Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Wed, 25 Oct 2023 09:38:58 -0600 Subject: [PATCH 04/16] Start on making all driver tests test all drivers --- .../CursesDriver/CursesDriver.cs | 10 ++++- .../ConsoleDrivers/CursesDriver/binding.cs | 8 ++++ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 44 +++++++++---------- UnitTests/ConsoleDrivers/AddRuneTests.cs | 20 +++++---- 4 files changed, 49 insertions(+), 33 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index fd22d3956e..4c561c6a42 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -15,8 +15,14 @@ namespace Terminal.Gui; /// internal class CursesDriver : ConsoleDriver { - //public override int Cols => Curses.Cols; - //public override int Rows => Curses.Lines; + public override int Cols { + get => Curses.Cols; + internal set => Curses.Cols = value; + } + public override int Rows { + get => Curses.Lines; + internal set => Curses.Lines = value; + } CursorVisibility? _initialCursorVisibility = null; CursorVisibility? _currentCursorVisibility = null; diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs index 3a9ae70a31..9f3d3f0c19 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs @@ -152,12 +152,20 @@ public static int Lines { get { return lines; } + internal set { + // For unit tests + lines = value; + } } public static int Cols { get { return cols; } + internal set { + // For unit tests + cols = value; + } } // diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index b864299546..ff1936f7c7 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -807,28 +807,26 @@ public WindowsDriver () internal override MainLoop Init () { _mainLoopDriver = new WindowsMainLoop (this); - if (RunningUnitTests) { - return new MainLoop (_mainLoopDriver); - } - - try { - if (WinConsole != null) { - // BUGBUG: The results from GetConsoleOutputWindow are incorrect when called from Init. - // Our thread in WindowsMainLoop.CheckWin will get the correct results. See #if HACK_CHECK_WINCHANGED - var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); - Cols = winSize.Width; - Rows = winSize.Height; - } - WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); + if (!RunningUnitTests) { + try { + if (WinConsole != null) { + // BUGBUG: The results from GetConsoleOutputWindow are incorrect when called from Init. + // Our thread in WindowsMainLoop.CheckWin will get the correct results. See #if HACK_CHECK_WINCHANGED + var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); + Cols = winSize.Width; + Rows = winSize.Height; + } + WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion); - if (_isWindowsTerminal) { - Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll); + if (_isWindowsTerminal) { + Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll); + } + } catch (Win32Exception e) { + // We are being run in an environment that does not support a console + // such as a unit test, or a pipe. + Debug.WriteLine ($"Likely running unit tests. Setting WinConsole to null so we can test it elsewhere. Exception: {e}"); + WinConsole = null; } - } catch (Win32Exception e) { - // We are being run in an environment that does not support a console - // such as a unit test, or a pipe. - Debug.WriteLine ($"Likely running unit tests. Setting WinConsole to null so we can test it elsewhere. Exception: {e}"); - WinConsole = null; } CurrentAttribute = new Attribute (Color.White, Color.Black); @@ -885,7 +883,7 @@ private void ChangeWin (Object s, SizeChangedEventArgs e) // It also is broken when modifiers keys are down too // //Key _keyDown = (Key)0xffffffff; - + internal void ProcessInput (WindowsConsole.InputRecord inputEvent) { switch (inputEvent.EventType) { @@ -976,9 +974,9 @@ internal void ProcessInput (WindowsConsole.InputRecord inputEvent) _keyModifiers ??= new KeyModifiers (); //if (_keyDown == (Key)0xffffffff) { - // Avoid sending repeat keydowns + // Avoid sending repeat keydowns // _keyDown = map; - OnKeyDown (new KeyEventEventArgs (new KeyEvent (map, _keyModifiers))); + OnKeyDown (new KeyEventEventArgs (new KeyEvent (map, _keyModifiers))); //} OnKeyPressed (new KeyEventEventArgs (new KeyEvent (map, _keyModifiers))); } else { diff --git a/UnitTests/ConsoleDrivers/AddRuneTests.cs b/UnitTests/ConsoleDrivers/AddRuneTests.cs index a58f0a83e4..317e7c2986 100644 --- a/UnitTests/ConsoleDrivers/AddRuneTests.cs +++ b/UnitTests/ConsoleDrivers/AddRuneTests.cs @@ -1,4 +1,5 @@ -using System.Buffers; +using System; +using System.Buffers; using System.Text; using Xunit; using Xunit.Abstractions; @@ -11,22 +12,25 @@ public class AddRuneTests { public AddRuneTests (ITestOutputHelper output) { + ConsoleDriver.RunningUnitTests = true; this._output = output; } - [Fact] - public void AddRune () + [Theory] + [InlineData (typeof (FakeDriver))] + [InlineData (typeof (NetDriver))] + [InlineData (typeof (CursesDriver))] + [InlineData (typeof (WindowsDriver))] + public void AddRune (Type driverType) { - - var driver = new FakeDriver (); - Application.Init (driver); + var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + driver.Rows = 25; + driver.Cols = 80; driver.Init (); - driver.AddRune (new Rune ('a')); Assert.Equal ((Rune)'a', driver.Contents [0, 0].Runes [0]); driver.End (); - Application.Shutdown (); } [Fact] From fc26a31db3e2216937d3f10861a9dc29abf154bb Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 25 Oct 2023 17:13:51 +0100 Subject: [PATCH 05/16] Improves handling if combining marks. --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 13 +++-- UnitTests/ConsoleDrivers/ContentsTests.cs | 10 ++-- UnitTests/TestHelpers.cs | 50 ++++++++++---------- 3 files changed, 37 insertions(+), 36 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 60396f5ee3..a2130e2e61 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -175,15 +175,14 @@ public void AddRune (Rune rune) // Normalize to Form C (Canonical Composition) string normalized = combined.Normalize (NormalizationForm.FormC); - Contents [Row, Col - 1].Runes = new List { (Rune)normalized [0] }; ; - Contents [Row, Col - 1].Attribute = CurrentAttribute; - Contents [Row, Col - 1].IsDirty = true; + List runes = new List (); - if (normalized.Length > 1) { - Contents [Row, Col].Runes = new List { (Rune)normalized [1] }; ; - Contents [Row, Col].Attribute = CurrentAttribute; - Contents [Row, Col].IsDirty = true; + foreach (var c in normalized) { + runes.Add((Rune) c); } + Contents [Row, Col - 1].Runes = runes; + Contents [Row, Col - 1].Attribute = CurrentAttribute; + Contents [Row, Col - 1].IsDirty = true; } else { Contents [Row, Col].Attribute = CurrentAttribute; Contents [Row, Col].IsDirty = true; diff --git a/UnitTests/ConsoleDrivers/ContentsTests.cs b/UnitTests/ConsoleDrivers/ContentsTests.cs index 337be747ac..98f049b253 100644 --- a/UnitTests/ConsoleDrivers/ContentsTests.cs +++ b/UnitTests/ConsoleDrivers/ContentsTests.cs @@ -27,13 +27,13 @@ public void AddStr_With_Combining_Characters (Type driverType) { var driver = (ConsoleDriver)Activator.CreateInstance (driverType); driver.Init (); - + var acuteaccent = new System.Text.Rune (0x0301); // Combining acute accent (é) var combined = "e" + acuteaccent; var expected = "é"; driver.AddStr (combined); - TestHelpers.AssertDriverContentsAre (expected, output, driver); + TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver); // 3 char combine // a + ogonek + acute = ( ą́ ) @@ -43,7 +43,7 @@ public void AddStr_With_Combining_Characters (Type driverType) driver.Move (0, 0); driver.AddStr (combined); - TestHelpers.AssertDriverContentsAre (expected, output, driver); + TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver); // o + ogonek + acute = ( ǫ́ ) ogonek = new System.Text.Rune (0x0328); // Combining ogonek (a small hook or comma shape) @@ -52,7 +52,8 @@ public void AddStr_With_Combining_Characters (Type driverType) driver.Move (0, 0); driver.AddStr (combined); - TestHelpers.AssertDriverContentsAre (expected, output, driver); + TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver); + driver.End (); } @@ -64,6 +65,7 @@ public void AddStr_With_Combining_Characters (Type driverType) public void Move_Bad_Coordinates (Type driverType) { var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + driver.Init (); Assert.Equal (0, driver.Col); Assert.Equal (0, driver.Row); diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index ed21d07cfb..c2bf922ccc 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -165,15 +165,15 @@ public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelp for (int r = 0; r < driver.Rows; r++) { for (int c = 0; c < driver.Cols; c++) { - // TODO: Remove hard-coded [0] once combining pairs is supported - Rune rune = contents [r, c].Runes [0]; - if (rune.DecodeSurrogatePair (out char [] spair)) { - sb.Append (spair); - } else { - sb.Append ((char)rune.Value); - } - if (rune.GetColumns () > 1) { - c++; + foreach (var rune in contents [r, c].Runes) { + if (rune.DecodeSurrogatePair (out char [] spair)) { + sb.Append (spair); + } else { + sb.Append ((char)rune.Value); + } + if (rune.GetColumns () > 1) { + c++; + } } } sb.AppendLine (); @@ -227,25 +227,25 @@ public static Rect AssertDriverContentsWithFrameAre (string expectedLook, ITestO for (var r = 0; r < driver.Rows; r++) { var runes = new List (); for (var c = 0; c < driver.Cols; c++) { - // TODO: Remove hard-coded [0] once combining pairs is supported - Rune rune = contents [r, c].Runes [0]; - if (rune != (Rune)' ') { - if (x == -1) { - x = c; - y = r; - for (int i = 0; i < c; i++) { - runes.InsertRange (i, new List () { (Rune)' ' }); + foreach (var rune in contents [r, c].Runes) { + if (rune != (Rune)' ' && !rune.IsCombiningMark ()) { + if (x == -1) { + x = c; + y = r; + for (int i = 0; i < c; i++) { + runes.InsertRange (i, new List () { (Rune)' ' }); + } } + if (rune.GetColumns () > 1) { + c++; + } + if (c + 1 > w) { + w = c + 1; + } + h = r - y + 1; } - if (rune.GetColumns () > 1) { - c++; - } - if (c + 1 > w) { - w = c + 1; - } - h = r - y + 1; + if (x > -1) runes.Add (rune); } - if (x > -1) runes.Add (rune); } if (runes.Count > 0) lines.Add (runes); } From b49184594d8dc09ce92c1360940e7847b109d43d Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 25 Oct 2023 17:54:16 +0100 Subject: [PATCH 06/16] Fix unit tests fails. --- UnitTests/ConsoleDrivers/AddRuneTests.cs | 4 ++-- UnitTests/ConsoleDrivers/ConsoleDriverTests.cs | 1 - UnitTests/ConsoleDrivers/DriverColorTests.cs | 6 ------ UnitTests/Drawing/AttributeTests.cs | 6 ------ 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/UnitTests/ConsoleDrivers/AddRuneTests.cs b/UnitTests/ConsoleDrivers/AddRuneTests.cs index 317e7c2986..98c38b70cf 100644 --- a/UnitTests/ConsoleDrivers/AddRuneTests.cs +++ b/UnitTests/ConsoleDrivers/AddRuneTests.cs @@ -24,6 +24,8 @@ public AddRuneTests (ITestOutputHelper output) public void AddRune (Type driverType) { var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + driver.Init (); + driver.Rows = 25; driver.Cols = 80; driver.Init (); @@ -146,7 +148,6 @@ public void AddRune_MovesToNextColumn_Wide () public void AddRune_Accented_Letter_With_Three_Combining_Unicode_Chars () { var driver = new FakeDriver (); - Application.Init (driver); driver.Init (); var expected = new Rune ('ắ'); @@ -193,6 +194,5 @@ public void AddRune_Accented_Letter_With_Three_Combining_Unicode_Chars () // TestHelpers.AssertDriverContentsWithFrameAre (@" //ắ", output); driver.End (); - Application.Shutdown (); } } diff --git a/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs b/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs index 55a97c129c..e54a6e1b57 100644 --- a/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs +++ b/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs @@ -47,7 +47,6 @@ public void End_Cleans_Up (Type driverType) var driver = (ConsoleDriver)Activator.CreateInstance (driverType); driver.Init (); driver.End (); - } [Theory] diff --git a/UnitTests/ConsoleDrivers/DriverColorTests.cs b/UnitTests/ConsoleDrivers/DriverColorTests.cs index 80c5a8caa3..bb5bb0e296 100644 --- a/UnitTests/ConsoleDrivers/DriverColorTests.cs +++ b/UnitTests/ConsoleDrivers/DriverColorTests.cs @@ -52,9 +52,6 @@ public void SupportsTrueColor_Defaults (Type driverType, bool expectedSetting) Assert.Equal (expectedSetting, driver.SupportsTrueColor); driver.End (); - - // Shutdown must be called to safely clean up Application if Init has been called - Application.Shutdown (); } [Theory] @@ -71,9 +68,6 @@ public void Force16Colors_Sets (Type driverType) Assert.True (driver.Force16Colors); driver.End (); - - // Shutdown must be called to safely clean up Application if Init has been called - Application.Shutdown (); } } } \ No newline at end of file diff --git a/UnitTests/Drawing/AttributeTests.cs b/UnitTests/Drawing/AttributeTests.cs index f837e70c87..8aab374c6c 100644 --- a/UnitTests/Drawing/AttributeTests.cs +++ b/UnitTests/Drawing/AttributeTests.cs @@ -90,7 +90,6 @@ public void ColorNamesAndColorConstructor () public void Constuctors_Constuct () { var driver = new FakeDriver (); - Application.Init (driver); driver.Init (); // Test parameterless constructor @@ -127,7 +126,6 @@ public void Constuctors_Constuct () Assert.Equal (bg, attr.Background); driver.End (); - Application.Shutdown (); } [Fact] @@ -196,7 +194,6 @@ public void MakeColorAndColorNames_ForegroundAndBackgroundShouldMatchInput () public void Implicit_Assign () { var driver = new FakeDriver (); - Application.Init (driver); driver.Init (); var attr = new Attribute (); @@ -216,7 +213,6 @@ public void Implicit_Assign () Assert.Equal (value, attr.PlatformColor); driver.End (); - Application.Shutdown (); } [Fact] @@ -237,7 +233,6 @@ public void Make_SetsNotInitialized_NoDriver () public void Make_Creates () { var driver = new FakeDriver (); - Application.Init (driver); driver.Init (); var fg = new Color (); @@ -252,7 +247,6 @@ public void Make_Creates () Assert.Equal (bg, attr.Background); driver.End (); - Application.Shutdown (); } [Fact] From f5552432267c2eee259b4491b48f94e6276070c4 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 25 Oct 2023 19:28:03 +0100 Subject: [PATCH 07/16] Fix unit tests fails. --- UnitTests/ConsoleDrivers/AddRuneTests.cs | 7 ------- UnitTests/ConsoleDrivers/DriverColorTests.cs | 5 ++--- UnitTests/Views/TreeViewTests.cs | 16 ++++++++-------- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/UnitTests/ConsoleDrivers/AddRuneTests.cs b/UnitTests/ConsoleDrivers/AddRuneTests.cs index 98c38b70cf..d5d2a99718 100644 --- a/UnitTests/ConsoleDrivers/AddRuneTests.cs +++ b/UnitTests/ConsoleDrivers/AddRuneTests.cs @@ -39,7 +39,6 @@ public void AddRune (Type driverType) public void AddRune_InvalidLocation_DoesNothing () { var driver = new FakeDriver (); - Application.Init (driver); driver.Init (); driver.Move (driver.Cols, driver.Rows); @@ -52,14 +51,12 @@ public void AddRune_InvalidLocation_DoesNothing () } driver.End (); - Application.Shutdown (); } [Fact] public void AddRune_MovesToNextColumn () { var driver = new FakeDriver (); - Application.Init (driver); driver.Init (); driver.AddRune ('a'); @@ -93,14 +90,12 @@ public void AddRune_MovesToNextColumn () } driver.End (); - Application.Shutdown (); } [Fact] public void AddRune_MovesToNextColumn_Wide () { var driver = new FakeDriver (); - Application.Init (driver); driver.Init (); // 🍕 Slice of Pizza "\U0001F355" @@ -140,10 +135,8 @@ public void AddRune_MovesToNextColumn_Wide () //} driver.End (); - Application.Shutdown (); } - [Fact] public void AddRune_Accented_Letter_With_Three_Combining_Unicode_Chars () { diff --git a/UnitTests/ConsoleDrivers/DriverColorTests.cs b/UnitTests/ConsoleDrivers/DriverColorTests.cs index bb5bb0e296..5a44ba6f7d 100644 --- a/UnitTests/ConsoleDrivers/DriverColorTests.cs +++ b/UnitTests/ConsoleDrivers/DriverColorTests.cs @@ -19,7 +19,7 @@ public DriverColorTests () public void SetColors_Changes_Colors (Type driverType) { var driver = (ConsoleDriver)Activator.CreateInstance (driverType); - Application.Init (driver); + driver.Init (); Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor); Assert.Equal (ConsoleColor.Black, Console.BackgroundColor); @@ -34,8 +34,7 @@ public void SetColors_Changes_Colors (Type driverType) Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor); Assert.Equal (ConsoleColor.Black, Console.BackgroundColor); - // Shutdown must be called to safely clean up Application if Init has been called - Application.Shutdown (); + driver.End (); } diff --git a/UnitTests/Views/TreeViewTests.cs b/UnitTests/Views/TreeViewTests.cs index 4ec9830771..9182516948 100644 --- a/UnitTests/Views/TreeViewTests.cs +++ b/UnitTests/Views/TreeViewTests.cs @@ -858,15 +858,15 @@ public void TestTreeViewColor () // Normal drawing of the tree view - TestHelpers.AssertDriverContentsAre ( -@"├-normal + TestHelpers.AssertDriverContentsAre (@" +├-normal │ ├─pink │ └─normal └─pink ", output); // Should all be the same color - TestHelpers.AssertDriverColorsAre ( -@"00000000 + TestHelpers.AssertDriverColorsAre (@" +00000000 00000000 0000000000 000000 @@ -887,16 +887,16 @@ public void TestTreeViewColor () tv.Draw (); // Same text - TestHelpers.AssertDriverContentsAre ( -@"├-normal + TestHelpers.AssertDriverContentsAre (@" +├-normal │ ├─pink │ └─normal └─pink ", output); // but now the item (only not lines) appear // in pink when they are the word "pink" - TestHelpers.AssertDriverColorsAre ( -@"00000000 + TestHelpers.AssertDriverColorsAre (@" +00000000 00001111 0000000000 001111 From 6f99b09085268b0066f8e3d3db5a283ef9e5271a Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 25 Oct 2023 21:12:02 +0100 Subject: [PATCH 08/16] Handling combining mask. --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 5 ++++- Terminal.Gui/Drawing/Cell.cs | 2 +- UnitTests/TestHelpers.cs | 7 ++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index c640952713..b3c059de75 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -175,7 +175,10 @@ public void AddRune (Rune rune) // Normalize to Form C (Canonical Composition) string normalized = combined.Normalize (NormalizationForm.FormC); - Contents [Row, Col - 1].Rune = (Rune)normalized [0]; ; + Contents [Row, Col - 1].Rune = (Rune)normalized [0]; + if (normalized.Length > 1) { + Contents [Row, Col - 1].CombiningMark = (Rune)normalized [1]; + } Contents [Row, Col - 1].Attribute = CurrentAttribute; Contents [Row, Col - 1].IsDirty = true; } else { diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index a125b97372..ab21069635 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -23,7 +23,7 @@ public class Cell { ///// ///// Only valid in the rare case where is a combining sequence that could not be normalized to a single Rune. ///// - //internal Rune CombiningMark { get; set; } + internal Rune CombiningMark { get; set; } /// /// The attributes to use when drawing the Glyph. diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index d88459f299..d8d2061485 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -175,6 +175,9 @@ public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelp if (rune.GetColumns () > 1) { c++; } + if (contents [r, c].CombiningMark.Value > 0) { + sb.Append ((char)contents [r, c].CombiningMark.Value); + } } sb.AppendLine (); } @@ -227,7 +230,6 @@ public static Rect AssertDriverContentsWithFrameAre (string expectedLook, ITestO for (var r = 0; r < driver.Rows; r++) { var runes = new List (); for (var c = 0; c < driver.Cols; c++) { - // TODO: Remove hard-coded [0] once combining pairs is supported Rune rune = contents [r, c].Rune; if (rune != (Rune)' ') { if (x == -1) { @@ -246,6 +248,9 @@ public static Rect AssertDriverContentsWithFrameAre (string expectedLook, ITestO h = r - y + 1; } if (x > -1) runes.Add (rune); + if (contents [r, c].CombiningMark.Value > 0) { + runes.Add (contents [r, c].CombiningMark); + } } if (runes.Count > 0) lines.Add (runes); } From cda0f01c7a158a230e90b5172ee1d27066f519b8 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 25 Oct 2023 21:12:53 +0100 Subject: [PATCH 09/16] Tying to fix this unit test that sometimes fail. --- UnitTests/Application/ApplicationTests.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/UnitTests/Application/ApplicationTests.cs b/UnitTests/Application/ApplicationTests.cs index 5dc7216d5b..d5f5d83d66 100644 --- a/UnitTests/Application/ApplicationTests.cs +++ b/UnitTests/Application/ApplicationTests.cs @@ -422,20 +422,25 @@ public void Run_Loaded_Ready_Unlodaded_Events () #region ShutdownTests [Fact] - public void Shutdown_Allows_Async () + public async void Shutdown_Allows_Async () { - static async Task TaskWithAsyncContinuation () + bool isCompletedSuccessfully = false; + + async Task TaskWithAsyncContinuation () { await Task.Yield (); await Task.Yield (); + + isCompletedSuccessfully = true; } Init (); Application.Shutdown (); - var task = TaskWithAsyncContinuation (); + Assert.False (isCompletedSuccessfully); + await TaskWithAsyncContinuation (); Thread.Sleep (100); - Assert.True (task.IsCompletedSuccessfully); + Assert.True (isCompletedSuccessfully); } [Fact] From 0293f6ac45592a7290eeb49d978cb40bc688a1df Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 25 Oct 2023 21:59:14 +0100 Subject: [PATCH 10/16] Add support for combining mask on NetDriver. --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 6 +++++- UICatalog/Scenarios/TabViewExample.cs | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 376e95190f..4c12ca50b2 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -810,7 +810,11 @@ public override void UpdateScreen () outputWidth++; var rune = (Rune)Contents [row, col].Rune; output.Append (rune.ToString ()); - if (rune.IsSurrogatePair () && rune.GetColumns () < 2) { + if (Contents [row, col].CombiningMark.Value > 0) { + output.Append (Contents [row, col].CombiningMark.ToString ()); + WriteToConsole (output, ref lastCol, row, ref outputWidth); + SetCursorPosition (col - 1, row); + } else if (rune.IsSurrogatePair () && rune.GetColumns () < 2) { WriteToConsole (output, ref lastCol, row, ref outputWidth); SetCursorPosition (col - 1, row); } diff --git a/UICatalog/Scenarios/TabViewExample.cs b/UICatalog/Scenarios/TabViewExample.cs index b0c74b019a..18e06dd588 100644 --- a/UICatalog/Scenarios/TabViewExample.cs +++ b/UICatalog/Scenarios/TabViewExample.cs @@ -68,6 +68,7 @@ public override void Setup () new Label ("This tab has a very long name which should be truncated. See TabView.MaxTabTextWidth")), false); tabView.AddTab (new Tab ("Les Mise" + Char.ConvertFromUtf32 (Int32.Parse ("0301", NumberStyles.HexNumber)) + "rables", new Label ("This tab name is unicode")), false); + tabView.AddTab (new Tab ("Les Mise" + Char.ConvertFromUtf32 (Int32.Parse ("0328", NumberStyles.HexNumber)) + Char.ConvertFromUtf32 (Int32.Parse ("0301", NumberStyles.HexNumber)) + "rables", new Label ("This tab name has two unicode combining masks")), false); for (int i = 0; i < 100; i++) { tabView.AddTab (new Tab ($"Tab{i}", new Label ($"Welcome to tab {i}")), false); From 41cd36457f034c74ed285947cf77c1ae56534cd3 Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 26 Oct 2023 18:30:41 +0100 Subject: [PATCH 11/16] Enable CombiningMarks as List. --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 4 +++- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 6 ++++-- Terminal.Gui/Drawing/Cell.cs | 4 ++-- UnitTests/ConsoleDrivers/ContentsTests.cs | 21 +++++++++++++++++++- UnitTests/TestHelpers.cs | 8 ++++---- 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index b3c059de75..f5895f125e 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -177,7 +177,9 @@ public void AddRune (Rune rune) Contents [Row, Col - 1].Rune = (Rune)normalized [0]; if (normalized.Length > 1) { - Contents [Row, Col - 1].CombiningMark = (Rune)normalized [1]; + for (int i = 1; i < normalized.Length; i++) { + Contents [Row, Col - 1].CombiningMarks.Add ((Rune)normalized [i]); + } } Contents [Row, Col - 1].Attribute = CurrentAttribute; Contents [Row, Col - 1].IsDirty = true; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 4c12ca50b2..d756ae0b8c 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -810,8 +810,10 @@ public override void UpdateScreen () outputWidth++; var rune = (Rune)Contents [row, col].Rune; output.Append (rune.ToString ()); - if (Contents [row, col].CombiningMark.Value > 0) { - output.Append (Contents [row, col].CombiningMark.ToString ()); + if (Contents [row, col].CombiningMarks.Count > 0) { + foreach (var combMark in Contents [row, col].CombiningMarks) { + output.Append (combMark.ToString ()); + } WriteToConsole (output, ref lastCol, row, ref outputWidth); SetCursorPosition (col - 1, row); } else if (rune.IsSurrogatePair () && rune.GetColumns () < 2) { diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index ab21069635..d286787d01 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -2,7 +2,7 @@ using System.Text; -namespace Terminal.Gui; +namespace Terminal.Gui; /// /// Represents a single row/column in a Terminal.Gui rendering surface @@ -23,7 +23,7 @@ public class Cell { ///// ///// Only valid in the rare case where is a combining sequence that could not be normalized to a single Rune. ///// - internal Rune CombiningMark { get; set; } + internal List CombiningMarks { get; set; } = new (); /// /// The attributes to use when drawing the Glyph. diff --git a/UnitTests/ConsoleDrivers/ContentsTests.cs b/UnitTests/ConsoleDrivers/ContentsTests.cs index 98f049b253..6ce1e9f1b6 100644 --- a/UnitTests/ConsoleDrivers/ContentsTests.cs +++ b/UnitTests/ConsoleDrivers/ContentsTests.cs @@ -45,11 +45,30 @@ public void AddStr_With_Combining_Characters (Type driverType) driver.AddStr (combined); TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver); + // e + ogonek + acute = ( ę́́ ) + combined = "e" + ogonek + acuteaccent; + expected = "ę́́"; + + driver.Move (0, 0); + driver.AddStr (combined); + TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver); + + // i + ogonek + acute = ( į́́́ ) + combined = "i" + ogonek + acuteaccent; + expected = "į́́́"; + + driver.Move (0, 0); + driver.AddStr (combined); + TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver); + // o + ogonek + acute = ( ǫ́ ) - ogonek = new System.Text.Rune (0x0328); // Combining ogonek (a small hook or comma shape) combined = "o" + ogonek + acuteaccent; expected = "ǫ́"; + // u + ogonek + acute = ( ų́́́́ ) + combined = "u" + ogonek + acuteaccent; + expected = "ų́́́́"; + driver.Move (0, 0); driver.AddStr (combined); TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver); diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index d8d2061485..40e52d3239 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -175,8 +175,8 @@ public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelp if (rune.GetColumns () > 1) { c++; } - if (contents [r, c].CombiningMark.Value > 0) { - sb.Append ((char)contents [r, c].CombiningMark.Value); + foreach (var combMark in contents [r, c].CombiningMarks) { + sb.Append ((char)combMark.Value); } } sb.AppendLine (); @@ -248,8 +248,8 @@ public static Rect AssertDriverContentsWithFrameAre (string expectedLook, ITestO h = r - y + 1; } if (x > -1) runes.Add (rune); - if (contents [r, c].CombiningMark.Value > 0) { - runes.Add (contents [r, c].CombiningMark); + foreach (var combMark in contents [r, c].CombiningMarks) { + runes.Add (combMark); } } if (runes.Count > 0) lines.Add (runes); From 877190bf9867c3564b4deabec1ad4dee76e28989 Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 26 Oct 2023 23:03:45 +0100 Subject: [PATCH 12/16] Prevents combining marks on invalid runes default and space. --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index f5895f125e..06e9d75644 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -175,11 +175,19 @@ public void AddRune (Rune rune) // Normalize to Form C (Canonical Composition) string normalized = combined.Normalize (NormalizationForm.FormC); - Contents [Row, Col - 1].Rune = (Rune)normalized [0]; - if (normalized.Length > 1) { - for (int i = 1; i < normalized.Length; i++) { - Contents [Row, Col - 1].CombiningMarks.Add ((Rune)normalized [i]); + if (Contents [Row, Col - 1].Rune != default && Contents [Row, Col - 1].Rune != (Rune)' ') { + Contents [Row, Col - 1].Rune = (Rune)normalized [0]; + if (normalized.Length > 1) { + for (int i = 1; i < normalized.Length; i++) { + Contents [Row, Col - 1].CombiningMarks.Add ((Rune)normalized [i]); + } } + Contents [Row, Col - 1].Attribute = CurrentAttribute; + Contents [Row, Col - 1].IsDirty = true; + } else { + Contents [Row, Col].Rune = rune; + Contents [Row, Col].Attribute = CurrentAttribute; + Contents [Row, Col].IsDirty = true; } Contents [Row, Col - 1].Attribute = CurrentAttribute; Contents [Row, Col - 1].IsDirty = true; From 0e9b0364eb9b511553bc8895d107db8a5c5a4553 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 27 Oct 2023 10:55:08 +0100 Subject: [PATCH 13/16] Formatting for CI tests. --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 06e9d75644..e82e54d43b 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -464,7 +464,7 @@ public virtual Attribute MakeColor (Color foreground, Color background) /// Called after a key has been pressed and released. Fires the event. /// /// - public void OnKeyPressed (KeyEventEventArgs a) => KeyPressed?.Invoke(this, a); + public void OnKeyPressed (KeyEventEventArgs a) => KeyPressed?.Invoke (this, a); /// /// Event fired when a key is released. @@ -487,7 +487,7 @@ public virtual Attribute MakeColor (Color foreground, Color background) /// /// public void OnKeyDown (KeyEventEventArgs a) => KeyDown?.Invoke (this, a); - + /// /// Event fired when a mouse event occurs. /// From 621c68a8a2166126219855d216559b913e437646 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 27 Oct 2023 18:47:20 +0100 Subject: [PATCH 14/16] Fix non-normalized combining mark to add 1 to Col. --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index e82e54d43b..6a49d628f1 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -188,9 +188,8 @@ public void AddRune (Rune rune) Contents [Row, Col].Rune = rune; Contents [Row, Col].Attribute = CurrentAttribute; Contents [Row, Col].IsDirty = true; + Col++; } - Contents [Row, Col - 1].Attribute = CurrentAttribute; - Contents [Row, Col - 1].IsDirty = true; } else { Contents [Row, Col].Attribute = CurrentAttribute; Contents [Row, Col].IsDirty = true; From 9a9e970981621a9c17a76c1423271ecedf82f5cc Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 27 Oct 2023 18:58:21 +0100 Subject: [PATCH 15/16] Reformatting for retest the CI. --- UnitTests/TestHelpers.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index 40e52d3239..8cd1e3f8ba 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -154,7 +154,7 @@ partial class TestHelpers { /// /// The ConsoleDriver to use. If null will be used. /// - public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelper output, ConsoleDriver driver = null, bool ignoreLeadingWhitespace = false) + public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelper output, ConsoleDriver driver = null, bool ignoreLeadingWhitespace = false) { #pragma warning restore xUnit1013 // Public method should be marked as test @@ -319,7 +319,7 @@ public static void AssertDriverColorsAre (string expectedLook, ConsoleDriver dri expectedLook = expectedLook.Trim (); driver ??= Application.Driver; - + var contents = driver.Contents; var r = 0; From 6893af599269bfb0bfe2f0f0af0e54e2f7ee4706 Mon Sep 17 00:00:00 2001 From: Tigger Kindel Date: Fri, 27 Oct 2023 16:08:50 -0600 Subject: [PATCH 16/16] Forces non-normalized CMs to be ignored. --- Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs | 54 ++++++++++++++------ Terminal.Gui/ConsoleDrivers/NetDriver.cs | 22 +++++--- Terminal.Gui/Drawing/Cell.cs | 27 ++++++---- Terminal.Gui/Text/TextFormatter.cs | 3 +- UICatalog/Scenarios/CharacterMap.cs | 23 ++++++++- UICatalog/Scenarios/CombiningMarks.cs | 35 +++++++++++++ UICatalog/Scenarios/TabViewExample.cs | 5 +- UICatalog/Scenarios/Unicode.cs | 7 ++- UnitTests/ConsoleDrivers/ContentsTests.cs | 40 ++++++++++----- UnitTests/TestHelpers.cs | 15 +++--- 10 files changed, 168 insertions(+), 63 deletions(-) create mode 100644 UICatalog/Scenarios/CombiningMarks.cs diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 6a49d628f1..0a92e0274d 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -6,6 +6,8 @@ using System.Collections.Generic; using System.Diagnostics; using static Terminal.Gui.ColorScheme; +using System.Linq; +using System.Data; namespace Terminal.Gui; @@ -165,26 +167,33 @@ public void AddRune (Rune rune) if (validLocation) { rune = rune.MakePrintable (); runeWidth = rune.GetColumns (); - if (runeWidth == 0 && rune.IsCombiningMark () && Col > 0) { - // This is a combining character, and we are not at the beginning of the line. - // TODO: Remove hard-coded [0] once combining pairs is supported - - // Convert Runes to string and concatenate - string combined = Contents [Row, Col - 1].Rune.ToString () + rune.ToString (); - - // Normalize to Form C (Canonical Composition) - string normalized = combined.Normalize (NormalizationForm.FormC); - - if (Contents [Row, Col - 1].Rune != default && Contents [Row, Col - 1].Rune != (Rune)' ') { - Contents [Row, Col - 1].Rune = (Rune)normalized [0]; - if (normalized.Length > 1) { - for (int i = 1; i < normalized.Length; i++) { - Contents [Row, Col - 1].CombiningMarks.Add ((Rune)normalized [i]); + if (runeWidth == 0 && rune.IsCombiningMark ()) { + if (Col > 0) { + if (Contents [Row, Col - 1].CombiningMarks.Count > 0) { + // Just add this mark to the list + Contents [Row, Col - 1].CombiningMarks.Add (rune); + // 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 (); + + // Normalize to Form C (Canonical Composition) + string normalized = combined.Normalize (NormalizationForm.FormC); + 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]; + // 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].CombiningMarks.Add (rune); + // Don't move to next column (let the driver figure out what to do). } } 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; @@ -277,8 +286,19 @@ public void AddRune (Rune rune) /// String. public void AddStr (string str) { - foreach (var rune in str.EnumerateRunes ()) { - AddRune (rune); + var runes = str.EnumerateRunes ().ToList (); + for (var i = 0; i < runes.Count; i++) { + //if (runes [i].IsCombiningMark()) { + + // // Attempt to normalize + // string combined = runes [i-1] + runes [i].ToString(); + + // // Normalize to Form C (Canonical Composition) + // string normalized = combined.Normalize (NormalizationForm.FormC); + + // runes [i-] + //} + AddRune (runes [i]); } } diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index d756ae0b8c..6b06057b30 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -784,8 +784,9 @@ public override void UpdateScreen () } else if (lastCol == -1) { lastCol = col; } - if (lastCol + 1 < cols) + if (lastCol + 1 < cols) { lastCol++; + } continue; } @@ -809,14 +810,19 @@ public override void UpdateScreen () } outputWidth++; var rune = (Rune)Contents [row, col].Rune; - output.Append (rune.ToString ()); + output.Append (rune); if (Contents [row, col].CombiningMarks.Count > 0) { - foreach (var combMark in Contents [row, col].CombiningMarks) { - output.Append (combMark.ToString ()); - } - WriteToConsole (output, ref lastCol, row, ref outputWidth); - SetCursorPosition (col - 1, row); - } else if (rune.IsSurrogatePair () && rune.GetColumns () < 2) { + // 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); + //} + // WriteToConsole (output, ref lastCol, row, ref outputWidth); + } else if ((rune.IsSurrogatePair () && rune.GetColumns () < 2)) { WriteToConsole (output, ref lastCol, row, ref outputWidth); SetCursorPosition (col - 1, row); } diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index d286787d01..2977269494 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -9,21 +9,26 @@ namespace Terminal.Gui; /// (e.g. and ). /// public class Cell { + Rune _rune; /// /// The character to display. If is , then is ignored. /// - public Rune Rune { get; set; } + public Rune Rune { + get => _rune; + set { + CombiningMarks.Clear (); + _rune = value; + } + } - // TODO: Uncomment this once combining sequences that could not be normalized are supported. - ///// - ///// The combining mark for that when combined makes this Cell a combining sequence that could - ///// not be normalized to a single Rune. - ///// If is , then is ignored. - ///// - ///// - ///// Only valid in the rare case where is a combining sequence that could not be normalized to a single Rune. - ///// - internal List CombiningMarks { get; set; } = new (); + /// + /// The combining marks for that when combined makes this Cell a combining sequence. + /// If empty, then is ignored. + /// + /// + /// Only valid in the rare case where is a combining sequence that could not be normalized to a single Rune. + /// + internal List CombiningMarks { get; } = new List (); /// /// The attributes to use when drawing the Glyph. diff --git a/Terminal.Gui/Text/TextFormatter.cs b/Terminal.Gui/Text/TextFormatter.cs index bc50b4418f..4fdd45b1eb 100644 --- a/Terminal.Gui/Text/TextFormatter.cs +++ b/Terminal.Gui/Text/TextFormatter.cs @@ -1411,7 +1411,8 @@ public void Draw (Rect bounds, Attribute normalColor, Attribute hotColor, Rect c } else { Application.Driver?.AddRune (rune); } - var runeWidth = Math.Max (rune.GetColumns (), 1); + // BUGBUG: I think this is a bug. If rune is a combining mark current should not be incremented. + var runeWidth = rune.GetColumns (); //Math.Max (rune.GetColumns (), 1); if (isVertical) { current++; } else { diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 6f2370209e..09cd0eb1fc 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -473,7 +473,28 @@ public override void OnDrawContentComplete (Rect contentArea) // are we at first row of the row? if (!ShowGlyphWidths || (y - ContentOffset.Y) % _rowHeight > 0) { - Driver.AddRune (rune); + if (width > 0) { + Driver.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. + var normal = sb.ToString ().Normalize (NormalizationForm.FormC); + if (normal.Length == 1) { + Driver.AddRune (normal [0]); + } else { + Driver.AddRune (Rune.ReplacementChar); + } + } + } } else { Driver.SetAttribute (ColorScheme.HotNormal); Driver.AddStr ($"{width}"); diff --git a/UICatalog/Scenarios/CombiningMarks.cs b/UICatalog/Scenarios/CombiningMarks.cs new file mode 100644 index 0000000000..41711dd18e --- /dev/null +++ b/UICatalog/Scenarios/CombiningMarks.cs @@ -0,0 +1,35 @@ +using Terminal.Gui; + +namespace UICatalog.Scenarios { + [ScenarioMetadata (Name: "Combining Marks", Description: "Illustrates how Unicode Combining Marks work (or don't).")] + [ScenarioCategory ("Text and Formatting")] + public class CombiningMarks : Scenario { + public override void Init () + { + Application.Init (); + ConfigurationManager.Themes.Theme = Theme; + ConfigurationManager.Apply (); + Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]; + } + + public override void Setup () + { + Application.Top.DrawContentComplete += (s, e) => { + Application.Driver.Move (0, 0); + Application.Driver.AddStr ("Terminal.Gui only supports combining marks that normalize. See Issue #2616."); + Application.Driver.Move (0, 2); + Application.Driver.AddStr ("\u0301\u0301\u0328<- \"\\u301\\u301\\u328]\" using AddStr."); + Application.Driver.Move (0, 3); + Application.Driver.AddStr ("[a\u0301\u0301\u0328]<- \"[a\\u301\\u301\\u328]\" using AddStr."); + Application.Driver.Move (0, 4); + Application.Driver.AddRune ('['); + Application.Driver.AddRune ('a'); + Application.Driver.AddRune ('\u0301'); + Application.Driver.AddRune ('\u0301'); + Application.Driver.AddRune ('\u0328'); + Application.Driver.AddRune (']'); + Application.Driver.AddStr ("<- \"[a\\u301\\u301\\u328]\" using AddRune for each."); + }; + } + } +} \ No newline at end of file diff --git a/UICatalog/Scenarios/TabViewExample.cs b/UICatalog/Scenarios/TabViewExample.cs index 18e06dd588..1904877513 100644 --- a/UICatalog/Scenarios/TabViewExample.cs +++ b/UICatalog/Scenarios/TabViewExample.cs @@ -67,9 +67,8 @@ public override void Setup () "Long name Tab, I mean seriously long. Like you would not believe how long this tab's name is its just too much really woooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooowwww thats long", new Label ("This tab has a very long name which should be truncated. See TabView.MaxTabTextWidth")), false); - tabView.AddTab (new Tab ("Les Mise" + Char.ConvertFromUtf32 (Int32.Parse ("0301", NumberStyles.HexNumber)) + "rables", new Label ("This tab name is unicode")), false); - tabView.AddTab (new Tab ("Les Mise" + Char.ConvertFromUtf32 (Int32.Parse ("0328", NumberStyles.HexNumber)) + Char.ConvertFromUtf32 (Int32.Parse ("0301", NumberStyles.HexNumber)) + "rables", new Label ("This tab name has two unicode combining masks")), false); - + tabView.AddTab (new Tab ("Les Mise" + '\u0301' + "rables", new Label ("This tab name is unicode")), false); + tabView.AddTab (new Tab ("Les Mise" + '\u0328' + '\u0301' + "rables", new Label ("This tab name has two combining marks. Only one will show due to Issue #2616.")), false); for (int i = 0; i < 100; i++) { tabView.AddTab (new Tab ($"Tab{i}", new Label ($"Welcome to tab {i}")), false); } diff --git a/UICatalog/Scenarios/Unicode.cs b/UICatalog/Scenarios/Unicode.cs index e5e53095c8..0093303c3e 100644 --- a/UICatalog/Scenarios/Unicode.cs +++ b/UICatalog/Scenarios/Unicode.cs @@ -42,9 +42,12 @@ public override void Setup () label = new Label ("Label (CanFocus):") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 }; Win.Add (label); - testlabel = new Label ("Стоял &он, дум великих полн") { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50), CanFocus = true, HotKeySpecifier = new Rune ('&') }; + var sb = new StringBuilder (); + sb.Append ('e'); + sb.Append ('\u0301'); + sb.Append ('\u0301'); + testlabel = new Label ($"Should be [e with two accents, but isn't due to #2616]: [{sb}]") { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50), CanFocus = true, HotKeySpecifier = new Rune ('&') }; Win.Add (testlabel); - label = new Label ("Button:") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 }; Win.Add (label); var button = new Button ("A123456789♥♦♣♠JQK") { X = 20, Y = Pos.Y (label) }; diff --git a/UnitTests/ConsoleDrivers/ContentsTests.cs b/UnitTests/ConsoleDrivers/ContentsTests.cs index 6ce1e9f1b6..235fcff152 100644 --- a/UnitTests/ConsoleDrivers/ContentsTests.cs +++ b/UnitTests/ConsoleDrivers/ContentsTests.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Text; using Xunit; using Xunit.Abstractions; @@ -18,6 +19,23 @@ public ContentsTests (ITestOutputHelper output) this.output = output; } + + [Theory] + [InlineData (typeof (FakeDriver))] + [InlineData (typeof (NetDriver))] + //[InlineData (typeof (CursesDriver))] // TODO: Uncomment when #2796 and #2615 are fixed + //[InlineData (typeof (WindowsDriver))] // TODO: Uncomment when #2610 is fixed + public void AddStr_Combining_Character_1st_Column (Type driverType) + { + var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + driver.Init (); + var expected = "\u0301!"; + driver.AddStr ("\u0301!"); // acute accent + exclamation mark + TestHelpers.AssertDriverContentsAre (expected, output, driver); + + driver.End (); + } + [Theory] [InlineData (typeof (FakeDriver))] [InlineData (typeof (NetDriver))] @@ -33,45 +51,41 @@ public void AddStr_With_Combining_Characters (Type driverType) var expected = "é"; driver.AddStr (combined); - TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver); + TestHelpers.AssertDriverContentsAre (expected, output, driver); // 3 char combine // a + ogonek + acute = ( ą́ ) var ogonek = new System.Text.Rune (0x0328); // Combining ogonek (a small hook or comma shape) combined = "a" + ogonek + acuteaccent; - expected = "ą́"; + expected = ("a" + ogonek).Normalize(NormalizationForm.FormC); // See Issue #2616 driver.Move (0, 0); driver.AddStr (combined); - TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver); + TestHelpers.AssertDriverContentsAre (expected, output, driver); // e + ogonek + acute = ( ę́́ ) combined = "e" + ogonek + acuteaccent; - expected = "ę́́"; + expected = ("e" + ogonek).Normalize (NormalizationForm.FormC); // See Issue #2616 driver.Move (0, 0); driver.AddStr (combined); - TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver); + TestHelpers.AssertDriverContentsAre (expected, output, driver); // i + ogonek + acute = ( į́́́ ) combined = "i" + ogonek + acuteaccent; - expected = "į́́́"; + expected = ("i" + ogonek).Normalize (NormalizationForm.FormC); // See Issue #2616 driver.Move (0, 0); driver.AddStr (combined); - TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver); - - // o + ogonek + acute = ( ǫ́ ) - combined = "o" + ogonek + acuteaccent; - expected = "ǫ́"; + TestHelpers.AssertDriverContentsAre (expected, output, driver); // u + ogonek + acute = ( ų́́́́ ) combined = "u" + ogonek + acuteaccent; - expected = "ų́́́́"; + expected = ("u" + ogonek).Normalize (NormalizationForm.FormC); // See Issue #2616 driver.Move (0, 0); driver.AddStr (combined); - TestHelpers.AssertDriverContentsWithFrameAre (expected, output, driver); + TestHelpers.AssertDriverContentsAre (expected, output, driver); driver.End (); } diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index 8cd1e3f8ba..6f09942a51 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -165,7 +165,6 @@ public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelp for (int r = 0; r < driver.Rows; r++) { for (int c = 0; c < driver.Cols; c++) { - // TODO: Remove hard-coded [0] once combining pairs is supported Rune rune = contents [r, c].Rune; if (rune.DecodeSurrogatePair (out char [] spair)) { sb.Append (spair); @@ -175,9 +174,10 @@ public static void AssertDriverContentsAre (string expectedLook, ITestOutputHelp if (rune.GetColumns () > 1) { c++; } - foreach (var combMark in contents [r, c].CombiningMarks) { - sb.Append ((char)combMark.Value); - } + // See Issue #2616 + //foreach (var combMark in contents [r, c].CombiningMarks) { + // sb.Append ((char)combMark.Value); + //} } sb.AppendLine (); } @@ -248,9 +248,10 @@ public static Rect AssertDriverContentsWithFrameAre (string expectedLook, ITestO h = r - y + 1; } if (x > -1) runes.Add (rune); - foreach (var combMark in contents [r, c].CombiningMarks) { - runes.Add (combMark); - } + // See Issue #2616 + //foreach (var combMark in contents [r, c].CombiningMarks) { + // runes.Add (combMark); + //} } if (runes.Count > 0) lines.Add (runes); }