View.Adornments.cs 11 KB

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