DimAuto.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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.ContentSizeTracksViewport)
  62. {
  63. // ContentSize was explicitly set. Ignore subviews.
  64. subviewsSize = dimension == Dimension.Width ? us.GetContentSize ().Width : us.GetContentSize ().Height;
  65. }
  66. else
  67. {
  68. // ContentSize was NOT explicitly set. Use subviews to determine size.
  69. // TODO: This whole body of code is a WIP (for https://github.com/gui-cs/Terminal.Gui/pull/3451).
  70. subviewsSize = 0;
  71. List<View> includedSubviews = us.Subviews.ToList();//.Where (v => !v.ExcludeFromLayout).ToList ();
  72. List<View> subviews;
  73. #region Not Anchored and Are Not Dependent
  74. // Start with subviews that are not anchored to the end, aligned, or dependent on content size
  75. // [x] PosAnchorEnd
  76. // [x] PosAlign
  77. // [ ] PosCenter
  78. // [ ] PosPercent
  79. // [ ] PosView
  80. // [ ] PosFunc
  81. // [x] DimFill
  82. // [ ] DimPercent
  83. // [ ] DimFunc
  84. // [ ] DimView
  85. if (dimension == Dimension.Width)
  86. {
  87. subviews = includedSubviews.Where (v => v.X is not PosAnchorEnd
  88. && v.X is not PosAlign
  89. // && v.X is not PosCenter
  90. && v.Width is not DimFill).ToList ();
  91. }
  92. else
  93. {
  94. subviews = includedSubviews.Where (v => v.Y is not PosAnchorEnd
  95. && v.Y is not PosAlign
  96. // && v.Y is not PosCenter
  97. && v.Height is not DimFill).ToList ();
  98. }
  99. for (var i = 0; i < subviews.Count; i++)
  100. {
  101. View v = subviews [i];
  102. int size = dimension == Dimension.Width ? v.Frame.X + v.Frame.Width : v.Frame.Y + v.Frame.Height;
  103. if (size > subviewsSize)
  104. {
  105. // BUGBUG: Should we break here? Or choose min/max?
  106. subviewsSize = size;
  107. }
  108. }
  109. #endregion Not Anchored and Are Not Dependent
  110. #region Anchored
  111. // Now, handle subviews that are anchored to the end
  112. // [x] PosAnchorEnd
  113. if (dimension == Dimension.Width)
  114. {
  115. subviews = includedSubviews.Where (v => v.X is PosAnchorEnd).ToList ();
  116. }
  117. else
  118. {
  119. subviews = includedSubviews.Where (v => v.Y is PosAnchorEnd).ToList ();
  120. }
  121. int maxAnchorEnd = 0;
  122. for (var i = 0; i < subviews.Count; i++)
  123. {
  124. View v = subviews [i];
  125. maxAnchorEnd = dimension == Dimension.Width ? v.Frame.Width : v.Frame.Height;
  126. }
  127. subviewsSize += maxAnchorEnd;
  128. #endregion Anchored
  129. //#region Center
  130. //// Now, handle subviews that are Centered
  131. //if (dimension == Dimension.Width)
  132. //{
  133. // subviews = us.Subviews.Where (v => v.X is PosCenter).ToList ();
  134. //}
  135. //else
  136. //{
  137. // subviews = us.Subviews.Where (v => v.Y is PosCenter).ToList ();
  138. //}
  139. //int maxCenter = 0;
  140. //for (var i = 0; i < subviews.Count; i++)
  141. //{
  142. // View v = subviews [i];
  143. // maxCenter = dimension == Dimension.Width ? v.Frame.Width : v.Frame.Height;
  144. //}
  145. //subviewsSize += maxCenter;
  146. //#endregion Center
  147. #region Are Dependent
  148. // Now, go back to those that are dependent on content size
  149. // [x] DimFill
  150. // [ ] DimPercent
  151. if (dimension == Dimension.Width)
  152. {
  153. subviews = includedSubviews.Where (v => v.Width is DimFill
  154. // || v.X is PosCenter
  155. ).ToList ();
  156. }
  157. else
  158. {
  159. subviews = includedSubviews.Where (v => v.Height is DimFill
  160. //|| v.Y is PosCenter
  161. ).ToList ();
  162. }
  163. int maxFill = 0;
  164. for (var i = 0; i < subviews.Count; i++)
  165. {
  166. View v = subviews [i];
  167. if (dimension == Dimension.Width)
  168. {
  169. v.SetRelativeLayout (new Size (autoMax - subviewsSize, 0));
  170. }
  171. else
  172. {
  173. v.SetRelativeLayout (new Size (0, autoMax - subviewsSize));
  174. }
  175. maxFill = dimension == Dimension.Width ? v.Frame.Width : v.Frame.Height;
  176. }
  177. subviewsSize += maxFill;
  178. #endregion Are Dependent
  179. }
  180. }
  181. // All sizes here are content-relative; ignoring adornments.
  182. // We take the largest of text and content.
  183. int max = int.Max (textSize, subviewsSize);
  184. // And, if min: is set, it wins if larger
  185. max = int.Max (max, autoMin);
  186. // Factor in adornments
  187. Thickness thickness = us.GetAdornmentsThickness ();
  188. max += dimension switch
  189. {
  190. Dimension.Width => thickness.Horizontal,
  191. Dimension.Height => thickness.Vertical,
  192. Dimension.None => 0,
  193. _ => throw new ArgumentOutOfRangeException (nameof (dimension), dimension, null)
  194. };
  195. return int.Min (max, autoMax);
  196. }
  197. internal override bool ReferencesOtherViews ()
  198. {
  199. // BUGBUG: This is not correct. _contentSize may be null.
  200. return false; //_style.HasFlag (DimAutoStyle.Content);
  201. }
  202. /// <inheritdoc/>
  203. public override bool Equals (object? other)
  204. {
  205. if (other is not DimAuto auto)
  206. {
  207. return false;
  208. }
  209. return auto.MinimumContentDim == MinimumContentDim &&
  210. auto.MaximumContentDim == MaximumContentDim &&
  211. auto.Style == Style;
  212. }
  213. /// <inheritdoc/>
  214. public override int GetHashCode ()
  215. {
  216. return HashCode.Combine (MinimumContentDim, MaximumContentDim, Style);
  217. }
  218. }