Skip to content

Commit

Permalink
Merge pull request #3772 from BDisp/v2_3771_textview-no-printable-run…
Browse files Browse the repository at this point in the history
…e-fix

Fixes #3771. TextView doesn't consider no-printable rune in draw and cursor position.
  • Loading branch information
tig authored Oct 11, 2024
2 parents bc1aeaa + ffe5154 commit a06dad5
Show file tree
Hide file tree
Showing 34 changed files with 1,680 additions and 1,145 deletions.
158 changes: 152 additions & 6 deletions Terminal.Gui/Drawing/Cell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@
/// Represents a single row/column in a Terminal.Gui rendering surface (e.g. <see cref="LineCanvas"/> and
/// <see cref="ConsoleDriver"/>).
/// </summary>
public record struct Cell ()
public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, Rune Rune = default)
{

/// <summary>The attributes to use when drawing the Glyph.</summary>
public Attribute? Attribute { get; set; } = null;
public Attribute? Attribute { get; set; } = Attribute;

/// <summary>
/// Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.Cell"/> has been modified since the
/// last time it was drawn.
/// </summary>
public bool IsDirty { get; set; } = false;
public bool IsDirty { get; set; } = IsDirty;

private Rune _rune = default;
private Rune _rune = Rune;

/// <summary>The character to display. If <see cref="Rune"/> is <see langword="null"/>, then <see cref="Rune"/> is ignored.</summary>
public Rune Rune
Expand All @@ -29,6 +28,8 @@ public Rune Rune
}
}

private List<Rune> _combiningMarks;

/// <summary>
/// The combining marks for <see cref="Rune"/> that when combined makes this Cell a combining sequence. If
/// <see cref="CombiningMarks"/> empty, then <see cref="CombiningMarks"/> is ignored.
Expand All @@ -37,8 +38,153 @@ public Rune Rune
/// Only valid in the rare case where <see cref="Rune"/> is a combining sequence that could not be normalized to a
/// single Rune.
/// </remarks>
internal List<Rune> CombiningMarks { get; } = new ();
internal List<Rune> CombiningMarks
{
get => _combiningMarks ?? [];
private set => _combiningMarks = value ?? [];
}

/// <inheritdoc/>
public override string ToString () { return $"[{Rune}, {Attribute}]"; }

/// <summary>Converts the string into a <see cref="List{Cell}"/>.</summary>
/// <param name="str">The string to convert.</param>
/// <param name="attribute">The <see cref="Gui.ColorScheme"/> to use.</param>
/// <returns></returns>
public static List<Cell> ToCellList (string str, Attribute? attribute = null)
{
List<Cell> cells = new ();

foreach (Rune rune in str.EnumerateRunes ())
{
cells.Add (new () { Rune = rune, Attribute = attribute });
}

return cells;
}

/// <summary>
/// Splits a string into a List that will contain a <see cref="List{Cell}"/> for each line.
/// </summary>
/// <param name="content">The string content.</param>
/// <param name="attribute">The color scheme.</param>
/// <returns>A <see cref="List{Cell}"/> for each line.</returns>
public static List<List<Cell>> StringToLinesOfCells (string content, Attribute? attribute = null)
{
List<Cell> cells = content.EnumerateRunes ()
.Select (x => new Cell { Rune = x, Attribute = attribute })
.ToList ();

return SplitNewLines (cells);
}

/// <summary>Converts a <see cref="Cell"/> generic collection into a string.</summary>
/// <param name="cells">The enumerable cell to convert.</param>
/// <returns></returns>
public static string ToString (IEnumerable<Cell> cells)
{
var str = string.Empty;

foreach (Cell cell in cells)
{
str += cell.Rune.ToString ();
}

return str;
}

/// <summary>Converts a <see cref="List{Cell}"/> generic collection into a string.</summary>
/// <param name="cellsList">The enumerable cell to convert.</param>
/// <returns></returns>
public static string ToString (List<List<Cell>> cellsList)
{
var str = string.Empty;

for (var i = 0; i < cellsList.Count; i++)
{
IEnumerable<Cell> cellList = cellsList [i];
str += ToString (cellList);

if (i + 1 < cellsList.Count)
{
str += Environment.NewLine;
}
}

return str;
}

// Turns the string into cells, this does not split the contents on a newline if it is present.

internal static List<Cell> StringToCells (string str, Attribute? attribute = null)
{
List<Cell> cells = [];

foreach (Rune rune in str.ToRunes ())
{
cells.Add (new () { Rune = rune, Attribute = attribute });
}

return cells;
}

internal static List<Cell> ToCells (IEnumerable<Rune> runes, Attribute? attribute = null)
{
List<Cell> cells = new ();

foreach (Rune rune in runes)
{
cells.Add (new () { Rune = rune, Attribute = attribute });
}

return cells;
}

private static List<List<Cell>> SplitNewLines (List<Cell> cells)
{
List<List<Cell>> lines = [];
int start = 0, i = 0;
var hasCR = false;

// ASCII code 13 = Carriage Return.
// ASCII code 10 = Line Feed.
for (; i < cells.Count; i++)
{
if (cells [i].Rune.Value == 13)
{
hasCR = true;

continue;
}

if (cells [i].Rune.Value == 10)
{
if (i - start > 0)
{
lines.Add (cells.GetRange (start, hasCR ? i - 1 - start : i - start));
}
else
{
lines.Add (StringToCells (string.Empty));
}

start = i + 1;
hasCR = false;
}
}

if (i - start >= 0)
{
lines.Add (cells.GetRange (start, i - start));
}

return lines;
}

/// <summary>
/// Splits a rune cell list into a List that will contain a <see cref="List{Cell}"/> for each line.
/// </summary>
/// <param name="cells">The cells list.</param>
/// <returns></returns>
public static List<List<Cell>> ToCells (List<Cell> cells) { return SplitNewLines (cells); }
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
namespace Terminal.Gui;

/// <summary>Args for events that relate to a specific <see cref="RuneCell"/>.</summary>
public class RuneCellEventArgs
/// <summary>Args for events that relate to a specific <see cref="Cell"/>.</summary>
public record struct CellEventArgs
{
/// <summary>Creates a new instance of the <see cref="RuneCellEventArgs"/> class.</summary>
/// <summary>Creates a new instance of the <see cref="CellEventArgs"/> class.</summary>
/// <param name="line">The line.</param>
/// <param name="col">The col index.</param>
/// <param name="unwrappedPosition">The unwrapped row and col index.</param>
public RuneCellEventArgs (List<RuneCell> line, int col, (int Row, int Col) unwrappedPosition)
public CellEventArgs (List<Cell> line, int col, (int Row, int Col) unwrappedPosition)
{
Line = line;
Col = col;
UnwrappedPosition = unwrappedPosition;
}

/// <summary>The index of the RuneCell in the line.</summary>
/// <summary>The index of the Cell in the line.</summary>
public int Col { get; }

/// <summary>The list of runes the RuneCell is part of.</summary>
public List<RuneCell> Line { get; }
/// <summary>The list of runes the Cell is part of.</summary>
public List<Cell> Line { get; }

/// <summary>
/// The unwrapped row and column index into the text containing the RuneCell. Unwrapped means the text without
/// The unwrapped row and column index into the text containing the Cell. Unwrapped means the text without
/// word wrapping or other visual formatting having been applied.
/// </summary>
public (int Row, int Col) UnwrappedPosition { get; }
Expand Down
2 changes: 1 addition & 1 deletion Terminal.Gui/Drawing/LineCanvas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public void AddLine (
int length,
Orientation orientation,
LineStyle style,
Attribute? attribute = default
Attribute? attribute = null
)
{
_cachedViewport = Rectangle.Empty;
Expand Down
2 changes: 1 addition & 1 deletion Terminal.Gui/Drawing/StraightLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public StraightLine (
int length,
Orientation orientation,
LineStyle style,
Attribute? attribute = default
Attribute? attribute = null
)
{
Start = start;
Expand Down
9 changes: 9 additions & 0 deletions Terminal.Gui/Resources/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 15 additions & 12 deletions Terminal.Gui/Resources/Strings.fr-FR.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,27 +117,27 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ctxSelectAll" xml:space="preserve">
<value>Tout _sélectionner</value>
</data>
<data name="ctxDeleteAll" xml:space="preserve">
<value>_Tout supprimer</value>
</data>
<data name="ctxCopy" xml:space="preserve">
<value>_Copier</value>
</data>
<data name="ctxCut" xml:space="preserve">
<value>Co_uper</value>
</data>
<data name="ctxDeleteAll" xml:space="preserve">
<value>_Tout supprimer</value>
</data>
<data name="ctxPaste" xml:space="preserve">
<value>C_oller</value>
</data>
<data name="ctxRedo" xml:space="preserve">
<value>_Rétablir</value>
</data>
<data name="ctxSelectAll" xml:space="preserve">
<value>Tout _sélectionner</value>
</data>
<data name="ctxUndo" xml:space="preserve">
<value>_Annuler</value>
</data>
<data name="ctxRedo" xml:space="preserve">
<value>_Rétablir</value>
</data>
<data name="fdDirectory" xml:space="preserve">
<value>_Dossier</value>
</data>
Expand Down Expand Up @@ -168,16 +168,19 @@
<data name="wzNext" xml:space="preserve">
<value>Prochai_n...</value>
</data>
<data name="btnOpen" xml:space="preserve">
<value>Ouvrir</value>
</data>
<data name="btnSave" xml:space="preserve">
<value>Enregistrer</value>
</data>
<data name="btnSaveAs" xml:space="preserve">
<value>E_nregistrer sous</value>
</data>
<data name="btnOpen" xml:space="preserve">
<value>Ouvrir</value>
</data>
<data name="dpTitle" xml:space="preserve">
<value>Sélecteur de Date</value>
</data>
<data name="ctxColors" xml:space="preserve">
<value>Cou_leurs</value>
</data>
</root>
Loading

0 comments on commit a06dad5

Please sign in to comment.