-
Notifications
You must be signed in to change notification settings - Fork 730
Fixes #4387. Runes should not be used on a cell, but rather should use a single grapheme rendering 1 or 2 columns #4388
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v2_develop
Are you sure you want to change the base?
Changes from all commits
3c6e945
3b36714
034ccbe
0e3f646
012356e
78cfe73
87ea2af
37c9718
178e1e7
d3d4c25
817b3e4
9b08101
7c836b9
8d6f162
53b5cc2
0fad703
4ae48d0
1e8a37d
5974e81
410bcbe
5e87af7
ff538e3
6afe4c8
f3e4a2d
c9b46b7
1060ac9
ff34a14
b6072bb
d346209
ffd78b6
1f95fbe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,93 +1,122 @@ | ||||||||||||||||||||||||
| | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| namespace Terminal.Gui.Drawing; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||
| /// Represents a single row/column in a Terminal.Gui rendering surface (e.g. <see cref="LineCanvas"/> and | ||||||||||||||||||||||||
| /// <see cref="IDriver"/>). | ||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||
| public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, Rune Rune = default) | ||||||||||||||||||||||||
| public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, string Grapheme = "") | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| /// <summary>The attributes to use when drawing the Glyph.</summary> | ||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||
| /// Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.Drawing.Cell"/> has been modified since the | ||||||||||||||||||||||||
| /// last time it was drawn. | ||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||
| public bool IsDirty { get; set; } = IsDirty; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| private Rune _rune = Rune; | ||||||||||||||||||||||||
| private string _grapheme = Grapheme; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// <summary>The character to display. If <see cref="Rune"/> is <see langword="null"/>, then <see cref="Rune"/> is ignored.</summary> | ||||||||||||||||||||||||
| public Rune Rune | ||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||
| /// The single grapheme cluster to display from this cell. If <see cref="Grapheme"/> is <see langword="null"/> or | ||||||||||||||||||||||||
| /// <see cref="string.Empty"/>, then <see cref="Cell"/> is ignored. | ||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||
| public string Grapheme | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| get => _rune; | ||||||||||||||||||||||||
| readonly get => _grapheme; | ||||||||||||||||||||||||
| set | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| _combiningMarks?.Clear (); | ||||||||||||||||||||||||
| _rune = value; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| if (GraphemeHelper.GetGraphemes(value).ToArray().Length > 1) | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| throw new InvalidOperationException ($"Only a single {nameof (Grapheme)} cluster is allowed per Cell."); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| private List<Rune>? _combiningMarks; | ||||||||||||||||||||||||
| if (!string.IsNullOrEmpty (value) && value.Length == 1 && char.IsSurrogate (value [0])) | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| throw new ArgumentException ($"Only valid Unicode scalar values are allowed in a single {nameof (Grapheme)} cluster."); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// <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. | ||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||
| /// <remarks> | ||||||||||||||||||||||||
| /// 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 IReadOnlyList<Rune> CombiningMarks | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| // PERFORMANCE: Downside of the interface return type is that List<T> 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<T> Enumerator directly via separate mechanism. | ||||||||||||||||||||||||
| get | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| // Avoid unnecessary list allocation. | ||||||||||||||||||||||||
| if (_combiningMarks == null) | ||||||||||||||||||||||||
| try | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| return Array.Empty<Rune> (); | ||||||||||||||||||||||||
| if (!string.IsNullOrEmpty (value) && !value.IsNormalized (NormalizationForm.FormC)) | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| _grapheme = value.Normalize (NormalizationForm.FormC); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| else | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| _grapheme = value; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+42
to
+49
|
||||||||||||||||||||||||
| if (!string.IsNullOrEmpty (value) && !value.IsNormalized (NormalizationForm.FormC)) | |
| { | |
| _grapheme = value.Normalize (NormalizationForm.FormC); | |
| } | |
| else | |
| { | |
| _grapheme = value; | |
| } | |
| _grapheme = (!string.IsNullOrEmpty (value) && !value.IsNormalized (NormalizationForm.FormC)) | |
| ? value.Normalize (NormalizationForm.FormC) | |
| : value; |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant call to 'ToString' on a String object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assignment to i is useless, since its value is never read.