ViewText.cs 8.0 KB

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