From 311acc03df96296f4901e7c1556b09de289df107 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 30 Apr 2022 01:03:41 +0100 Subject: [PATCH] Fixes drivers for wide runes handling which also works on Windows Terminal. --- .../CursesDriver/CursesDriver.cs | 36 +++- .../ConsoleDrivers/FakeDriver/FakeConsole.cs | 7 +- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 183 ++++++++++-------- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 118 +++++++---- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 38 +++- Terminal.Gui/Core/ConsoleDriver.cs | 18 +- UnitTests/ConsoleDriverTests.cs | 14 +- 7 files changed, 267 insertions(+), 147 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 777736e80..545d018d9 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -52,27 +52,47 @@ namespace Terminal.Gui { static bool sync = false; public override void AddRune (Rune rune) { - if (Clip.Contains (ccol, crow)) { + rune = MakePrintable (rune); + var runeWidth = Rune.ColumnWidth (rune); + var validClip = IsValidContent (ccol, crow, Clip); + + if (validClip) { if (needMove) { Curses.move (crow, ccol); needMove = false; } - rune = MakePrintable (rune); + if (runeWidth < 2 && ccol > 0 + && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { + + Curses.move (crow, ccol - 1); + Curses.addch ((int)(uint)' '); + contents [crow, ccol - 1, 0] = (int)(uint)' '; + Curses.move (crow, ccol); + } else if (runeWidth < 2 && ccol < Cols - 1 && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { + + Curses.move (crow, ccol + 1); + Curses.addch ((int)(uint)' '); + contents [crow, ccol + 1, 0] = (int)(uint)' '; + Curses.move (crow, ccol); + } Curses.addch ((int)(uint)rune); contents [crow, ccol, 0] = (int)(uint)rune; contents [crow, ccol, 1] = currentAttribute; contents [crow, ccol, 2] = 1; } else needMove = true; - if (sync) - Application.Driver.Refresh (); + ccol++; - var runeWidth = Rune.ColumnWidth (rune); if (runeWidth > 1) { - for (int i = 1; i < runeWidth; i++) { - ccol++; + if (validClip) { + contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 2] = 0; } + ccol++; } + + if (sync) + UpdateScreen (); } public override void AddStr (ustring str) @@ -933,7 +953,7 @@ namespace Terminal.Gui { } } - void UpdateOffScreen () + public override void UpdateOffScreen () { contents = new int [Rows, Cols, 3]; for (int row = 0; row < Rows; row++) { diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs index 56f0c4cf2..f365fc2f1 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs @@ -922,6 +922,7 @@ namespace Terminal.Gui { { BufferWidth = width; BufferHeight = height; + _buffer = new char [BufferWidth, BufferHeight]; } // @@ -1060,7 +1061,8 @@ namespace Terminal.Gui { /// public static void SetWindowPosition (int left, int top) { - throw new NotImplementedException (); + WindowLeft = left; + WindowTop = top; } // @@ -1094,7 +1096,8 @@ namespace Terminal.Gui { /// public static void SetWindowSize (int width, int height) { - throw new NotImplementedException (); + WindowWidth = width; + WindowHeight = height; } // diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index caf479143..87c5907a3 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -37,23 +37,23 @@ namespace Terminal.Gui { /// internal override int [,,] Contents => contents; - void UpdateOffscreen () - { - int cols = Cols; - int rows = Rows; + //void UpdateOffscreen () + //{ + // int cols = Cols; + // int rows = Rows; - contents = new int [rows, cols, 3]; - for (int r = 0; r < rows; r++) { - for (int c = 0; c < cols; c++) { - contents [r, c, 0] = ' '; - contents [r, c, 1] = MakeColor (ConsoleColor.Gray, ConsoleColor.Black); - contents [r, c, 2] = 0; - } - } - dirtyLine = new bool [rows]; - for (int row = 0; row < rows; row++) - dirtyLine [row] = true; - } + // contents = new int [rows, cols, 3]; + // for (int r = 0; r < rows; r++) { + // for (int c = 0; c < cols; c++) { + // contents [r, c, 0] = ' '; + // contents [r, c, 1] = MakeColor (ConsoleColor.Gray, ConsoleColor.Black); + // contents [r, c, 2] = 0; + // } + // } + // dirtyLine = new bool [rows]; + // for (int row = 0; row < rows; row++) + // dirtyLine [row] = true; + //} static bool sync = false; @@ -89,13 +89,14 @@ namespace Terminal.Gui { FakeConsole.CursorLeft = Clip.X; needMove = true; } - } public override void AddRune (Rune rune) { rune = MakePrintable (rune); - if (Clip.Contains (ccol, crow)) { + var validClip = IsValidContent (ccol, crow, Clip); + + if (validClip) { if (needMove) { //MockConsole.CursorLeft = ccol; //MockConsole.CursorTop = crow; @@ -107,7 +108,20 @@ namespace Terminal.Gui { dirtyLine [crow] = true; } else needMove = true; + ccol++; + var runeWidth = Rune.ColumnWidth (rune); + if (runeWidth > 1) { + for (int i = 1; i < runeWidth; i++) { + if (validClip) { + contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 2] = 0; + } else { + break; + } + ccol++; + } + } //if (ccol == Cols) { // ccol = 0; // if (crow + 1 < Rows) @@ -218,26 +232,7 @@ namespace Terminal.Gui { { int top = Top; int left = Left; - int rows = Math.Min (Console.WindowHeight + top, Rows); - int cols = Cols; - - FakeConsole.CursorTop = 0; - FakeConsole.CursorLeft = 0; - for (int row = top; row < rows; row++) { - dirtyLine [row] = false; - for (int col = left; col < cols; col++) { - contents [row, col, 2] = 0; - var color = contents [row, col, 1]; - if (color != redrawColor) - SetColor (color); - FakeConsole.Write ((char)contents [row, col, 0]); - } - } - } - - public override void Refresh () - { - int rows = Rows; + int rows = Math.Min (FakeConsole.WindowHeight + top, Rows); int cols = Cols; var savedRow = FakeConsole.CursorTop; @@ -247,12 +242,21 @@ namespace Terminal.Gui { continue; dirtyLine [row] = false; for (int col = 0; col < cols; col++) { - if (contents [row, col, 2] != 1) - continue; - FakeConsole.CursorTop = row; FakeConsole.CursorLeft = col; - for (; col < cols && contents [row, col, 2] == 1; col++) { + for (; col < cols; col++) { + if (col > 0 && contents [row, col, 2] == 0 + && Rune.ColumnWidth ((char)contents [row, col - 1, 0]) > 1) { + FakeConsole.CursorLeft = col + 1; + continue; + } + + if (col < cols - 1 && Rune.ColumnWidth ((char)contents [row, col, 0]) > 1 + && (contents [row, col + 1, 2] == 1 || col == cols - 1)) { + + contents [row, col, 0] = ' '; + } + var color = contents [row, col, 1]; if (color != redrawColor) SetColor (color); @@ -266,6 +270,12 @@ namespace Terminal.Gui { FakeConsole.CursorLeft = savedCol; } + public override void Refresh () + { + UpdateScreen (); + UpdateCursor (); + } + Attribute currentAttribute; public override void SetAttribute (Attribute c) { @@ -388,6 +398,7 @@ namespace Terminal.Gui { Action keyHandler; Action keyUpHandler; + private CursorVisibility savedCursorVisibility; public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { @@ -427,31 +438,32 @@ namespace Terminal.Gui { /// public override bool GetCursorVisibility (out CursorVisibility visibility) { - if (FakeConsole.CursorVisible) { - visibility = CursorVisibility.Default; - } else { - visibility = CursorVisibility.Invisible; - } + visibility = FakeConsole.CursorVisible + ? CursorVisibility.Default + : CursorVisibility.Invisible; - return false; + return FakeConsole.CursorVisible; } /// public override bool SetCursorVisibility (CursorVisibility visibility) { - if (visibility == CursorVisibility.Invisible) { - FakeConsole.CursorVisible = false; - } else { - FakeConsole.CursorVisible = true; - } - - return false; + savedCursorVisibility = visibility; + return FakeConsole.CursorVisible = visibility == CursorVisibility.Default; } /// public override bool EnsureCursorVisibility () { - return false; + if (!(ccol >= 0 && crow >= 0 && ccol < Cols && crow < Rows)) { + GetCursorVisibility (out CursorVisibility cursorVisibility); + savedCursorVisibility = cursorVisibility; + SetCursorVisibility (CursorVisibility.Invisible); + return false; + } + + SetCursorVisibility (savedCursorVisibility); + return FakeConsole.CursorVisible; } public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) @@ -461,20 +473,24 @@ namespace Terminal.Gui { public void SetBufferSize (int width, int height) { - cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = width; - rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = height; + FakeConsole.SetBufferSize (width, height); + cols = width; + rows = height; + if (!HeightAsBuffer) { + SetWindowSize (width, height); + } ProcessResize (); } public void SetWindowSize (int width, int height) { - FakeConsole.WindowWidth = width; - FakeConsole.WindowHeight = height; - if (width > cols || !HeightAsBuffer) { - cols = FakeConsole.BufferWidth = width; - } - if (height > rows || !HeightAsBuffer) { - rows = FakeConsole.BufferHeight = height; + FakeConsole.SetWindowSize (width, height); + if (!HeightAsBuffer) { + if (width != cols || height != rows) { + SetBufferSize (width, height); + cols = width; + rows = height; + } } ProcessResize (); } @@ -482,12 +498,13 @@ namespace Terminal.Gui { public void SetWindowPosition (int left, int top) { if (HeightAsBuffer) { - this.left = FakeConsole.WindowLeft = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); - this.top = FakeConsole.WindowTop = Math.Max (Math.Min (top, Rows - Console.WindowHeight), 0); + this.left = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); + this.top = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0); } else if (this.left > 0 || this.top > 0) { - this.left = FakeConsole.WindowLeft = 0; - this.top = FakeConsole.WindowTop = 0; + this.left = 0; + this.top = 0; } + FakeConsole.SetWindowPosition (this.left, this.top); } void ProcessResize () @@ -500,14 +517,14 @@ namespace Terminal.Gui { void ResizeScreen () { if (!HeightAsBuffer) { - if (Console.WindowHeight > 0) { + if (FakeConsole.WindowHeight > 0) { // Can raise an exception while is still resizing. try { #pragma warning disable CA1416 - Console.CursorTop = 0; - Console.CursorLeft = 0; - Console.WindowTop = 0; - Console.WindowLeft = 0; + FakeConsole.CursorTop = 0; + FakeConsole.CursorLeft = 0; + FakeConsole.WindowTop = 0; + FakeConsole.WindowLeft = 0; #pragma warning restore CA1416 } catch (System.IO.IOException) { return; @@ -518,8 +535,8 @@ namespace Terminal.Gui { } else { try { #pragma warning disable CA1416 - Console.WindowLeft = Math.Max (Math.Min (left, Cols - Console.WindowWidth), 0); - Console.WindowTop = Math.Max (Math.Min (top, Rows - Console.WindowHeight), 0); + FakeConsole.WindowLeft = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); + FakeConsole.WindowTop = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0); #pragma warning restore CA1416 } catch (Exception) { return; @@ -532,7 +549,7 @@ namespace Terminal.Gui { dirtyLine = new bool [Rows]; } - void UpdateOffScreen () + public override void UpdateOffScreen () { // Can raise an exception while is still resizing. try { @@ -569,7 +586,17 @@ namespace Terminal.Gui { #region Unused public override void UpdateCursor () { - // + if (!EnsureCursorVisibility ()) + return; + + // Prevents the exception of size changing during resizing. + try { + if (ccol >= 0 && ccol < FakeConsole.BufferWidth && crow >= 0 && crow < FakeConsole.BufferHeight) { + FakeConsole.SetCursorPosition (ccol, crow); + } + } catch (System.IO.IOException) { + } catch (ArgumentOutOfRangeException) { + } } public override void StartReportingMouseMoves () diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 73fc92342..f97a697d1 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1216,6 +1216,7 @@ namespace Terminal.Gui { // Current row, and current col, tracked by Move/AddCh only int ccol, crow; + public override void Move (int col, int row) { ccol = col; @@ -1229,28 +1230,25 @@ namespace Terminal.Gui { } rune = MakePrintable (rune); var runeWidth = Rune.ColumnWidth (rune); - if (Clip.Contains (ccol, crow) && ccol + Math.Max (runeWidth, 1) <= Cols) { + var validClip = IsValidContent (ccol, crow, Clip); + + if (validClip) { contents [crow, ccol, 0] = (int)(uint)rune; contents [crow, ccol, 1] = currentAttribute; contents [crow, ccol, 2] = 1; - dirtyLine [crow] = true; - ccol++; - if (runeWidth > 1) { - for (int i = 1; i < runeWidth; i++) { - if (ccol < cols) { - contents [crow, ccol, 2] = 0; - } else { - break; - } - ccol++; - } - } - } else if (ccol > -1 && crow > -1 && ccol < cols && crow < rows) { - contents [crow, ccol, 2] = 1; dirtyLine [crow] = true; } + ccol++; + if (runeWidth > 1) { + if (validClip) { + contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 2] = 0; + } + ccol++; + } + //if (ccol == Cols) { // ccol = 0; // if (crow + 1 < Rows) @@ -1306,7 +1304,6 @@ namespace Terminal.Gui { cols = Console.WindowWidth; rows = Console.WindowHeight; - Clear (); ResizeScreen (); UpdateOffScreen (); @@ -1352,6 +1349,8 @@ namespace Terminal.Gui { Colors.Error.HotNormal = MakeColor (ConsoleColor.Black, ConsoleColor.White); Colors.Error.HotFocus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkRed); Colors.Error.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.White); + + Clear (); } void ResizeScreen () @@ -1395,28 +1394,29 @@ namespace Terminal.Gui { $";{Rows};{Cols}w"); } } - Clip = new Rect (0, 0, Cols, Rows); - - contents = new int [Rows, Cols, 3]; - dirtyLine = new bool [Rows]; } - void UpdateOffScreen () + public override void UpdateOffScreen () { - // Can raise an exception while is still resizing. - try { - for (int row = 0; row < rows; row++) { - for (int c = 0; c < cols; c++) { - contents [row, c, 0] = ' '; - contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; - contents [row, c, 2] = 0; - dirtyLine [row] = true; - } - } - } catch (IndexOutOfRangeException) { } + contents = new int [Rows, Cols, 3]; + dirtyLine = new bool [Rows]; - winChanging = false; + lock (contents) { + // Can raise an exception while is still resizing. + try { + for (int row = 0; row < rows; row++) { + for (int c = 0; c < cols; c++) { + contents [row, c, 0] = ' '; + contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; + contents [row, c, 2] = 0; + dirtyLine [row] = true; + } + } + } catch (IndexOutOfRangeException) { } + + winChanging = false; + } } public override Attribute MakeAttribute (Color fore, Color back) @@ -1427,6 +1427,7 @@ namespace Terminal.Gui { public override void Refresh () { UpdateScreen (); + UpdateCursor (); } int redrawAttr = -1; @@ -1443,7 +1444,7 @@ namespace Terminal.Gui { int rows = Math.Min (Console.WindowHeight + top, Rows); int cols = Cols; - Console.CursorVisible = false; + var savedCursorVisible = Console.CursorVisible = false; for (int row = top; row < rows; row++) { if (!dirtyLine [row]) { continue; @@ -1454,13 +1455,29 @@ namespace Terminal.Gui { if (Console.WindowHeight > 0 && !SetCursorPosition (col, row)) { return; } + var lastCol = -1; for (; col < cols; col++) { - if (contents [row, col, 2] != 1) { + if (col > 0 && contents [row, col, 2] == 0 + && Rune.ColumnWidth ((char)contents [row, col - 1, 0]) > 1) { + + if (col == cols - 1 && output.Length > 0) { + Console.CursorLeft = lastCol; + Console.Write (output); + } continue; } + + if (col < cols - 1 && Rune.ColumnWidth ((char)contents [row, col, 0]) > 1 + && (contents [row, col + 1, 2] == 1 || col == cols - 1)) { + + contents [row, col, 0] = ' '; + } + var attr = contents [row, col, 1]; if (attr != redrawAttr) { output.Append (WriteAttributes (attr)); + if (lastCol == -1) + lastCol = col; } if (AlwaysSetPosition && !SetCursorPosition (col, row)) { return; @@ -1469,6 +1486,8 @@ namespace Terminal.Gui { Console.Write ($"{output}{(char)contents [row, col, 0]}"); } else { output.Append ((char)contents [row, col, 0]); + if (lastCol == -1) + lastCol = col; } contents [row, col, 2] = 0; if (!AlwaysSetPosition && col == cols - 1) { @@ -1477,8 +1496,7 @@ namespace Terminal.Gui { } } } - Console.CursorVisible = true; - UpdateCursor (); + Console.CursorVisible = savedCursorVisible; } System.Text.StringBuilder WriteAttributes (int attr) @@ -1553,11 +1571,16 @@ namespace Terminal.Gui { } } + private CursorVisibility? savedCursorVisibility; + public override void UpdateCursor () { + if (!EnsureCursorVisibility ()) + return; + // Prevents the exception of size changing during resizing. try { - if (ccol >= 0 && ccol <= cols && crow >= 0 && crow <= rows) { + if (ccol >= 0 && ccol < Console.BufferWidth && crow >= 0 && crow < Console.BufferHeight) { Console.SetCursorPosition (ccol, crow); } } catch (System.IO.IOException) { @@ -1885,21 +1908,30 @@ namespace Terminal.Gui { /// public override bool GetCursorVisibility (out CursorVisibility visibility) { - visibility = CursorVisibility.Default; - - return false; + visibility = savedCursorVisibility ?? CursorVisibility.Default; + return visibility == CursorVisibility.Default; } + /// public override bool SetCursorVisibility (CursorVisibility visibility) { - return false; + savedCursorVisibility = visibility; + return Console.CursorVisible = visibility == CursorVisibility.Default; } /// public override bool EnsureCursorVisibility () { - return false; + if (!(ccol >= 0 && crow >= 0 && ccol < Cols && crow < Rows)) { + GetCursorVisibility (out CursorVisibility cursorVisibility); + savedCursorVisibility = cursorVisibility; + SetCursorVisibility (CursorVisibility.Invisible); + return false; + } + + SetCursorVisibility (savedCursorVisibility ?? CursorVisibility.Default); + return savedCursorVisibility == CursorVisibility.Default; } public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 50c717fbe..01da78595 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1443,7 +1443,7 @@ namespace Terminal.Gui { WinConsole.ForceRefreshCursorVisibility (); } - void UpdateOffScreen () + public override void UpdateOffScreen () { contents = new int [rows, cols, 3]; for (int row = 0; row < rows; row++) { @@ -1468,9 +1468,26 @@ namespace Terminal.Gui { public override void AddRune (Rune rune) { rune = MakePrintable (rune); + var runeWidth = Rune.ColumnWidth (rune); var position = crow * Cols + ccol; + var validClip = IsValidContent (ccol, crow, Clip); - if (Clip.Contains (ccol, crow)) { + if (validClip) { + 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 < Cols - 1 && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { + + var prevPosition = crow * Cols + ccol + 1; + OutputBuffer [prevPosition].Char.UnicodeChar = (char)' '; + contents [crow, ccol + 1, 0] = (int)(uint)' '; + } else if (rune != ' ') { + + } OutputBuffer [position].Attributes = (ushort)currentAttribute; OutputBuffer [position].Char.UnicodeChar = (char)rune; contents [crow, ccol, 0] = (int)(uint)rune; @@ -1480,17 +1497,18 @@ namespace Terminal.Gui { } ccol++; - var runeWidth = Rune.ColumnWidth (rune); if (runeWidth > 1) { - for (int i = 1; i < runeWidth; i++) { - AddStr (" "); + if (validClip) { + position = crow * Cols + ccol; + OutputBuffer [position].Attributes = (ushort)currentAttribute; + OutputBuffer [position].Char.UnicodeChar = '\0'; + contents [crow, ccol, 0] = (int)(uint)'\0'; + contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 2] = 0; } + ccol++; } - //if (ccol == Cols) { - // ccol = 0; - // if (crow + 1 < Rows) - // crow++; - //} + if (sync) UpdateScreen (); } diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 4ea70ea8b..ce53de12f 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -701,6 +701,16 @@ namespace Terminal.Gui { } } + /// + /// Ensures that the column and line are in a valid range from the size of the driver. + /// + /// The column. + /// The row. + /// The clip. + /// trueif it's a valid range,falseotherwise. + public bool IsValidContent (int col, int row, Rect clip) => + col >= 0 && row >= 0 && col < Cols && row < Rows && clip.Contains (col, row); + /// /// Adds the specified /// @@ -751,6 +761,11 @@ namespace Terminal.Gui { /// public abstract void End (); + /// + /// Reset and recreate the contents and the driver buffer. + /// + public abstract void UpdateOffScreen (); + /// /// Redraws the physical screen with the contents that have been queued up via any of the printing commands. /// @@ -824,7 +839,8 @@ namespace Terminal.Gui { if (!ustring.IsNullOrEmpty (title) && width > 4 && region.Y + paddingTop <= region.Y + paddingBottom) { Move (region.X + 1 + paddingLeft, region.Y + paddingTop); AddRune (' '); - var str = title.RuneCount >= width ? title [0, width - 2] : title; + var str = title.Sum (r => Math.Max (Rune.ColumnWidth (r), 1)) >= width + ? TextFormatter.Format (title, width - 2, false, false) [0] : title; AddStr (str); AddRune (' '); } diff --git a/UnitTests/ConsoleDriverTests.cs b/UnitTests/ConsoleDriverTests.cs index fc4475bf9..20f56fab1 100644 --- a/UnitTests/ConsoleDriverTests.cs +++ b/UnitTests/ConsoleDriverTests.cs @@ -339,10 +339,10 @@ namespace Terminal.Gui.ConsoleDrivers { Assert.Equal (25, Application.Driver.Rows); Assert.Equal (120, Console.BufferWidth); Assert.Equal (25, Console.BufferHeight); - Assert.Equal (120, Console.WindowWidth); + Assert.Equal (80, Console.WindowWidth); Assert.Equal (25, Console.WindowHeight); driver.SetWindowPosition (121, 25); - Assert.Equal (0, Console.WindowLeft); + Assert.Equal (40, Console.WindowLeft); Assert.Equal (0, Console.WindowTop); driver.SetWindowSize (90, 25); @@ -388,17 +388,19 @@ namespace Terminal.Gui.ConsoleDrivers { Assert.Equal (0, Console.WindowLeft); Assert.Equal (0, Console.WindowTop); - // MockDriver will now be sets to 120x25 + // MockDriver will now be sets to 80x40 driver.SetBufferSize (80, 40); Assert.Equal (80, Application.Driver.Cols); Assert.Equal (40, Application.Driver.Rows); Assert.Equal (80, Console.BufferWidth); Assert.Equal (40, Console.BufferHeight); Assert.Equal (80, Console.WindowWidth); - Assert.Equal (40, Console.WindowHeight); - driver.SetWindowPosition (80, 40); + Assert.Equal (25, Console.WindowHeight); Assert.Equal (0, Console.WindowLeft); Assert.Equal (0, Console.WindowTop); + driver.SetWindowPosition (80, 40); + Assert.Equal (0, Console.WindowLeft); + Assert.Equal (15, Console.WindowTop); driver.SetWindowSize (80, 20); Assert.Equal (80, Application.Driver.Cols); @@ -407,6 +409,8 @@ namespace Terminal.Gui.ConsoleDrivers { Assert.Equal (40, Console.BufferHeight); Assert.Equal (80, Console.WindowWidth); Assert.Equal (20, Console.WindowHeight); + Assert.Equal (0, Console.WindowLeft); + Assert.Equal (15, Console.WindowTop); driver.SetWindowPosition (80, 41); Assert.Equal (0, Console.WindowLeft); Assert.Equal (20, Console.WindowTop);