From 788eed4a4dd9cd15fe9d7501d4827d51a57c8db2 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 5 Feb 2023 00:25:14 +0000 Subject: [PATCH] Fixes #2317. Drivers do not normalize accented letters. --- .../CursesDriver/CursesDriver.cs | 75 ++++++++++++------- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 52 ++++++++----- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 56 +++++++++----- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 69 +++++++++++------ UnitTests/Views/TabViewTests.cs | 4 +- 5 files changed, 166 insertions(+), 90 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index d8d38392f..0a61f3bbb 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -62,40 +62,59 @@ namespace Terminal.Gui { Curses.move (crow, ccol); needMove = false; } - if (runeWidth < 2 && ccol > 0 - && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { + if (runeWidth == 0 && ccol > 0) { + var r = contents [crow, ccol - 1, 0]; + var s = new string (new char [] { (char)r, (char)rune }); + string sn; + if (!s.IsNormalized ()) { + sn = s.Normalize (); + } else { + sn = s; + } + var c = sn [0]; + Curses.mvaddch (crow, ccol - 1, (int)(uint)c); + contents [crow, ccol - 1, 0] = c; + contents [crow, ccol - 1, 1] = currentAttribute; + contents [crow, ccol - 1, 2] = 1; - var curAtttib = currentAttribute; - Curses.attrset (contents [crow, ccol - 1, 1]); - Curses.mvaddch (crow, ccol - 1, (int)(uint)' '); - contents [crow, ccol - 1, 0] = (int)(uint)' '; - Curses.move (crow, ccol); - Curses.attrset (curAtttib); - - } else if (runeWidth < 2 && ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { - - var curAtttib = currentAttribute; - Curses.attrset (contents [crow, ccol + 1, 1]); - Curses.mvaddch (crow, ccol + 1, (int)(uint)' '); - contents [crow, ccol + 1, 0] = (int)(uint)' '; - Curses.move (crow, ccol); - Curses.attrset (curAtttib); - - } - if (runeWidth > 1 && ccol == Clip.Right - 1) { - Curses.addch ((int)(uint)' '); - contents [crow, ccol, 0] = (int)(uint)' '; } else { - Curses.addch ((int)(uint)rune); - contents [crow, ccol, 0] = (int)(uint)rune; + if (runeWidth < 2 && ccol > 0 + && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { + + var curAtttib = currentAttribute; + Curses.attrset (contents [crow, ccol - 1, 1]); + Curses.mvaddch (crow, ccol - 1, (int)(uint)' '); + contents [crow, ccol - 1, 0] = (int)(uint)' '; + Curses.move (crow, ccol); + Curses.attrset (curAtttib); + + } else if (runeWidth < 2 && ccol <= Clip.Right - 1 + && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { + + var curAtttib = currentAttribute; + Curses.attrset (contents [crow, ccol + 1, 1]); + Curses.mvaddch (crow, ccol + 1, (int)(uint)' '); + contents [crow, ccol + 1, 0] = (int)(uint)' '; + Curses.move (crow, ccol); + Curses.attrset (curAtttib); + + } + if (runeWidth > 1 && ccol == Clip.Right - 1) { + Curses.addch ((int)(uint)' '); + contents [crow, ccol, 0] = (int)(uint)' '; + } else { + Curses.addch ((int)(uint)rune); + contents [crow, ccol, 0] = (int)(uint)rune; + } + contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 2] = 1; } - contents [crow, ccol, 1] = currentAttribute; - contents [crow, ccol, 2] = 1; } else needMove = true; - ccol++; + if (runeWidth < 0 || runeWidth > 0) { + ccol++; + } if (runeWidth > 1) { if (validClip && ccol < Clip.Right) { contents [crow, ccol, 1] = currentAttribute; diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 77526629b..84ced2be2 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -33,7 +33,7 @@ namespace Terminal.Gui { UseFakeClipboard = useFakeClipboard; FakeClipboardAlwaysThrowsNotSupportedException = fakeClipboardAlwaysThrowsNotSupportedException; FakeClipboardIsSupportedAlwaysFalse = fakeClipboardIsSupportedAlwaysTrue; - + // double check usage is correct Debug.Assert (useFakeClipboard == false && fakeClipboardAlwaysThrowsNotSupportedException == false); Debug.Assert (useFakeClipboard == false && fakeClipboardIsSupportedAlwaysTrue == false); @@ -131,31 +131,49 @@ namespace Terminal.Gui { //MockConsole.CursorTop = crow; needMove = false; } - if (runeWidth < 2 && ccol > 0 + if (runeWidth == 0 && ccol > 0) { + var r = contents [crow, ccol - 1, 0]; + var s = new string (new char [] { (char)r, (char)rune }); + string sn; + if (!s.IsNormalized ()) { + sn = s.Normalize (); + } else { + sn = s; + } + var c = sn [0]; + contents [crow, ccol - 1, 0] = c; + contents [crow, ccol - 1, 1] = currentAttribute; + contents [crow, ccol - 1, 2] = 1; + + } else { + if (runeWidth < 2 && ccol > 0 && Rune.ColumnWidth ((Rune)contents [crow, ccol - 1, 0]) > 1) { - contents [crow, ccol - 1, 0] = (int)(uint)' '; + contents [crow, ccol - 1, 0] = (int)(uint)' '; - } else if (runeWidth < 2 && ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((Rune)contents [crow, ccol, 0]) > 1) { + } else if (runeWidth < 2 && ccol <= Clip.Right - 1 + && Rune.ColumnWidth ((Rune)contents [crow, ccol, 0]) > 1) { - contents [crow, ccol + 1, 0] = (int)(uint)' '; - contents [crow, ccol + 1, 2] = 1; + contents [crow, ccol + 1, 0] = (int)(uint)' '; + contents [crow, ccol + 1, 2] = 1; + } + if (runeWidth > 1 && ccol == Clip.Right - 1) { + contents [crow, ccol, 0] = (int)(uint)' '; + } else { + contents [crow, ccol, 0] = (int)(uint)rune; + } + contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 2] = 1; + + dirtyLine [crow] = true; } - if (runeWidth > 1 && ccol == Clip.Right - 1) { - contents [crow, ccol, 0] = (int)(uint)' '; - } else { - contents [crow, ccol, 0] = (int)(uint)rune; - } - contents [crow, ccol, 1] = currentAttribute; - contents [crow, ccol, 2] = 1; - - dirtyLine [crow] = true; } else needMove = true; - ccol++; + if (runeWidth < 0 || runeWidth > 0) { + ccol++; + } if (runeWidth > 1) { if (validClip && ccol < Clip.Right) { contents [crow, ccol, 1] = currentAttribute; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 37b43244b..0b1da2d2a 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1241,30 +1241,48 @@ namespace Terminal.Gui { var validClip = IsValidContent (ccol, crow, Clip); if (validClip) { - if (runeWidth < 2 && ccol > 0 - && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { + if (runeWidth == 0 && ccol > 0) { + var r = contents [crow, ccol - 1, 0]; + var s = new string (new char [] { (char)r, (char)rune }); + string sn; + if (!s.IsNormalized ()) { + sn = s.Normalize (); + } else { + sn = s; + } + var c = sn [0]; + contents [crow, ccol - 1, 0] = c; + contents [crow, ccol - 1, 1] = currentAttribute; + contents [crow, ccol - 1, 2] = 1; - contents [crow, ccol - 1, 0] = (int)(uint)' '; - - } else if (runeWidth < 2 && ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { - - contents [crow, ccol + 1, 0] = (int)(uint)' '; - contents [crow, ccol + 1, 2] = 1; - - } - if (runeWidth > 1 && ccol == Clip.Right - 1) { - contents [crow, ccol, 0] = (int)(uint)' '; } else { - contents [crow, ccol, 0] = (int)(uint)rune; - } - contents [crow, ccol, 1] = currentAttribute; - contents [crow, ccol, 2] = 1; + if (runeWidth < 2 && ccol > 0 + && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { + contents [crow, ccol - 1, 0] = (int)(uint)' '; + + } else if (runeWidth < 2 && ccol <= Clip.Right - 1 + && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { + + contents [crow, ccol + 1, 0] = (int)(uint)' '; + contents [crow, ccol + 1, 2] = 1; + + } + if (runeWidth > 1 && ccol == Clip.Right - 1) { + contents [crow, ccol, 0] = (int)(uint)' '; + } else { + contents [crow, ccol, 0] = (int)(uint)rune; + } + contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 2] = 1; + + } dirtyLine [crow] = true; } - ccol++; + if (runeWidth < 0 || runeWidth > 0) { + ccol++; + } if (runeWidth > 1) { if (validClip && ccol < Clip.Right) { contents [crow, ccol, 1] = currentAttribute; @@ -1485,7 +1503,7 @@ namespace Terminal.Gui { outputWidth++; var rune = contents [row, col, 0]; char [] spair; - if (Rune.DecodeSurrogatePair((uint) rune, out spair)) { + if (Rune.DecodeSurrogatePair ((uint)rune, out spair)) { output.Append (spair); } else { output.Append ((char)rune); diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 28013fe71..8d1b71646 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1517,35 +1517,56 @@ namespace Terminal.Gui { var validClip = IsValidContent (ccol, crow, Clip); if (validClip) { - if (runeWidth < 2 && ccol > 0 - && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { - + if (runeWidth == 0 && ccol > 0) { + var r = contents [crow, ccol - 1, 0]; + var s = new string (new char [] { (char)r, (char)rune }); + string sn; + if (!s.IsNormalized ()) { + sn = s.Normalize (); + } else { + sn = s; + } + var c = sn [0]; var prevPosition = crow * Cols + (ccol - 1); - OutputBuffer [prevPosition].Char.UnicodeChar = ' '; - contents [crow, ccol - 1, 0] = (int)(uint)' '; - - } else if (runeWidth < 2 && ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { - - var prevPosition = GetOutputBufferPosition () + 1; - OutputBuffer [prevPosition].Char.UnicodeChar = (char)' '; - contents [crow, ccol + 1, 0] = (int)(uint)' '; - - } - if (runeWidth > 1 && ccol == Clip.Right - 1) { - OutputBuffer [position].Char.UnicodeChar = (char)' '; - contents [crow, ccol, 0] = (int)(uint)' '; + OutputBuffer [prevPosition].Char.UnicodeChar = c; + contents [crow, ccol - 1, 0] = c; + OutputBuffer [prevPosition].Attributes = (ushort)currentAttribute; + contents [crow, ccol - 1, 1] = currentAttribute; + contents [crow, ccol - 1, 2] = 1; + WindowsConsole.SmallRect.Update (ref damageRegion, (short)(ccol - 1), (short)crow); } else { - OutputBuffer [position].Char.UnicodeChar = (char)rune; - contents [crow, ccol, 0] = (int)(uint)rune; + if (runeWidth < 2 && ccol > 0 + && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { + + var prevPosition = crow * Cols + (ccol - 1); + OutputBuffer [prevPosition].Char.UnicodeChar = ' '; + contents [crow, ccol - 1, 0] = (int)(uint)' '; + + } else if (runeWidth < 2 && ccol <= Clip.Right - 1 + && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { + + var prevPosition = GetOutputBufferPosition () + 1; + OutputBuffer [prevPosition].Char.UnicodeChar = (char)' '; + contents [crow, ccol + 1, 0] = (int)(uint)' '; + + } + if (runeWidth > 1 && ccol == Clip.Right - 1) { + OutputBuffer [position].Char.UnicodeChar = (char)' '; + contents [crow, ccol, 0] = (int)(uint)' '; + } else { + OutputBuffer [position].Char.UnicodeChar = (char)rune; + contents [crow, ccol, 0] = (int)(uint)rune; + } + OutputBuffer [position].Attributes = (ushort)currentAttribute; + contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 2] = 1; + WindowsConsole.SmallRect.Update (ref damageRegion, (short)ccol, (short)crow); } - OutputBuffer [position].Attributes = (ushort)currentAttribute; - contents [crow, ccol, 1] = currentAttribute; - contents [crow, ccol, 2] = 1; - WindowsConsole.SmallRect.Update (ref damageRegion, (short)ccol, (short)crow); } - ccol++; + if (runeWidth < 0 || runeWidth > 0) { + ccol++; + } if (runeWidth > 1) { if (validClip && ccol < Clip.Right) { position = GetOutputBufferPosition (); diff --git a/UnitTests/Views/TabViewTests.cs b/UnitTests/Views/TabViewTests.cs index 5177b638e..0e3d43938 100644 --- a/UnitTests/Views/TabViewTests.cs +++ b/UnitTests/Views/TabViewTests.cs @@ -719,7 +719,7 @@ namespace Terminal.Gui.ViewTests { TestHelpers.AssertDriverContentsWithFrameAre (@" ┌──────────────┐ -│Les Misérables│ +│Les Misérables│ ◄ └───┐ │hi2 │ └──────────────────┘", output); @@ -756,7 +756,7 @@ namespace Terminal.Gui.ViewTests { ┌──────────────────┐ │hi2 │ ◄ ┌───┘ -│Les Misérables│ +│Les Misérables│ └──────────────┘ ", output); }