Cell.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. namespace Terminal.Gui;
  2. /// <summary>
  3. /// Represents a single row/column in a Terminal.Gui rendering surface (e.g. <see cref="LineCanvas"/> and
  4. /// <see cref="ConsoleDriver"/>).
  5. /// </summary>
  6. public record struct Cell (Attribute? Attribute = null, bool IsDirty = false, Rune Rune = default)
  7. {
  8. /// <summary>The attributes to use when drawing the Glyph.</summary>
  9. public Attribute? Attribute { get; set; } = Attribute;
  10. /// <summary>
  11. /// Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.Cell"/> has been modified since the
  12. /// last time it was drawn.
  13. /// </summary>
  14. public bool IsDirty { get; set; } = IsDirty;
  15. private Rune _rune = Rune;
  16. /// <summary>The character to display. If <see cref="Rune"/> is <see langword="null"/>, then <see cref="Rune"/> is ignored.</summary>
  17. public Rune Rune
  18. {
  19. get => _rune;
  20. set
  21. {
  22. CombiningMarks.Clear ();
  23. _rune = value;
  24. }
  25. }
  26. private List<Rune> _combiningMarks;
  27. /// <summary>
  28. /// The combining marks for <see cref="Rune"/> that when combined makes this Cell a combining sequence. If
  29. /// <see cref="CombiningMarks"/> empty, then <see cref="CombiningMarks"/> is ignored.
  30. /// </summary>
  31. /// <remarks>
  32. /// Only valid in the rare case where <see cref="Rune"/> is a combining sequence that could not be normalized to a
  33. /// single Rune.
  34. /// </remarks>
  35. internal List<Rune> CombiningMarks
  36. {
  37. get => _combiningMarks ?? [];
  38. private set => _combiningMarks = value ?? [];
  39. }
  40. /// <inheritdoc/>
  41. public override string ToString () { return $"[{Rune}, {Attribute}]"; }
  42. /// <summary>Converts the string into a <see cref="List{Cell}"/>.</summary>
  43. /// <param name="str">The string to convert.</param>
  44. /// <param name="attribute">The <see cref="Gui.ColorScheme"/> to use.</param>
  45. /// <returns></returns>
  46. public static List<Cell> ToCellList (string str, Attribute? attribute = null)
  47. {
  48. List<Cell> cells = new ();
  49. foreach (Rune rune in str.EnumerateRunes ())
  50. {
  51. cells.Add (new () { Rune = rune, Attribute = attribute });
  52. }
  53. return cells;
  54. }
  55. /// <summary>
  56. /// Splits a string into a List that will contain a <see cref="List{Cell}"/> for each line.
  57. /// </summary>
  58. /// <param name="content">The string content.</param>
  59. /// <param name="attribute">The color scheme.</param>
  60. /// <returns>A <see cref="List{Cell}"/> for each line.</returns>
  61. public static List<List<Cell>> StringToLinesOfCells (string content, Attribute? attribute = null)
  62. {
  63. List<Cell> cells = content.EnumerateRunes ()
  64. .Select (x => new Cell { Rune = x, Attribute = attribute })
  65. .ToList ();
  66. return SplitNewLines (cells);
  67. }
  68. /// <summary>Converts a <see cref="Cell"/> generic collection into a string.</summary>
  69. /// <param name="cells">The enumerable cell to convert.</param>
  70. /// <returns></returns>
  71. public static string ToString (IEnumerable<Cell> cells)
  72. {
  73. var str = string.Empty;
  74. foreach (Cell cell in cells)
  75. {
  76. str += cell.Rune.ToString ();
  77. }
  78. return str;
  79. }
  80. /// <summary>Converts a <see cref="List{Cell}"/> generic collection into a string.</summary>
  81. /// <param name="cellsList">The enumerable cell to convert.</param>
  82. /// <returns></returns>
  83. public static string ToString (List<List<Cell>> cellsList)
  84. {
  85. var str = string.Empty;
  86. for (var i = 0; i < cellsList.Count; i++)
  87. {
  88. IEnumerable<Cell> cellList = cellsList [i];
  89. str += ToString (cellList);
  90. if (i + 1 < cellsList.Count)
  91. {
  92. str += Environment.NewLine;
  93. }
  94. }
  95. return str;
  96. }
  97. // Turns the string into cells, this does not split the contents on a newline if it is present.
  98. internal static List<Cell> StringToCells (string str, Attribute? attribute = null)
  99. {
  100. List<Cell> cells = [];
  101. foreach (Rune rune in str.ToRunes ())
  102. {
  103. cells.Add (new () { Rune = rune, Attribute = attribute });
  104. }
  105. return cells;
  106. }
  107. internal static List<Cell> ToCells (IEnumerable<Rune> runes, Attribute? attribute = null)
  108. {
  109. List<Cell> cells = new ();
  110. foreach (Rune rune in runes)
  111. {
  112. cells.Add (new () { Rune = rune, Attribute = attribute });
  113. }
  114. return cells;
  115. }
  116. private static List<List<Cell>> SplitNewLines (List<Cell> cells)
  117. {
  118. List<List<Cell>> lines = [];
  119. int start = 0, i = 0;
  120. var hasCR = false;
  121. // ASCII code 13 = Carriage Return.
  122. // ASCII code 10 = Line Feed.
  123. for (; i < cells.Count; i++)
  124. {
  125. if (cells [i].Rune.Value == 13)
  126. {
  127. hasCR = true;
  128. continue;
  129. }
  130. if (cells [i].Rune.Value == 10)
  131. {
  132. if (i - start > 0)
  133. {
  134. lines.Add (cells.GetRange (start, hasCR ? i - 1 - start : i - start));
  135. }
  136. else
  137. {
  138. lines.Add (StringToCells (string.Empty));
  139. }
  140. start = i + 1;
  141. hasCR = false;
  142. }
  143. }
  144. if (i - start >= 0)
  145. {
  146. lines.Add (cells.GetRange (start, i - start));
  147. }
  148. return lines;
  149. }
  150. /// <summary>
  151. /// Splits a rune cell list into a List that will contain a <see cref="List{Cell}"/> for each line.
  152. /// </summary>
  153. /// <param name="cells">The cells list.</param>
  154. /// <returns></returns>
  155. public static List<List<Cell>> ToCells (List<Cell> cells) { return SplitNewLines (cells); }
  156. }