DimAuto.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #nullable enable
  2. namespace Terminal.Gui;
  3. /// <summary>
  4. /// Represents a dimension that automatically sizes the view to fit all the view's Content, SubViews, and/or Text.
  5. /// </summary>
  6. /// <remarks>
  7. /// <para>
  8. /// See <see cref="DimAutoStyle"/>.
  9. /// </para>
  10. /// <para>
  11. /// This is a low-level API that is typically used internally by the layout system. Use the various static
  12. /// methods on the <see cref="Dim"/> class to create <see cref="Dim"/> objects instead.
  13. /// </para>
  14. /// </remarks>
  15. public class DimAuto () : Dim
  16. {
  17. private readonly Dim? _maximumContentDim;
  18. /// <summary>
  19. /// Gets the maximum dimension the View's ContentSize will be fit to. NOT CURRENTLY SUPPORTED.
  20. /// </summary>
  21. // ReSharper disable once ConvertToAutoProperty
  22. public required Dim? MaximumContentDim
  23. {
  24. get => _maximumContentDim;
  25. init => _maximumContentDim = value;
  26. }
  27. private readonly Dim? _minimumContentDim;
  28. /// <summary>
  29. /// Gets the minimum dimension the View's ContentSize will be constrained to.
  30. /// </summary>
  31. // ReSharper disable once ConvertToAutoProperty
  32. public required Dim? MinimumContentDim
  33. {
  34. get => _minimumContentDim;
  35. init => _minimumContentDim = value;
  36. }
  37. private readonly DimAutoStyle _style;
  38. /// <summary>
  39. /// Gets the style of the DimAuto.
  40. /// </summary>
  41. // ReSharper disable once ConvertToAutoProperty
  42. public required DimAutoStyle Style
  43. {
  44. get => _style;
  45. init => _style = value;
  46. }
  47. /// <inheritdoc/>
  48. public override string ToString () { return $"Auto({Style},{MinimumContentDim},{MaximumContentDim})"; }
  49. internal override int Calculate (int location, int superviewContentSize, View us, Dimension dimension)
  50. {
  51. var textSize = 0;
  52. var subviewsSize = 0;
  53. int autoMin = MinimumContentDim?.GetAnchor (superviewContentSize) ?? 0;
  54. int autoMax = MaximumContentDim?.GetAnchor (superviewContentSize) ?? int.MaxValue;
  55. if (Style.FastHasFlags (DimAutoStyle.Text))
  56. {
  57. textSize = int.Max (autoMin, dimension == Dimension.Width ? us.TextFormatter.Size.Width : us.TextFormatter.Size.Height);
  58. }
  59. if (Style.FastHasFlags (DimAutoStyle.Content))
  60. {
  61. if (us._contentSize is { })
  62. {
  63. subviewsSize = dimension == Dimension.Width ? us.ContentSize.Width : us.ContentSize.Height;
  64. }
  65. else
  66. {
  67. // TODO: This whole body of code is a WIP (for https://github.com/gui-cs/Terminal.Gui/pull/3451).
  68. subviewsSize = 0;
  69. List<View> visibleSubviews = us.Subviews.Where (v => v.Visible).ToList ();
  70. List<View> subviews;
  71. #region Not Anchored and Are Not Dependent
  72. // Start with subviews that are not anchored to the end, aligned, or dependent on content size
  73. // [x] PosAnchorEnd
  74. // [x] PosAlign
  75. // [ ] PosCenter
  76. // [ ] PosPercent
  77. // [ ] PosView
  78. // [ ] PosFunc
  79. // [x] DimFill
  80. // [ ] DimPercent
  81. // [ ] DimFunc
  82. // [ ] DimView
  83. if (dimension == Dimension.Width)
  84. {
  85. subviews = visibleSubviews.Where (v => v.X is not PosAnchorEnd
  86. && v.X is not PosAlign
  87. // && v.X is not PosCenter
  88. && v.Width is not DimFill).ToList ();
  89. }
  90. else
  91. {
  92. subviews = visibleSubviews.Where (v => v.Y is not PosAnchorEnd
  93. && v.Y is not PosAlign
  94. // && v.Y is not PosCenter
  95. && v.Height is not DimFill).ToList ();
  96. }
  97. for (var i = 0; i < subviews.Count; i++)
  98. {
  99. View v = subviews [i];
  100. int size = dimension == Dimension.Width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height;
  101. if (size > subviewsSize)
  102. {
  103. // BUGBUG: Should we break here? Or choose min/max?
  104. subviewsSize = size;
  105. }
  106. }
  107. #endregion Not Anchored and Are Not Dependent
  108. #region Anchored
  109. // Now, handle subviews that are anchored to the end
  110. // [x] PosAnchorEnd
  111. if (dimension == Dimension.Width)
  112. {
  113. subviews = visibleSubviews.Where (v => v.X is PosAnchorEnd).ToList ();
  114. }
  115. else
  116. {
  117. subviews = visibleSubviews.Where (v => v.Y is PosAnchorEnd).ToList ();
  118. }
  119. int maxAnchorEnd = 0;
  120. for (var i = 0; i < subviews.Count; i++)
  121. {
  122. View v = subviews [i];
  123. maxAnchorEnd = dimension == Dimension.Width ? v.Frame.Width : v.Frame.Height;
  124. }
  125. subviewsSize += maxAnchorEnd;
  126. #endregion Anchored
  127. //#region Center
  128. //// Now, handle subviews that are Centered
  129. //if (dimension == Dimension.Width)
  130. //{
  131. // subviews = us.Subviews.Where (v => v.X is PosCenter).ToList ();
  132. //}
  133. //else
  134. //{
  135. // subviews = us.Subviews.Where (v => v.Y is PosCenter).ToList ();
  136. //}
  137. //int maxCenter = 0;
  138. //for (var i = 0; i < subviews.Count; i++)
  139. //{
  140. // View v = subviews [i];
  141. // maxCenter = dimension == Dimension.Width ? v.Frame.Width : v.Frame.Height;
  142. //}
  143. //subviewsSize += maxCenter;
  144. //#endregion Center
  145. #region Are Dependent
  146. // Now, go back to those that are dependent on content size
  147. // [x] DimFill
  148. // [ ] DimPercent
  149. if (dimension == Dimension.Width)
  150. {
  151. subviews = visibleSubviews.Where (v => v.Width is DimFill
  152. // || v.X is PosCenter
  153. ).ToList ();
  154. }
  155. else
  156. {
  157. subviews = visibleSubviews.Where (v => v.Height is DimFill
  158. //|| v.Y is PosCenter
  159. ).ToList ();
  160. }
  161. int maxFill = 0;
  162. for (var i = 0; i < subviews.Count; i++)
  163. {
  164. View v = subviews [i];
  165. if (dimension == Dimension.Width)
  166. {
  167. v.SetRelativeLayout (new Size (autoMax - subviewsSize, 0));
  168. }
  169. else
  170. {
  171. v.SetRelativeLayout (new Size (0, autoMax - subviewsSize));
  172. }
  173. maxFill = dimension == Dimension.Width ? v.Frame.Width : v.Frame.Height;
  174. }
  175. subviewsSize += maxFill;
  176. #endregion Are Dependent
  177. }
  178. }
  179. // All sizes here are content-relative; ignoring adornments.
  180. // We take the largest of text and content.
  181. int max = int.Max (textSize, subviewsSize);
  182. // And, if min: is set, it wins if larger
  183. max = int.Max (max, autoMin);
  184. // Factor in adornments
  185. Thickness thickness = us.GetAdornmentsThickness ();
  186. max += dimension switch
  187. {
  188. Dimension.Width => thickness.Horizontal,
  189. Dimension.Height => thickness.Vertical,
  190. Dimension.None => 0,
  191. _ => throw new ArgumentOutOfRangeException (nameof (dimension), dimension, null)
  192. };
  193. return int.Min (max, autoMax);
  194. }
  195. internal override bool ReferencesOtherViews ()
  196. {
  197. // BUGBUG: This is not correct. _contentSize may be null.
  198. return false; //_style.HasFlag (DimAutoStyle.Content);
  199. }
  200. /// <inheritdoc/>
  201. public override bool Equals (object? other)
  202. {
  203. if (other is not DimAuto auto)
  204. {
  205. return false;
  206. }
  207. return auto.MinimumContentDim == MinimumContentDim &&
  208. auto.MaximumContentDim == MaximumContentDim &&
  209. auto.Style == Style;
  210. }
  211. /// <inheritdoc/>
  212. public override int GetHashCode ()
  213. {
  214. return HashCode.Combine (MinimumContentDim, MaximumContentDim, Style);
  215. }
  216. }