View.Text.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #nullable enable
  2. namespace Terminal.Gui;
  3. public partial class View // Text Property APIs
  4. {
  5. private string _text = string.Empty;
  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. /// <summary>
  11. /// Gets or sets whether trailing spaces at the end of word-wrapped lines are preserved
  12. /// or not when <see cref="TextFormatter.WordWrap"/> is enabled.
  13. /// If <see langword="true"/> trailing spaces at the end of wrapped lines will be removed when
  14. /// <see cref="Text"/> is formatted for display. The default is <see langword="false"/>.
  15. /// </summary>
  16. public bool PreserveTrailingSpaces
  17. {
  18. get => TextFormatter.PreserveTrailingSpaces;
  19. set
  20. {
  21. if (TextFormatter.PreserveTrailingSpaces != value)
  22. {
  23. TextFormatter.PreserveTrailingSpaces = value;
  24. TextFormatter.NeedsFormat = true;
  25. SetNeedsLayout ();
  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. if (_text == value)
  57. {
  58. return;
  59. }
  60. string old = _text;
  61. _text = value;
  62. UpdateTextFormatterText ();
  63. SetNeedsLayout ();
  64. OnTextChanged ();
  65. }
  66. }
  67. // TODO: Make this non-virtual. Nobody overrides it.
  68. /// <summary>
  69. /// Gets or sets how the View's <see cref="Text"/> is aligned horizontally when drawn. Changing this property will
  70. /// redisplay the <see cref="View"/>.
  71. /// </summary>
  72. /// <remarks>
  73. /// <para>
  74. /// <see cref="View.Width"/> or <see cref="View.Height"/> are using <see cref="DimAutoStyle.Text"/>, the
  75. /// <see cref="GetContentSize ()"/> will be adjusted to fit the text.
  76. /// </para>
  77. /// </remarks>
  78. /// <value>The text alignment.</value>
  79. public virtual Alignment TextAlignment
  80. {
  81. get => TextFormatter.Alignment;
  82. set
  83. {
  84. TextFormatter.Alignment = value;
  85. UpdateTextFormatterText ();
  86. SetNeedsLayout ();
  87. }
  88. }
  89. /// <summary>
  90. /// Text changed event, raised when the text has changed.
  91. /// </summary>
  92. public event EventHandler? TextChanged;
  93. // TODO: Make this non-virtual. Nobody overrides it.
  94. /// <summary>
  95. /// Gets or sets the direction of the View's <see cref="Text"/>. Changing this property will redisplay the
  96. /// <see cref="View"/>.
  97. /// </summary>
  98. /// <remarks>
  99. /// <para>
  100. /// <see cref="View.Width"/> or <see cref="View.Height"/> are using <see cref="DimAutoStyle.Text"/>, the
  101. /// <see cref="GetContentSize ()"/> will be adjusted to fit the text.
  102. /// </para>
  103. /// </remarks>
  104. /// <value>The text direction.</value>
  105. public virtual TextDirection TextDirection
  106. {
  107. get => TextFormatter.Direction;
  108. set => UpdateTextDirection (value);
  109. }
  110. /// <summary>
  111. /// Gets or sets the <see cref="Gui.TextFormatter"/> used to format <see cref="Text"/>.
  112. /// </summary>
  113. public TextFormatter TextFormatter { get; init; } = new ();
  114. // TODO: Make this non-virtual. Nobody overrides it.
  115. /// <summary>
  116. /// Gets or sets how the View's <see cref="Text"/> is aligned vertically when drawn. Changing this property will
  117. /// redisplay
  118. /// the <see cref="View"/>.
  119. /// </summary>
  120. /// <remarks>
  121. /// <para>
  122. /// <see cref="View.Width"/> or <see cref="View.Height"/> are using <see cref="DimAutoStyle.Text"/>, the
  123. /// <see cref="GetContentSize ()"/> will be adjusted to fit the text.
  124. /// </para>
  125. /// </remarks>
  126. /// <value>The vertical text alignment.</value>
  127. public virtual Alignment VerticalTextAlignment
  128. {
  129. get => TextFormatter.VerticalAlignment;
  130. set
  131. {
  132. TextFormatter.VerticalAlignment = value;
  133. SetNeedsDraw ();
  134. }
  135. }
  136. // TODO: Add a OnUpdateTextFormatterText method that invokes UpdateTextFormatterText so that overrides don't have to call base.
  137. /// <summary>
  138. /// Can be overridden if the <see cref="TextFormatter.Text"/> has
  139. /// different format than the default.
  140. /// </summary>
  141. /// <remarks>
  142. /// Overrides must call <c>base.UpdateTextFormatterText</c> before updating <see cref="TextFormatter.Text"/>.
  143. /// </remarks>
  144. protected virtual void UpdateTextFormatterText ()
  145. {
  146. TextFormatter.Text = _text;
  147. TextFormatter.ConstrainToWidth = null;
  148. TextFormatter.ConstrainToHeight = null;
  149. }
  150. /// <summary>
  151. /// Internal API. Sets <see cref="TextFormatter"/>.Width/Height.
  152. /// </summary>
  153. /// <remarks>
  154. /// Use this API to set <see cref="Gui.TextFormatter.ConstrainToWidth"/>/Height when the view has changed such that the
  155. /// size required to fit the text has changed.
  156. /// changes.
  157. /// </remarks>
  158. /// <returns></returns>
  159. internal void SetTextFormatterSize ()
  160. {
  161. // View subclasses can override UpdateTextFormatterText to modify the Text it holds (e.g. Checkbox and Button).
  162. // We need to ensure TextFormatter is accurate by calling it here.
  163. UpdateTextFormatterText ();
  164. // Default is to use GetContentSize ().
  165. Size? size = _contentSize;
  166. // Use _width & _height instead of Width & Height to avoid debug spew
  167. var widthAuto = _width as DimAuto;
  168. var heightAuto = _height as DimAuto;
  169. if (widthAuto is { } && widthAuto.Style.FastHasFlags (DimAutoStyle.Text))
  170. {
  171. TextFormatter.ConstrainToWidth = null;
  172. }
  173. else
  174. {
  175. if (size is { })
  176. {
  177. TextFormatter.ConstrainToWidth = size?.Width;
  178. }
  179. }
  180. if (heightAuto is { } && heightAuto.Style.FastHasFlags (DimAutoStyle.Text))
  181. {
  182. TextFormatter.ConstrainToHeight = null;
  183. }
  184. else
  185. {
  186. if (size is { })
  187. {
  188. TextFormatter.ConstrainToHeight = size?.Height;
  189. }
  190. }
  191. }
  192. /// <summary>
  193. /// Initializes the Text of the View. Called by the constructor.
  194. /// </summary>
  195. private void SetupText ()
  196. {
  197. Text = string.Empty;
  198. TextDirection = TextDirection.LeftRight_TopBottom;
  199. }
  200. private void UpdateTextDirection (TextDirection newDirection)
  201. {
  202. bool directionChanged = TextFormatter.IsHorizontalDirection (TextFormatter.Direction) != TextFormatter.IsHorizontalDirection (newDirection);
  203. TextFormatter.Direction = newDirection;
  204. UpdateTextFormatterText ();
  205. if (directionChanged)
  206. {
  207. TextFormatter.ConstrainToWidth = null;
  208. TextFormatter.ConstrainToHeight = null;
  209. SetNeedsLayout ();
  210. }
  211. SetNeedsDraw ();
  212. }
  213. }