View.Text.cs 8.1 KB

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