View.Adornments.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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. // It's tempting to try to optimize this by checking that old != value and returning.
  141. // Do not.
  142. CancelEventArgs<LineStyle> e = new (ref old, ref value);
  143. if (OnBorderStyleChanging (e) || e.Cancel)
  144. {
  145. return;
  146. }
  147. BorderStyleChanging?.Invoke (this, e);
  148. if (e.Cancel)
  149. {
  150. return;
  151. }
  152. SetBorderStyle (e.NewValue);
  153. SetAdornmentFrames ();
  154. SetNeedsLayout ();
  155. }
  156. }
  157. /// <summary>
  158. /// Called when the <see cref="BorderStyle"/> is changing.
  159. /// </summary>
  160. /// <remarks>
  161. /// Set e.Cancel to true to prevent the <see cref="BorderStyle"/> from changing.
  162. /// </remarks>
  163. /// <param name="e"></param>
  164. protected virtual bool OnBorderStyleChanging (CancelEventArgs<LineStyle> e) { return false; }
  165. /// <summary>
  166. /// Fired when the <see cref="BorderStyle"/> is changing. Allows the event to be cancelled.
  167. /// </summary>
  168. public event EventHandler<CancelEventArgs<LineStyle>>? BorderStyleChanging;
  169. /// <summary>
  170. /// Sets the <see cref="BorderStyle"/> of the view to the specified value.
  171. /// </summary>
  172. /// <remarks>
  173. /// <para>
  174. /// <see cref="BorderStyle"/> is a helper for manipulating the view's <see cref="Border"/>. Setting this property
  175. /// to any value other
  176. /// than <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
  177. /// <see cref="Adornment.Thickness"/> to `1` and <see cref="BorderStyle"/> to the value.
  178. /// </para>
  179. /// <para>
  180. /// Setting this property to <see cref="LineStyle.None"/> is equivalent to setting <see cref="Border"/>'s
  181. /// <see cref="Adornment.Thickness"/> to `0` and <see cref="BorderStyle"/> to <see cref="LineStyle.None"/>.
  182. /// </para>
  183. /// <para>For more advanced customization of the view's border, manipulate see <see cref="Border"/> directly.</para>
  184. /// </remarks>
  185. /// <param name="value"></param>
  186. public virtual void SetBorderStyle (LineStyle value)
  187. {
  188. if (value != LineStyle.None)
  189. {
  190. if (Border!.Thickness == Thickness.Empty)
  191. {
  192. Border.Thickness = new (1);
  193. }
  194. }
  195. else
  196. {
  197. Border!.Thickness = new (0);
  198. }
  199. Border.LineStyle = value;
  200. }
  201. /// <summary>
  202. /// The <see cref="Adornment"/> inside of the view that offsets the <see cref="Viewport"/>
  203. /// from the <see cref="Border"/>.
  204. /// </summary>
  205. /// <remarks>
  206. /// <para>
  207. /// The adornments (<see cref="Margin"/>, <see cref="Border"/>, and <see cref="Padding"/>) are not part of the
  208. /// View's content and are not clipped by the View's Clip Area.
  209. /// </para>
  210. /// <para>
  211. /// Changing the size of an adornment (<see cref="Margin"/>, <see cref="Border"/>, or <see cref="Padding"/>) will
  212. /// change the size of <see cref="Frame"/> which will call <see cref="SetNeedsLayout"/> to update the layout of the
  213. /// <see cref="SuperView"/> and its <see cref="SubViews"/>.
  214. /// </para>
  215. /// </remarks>
  216. public Padding? Padding { get; private set; }
  217. /// <summary>
  218. /// <para>Gets the thickness describing the sum of the Adornments' thicknesses.</para>
  219. /// </summary>
  220. /// <remarks>
  221. /// <para>
  222. /// The <see cref="Viewport"/> is offset from the <see cref="Frame"/> by the thickness returned by this method.
  223. /// </para>
  224. /// </remarks>
  225. /// <returns>A thickness that describes the sum of the Adornments' thicknesses.</returns>
  226. public Thickness GetAdornmentsThickness ()
  227. {
  228. Thickness result = Thickness.Empty;
  229. if (Margin is { })
  230. {
  231. result += Margin.Thickness;
  232. }
  233. if (Border is { })
  234. {
  235. result += Border.Thickness;
  236. }
  237. if (Padding is { })
  238. {
  239. result += Padding.Thickness;
  240. }
  241. return result;
  242. }
  243. /// <summary>Sets the Frame's of the Margin, Border, and Padding.</summary>
  244. internal void SetAdornmentFrames ()
  245. {
  246. if (this is Adornment)
  247. {
  248. // Adornments do not have Adornments
  249. return;
  250. }
  251. if (Margin is { })
  252. {
  253. Margin!.Frame = Rectangle.Empty with { Size = Frame.Size };
  254. }
  255. if (Border is { } && Margin is { })
  256. {
  257. Border!.Frame = Margin!.Thickness.GetInside (Margin!.Frame);
  258. }
  259. if (Padding is { } && Border is { })
  260. {
  261. Padding!.Frame = Border!.Thickness.GetInside (Border!.Frame);
  262. }
  263. }
  264. }