Label.cs 5.3 KB

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