Label.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. //
  2. // Label.cs: Label control
  3. //
  4. // Authors:
  5. // Miguel de Icaza ([email protected])
  6. //
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using NStack;
  11. namespace Terminal.Gui {
  12. /// <summary>
  13. /// Text alignment enumeration, controls how text is displayed.
  14. /// </summary>
  15. public enum TextAlignment {
  16. /// <summary>
  17. /// Aligns the text to the left of the frame.
  18. /// </summary>
  19. Left,
  20. /// <summary>
  21. /// Aligns the text to the right side of the frame.
  22. /// </summary>
  23. Right,
  24. /// <summary>
  25. /// Centers the text in the frame.
  26. /// </summary>
  27. Centered,
  28. /// <summary>
  29. /// Shows the line as justified text in the line.
  30. /// </summary>
  31. Justified
  32. }
  33. /// <summary>
  34. /// Label view, displays a string at a given position, can include multiple lines.
  35. /// </summary>
  36. public class Label : View {
  37. List<ustring> lines = new List<ustring> ();
  38. bool recalcPending = true;
  39. ustring text;
  40. TextAlignment textAlignment;
  41. static Rect CalcRect (int x, int y, ustring s)
  42. {
  43. int mw = 0;
  44. int ml = 1;
  45. int cols = 0;
  46. foreach (var rune in s) {
  47. if (rune == '\n') {
  48. ml++;
  49. if (cols > mw)
  50. mw = cols;
  51. cols = 0;
  52. } else
  53. cols++;
  54. }
  55. return new Rect (x, y, cols, ml);
  56. }
  57. /// <summary>
  58. /// Public constructor: creates a label at the given
  59. /// coordinate with the given string, computes the bounding box
  60. /// based on the size of the string, assumes that the string contains
  61. /// newlines for multiple lines, no special breaking rules are used.
  62. /// </summary>
  63. public Label (int x, int y, ustring text) : this (CalcRect (x, y, text), text)
  64. {
  65. }
  66. /// <summary>
  67. /// Public constructor: creates a label at the given
  68. /// coordinate with the given string and uses the specified
  69. /// frame for the string.
  70. /// </summary>
  71. public Label (Rect rect, ustring text) : base (rect)
  72. {
  73. this.text = text;
  74. }
  75. static char [] whitespace = new char [] { ' ', '\t' };
  76. static ustring ClipAndJustify (ustring str, int width, TextAlignment talign)
  77. {
  78. int slen = str.Length;
  79. if (slen > width)
  80. return str [0, width];
  81. else {
  82. if (talign == TextAlignment.Justified) {
  83. // TODO: ustring needs this
  84. var words = str.ToString ().Split (whitespace, StringSplitOptions.RemoveEmptyEntries);
  85. int textCount = words.Sum (arg => arg.Length);
  86. var spaces = (width- textCount) / (words.Length - 1);
  87. var extras = (width - textCount) % words.Length;
  88. var s = new System.Text.StringBuilder ();
  89. //s.Append ($"tc={textCount} sp={spaces},x={extras} - ");
  90. for (int w = 0; w < words.Length; w++) {
  91. var x = words [w];
  92. s.Append (x);
  93. if (w + 1 < words.Length)
  94. for (int i = 0; i < spaces; i++)
  95. s.Append (' ');
  96. if (extras > 0) {
  97. s.Append ('_');
  98. extras--;
  99. }
  100. }
  101. return ustring.Make (s.ToString ());
  102. }
  103. return str;
  104. }
  105. }
  106. void Recalc ()
  107. {
  108. recalcPending = false;
  109. Recalc (text, lines, Frame.Width, textAlignment);
  110. }
  111. static void Recalc (ustring textStr, List<ustring> lineResult, int width, TextAlignment talign)
  112. {
  113. lineResult.Clear ();
  114. if (textStr.IndexOf ('\n') == -1) {
  115. lineResult.Add (ClipAndJustify (textStr, width, talign));
  116. return;
  117. }
  118. int textLen = textStr.Length;
  119. int lp = 0;
  120. for (int i = 0; i < textLen; i++) {
  121. Rune c = textStr [i];
  122. if (c == '\n') {
  123. lineResult.Add (ClipAndJustify (textStr [lp, i], width, talign));
  124. lp = i + 1;
  125. }
  126. }
  127. }
  128. public override void Redraw (Rect region)
  129. {
  130. if (recalcPending)
  131. Recalc ();
  132. if (TextColor != -1)
  133. Driver.SetAttribute (TextColor);
  134. else
  135. Driver.SetAttribute (ColorScheme.Normal);
  136. Clear ();
  137. Move (Frame.X, Frame.Y);
  138. for (int line = 0; line < lines.Count; line++) {
  139. if (line < region.Top || line >= region.Bottom)
  140. continue;
  141. var str = lines [line];
  142. int x;
  143. switch (textAlignment) {
  144. case TextAlignment.Left:
  145. case TextAlignment.Justified:
  146. x = 0;
  147. break;
  148. case TextAlignment.Right:
  149. x = Frame.Right - str.Length;
  150. break;
  151. case TextAlignment.Centered:
  152. x = Frame.Left + (Frame.Width - str.Length) / 2;
  153. break;
  154. default:
  155. throw new ArgumentOutOfRangeException ();
  156. }
  157. Move (x, line);
  158. Driver.AddStr (str);
  159. }
  160. }
  161. /// <summary>
  162. /// Computes the number of lines needed to render the specified text by the Label control
  163. /// </summary>
  164. /// <returns>Number of lines.</returns>
  165. /// <param name="text">Text, may contain newlines.</param>
  166. /// <param name="width">The width for the text.</param>
  167. public static int MeasureLines (ustring text, int width)
  168. {
  169. var result = new List<ustring> ();
  170. Recalc (text, result, width, TextAlignment.Left);
  171. return result.Count;
  172. }
  173. /// <summary>
  174. /// The text displayed by this widget.
  175. /// </summary>
  176. public virtual ustring Text {
  177. get => text;
  178. set {
  179. text = value;
  180. recalcPending = true;
  181. SetNeedsDisplay ();
  182. }
  183. }
  184. /// <summary>
  185. /// Controls the text-alignemtn property of the label, changing it will redisplay the label.
  186. /// </summary>
  187. /// <value>The text alignment.</value>
  188. public TextAlignment TextAlignment {
  189. get => textAlignment;
  190. set {
  191. textAlignment = value;
  192. SetNeedsDisplay ();
  193. }
  194. }
  195. Attribute textColor = -1;
  196. /// <summary>
  197. /// The color used for the label
  198. /// </summary>
  199. public Attribute TextColor {
  200. get => textColor;
  201. set {
  202. textColor = value;
  203. SetNeedsDisplay ();
  204. }
  205. }
  206. }
  207. }