View.Adornments.cs 11 KB

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