View.Adornments.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. #nullable enable
  2. namespace Terminal.Gui;
  3. public partial class View // Adornments
  4. {
  5. /// <summary>
  6. /// Initializes the Adornments of the View. Called by the constructor.
  7. /// </summary>
  8. private void SetupAdornments ()
  9. {
  10. // TODO: Move this to Adornment as a static factory method
  11. if (this is not Adornment)
  12. {
  13. // TODO: Make the Adornments Lazy and only create them when needed
  14. Margin = new (this);
  15. Border = new (this);
  16. Padding = new (this);
  17. }
  18. }
  19. private void BeginInitAdornments ()
  20. {
  21. Margin?.BeginInit ();
  22. Border?.BeginInit ();
  23. Padding?.BeginInit ();
  24. }
  25. private void EndInitAdornments ()
  26. {
  27. Margin?.EndInit ();
  28. Border?.EndInit ();
  29. Padding?.EndInit ();
  30. }
  31. private void DisposeAdornments ()
  32. {
  33. Margin?.Dispose ();
  34. Margin = null;
  35. Border?.Dispose ();
  36. Border = null;
  37. Padding?.Dispose ();
  38. Padding = null;
  39. }
  40. /// <summary>
  41. /// The <see cref="Adornment"/> that enables separation of a View from other SubViews of the same
  42. /// SuperView. The margin offsets the <see cref="Viewport"/> from the <see cref="Frame"/>.
  43. /// </summary>
  44. /// <remarks>
  45. /// <para>
  46. /// The margin is typically transparent. This can be overriden by explicitly setting <see cref="ColorScheme"/>.
  47. /// </para>
  48. /// <para>
  49. /// Enabling <see cref="ShadowStyle"/> will change the Thickness of the Margin to include the shadow.
  50. /// </para>
  51. /// <para>
  52. /// The adornments (<see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/>) are not part of the
  53. /// View's content and are not clipped by the View's Clip Area.
  54. /// </para>
  55. /// <para>
  56. /// Changing the size of an adornment (<see cref="Margin"/>, <see cref="Border"/>, or <see cref="Padding"/>) will
  57. /// change the size of <see cref="Frame"/> which will call <see cref="SetNeedsLayout"/> to update the layout of the
  58. /// <see cref="SuperView"/> and its <see cref="Subviews"/>.
  59. /// </para>
  60. /// </remarks>
  61. public Margin? Margin { get; private set; }
  62. private ShadowStyle _shadowStyle;
  63. /// <summary>
  64. /// Gets or sets whether the View is shown with a shadow effect. The shadow is drawn on the right and bottom sides of
  65. /// the
  66. /// Margin.
  67. /// </summary>
  68. /// <remarks>
  69. /// Setting this property to <see langword="true"/> will add a shadow to the right and bottom sides of the Margin.
  70. /// The View 's <see cref="Frame"/> will be expanded to include the shadow.
  71. /// </remarks>
  72. public virtual ShadowStyle ShadowStyle
  73. {
  74. get => _shadowStyle;
  75. set
  76. {
  77. if (_shadowStyle == value)
  78. {
  79. return;
  80. }
  81. _shadowStyle = value;
  82. if (Margin is { })
  83. {
  84. Margin.ShadowStyle = value;
  85. }
  86. }
  87. }
  88. /// <summary>
  89. /// The <see cref="Adornment"/> that offsets the <see cref="Viewport"/> from the <see cref="Margin"/>.
  90. /// <para>
  91. /// The Border provides the space for a visual border (drawn using
  92. /// line-drawing glyphs) and the Title. The Border expands inward; in other words if `Border.Thickness.Top == 2`
  93. /// the
  94. /// border and title will take up the first row and the second row will be filled with spaces.
  95. /// </para>
  96. /// <para>
  97. /// The Border provides the UI for mouse and keyboard arrangement of the View. See <see cref="Arrangement"/>.
  98. /// </para>
  99. /// </summary>
  100. /// <remarks>
  101. /// <para><see cref="BorderStyle"/> provides a simple helper for turning a simple border frame on or off.</para>
  102. /// <para>
  103. /// The adornments (<see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/>) are not part of the
  104. /// View's content and are not clipped by the View's Clip Area.
  105. /// </para>
  106. /// <para>
  107. /// Changing the size of an adornment (<see cref="Margin"/>, <see cref="Border"/>, or <see cref="Padding"/>) will
  108. /// change the size of <see cref="Frame"/> which will call <see cref="SetNeedsLayout"/> to update the layout of the
  109. /// <see cref="SuperView"/> and its <see cref="Subviews"/>.
  110. /// </para>
  111. /// </remarks>
  112. public Border? Border { get; private set; }
  113. /// <summary>Gets or sets whether the view has a one row/col thick border.</summary>
  114. /// <remarks>
  115. /// <para>
  116. /// This is a helper for manipulating the view's <see cref="Border"/>. Setting this property to any value other
  117. /// than <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
  118. /// <see cref="Adornment.Thickness"/> to `1` and <see cref="BorderStyle"/> to the value.
  119. /// </para>
  120. /// <para>
  121. /// Setting this property to <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
  122. /// <see cref="Adornment.Thickness"/> to `0` and <see cref="BorderStyle"/> to <see cref="LineStyle.None"/>.
  123. /// </para>
  124. /// <para>
  125. /// Calls <see cref="OnBorderStyleChanging"/> and raises <see cref="BorderStyleChanging"/>, which allows change
  126. /// to be cancelled.
  127. /// </para>
  128. /// <para>For more advanced customization of the view's border, manipulate see <see cref="Border"/> directly.</para>
  129. /// </remarks>
  130. public LineStyle BorderStyle
  131. {
  132. get => Border?.LineStyle ?? LineStyle.Single;
  133. set
  134. {
  135. if (Border is null)
  136. {
  137. return;
  138. }
  139. LineStyle old = Border?.LineStyle ?? LineStyle.None;
  140. CancelEventArgs<LineStyle> e = new (ref old, ref value);
  141. if (OnBorderStyleChanging (e) || e.Cancel)
  142. {
  143. return;
  144. }
  145. BorderStyleChanging?.Invoke (this, e);
  146. if (e.Cancel)
  147. {
  148. return;
  149. }
  150. SetBorderStyle (e.NewValue);
  151. SetAdornmentFrames ();
  152. SetNeedsLayout ();
  153. }
  154. }
  155. /// <summary>
  156. /// Called when the <see cref="BorderStyle"/> is changing.
  157. /// </summary>
  158. /// <remarks>
  159. /// Set e.Cancel to true to prevent the <see cref="BorderStyle"/> from changing.
  160. /// </remarks>
  161. /// <param name="e"></param>
  162. protected virtual bool OnBorderStyleChanging (CancelEventArgs<LineStyle> e) { return false; }
  163. /// <summary>
  164. /// Fired when the <see cref="BorderStyle"/> is changing. Allows the event to be cancelled.
  165. /// </summary>
  166. public event EventHandler<CancelEventArgs<LineStyle>>? BorderStyleChanging;
  167. /// <summary>
  168. /// Sets the <see cref="BorderStyle"/> of the view to the specified value.
  169. /// </summary>
  170. /// <remarks>
  171. /// <para>
  172. /// <see cref="BorderStyle"/> is a helper for manipulating the view's <see cref="Border"/>. Setting this property
  173. /// to any value other
  174. /// than <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
  175. /// <see cref="Adornment.Thickness"/> to `1` and <see cref="BorderStyle"/> to the value.
  176. /// </para>
  177. /// <para>
  178. /// Setting this property to <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
  179. /// <see cref="Adornment.Thickness"/> to `0` and <see cref="BorderStyle"/> to <see cref="LineStyle.None"/>.
  180. /// </para>
  181. /// <para>For more advanced customization of the view's border, manipulate see <see cref="Border"/> directly.</para>
  182. /// </remarks>
  183. /// <param name="value"></param>
  184. public virtual void SetBorderStyle (LineStyle value)
  185. {
  186. if (value != LineStyle.None)
  187. {
  188. if (Border!.Thickness == Thickness.Empty)
  189. {
  190. Border.Thickness = new (1);
  191. }
  192. }
  193. else
  194. {
  195. Border!.Thickness = new (0);
  196. }
  197. Border.LineStyle = value;
  198. }
  199. /// <summary>
  200. /// The <see cref="Adornment"/> inside of the view that offsets the <see cref="Viewport"/>
  201. /// from the <see cref="Border"/>.
  202. /// </summary>
  203. /// <remarks>
  204. /// <para>
  205. /// The adornments (<see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/>) are not part of the
  206. /// View's content and are not clipped by the View's Clip Area.
  207. /// </para>
  208. /// <para>
  209. /// Changing the size of an adornment (<see cref="Margin"/>, <see cref="Border"/>, or <see cref="Padding"/>) will
  210. /// change the size of <see cref="Frame"/> which will call <see cref="SetNeedsLayout"/> to update the layout of the
  211. /// <see cref="SuperView"/> and its <see cref="Subviews"/>.
  212. /// </para>
  213. /// </remarks>
  214. public Padding? Padding { get; private set; }
  215. /// <summary>
  216. /// <para>Gets the thickness describing the sum of the Adornments' thicknesses.</para>
  217. /// </summary>
  218. /// <remarks>
  219. /// <para>
  220. /// The <see cref="Viewport"/> is offset from the <see cref="Frame"/> by the thickness returned by this method.
  221. /// </para>
  222. /// </remarks>
  223. /// <returns>A thickness that describes the sum of the Adornments' thicknesses.</returns>
  224. public Thickness GetAdornmentsThickness ()
  225. {
  226. Thickness result = Thickness.Empty;
  227. if (Margin is { })
  228. {
  229. result += Margin.Thickness;
  230. }
  231. if (Border is { })
  232. {
  233. result += Border.Thickness;
  234. }
  235. if (Padding is { })
  236. {
  237. result += Padding.Thickness;
  238. }
  239. return result;
  240. }
  241. /// <summary>Sets the Frame's of the Margin, Border, and Padding.</summary>
  242. internal void SetAdornmentFrames ()
  243. {
  244. if (this is Adornment)
  245. {
  246. // Adornments do not have Adornments
  247. return;
  248. }
  249. if (Margin is { })
  250. {
  251. Margin!.Frame = Rectangle.Empty with { Size = Frame.Size };
  252. }
  253. if (Border is { } && Margin is { })
  254. {
  255. Border!.Frame = Margin!.Thickness.GetInside (Margin!.Frame);
  256. }
  257. if (Padding is { } && Border is { })
  258. {
  259. Padding!.Frame = Border!.Thickness.GetInside (Border!.Frame);
  260. }
  261. }
  262. }