diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs index 46b7f5033..f2a08ee85 100644 --- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs @@ -217,7 +217,7 @@ public abstract class ConsoleDriver : IConsoleDriver if (Contents [Row, Col - 1].CombiningMarks.Count > 0) { // Just add this mark to the list - Contents [Row, Col - 1].CombiningMarks.Add (rune); + Contents [Row, Col - 1].AddCombiningMark (rune); // Ignore. Don't move to next column (let the driver figure out what to do). } @@ -240,7 +240,7 @@ public abstract class ConsoleDriver : IConsoleDriver else { // It didn't normalize. Add it to the Cell to left's CM list - Contents [Row, Col - 1].CombiningMarks.Add (rune); + Contents [Row, Col - 1].AddCombiningMark (rune); // Ignore. Don't move to next column (let the driver figure out what to do). } @@ -298,7 +298,7 @@ public abstract class ConsoleDriver : IConsoleDriver else if (!Clip.Contains (Col, Row)) { // Our 1st column is outside the clip, so we can't display a wide character. - Contents [Row, Col+1].Rune = Rune.ReplacementChar; + Contents [Row, Col + 1].Rune = Rune.ReplacementChar; } else { diff --git a/Terminal.Gui/ConsoleDrivers/V2/OutputBuffer.cs b/Terminal.Gui/ConsoleDrivers/V2/OutputBuffer.cs index 44d137ff3..248c266fa 100644 --- a/Terminal.Gui/ConsoleDrivers/V2/OutputBuffer.cs +++ b/Terminal.Gui/ConsoleDrivers/V2/OutputBuffer.cs @@ -164,7 +164,7 @@ public class OutputBuffer : IOutputBuffer if (Contents [Row, Col - 1].CombiningMarks.Count > 0) { // Just add this mark to the list - Contents [Row, Col - 1].CombiningMarks.Add (rune); + Contents [Row, Col - 1].AddCombiningMark (rune); // Ignore. Don't move to next column (let the driver figure out what to do). } @@ -187,7 +187,7 @@ public class OutputBuffer : IOutputBuffer else { // It didn't normalize. Add it to the Cell to left's CM list - Contents [Row, Col - 1].CombiningMarks.Add (rune); + Contents [Row, Col - 1].AddCombiningMark (rune); // Ignore. Don't move to next column (let the driver figure out what to do). } diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs index 5ce4e21df..fc7ef2b0b 100644 --- a/Terminal.Gui/Drawing/Cell.cs +++ b/Terminal.Gui/Drawing/Cell.cs @@ -1,4 +1,6 @@ -namespace Terminal.Gui; +#nullable enable + +namespace Terminal.Gui; /// /// Represents a single row/column in a Terminal.Gui rendering surface (e.g. and @@ -23,12 +25,12 @@ public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, Ru get => _rune; set { - CombiningMarks.Clear (); + _combiningMarks?.Clear (); _rune = value; } } - private List _combiningMarks; + private List? _combiningMarks; /// /// The combining marks for that when combined makes this Cell a combining sequence. If @@ -38,10 +40,37 @@ public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, Ru /// Only valid in the rare case where is a combining sequence that could not be normalized to a /// single Rune. /// - internal List CombiningMarks + internal IReadOnlyList CombiningMarks { - get => _combiningMarks ?? []; - private set => _combiningMarks = value ?? []; + // PERFORMANCE: Downside of the interface return type is that List struct enumerator cannot be utilized, i.e. enumerator is allocated. + // If enumeration is used heavily in the future then might be better to expose the List Enumerator directly via separate mechanism. + get + { + // Avoid unnecessary list allocation. + if (_combiningMarks == null) + { + return Array.Empty (); + } + return _combiningMarks; + } + } + + /// + /// Adds combining mark to the cell. + /// + /// The combining mark to add to the cell. + internal void AddCombiningMark (Rune combiningMark) + { + _combiningMarks ??= []; + _combiningMarks.Add (combiningMark); + } + + /// + /// Clears combining marks of the cell. + /// + internal void ClearCombiningMarks () + { + _combiningMarks?.Clear (); } ///