View.Text.cs 8.0 KB

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