ViewText.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #nullable enable
  2. using System.Diagnostics;
  3. using static Unix.Terminal.Curses;
  4. namespace Terminal.Gui;
  5. public partial class View
  6. {
  7. /// <summary>
  8. /// Initializes the Text of the View. Called by the constructor.
  9. /// </summary>
  10. private void SetupText ()
  11. {
  12. Text = string.Empty;
  13. TextDirection = TextDirection.LeftRight_TopBottom;
  14. }
  15. private string _text;
  16. /// <summary>
  17. /// Gets or sets whether trailing spaces at the end of word-wrapped lines are preserved
  18. /// or not when <see cref="TextFormatter.WordWrap"/> is enabled.
  19. /// If <see langword="true"/> trailing spaces at the end of wrapped lines will be removed when
  20. /// <see cref="Text"/> is formatted for display. The default is <see langword="false"/>.
  21. /// </summary>
  22. public virtual bool PreserveTrailingSpaces
  23. {
  24. get => TextFormatter.PreserveTrailingSpaces;
  25. set
  26. {
  27. if (TextFormatter.PreserveTrailingSpaces != value)
  28. {
  29. TextFormatter.PreserveTrailingSpaces = value;
  30. TextFormatter.NeedsFormat = true;
  31. }
  32. }
  33. }
  34. /// <summary>
  35. /// The text displayed by the <see cref="View"/>.
  36. /// </summary>
  37. /// <remarks>
  38. /// <para>
  39. /// The text will be drawn before any subviews are drawn.
  40. /// </para>
  41. /// <para>
  42. /// The text will be drawn starting at the view origin (0, 0) and will be formatted according
  43. /// to <see cref="TextAlignment"/> and <see cref="TextDirection"/>.
  44. /// </para>
  45. /// <para>
  46. /// The text will word-wrap to additional lines if it does not fit horizontally. If <see cref="GetContentSize ()"/>'s height
  47. /// is 1, the text will be clipped.
  48. /// </para>
  49. /// <para>If <see cref="View.Width"/> or <see cref="View.Height"/> are using <see cref="DimAutoStyle.Text"/>,
  50. /// the <see cref="GetContentSize ()"/> will be adjusted to fit the text.</para>
  51. /// <para>When the text changes, the <see cref="TextChanged"/> is fired.</para>
  52. /// </remarks>
  53. public virtual string Text
  54. {
  55. get => _text;
  56. set
  57. {
  58. string old = _text;
  59. _text = value;
  60. UpdateTextFormatterText ();
  61. OnResizeNeeded ();
  62. #if DEBUG
  63. if (_text is { } && string.IsNullOrEmpty (Id))
  64. {
  65. Id = _text;
  66. }
  67. #endif
  68. OnTextChanged ();
  69. }
  70. }
  71. /// <summary>
  72. /// Called when the <see cref="Text"/> has changed. Fires the <see cref="TextChanged"/> event.
  73. /// </summary>
  74. public void OnTextChanged ()
  75. {
  76. TextChanged?.Invoke (this, EventArgs.Empty);
  77. }
  78. /// <summary>
  79. /// Text changed event, raised when the text has changed.
  80. /// </summary>
  81. public event EventHandler? TextChanged;
  82. // TODO: Make this non-virtual. Nobody overrides it.
  83. /// <summary>
  84. /// Gets or sets how the View's <see cref="Text"/> is aligned horizontally when drawn. Changing this property will
  85. /// redisplay the <see cref="View"/>.
  86. /// </summary>
  87. /// <remarks>
  88. /// <para> <see cref="View.Width"/> or <see cref="View.Height"/> are using <see cref="DimAutoStyle.Text"/>, the <see cref="GetContentSize ()"/> will be adjusted to fit the text.</para>
  89. /// </remarks>
  90. /// <value>The text alignment.</value>
  91. public virtual Alignment TextAlignment
  92. {
  93. get => TextFormatter.Alignment;
  94. set
  95. {
  96. TextFormatter.Alignment = value;
  97. UpdateTextFormatterText ();
  98. OnResizeNeeded ();
  99. }
  100. }
  101. // TODO: Make this non-virtual. Nobody overrides it.
  102. /// <summary>
  103. /// Gets or sets the direction of the View's <see cref="Text"/>. Changing this property will redisplay the
  104. /// <see cref="View"/>.
  105. /// </summary>
  106. /// <remarks>
  107. /// <para> <see cref="View.Width"/> or <see cref="View.Height"/> are using <see cref="DimAutoStyle.Text"/>, the <see cref="GetContentSize ()"/> will be adjusted to fit the text.</para>
  108. /// </remarks>
  109. /// <value>The text direction.</value>
  110. public virtual TextDirection TextDirection
  111. {
  112. get => TextFormatter.Direction;
  113. set => UpdateTextDirection (value);
  114. }
  115. /// <summary>
  116. /// Gets or sets the <see cref="Gui.TextFormatter"/> used to format <see cref="Text"/>.
  117. /// </summary>
  118. public TextFormatter TextFormatter { get; init; } = new () { };
  119. // TODO: Make this non-virtual. Nobody overrides it.
  120. /// <summary>
  121. /// Gets or sets how the View's <see cref="Text"/> is aligned vertically when drawn. Changing this property will
  122. /// redisplay
  123. /// the <see cref="View"/>.
  124. /// </summary>
  125. /// <remarks>
  126. /// <para> <see cref="View.Width"/> or <see cref="View.Height"/> are using <see cref="DimAutoStyle.Text"/>, the <see cref="GetContentSize ()"/> will be adjusted to fit the text.</para>
  127. /// </remarks>
  128. /// <value>The vertical text alignment.</value>
  129. public virtual Alignment VerticalTextAlignment
  130. {
  131. get => TextFormatter.VerticalAlignment;
  132. set
  133. {
  134. TextFormatter.VerticalAlignment = value;
  135. SetNeedsDisplay ();
  136. }
  137. }
  138. // TODO: Add a OnUpdateTextFormatterText method that invokes UpdateTextFormatterText so that overrides don't have to call base.
  139. /// <summary>
  140. /// Can be overridden if the <see cref="Terminal.Gui.TextFormatter.Text"/> has
  141. /// different format than the default.
  142. /// </summary>
  143. protected virtual void UpdateTextFormatterText ()
  144. {
  145. if (TextFormatter is { })
  146. {
  147. TextFormatter.Text = _text;
  148. TextFormatter.Width = null;
  149. TextFormatter.Height = null;
  150. }
  151. }
  152. /// <summary>
  153. /// Internal API. Sets <see cref="TextFormatter"/>.Size to the current <see cref="Viewport"/> size, adjusted for
  154. /// <see cref="TextFormatter.HotKeySpecifier"/>.
  155. /// </summary>
  156. /// <remarks>
  157. /// Use this API to set <see cref="TextFormatter.Size"/> when the view has changed such that the
  158. /// size required to fit the text has changed.
  159. /// changes.
  160. /// </remarks>
  161. /// <returns></returns>
  162. internal void SetTextFormatterSize ()
  163. {
  164. // View subclasses can override UpdateTextFormatterText to modify the Text it holds (e.g. Checkbox and Button).
  165. // We need to ensure TextFormatter is accurate by calling it here.
  166. UpdateTextFormatterText ();
  167. // Default is to use GetContentSize ().
  168. Size? size = _contentSize;
  169. // TODO: This is a hack. Figure out how to move this logic into DimAuto
  170. // Use _width & _height instead of Width & Height to avoid debug spew
  171. DimAuto? widthAuto = _width as DimAuto;
  172. DimAuto? heightAuto = _height as DimAuto;
  173. if ((widthAuto is { } && widthAuto.Style.FastHasFlags (DimAutoStyle.Text)))
  174. {
  175. TextFormatter.Width = null;
  176. }
  177. else
  178. {
  179. if (size is { })
  180. {
  181. TextFormatter.Width = size?.Width;
  182. }
  183. }
  184. if ((heightAuto is { } && heightAuto.Style.FastHasFlags (DimAutoStyle.Text)))
  185. {
  186. TextFormatter.Height = null;
  187. }
  188. else
  189. {
  190. if (size is { })
  191. {
  192. TextFormatter.Height = size?.Height;
  193. }
  194. }
  195. }
  196. private void UpdateTextDirection (TextDirection newDirection)
  197. {
  198. bool directionChanged = TextFormatter.IsHorizontalDirection (TextFormatter.Direction) != TextFormatter.IsHorizontalDirection (newDirection);
  199. TextFormatter.Direction = newDirection;
  200. UpdateTextFormatterText ();
  201. if (directionChanged)
  202. {
  203. TextFormatter.Width = null;
  204. TextFormatter.Height = null;
  205. OnResizeNeeded ();
  206. }
  207. SetNeedsDisplay ();
  208. }
  209. }