ViewAdornments.cs 9.7 KB

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