PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/FunnyFoods2/Assets/FullInspector2/Modules/tkControl/Controls/tkHorizontalGroup.cs

https://gitlab.com/saneknovco/refregerator
C# | 249 lines | 160 code | 44 blank | 45 comment | 23 complexity | d13448d8af6dfd4e5bbfa6a088233606 MD5 | raw file
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. namespace FullInspector {
  6. public partial class tk<T, TContext> {
  7. /// <summary>
  8. /// Does a horizontal layout similar to GUILayout.Horizontal.
  9. /// </summary>
  10. public class HorizontalGroup : tkControl<T, TContext>, IEnumerable {
  11. private struct SectionItem
  12. #if UNITY_EDITOR
  13. : tkCustomEditor
  14. #endif
  15. {
  16. // We wrap _minWidth and _fillStrength into properties so that
  17. // we can verify their values when the user modifies them in the
  18. // inspector
  19. private float _minWidth;
  20. [ShowInInspector]
  21. public float MinWidth {
  22. get { return _minWidth; }
  23. set { _minWidth = Math.Max(value, 0); }
  24. }
  25. private float _fillStrength;
  26. [ShowInInspector]
  27. public float FillStrength {
  28. get { return _fillStrength; }
  29. set { _fillStrength = Math.Max(value, 0); }
  30. }
  31. /// <summary>
  32. /// Should this layout item match the height of the rect passed into the horizontal group?
  33. /// If this is true, then the subrect will not have its height trimmed. Note that for the
  34. /// total group height calculation, this is not used -- this means that the minimum height
  35. /// passed to a control will always be >= its requested height, just when MatchParentHeight
  36. /// is true the height passed into Edit may be > than the height requested from GetHeight.
  37. /// </summary>
  38. public bool MatchParentHeight;
  39. public tkControl<T, TContext> Rule;
  40. // The following variables are used inside of the layout logic.
  41. public bool Layout_IsFlexible;
  42. public float Layout_FlexibleWidth;
  43. public float Layout_Width {
  44. get {
  45. if (Layout_IsFlexible) return Layout_FlexibleWidth;
  46. return MinWidth;
  47. }
  48. }
  49. // TODO: Support DLL-based builds for this (small) feature
  50. #if UNITY_EDITOR
  51. public tkControlEditor GetEditor() {
  52. return new tkControlEditor(
  53. new tk<SectionItem, tkDefaultContext>.VerticalGroup {
  54. new tk<SectionItem, tkDefaultContext>.PropertyEditor("MinWidth"),
  55. new tk<SectionItem, tkDefaultContext>.PropertyEditor("FillStrength"),
  56. new tk<SectionItem, tkDefaultContext>.PropertyEditor("MatchParentHeight"),
  57. new tk<SectionItem, tkDefaultContext>.StyleProxy {
  58. Style = new tk<SectionItem, tkDefaultContext>.ReadOnly(),
  59. Control =
  60. new tk<SectionItem, tkDefaultContext>.VerticalGroup {
  61. new tk<SectionItem, tkDefaultContext>.Label("Runtime Layout Information", FontStyle.Bold,
  62. new tk<SectionItem, tkDefaultContext>.VerticalGroup {
  63. new tk<SectionItem, tkDefaultContext>.HorizontalGroup {
  64. { 130, new tk<SectionItem, tkDefaultContext>.PropertyEditor("Flexible?", "Layout_IsFlexible") },
  65. new tk<SectionItem, tkDefaultContext>.PropertyEditor("FlexWidth", "Layout_FlexibleWidth")
  66. },
  67. new tk<SectionItem, tkDefaultContext>.PropertyEditor("Used Width", "Layout_Width")
  68. })
  69. }
  70. },
  71. new tk<SectionItem, tkDefaultContext>.PropertyEditor("Rule")
  72. });
  73. }
  74. #endif
  75. }
  76. [ShowInInspector]
  77. private readonly List<SectionItem> _items = new List<SectionItem>();
  78. private static readonly tkControl<T, TContext> DefaultRule = new VerticalGroup();
  79. protected override IEnumerable<tkIControl> NonMemberChildControls {
  80. get {
  81. foreach (var item in _items) {
  82. yield return item.Rule;
  83. }
  84. }
  85. }
  86. /// <summary>
  87. /// Create an rule with auto width.
  88. /// </summary>
  89. public void Add(tkControl<T, TContext> rule) {
  90. InternalAdd(false, 0, 1, rule);
  91. }
  92. /// <summary>
  93. /// Create a rule with auto width that can control if it matches the parent height.
  94. /// </summary>
  95. /// <param name="matchParentHeight">If true, then the height of the rect passed to the
  96. /// rule will be equal to the height of the overall rect passed to this horizontal group.</param>
  97. public void Add(bool matchParentHeight, tkControl<T, TContext> rule) {
  98. InternalAdd(matchParentHeight, 0, 1, rule);
  99. }
  100. /// <summary>
  101. /// Create a divider.
  102. /// </summary>
  103. public void Add(float width) {
  104. InternalAdd(false, width, 0, DefaultRule);
  105. }
  106. /// <summary>
  107. /// Create a rule with the specific width.
  108. /// </summary>
  109. public void Add(float width, tkControl<T, TContext> rule) {
  110. InternalAdd(false, width, 0, rule);
  111. }
  112. private void InternalAdd(bool matchParentHeight, float width, float fillStrength, tkControl<T, TContext> rule) {
  113. if (width < 0) throw new ArgumentException("width must be >= 0");
  114. if (fillStrength < 0) throw new ArgumentException("fillStrength must be >= 0");
  115. _items.Add(new SectionItem {
  116. MatchParentHeight = matchParentHeight,
  117. MinWidth = width,
  118. FillStrength = fillStrength,
  119. Rule = rule
  120. });
  121. }
  122. private void DoLayout(Rect rect, T obj, TContext context, fiGraphMetadata metadata) {
  123. // The layout algorithm is relatively simple once you understand what is going on.
  124. // It can run up to N times where N is the number of items that have a fill strength > 0.
  125. //
  126. // The idea is simple: we initially treat every item that wants to be flexible as flexible. If
  127. // that item has a minimum width and it will not be met using the flexible layout logic, then
  128. // we rerun the layout except that item is treated as a fixed-width item instead.
  129. //
  130. // We complete the algorithm when every item has met its minimum width requirement.
  131. // step1: Try to treat every item that wants to be flexible as flexible.
  132. for (int i = 0; i < _items.Count; ++i) {
  133. SectionItem item = _items[i];
  134. item.Layout_IsFlexible = item.FillStrength > 0;
  135. _items[i] = item;
  136. }
  137. // step2..n: Iterate until each item has a width greater than its minimum width.
  138. while (true) {
  139. tryAgain:
  140. float requiredMinSpace = 0;
  141. float totalFillStrength = 0;
  142. for (int i = 0; i < _items.Count; ++i) {
  143. var item = _items[i];
  144. if (item.Rule.ShouldShow(obj, context, metadata) == false) continue;
  145. if (item.Layout_IsFlexible) {
  146. totalFillStrength += item.FillStrength;
  147. }
  148. else {
  149. requiredMinSpace += item.MinWidth;
  150. }
  151. }
  152. float growableSpace = rect.width - requiredMinSpace;
  153. for (int i = 0; i < _items.Count; ++i) {
  154. var item = _items[i];
  155. if (item.Rule.ShouldShow(obj, context, metadata) == false) continue;
  156. if (item.Layout_IsFlexible) {
  157. item.Layout_FlexibleWidth = growableSpace * item.FillStrength / totalFillStrength;
  158. _items[i] = item;
  159. // There is not enough flexible room for this item; try again but with this item at
  160. // its fixed width. We have to rerun the entire algorithm because setting an item
  161. // to fixed width can alter how wide flexible items before us are.
  162. if (item.Layout_FlexibleWidth < item.MinWidth) {
  163. item.Layout_IsFlexible = false;
  164. _items[i] = item;
  165. goto tryAgain;
  166. }
  167. }
  168. }
  169. break;
  170. }
  171. }
  172. protected override T DoEdit(Rect rect, T obj, TContext context, fiGraphMetadata metadata) {
  173. DoLayout(rect, obj, context, metadata);
  174. for (int i = 0; i < _items.Count; ++i) {
  175. var item = _items[i];
  176. if (item.Rule.ShouldShow(obj, context, metadata) == false) continue;
  177. float width = item.Layout_Width;
  178. Rect itemRect = rect;
  179. itemRect.width = width;
  180. // If we're not matching the parent height then manually trim the rects height
  181. // so the layout gets a rect equal to the height it requests.
  182. if (item.MatchParentHeight == false) {
  183. itemRect.height = item.Rule.GetHeight(obj, context, metadata);
  184. }
  185. obj = item.Rule.Edit(itemRect, obj, context, metadata);
  186. rect.x += width;
  187. }
  188. return obj;
  189. }
  190. protected override float DoGetHeight(T obj, TContext context, fiGraphMetadata metadata) {
  191. float height = 0;
  192. for (int i = 0; i < _items.Count; ++i) {
  193. var item = _items[i];
  194. if (item.Rule.ShouldShow(obj, context, metadata) == false) continue;
  195. height = Math.Max(height, item.Rule.GetHeight(obj, context, metadata));
  196. }
  197. return height;
  198. }
  199. IEnumerator IEnumerable.GetEnumerator() {
  200. // We only implement this so that the inline add syntax works
  201. throw new NotSupportedException();
  202. }
  203. }
  204. }
  205. }