diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 6a133064b..11ddcff6c 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -39,14 +39,14 @@ namespace Terminal.Gui { } static bool sync = false; - public override void AddRune (Rune rune) + public override void AddRune (Rune rune) { if (Clip.Contains (ccol, crow)) { if (needMove) { Curses.move (crow, ccol); needMove = false; } - Curses.addch ((int)(uint)rune); + Curses.addch ((int)(uint)MakePrintable(rune)); } else needMove = true; if (sync) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 19196397c..f1c52c40d 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -90,6 +90,7 @@ namespace Terminal.Gui { /// public override void AddRune (Rune rune) { + rune = MakePrintable (rune); if (Clip.Contains (ccol, crow)) { if (needMove) { //MockConsole.CursorLeft = ccol; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 91a39b3bf..c176d8fd2 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -70,6 +70,7 @@ namespace Terminal.Gui { public override void AddRune (Rune rune) { + rune = MakePrintable (rune); if (Clip.Contains (ccol, crow)) { if (needMove) { //Console.CursorLeft = ccol; diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 6397967e0..3618a849f 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1126,6 +1126,7 @@ namespace Terminal.Gui { public override void AddRune (Rune rune) { + rune = MakePrintable (rune); var position = crow * Cols + ccol; if (Clip.Contains (ccol, crow)) { diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 77f61f1e2..dc709cd70 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -1,4 +1,4 @@ -// +// // ConsoleDriver.cs: Definition for the Console Driver API // // Authors: @@ -558,6 +558,28 @@ namespace Terminal.Gui { /// Rune to add. public abstract void AddRune (Rune rune); /// + /// Ensures a Rune is not a control character and can be displayed by translating characters below 0x20 + /// to equivalent, printable, Unicode chars. + /// + /// Rune to translate + /// + public static Rune MakePrintable (Rune c) + { + if (c <= 0x1F) { + // ASCII (C0) control characters. + return new Rune (c + 0x2400); + } else if (c >= 0x80 && c <= 0x9F) { + // C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1) + return new Rune (0x25a1); // U+25A1, WHITE SQUARE, □: + } else if (Rune.ColumnWidth (c) > 1) { + // BUGBUG: Until we figure out how to fix #41. Note this still doesn't help when + // an Emoji or other char doesn't represent it's width correctly + return new Rune (0x25a1); // U+25A1, WHITE SQUARE, □: + } else { + return c; + } + } + /// /// Adds the specified /// /// String. diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index 3e2aaaaf1..b9e475338 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -166,14 +166,6 @@ namespace Terminal.Gui { else shown_text = ustring.Make (_leftBracket) + " " + text + " " + ustring.Make (_rightBracket); - shown_text = shown_text - .Replace ("\f", "\u21a1") // U+21A1 ↡ DOWNWARDS TWO HEADED ARROW - .Replace ("\n", "\u240a") // U+240A (SYMBOL FOR LINE FEED, ␊) - .Replace ("\r", "\u240d") // U+240D (SYMBOL FOR CARRIAGE RETURN, ␍) - .Replace ("\t", "\u2409") // U+2409 ␉ SYMBOL FOR HORIZONTAL TABULATION - .Replace ("\v", "\u240b") // U+240B ␋ SYMBOL FOR VERTICAL TABULATION - .TrimSpace (); - shown_text = GetTextFromHotKey (shown_text, '_', out hot_pos, out hot_key); SetNeedsDisplay (); diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index ca4b5303d..a875c4658 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -37,6 +37,9 @@ namespace UICatalog { } var radioItems = new (ustring radioLabel, int start, int end) [] { + CreateRadio("ASCII Control Characterss", 0x00, 0x1F), + CreateRadio("C0 Control Characters", 0x80, 0x9f), + CreateRadio("Hangul Jamo", 0x1100, 0x11ff), // This is where wide chars tend to start CreateRadio("Currency Symbols", 0x20A0, 0x20CF), CreateRadio("Letterlike Symbols", 0x2100, 0x214F), CreateRadio("Arrows", 0x2190, 0x21ff), @@ -106,14 +109,14 @@ namespace UICatalog { #if true private void CharMap_DrawContent (Rect viewport) { - Rune ReplaceNonPrintables (Rune c) - { - if (c < 0x20) { - return new Rune (c + 0x2400); // U+25A1 □ WHITE SQUARE - } else { - return c; - } - } + //Rune ReplaceNonPrintables (Rune c) + //{ + // if (c < 0x20) { + // return new Rune (c + 0x2400); // U+25A1 □ WHITE SQUARE + // } else { + // return c; + // } + //} for (int header = 0; header < 16; header++) { Move (viewport.X + RowHeaderWidth + (header * 2), 0); @@ -125,10 +128,12 @@ namespace UICatalog { var rowLabel = $"U+{val / 16:x4}x"; Move (0, y + 1); Driver.AddStr (rowLabel); + var prevColWasWide = false; for (int col = 0; col < 16; col++) { - Move (viewport.X + RowHeaderWidth + (col * 2), 0 + y + 1); - Driver.AddRune (' '); - Driver.AddRune (ReplaceNonPrintables (new Rune (((uint)((uint)(-viewport.Y + row) * 16 + col))))); + var rune = new Rune ((uint)((uint)(-viewport.Y + row) * 16 + col)); + Move (viewport.X + RowHeaderWidth + (col * 2) + (prevColWasWide ? 0 : 1), 0 + y + 1); + Driver.AddRune (rune); + //prevColWasWide = Rune.ColumnWidth(rune) > 1; } } }