diff --git a/Examples/UICatalog/Resources/config.json b/Examples/UICatalog/Resources/config.json
index 74586e878..cc7b5009f 100644
--- a/Examples/UICatalog/Resources/config.json
+++ b/Examples/UICatalog/Resources/config.json
@@ -9,6 +9,7 @@
"Themes": [
{
"Hot Dog Stand": {
+ "Glyphs.WideGlyphReplacement": "①",
"Schemes": [
{
"Runnable": {
@@ -134,7 +135,7 @@
}
},
{
- "UI Catalog Theme": {
+ "UI Catalog": {
"Window.DefaultShadow": "Transparent",
"Button.DefaultShadow": "None",
"CheckBox.DefaultHighlightStates": "In, Pressed, PressedOutside",
diff --git a/Examples/UICatalog/Scenarios/WideGlyphs.cs b/Examples/UICatalog/Scenarios/WideGlyphs.cs
index 9b2e80d26..771355f1a 100644
--- a/Examples/UICatalog/Scenarios/WideGlyphs.cs
+++ b/Examples/UICatalog/Scenarios/WideGlyphs.cs
@@ -34,10 +34,6 @@ public sealed class WideGlyphs : Scenario
AutoSelectAdornments = false,
ShowViewIdentifier = true
};
- adornmentsEditor.ExpanderButton.Accepting += (sender, args) =>
- {
- //adornmentsEditor.ExpanderButton.Collapsed = args.NewValue;
- };
appWindow.Add (adornmentsEditor);
ViewportSettingsEditor viewportSettingsEditor = new ()
diff --git a/Terminal.Gui/Drawing/Glyphs.cs b/Terminal.Gui/Drawing/Glyphs.cs
index 71336009d..f83666006 100644
--- a/Terminal.Gui/Drawing/Glyphs.cs
+++ b/Terminal.Gui/Drawing/Glyphs.cs
@@ -26,6 +26,11 @@ public class Glyphs
// IMPORTANT: Configuration Manager test SaveDefaults uses this class to generate the default config file
// IMPORTANT: in ./UnitTests/bin/Debug/netX.0/config.json
+ /// Unicode replacement character; used by Drivers when rendering in cases where a wide glyph can't
+ /// be output because it would be clipped. Defaults to ' ' (Space).
+ [ConfigurationProperty (Scope = typeof (ThemeScope))]
+ public static Rune WideGlyphReplacement { get; set; } = (Rune)' ';
+
/// File icon. Defaults to ☰ (Trigram For Heaven)
[ConfigurationProperty (Scope = typeof (ThemeScope))]
public static Rune File { get; set; } = (Rune)'☰';
diff --git a/Terminal.Gui/Drivers/DriverImpl.cs b/Terminal.Gui/Drivers/DriverImpl.cs
index 2368dd631..ed61da17a 100644
--- a/Terminal.Gui/Drivers/DriverImpl.cs
+++ b/Terminal.Gui/Drivers/DriverImpl.cs
@@ -146,6 +146,9 @@ internal class DriverImpl : IDriver
private readonly IOutput _output;
+ ///
+ public IOutputBuffer GetOutputBuffer () => OutputBuffer;
+
public IOutput GetOutput () => _output;
private readonly IInputProcessor _inputProcessor;
diff --git a/Terminal.Gui/Drivers/IDriver.cs b/Terminal.Gui/Drivers/IDriver.cs
index 41ff4d091..eda8841c8 100644
--- a/Terminal.Gui/Drivers/IDriver.cs
+++ b/Terminal.Gui/Drivers/IDriver.cs
@@ -64,7 +64,13 @@ public interface IDriver : IDisposable
IInputProcessor GetInputProcessor ();
///
- /// Gets the output handler responsible for writing to the terminal.
+ /// Gets the containing the buffered screen contents.
+ ///
+ ///
+ IOutputBuffer GetOutputBuffer ();
+
+ ///
+ /// Gets the responsible for writing to the terminal.
///
IOutput GetOutput ();
diff --git a/Terminal.Gui/Drivers/IOutputBuffer.cs b/Terminal.Gui/Drivers/IOutputBuffer.cs
index 3344d0ba8..cf096b834 100644
--- a/Terminal.Gui/Drivers/IOutputBuffer.cs
+++ b/Terminal.Gui/Drivers/IOutputBuffer.cs
@@ -1,5 +1,4 @@
-
-namespace Terminal.Gui.Drivers;
+namespace Terminal.Gui.Drivers;
///
/// Represents the desired screen state for console rendering. This interface provides methods for building up
@@ -128,4 +127,15 @@ public interface IOutputBuffer
/// Changing this may have unexpected consequences.
///
int Top { get; set; }
+
+ ///
+ /// Sets the replacement character that will be used when a wide glyph (double-width character) cannot fit in the
+ /// available space.
+ /// If not set, the default will be .
+ ///
+ ///
+ /// The character used when the first column of a wide character is invalid (for example, when it is overlapped by the
+ /// trailing half of a previous wide character).
+ ///
+ void SetWideGlyphReplacement (Rune column1ReplacementChar);
}
diff --git a/Terminal.Gui/Drivers/OutputBufferImpl.cs b/Terminal.Gui/Drivers/OutputBufferImpl.cs
index be03a26c6..9f0b074fe 100644
--- a/Terminal.Gui/Drivers/OutputBufferImpl.cs
+++ b/Terminal.Gui/Drivers/OutputBufferImpl.cs
@@ -65,6 +65,14 @@ public class OutputBufferImpl : IOutputBuffer
/// The topmost row in the terminal.
public virtual int Top { get; set; } = 0;
+ private Rune _column1ReplacementChar = Glyphs.WideGlyphReplacement;
+
+ ///
+ public void SetWideGlyphReplacement (Rune column1ReplacementChar)
+ {
+ _column1ReplacementChar = column1ReplacementChar;
+ }
+
///
/// Indicates which lines have been modified and need to be redrawn.
///
@@ -205,7 +213,7 @@ public class OutputBufferImpl : IOutputBuffer
{
if (col > 0 && Contents! [row, col - 1].Grapheme.GetColumns () > 1)
{
- Contents [row, col - 1].Grapheme = Rune.ReplacementChar.ToString ();
+ Contents [row, col - 1].Grapheme = _column1ReplacementChar.ToString ();
Contents [row, col - 1].IsDirty = true;
}
}
@@ -273,17 +281,7 @@ public class OutputBufferImpl : IOutputBuffer
if (!Clip!.Contains (col + 1, row))
{
// Second column is outside clip - can't fit wide char here
- Contents! [row, col].Grapheme = Rune.ReplacementChar.ToString ();
- }
- else if (!Clip.Contains (col, row))
- {
- // First column is outside clip but second isn't
- // Mark second column as replacement to indicate partial overlap
- if (col + 1 < Cols)
- {
- Contents! [row, col + 1].Grapheme = Rune.ReplacementChar.ToString ();
- Contents! [row, col + 1].IsDirty = true;
- }
+ Contents! [row, col].Grapheme = _column1ReplacementChar.ToString ();
}
else
{
diff --git a/Terminal.Gui/ViewBase/View.Mouse.cs b/Terminal.Gui/ViewBase/View.Mouse.cs
index a1df6dd59..88cc4579d 100644
--- a/Terminal.Gui/ViewBase/View.Mouse.cs
+++ b/Terminal.Gui/ViewBase/View.Mouse.cs
@@ -256,7 +256,7 @@ public partial class View // Mouse APIs
/// -
///
/// Invokes commands bound to mouse clicks via
- /// (default: → event)
+ /// (default: → event)
///
///
/// -
@@ -295,7 +295,7 @@ public partial class View // Mouse APIs
///
///
///
- ///
+ ///
///
///
public bool? NewMouseEvent (MouseEventArgs mouseEvent)
@@ -414,8 +414,8 @@ public partial class View // Mouse APIs
///
/// INTERNAL: For cases where the view is grabbed and the mouse is pressed, this method handles the pressed events from
/// the driver.
- /// When is set, this method will raise the Clicked/Selecting event
- /// via each time it is called (after the first time the mouse is pressed).
+ /// When is set, this method will raise the Clicked/Activating event
+ /// via each time it is called (after the first time the mouse is pressed).
///
///
/// , if processing should stop, otherwise.
@@ -531,7 +531,7 @@ public partial class View // Mouse APIs
///
/// INTERNAL API: Converts mouse click events into s by invoking the commands bound
/// to the mouse button via . By default, all mouse clicks are bound to
- /// which raises the event.
+ /// which raises the event.
///
protected bool RaiseCommandsBoundToMouse (MouseEventArgs args)
{
diff --git a/Tests/UnitTests/View/Draw/ClipTests.cs b/Tests/UnitTests/View/Draw/ClipTests.cs
index 565795f85..bbeff3518 100644
--- a/Tests/UnitTests/View/Draw/ClipTests.cs
+++ b/Tests/UnitTests/View/Draw/ClipTests.cs
@@ -178,6 +178,7 @@ public class ClipTests (ITestOutputHelper _output)
public void Clipping_Wide_Runes ()
{
Application.Driver!.SetScreenSize (30, 1);
+ Application.Driver!.GetOutputBuffer ().SetWideGlyphReplacement ((Rune)'①');
var top = new View
{
@@ -231,9 +232,9 @@ public class ClipTests (ITestOutputHelper _output)
// 012 34 56 78 90 12 34 56 78 90 12 34 56 78
// │こ れ は 広 い ル ー ン ラ イ ン で す 。
// 01 2345678901234 56 78 90 12 34 56
- // │� |0123456989│� ン ラ イ ン で す 。
+ // │① |0123456989│① ン ラ イ ン で す 。
expectedOutput = """
- │�│0123456789│ ンラインです。
+ │①│0123456789│ ンラインです。
""";
DriverAssert.AssertDriverContentsWithFrameAre (expectedOutput, _output);
diff --git a/Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs b/Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs
index 4ec35f770..522f917c6 100644
--- a/Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs
+++ b/Tests/UnitTestsParallelizable/Drivers/AddRuneTests.cs
@@ -181,9 +181,9 @@ public class AddRuneTests (ITestOutputHelper output) : FakeDriverBase
{
IDriver? driver = CreateFakeDriver ();
driver.SetScreenSize (6, 3);
+ driver.GetOutputBuffer ().SetWideGlyphReplacement ((Rune)'①');
driver!.Clip = new (driver.Screen);
-
driver.Move (1, 0);
driver.AddStr ("┌");
driver.Move (2, 0);
@@ -197,14 +197,14 @@ public class AddRuneTests (ITestOutputHelper output) : FakeDriverBase
DriverAssert.AssertDriverContentsAre (
"""
- �┌─┐🍎
+ ①┌─┐🍎
""",
output,
driver);
driver.Refresh ();
- DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;0;0;0m\x1b[48;2;0;0;0m�┌─┐🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m",
+ DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;0;0;0m\x1b[48;2;0;0;0m①┌─┐🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m",
output, driver);
}
}
diff --git a/Tests/UnitTestsParallelizable/Drivers/DriverTests.cs b/Tests/UnitTestsParallelizable/Drivers/DriverTests.cs
index 9d88eb730..371331deb 100644
--- a/Tests/UnitTestsParallelizable/Drivers/DriverTests.cs
+++ b/Tests/UnitTestsParallelizable/Drivers/DriverTests.cs
@@ -1,4 +1,5 @@
#nullable enable
+using System.Text;
using UnitTests;
using Xunit.Abstractions;
@@ -104,6 +105,7 @@ public class DriverTests (ITestOutputHelper output) : FakeDriverBase
IApplication? app = Application.Create ();
app.Init (driverName);
IDriver driver = app.Driver!;
+ driver.GetOutputBuffer ().SetWideGlyphReplacement ((Rune)'①');
// Need to force "windows" driver to override legacy console mode for this test
driver.IsLegacyConsole = false;
@@ -127,14 +129,14 @@ public class DriverTests (ITestOutputHelper output) : FakeDriverBase
DriverAssert.AssertDriverContentsAre (
"""
- �┌─┐🍎
+ ①┌─┐🍎
""",
output,
driver);
driver.Refresh ();
- DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;0;0;0m\x1b[48;2;0;0;0m�┌─┐🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m",
+ DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;0;0;0m\x1b[48;2;0;0;0m①┌─┐🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m",
output, driver);
}
}
diff --git a/Tests/UnitTestsParallelizable/Drivers/OutputBaseTests.cs b/Tests/UnitTestsParallelizable/Drivers/OutputBaseTests.cs
index 19c6ddcec..9931551b0 100644
--- a/Tests/UnitTestsParallelizable/Drivers/OutputBaseTests.cs
+++ b/Tests/UnitTestsParallelizable/Drivers/OutputBaseTests.cs
@@ -1,4 +1,7 @@
-namespace DriverTests;
+using System.Text;
+using Terminal.Gui.Drivers;
+
+namespace DriverTests;
public class OutputBaseTests
{
@@ -161,6 +164,8 @@ public class OutputBaseTests
// FakeOutput exposes this because it's in test scope
var output = new FakeOutput { IsLegacyConsole = isLegacyConsole };
IOutputBuffer buffer = output.GetLastBuffer ()!;
+ buffer.SetWideGlyphReplacement ((Rune)'①');
+
buffer.SetSize (3, 1);
// Write '🦮' at col 0 and 'A' at col 2
@@ -209,7 +214,7 @@ public class OutputBaseTests
output.Write (buffer);
- Assert.Contains ("�", output.GetLastOutput ());
+ Assert.Contains ("①", output.GetLastOutput ());
Assert.Contains ("X", output.GetLastOutput ());
// Dirty flags cleared for the written cells
diff --git a/Tests/UnitTestsParallelizable/Drivers/OutputBufferWideCharTests.cs b/Tests/UnitTestsParallelizable/Drivers/OutputBufferWideCharTests.cs
index d8da44b65..4e6a596af 100644
--- a/Tests/UnitTestsParallelizable/Drivers/OutputBufferWideCharTests.cs
+++ b/Tests/UnitTestsParallelizable/Drivers/OutputBufferWideCharTests.cs
@@ -1,14 +1,14 @@
using System.Text;
-using Xunit.Abstractions;
namespace DriverTests;
///
/// Tests for https://github.com/gui-cs/Terminal.Gui/issues/4466.
/// These tests validate that FillRect properly handles wide characters when overlapping existing content.
-/// Specifically, they ensure that wide characters are properly invalidated and replaced when a MessageBox border or similar UI element is drawn over them, preventing visual corruption.
+/// Specifically, they ensure that wide characters are properly invalidated and replaced when a MessageBox border or
+/// similar UI element is drawn over them, preventing visual corruption.
///
-public class OutputBufferWideCharTests (ITestOutputHelper output)
+public class OutputBufferWideCharTests
{
///
/// Tests that FillRect properly invalidates wide characters when overwriting them.
@@ -100,7 +100,7 @@ public class OutputBufferWideCharTests (ITestOutputHelper output)
// With the fix: The original wide character at col 2 should be invalidated
// because we're overwriting its second column
Assert.True (buffer.Contents [1, 2].IsDirty, "Wide char at col 2 should be invalidated when its second column is overwritten");
- Assert.Equal (buffer.Contents [1, 2].Grapheme, Rune.ReplacementChar.ToString ());
+ Assert.Equal (buffer.Contents [1, 2].Grapheme, Glyphs.WideGlyphReplacement.ToString ());
Assert.Equal ("│", buffer.Contents [1, 3].Grapheme);
Assert.True (buffer.Contents [1, 3].IsDirty);
@@ -154,7 +154,7 @@ public class OutputBufferWideCharTests (ITestOutputHelper output)
// The second character "好" at col 7 had its second column overwritten
// so it should be replaced with replacement char
- Assert.Equal (buffer.Contents [3, 7].Grapheme, Rune.ReplacementChar.ToString ());
+ Assert.Equal (buffer.Contents [3, 7].Grapheme, Glyphs.WideGlyphReplacement.ToString ());
Assert.True (buffer.Contents [3, 7].IsDirty, "Invalidated wide char should be marked dirty");
// The border should be drawn at col 8
@@ -356,4 +356,189 @@ public class OutputBufferWideCharTests (ITestOutputHelper output)
buffer.Contents [2, 7].IsDirty,
"Adjacent cell should be dirty after wide char replacement");
}
+
+ ///
+ /// Tests the edge case where a wide character's first column is outside the clip region
+ /// but the second column is inside.
+ /// IMPORTANT: This test documents that the code path in WriteWideGrapheme where:
+ /// - !Clip.Contains(col, row) is true (first column outside)
+ /// - Clip.Contains(col + 1, row) is true (second column inside)
+ /// is CURRENTLY UNREACHABLE because IsValidLocation checks Clip.Contains(col, row) and
+ /// returns false before WriteWideGrapheme is called. This test verifies the current behavior
+ /// (nothing is written when first column is outside clip).
+ /// If the behavior should change to write the second column with a replacement character,
+ /// the logic in IsValidLocation or AddGrapheme needs to be modified.
+ ///
+ [Fact]
+ [Trait ("Category", "Output")]
+ public void AddStr_WideChar_FirstColumnOutsideClip_SecondColumnInside_CurrentBehavior ()
+ {
+ // Arrange
+ OutputBufferImpl buffer = new ()
+ {
+ Rows = 5,
+ Cols = 10,
+ CurrentAttribute = new (Color.White, Color.Black)
+ };
+
+ // Set custom replacement characters to verify they're being used
+ Rune customColumn1Replacement = new ('◄');
+ Rune customColumn2Replacement = new ('►');
+ buffer.SetWideGlyphReplacement (customColumn1Replacement);
+
+ // Set clip region that starts at column 3 (odd column)
+ // This creates a scenario where col 2 is outside clip, but col 3 is inside
+ buffer.Clip = new (new (3, 1, 5, 3));
+
+ // Clear initial contents to ensure clean state
+ for (var r = 0; r < buffer.Rows; r++)
+ {
+ for (var c = 0; c < buffer.Cols; c++)
+ {
+ buffer.Contents! [r, c].IsDirty = false;
+ buffer.Contents [r, c].Grapheme = " ";
+ }
+ }
+
+ // Act - Try to draw a wide character at column 2
+ // Column 2 is outside clip, but column 3 is inside clip
+ buffer.Move (2, 1);
+ buffer.AddStr ("你"); // Chinese character "you", 2 columns wide
+
+ // Assert
+ // CURRENT BEHAVIOR: IsValidLocation returns false when col 2 is outside clip,
+ // so NOTHING is written - neither column 2 nor column 3
+ Assert.Equal (" ", buffer.Contents! [1, 2].Grapheme);
+ Assert.False (buffer.Contents [1, 2].IsDirty, "Cell outside clip should not be marked dirty");
+
+ // Column 3 is also not written because IsValidLocation returned false
+ // The code path in WriteWideGrapheme that would write the replacement char
+ // to column 3 is never reached
+ Assert.Equal (" ", buffer.Contents [1, 3].Grapheme);
+
+ Assert.False (
+ buffer.Contents [1, 3].IsDirty,
+ "Currently, second column is not written when first column is outside clip");
+
+ // Verify Col has been advanced by only 1 (not by the wide character width)
+ // because the grapheme was not validated/processed when IsValidLocation returned false
+ Assert.Equal (3, buffer.Col);
+ }
+
+ ///
+ /// Tests the complementary case: wide character's second column is outside clip
+ /// but first column is inside. This should use the column 1 replacement character.
+ ///
+ [Fact]
+ [Trait ("Category", "Output")]
+ public void AddStr_WideChar_SecondColumnOutsideClip_FirstColumnInside_UsesColumn1Replacement ()
+ {
+ // Arrange
+ OutputBufferImpl buffer = new ()
+ {
+ Rows = 5,
+ Cols = 10,
+ CurrentAttribute = new (Color.White, Color.Black)
+ };
+
+ // Set custom replacement characters
+ Rune customColumn1Replacement = new ('◄');
+ Rune customColumn2Replacement = new ('►');
+ buffer.SetWideGlyphReplacement (customColumn1Replacement);
+
+ // Set clip region that ends at column 6 (even column)
+ // This creates a scenario where col 5 is inside, but col 6 is outside
+ buffer.Clip = new (new (0, 1, 6, 3));
+
+ // Clear initial contents
+ for (var r = 0; r < buffer.Rows; r++)
+ {
+ for (var c = 0; c < buffer.Cols; c++)
+ {
+ buffer.Contents! [r, c].IsDirty = false;
+ buffer.Contents [r, c].Grapheme = " ";
+ }
+ }
+
+ // Act - Try to draw a wide character at column 5
+ // Column 5 is inside clip, but column 6 is outside clip
+ buffer.Move (5, 1);
+ buffer.AddStr ("好"); // Chinese character, 2 columns wide
+
+ // Assert
+ // The first column (col 5) is inside clip but second column (6) is outside
+ // Should use column 1 replacement char to indicate it can't fit
+ Assert.Equal (
+ customColumn1Replacement.ToString (),
+ buffer.Contents! [1, 5].Grapheme);
+
+ Assert.True (
+ buffer.Contents [1, 5].IsDirty,
+ "First column should be marked dirty with replacement char when second column is clipped");
+
+ // The second column is outside clip boundaries entirely
+ Assert.Equal (" ", buffer.Contents [1, 6].Grapheme);
+ Assert.False (buffer.Contents [1, 6].IsDirty, "Cell outside clip should not be modified");
+
+ // Verify Col has been advanced by 2 (wide character width)
+ Assert.Equal (7, buffer.Col);
+ }
+
+ ///
+ /// Tests that when both columns of a wide character are inside the clip,
+ /// the character is drawn normally without replacement characters.
+ ///
+ [Fact]
+ [Trait ("Category", "Output")]
+ public void AddStr_WideChar_BothColumnsInsideClip_DrawsNormally ()
+ {
+ // Arrange
+ OutputBufferImpl buffer = new ()
+ {
+ Rows = 5,
+ Cols = 10,
+ CurrentAttribute = new (Color.White, Color.Black)
+ };
+
+ // Set custom replacement characters (should NOT be used in this case)
+ Rune customColumn1Replacement = new ('◄');
+ Rune customColumn2Replacement = new ('►');
+ buffer.SetWideGlyphReplacement (customColumn1Replacement);
+
+ // Set clip region that includes columns 2-7
+ buffer.Clip = new (new (2, 1, 6, 3));
+
+ // Clear initial contents
+ for (var r = 0; r < buffer.Rows; r++)
+ {
+ for (var c = 0; c < buffer.Cols; c++)
+ {
+ buffer.Contents! [r, c].IsDirty = false;
+ buffer.Contents [r, c].Grapheme = " ";
+ }
+ }
+
+ // Act - Draw a wide character at column 4 (both 4 and 5 are inside clip)
+ buffer.Move (4, 1);
+ buffer.AddStr ("山"); // Chinese character "mountain", 2 columns wide
+
+ // Assert
+ // Both columns are inside clip, so the wide character should be drawn normally
+ Assert.Equal ("山", buffer.Contents! [1, 4].Grapheme);
+ Assert.True (buffer.Contents [1, 4].IsDirty, "First column should be marked dirty");
+
+ // The second column should NOT be marked dirty by WriteWideGrapheme
+ // The wide glyph naturally renders across both columns without modifying column N+1
+ // See: https://github.com/gui-cs/Terminal.Gui/issues/4258
+ Assert.False (
+ buffer.Contents [1, 5].IsDirty,
+ "Adjacent cell should NOT be marked dirty when writing wide char (see #4258)");
+
+ // Verify no replacement characters were used
+ Assert.NotEqual (customColumn1Replacement.ToString (), buffer.Contents [1, 4].Grapheme);
+ Assert.NotEqual (customColumn2Replacement.ToString (), buffer.Contents [1, 5].Grapheme);
+
+ // Verify Col has been advanced by 2
+ Assert.Equal (6, buffer.Col);
+ }
}
diff --git a/Tests/UnitTestsParallelizable/ViewBase/Draw/ViewDrawingClippingTests.cs b/Tests/UnitTestsParallelizable/ViewBase/Draw/ViewDrawingClippingTests.cs
index 7774d1886..e24280295 100644
--- a/Tests/UnitTestsParallelizable/ViewBase/Draw/ViewDrawingClippingTests.cs
+++ b/Tests/UnitTestsParallelizable/ViewBase/Draw/ViewDrawingClippingTests.cs
@@ -574,6 +574,7 @@ public class ViewDrawingClippingTests (ITestOutputHelper output) : FakeDriverBas
};
superView.Add (viewWithBorderAtX0, viewWithBorderAtX1, viewWithBorderAtX2);
+ driver.GetOutputBuffer ().SetWideGlyphReplacement ((Rune)'①');
app.Begin (superView);
// Begin calls LayoutAndDraw, so no need to call it again here
// app.LayoutAndDraw();
@@ -585,9 +586,9 @@ public class ViewDrawingClippingTests (ITestOutputHelper output) : FakeDriverBas
┆viewWithBorderAtX0┆🍎🍎🍎
└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘🍎🍎🍎
🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎
- �┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐ 🍎🍎
- �┆viewWithBorderAtX1┆ 🍎🍎
- �└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘ 🍎🍎
+ ①┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐ 🍎🍎
+ ①┆viewWithBorderAtX1┆ 🍎🍎
+ ①└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘ 🍎🍎
🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎
🍎┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐🍎🍎
🍎┆viewWithBorderAtX2┆🍎🍎
@@ -597,7 +598,7 @@ public class ViewDrawingClippingTests (ITestOutputHelper output) : FakeDriverBas
output,
driver);
- DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m┆viewWithBorderAtX0┆🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m�┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐ 🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m�┆viewWithBorderAtX1┆ 🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m�└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘ 🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎┆viewWithBorderAtX2┆🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m",
+ DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m┆viewWithBorderAtX0┆🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m①┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐ 🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m①┆viewWithBorderAtX1┆ 🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m①└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘ 🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎┆viewWithBorderAtX2┆🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m \x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎\x1b[38;2;255;255;255m\x1b[48;2;0;0;0m",
output, driver);
DriverImpl? driverImpl = driver as DriverImpl;
@@ -617,9 +618,9 @@ public class ViewDrawingClippingTests (ITestOutputHelper output) : FakeDriverBas
┆viewWithBorderAtX0┆🍎🍎🍎
└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘🍎🍎🍎
🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎
- �┌──────────────────┐ 🍎🍎
- �│viewWithBorderAtX1│ 🍎🍎
- �└──────────────────┘ 🍎🍎
+ ①┌──────────────────┐ 🍎🍎
+ ①│viewWithBorderAtX1│ 🍎🍎
+ ①└──────────────────┘ 🍎🍎
🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎🍎
🍎┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐🍎🍎
🍎┆viewWithBorderAtX2┆🍎🍎
@@ -675,18 +676,19 @@ public class ViewDrawingClippingTests (ITestOutputHelper output) : FakeDriverBas
};
superView.Add (viewWithBorder);
+ driver.GetOutputBuffer ().SetWideGlyphReplacement ((Rune)'①');
app.Begin (superView);
DriverAssert.AssertDriverContentsAre (
"""
- �┌─┐🍎
- �│X│🍎
- �└─┘🍎
+ ①┌─┐🍎
+ ①│X│🍎
+ ①└─┘🍎
""",
output,
driver);
- DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;95;158;160m\x1b[48;2;54;69;79m�┌─┐🍎�│X│🍎�└─┘🍎",
+ DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;95;158;160m\x1b[48;2;54;69;79m①┌─┐🍎①│X│🍎①└─┘🍎",
output, driver);
DriverImpl? driverImpl = driver as DriverImpl;
@@ -738,19 +740,21 @@ public class ViewDrawingClippingTests (ITestOutputHelper output) : FakeDriverBas
Height = 3
};
+ driver.GetOutputBuffer ().SetWideGlyphReplacement ((Rune)'①');
+
superView.Add (viewWithBorder);
app.Begin (superView);
DriverAssert.AssertDriverContentsAre (
"""
- 🍎�┌─┐
- 🍎�│X│
- 🍎�└─┘
+ 🍎①┌─┐
+ 🍎①│X│
+ 🍎①└─┘
""",
output,
driver);
- DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎�┌─┐🍎�│X│🍎�└─┘",
+ DriverAssert.AssertDriverOutputIs (@"\x1b[38;2;95;158;160m\x1b[48;2;54;69;79m🍎①┌─┐🍎①│X│🍎①└─┘",
output, driver);
DriverImpl? driverImpl = driver as DriverImpl;