Tanoda
FlowLayoutGroup.cs
Go to the documentation of this file.
1
7
8using System.Collections.Generic;
9
11{
15 [AddComponentMenu("Layout/Extensions/Flow Layout Group")]
16 public class FlowLayoutGroup : LayoutGroup
17 {
18 public enum Axis { Horizontal = 0, Vertical = 1 }
19
20 public float SpacingX = 0f;
21 public float SpacingY = 0f;
22 public bool ExpandHorizontalSpacing = false;
23
24 public bool ChildForceExpandWidth = false;
25 public bool ChildForceExpandHeight = false;
26 public bool invertOrder = false;
27 private float _layoutHeight;
28 private float _layoutWidth;
29
30 [SerializeField] protected Axis m_StartAxis = Axis.Horizontal;
31 public Axis startAxis { get { return m_StartAxis; } set { SetProperty(ref m_StartAxis, value); } }
32
33 public override void CalculateLayoutInputHorizontal()
34 {
35 if (startAxis == Axis.Horizontal) {
36 base.CalculateLayoutInputHorizontal ();
37 var minWidth = GetGreatestMinimumChildWidth () + padding.left + padding.right;
38 SetLayoutInputForAxis (minWidth, -1, -1, 0);
39 } else {
40 _layoutWidth = SetLayout (0, true);
41 }
42
43 }
44
45 public override void SetLayoutHorizontal()
46 {
47 SetLayout(0, false);
48 }
49
50 public override void SetLayoutVertical()
51 {
52 SetLayout(1, false);
53 }
54
55 public override void CalculateLayoutInputVertical()
56 {
57 if (startAxis == Axis.Horizontal) {
58 _layoutHeight = SetLayout (1, true);
59 } else {
60 base.CalculateLayoutInputHorizontal ();
61 var minHeight = GetGreatestMinimumChildHeigth () + padding.bottom + padding.top;
62 SetLayoutInputForAxis (minHeight, -1, -1, 1);
63 }
64 }
65
66 protected bool IsCenterAlign
67 {
68 get
69 {
70 return childAlignment == TextAnchor.LowerCenter || childAlignment == TextAnchor.MiddleCenter ||
71 childAlignment == TextAnchor.UpperCenter;
72 }
73 }
74
75 protected bool IsRightAlign
76 {
77 get
78 {
79 return childAlignment == TextAnchor.LowerRight || childAlignment == TextAnchor.MiddleRight ||
80 childAlignment == TextAnchor.UpperRight;
81 }
82 }
83
84 protected bool IsMiddleAlign
85 {
86 get
87 {
88 return childAlignment == TextAnchor.MiddleLeft || childAlignment == TextAnchor.MiddleRight ||
89 childAlignment == TextAnchor.MiddleCenter;
90 }
91 }
92
93 protected bool IsLowerAlign
94 {
95 get
96 {
97 return childAlignment == TextAnchor.LowerLeft || childAlignment == TextAnchor.LowerRight ||
98 childAlignment == TextAnchor.LowerCenter;
99 }
100 }
101
105 private readonly IList<RectTransform> _itemList = new List<RectTransform>();
106
113 public float SetLayout(int axis, bool layoutInput)
114 {
115 //container height and width
116 var groupHeight = rectTransform.rect.height;
117 var groupWidth = rectTransform.rect.width;
118
119 float spacingBetweenBars = 0;
120 float spacingBetweenElements = 0;
121 float offset = 0;
122 float counterOffset = 0;
123 float groupSize = 0;
124 float workingSize = 0;
125 if (startAxis == Axis.Horizontal) {
126 groupSize = groupHeight;
127 workingSize = groupWidth - padding.left - padding.right;
128 if (IsLowerAlign) {
129 offset = (float)padding.bottom;
130 counterOffset = (float)padding.top;
131 } else {
132 offset = (float)padding.top;
133 counterOffset = (float)padding.bottom;
134 }
135 spacingBetweenBars = SpacingY;
136 spacingBetweenElements = SpacingX;
137 } else if (startAxis == Axis.Vertical) {
138 groupSize = groupWidth;
139 workingSize = groupHeight - padding.top - padding.bottom;
140 if (IsRightAlign) {
141 offset = (float)padding.right;
142 counterOffset = (float)padding.left;
143 } else {
144 offset = (float)padding.left;
145 counterOffset = (float)padding.right;
146 }
147 spacingBetweenBars = SpacingX;
148 spacingBetweenElements = SpacingY;
149 }
150
151 var currentBarSize = 0f;
152 var currentBarSpace = 0f;
153
154 for (var i = 0; i < rectChildren.Count; i++) {
155
156 int index = i;
157 var child = rectChildren [index];
158 float childSize = 0;
159 float childOtherSize = 0;
160 //get height and width of elements.
161 if (startAxis == Axis.Horizontal) {
162 if (invertOrder) {
163 index = IsLowerAlign ? rectChildren.Count - 1 - i : i;
164 }
165 child = rectChildren [index];
166 childSize = LayoutUtility.GetPreferredSize (child, 0);
167 childSize = Mathf.Min (childSize, workingSize);
168 childOtherSize = LayoutUtility.GetPreferredSize (child, 1);
169 childOtherSize = Mathf.Min (childOtherSize, workingSize);
170 } else if (startAxis == Axis.Vertical) {
171 if (invertOrder) {
172 index = IsRightAlign ? rectChildren.Count - 1 - i : i;
173 }
174 child = rectChildren [index];
175 childSize = LayoutUtility.GetPreferredSize (child, 1);
176 childSize = Mathf.Min (childSize, workingSize);
177 childOtherSize = LayoutUtility.GetPreferredSize (child, 0);
178 childOtherSize = Mathf.Min (childOtherSize, workingSize);
179 }
180
181 // If adding this element would exceed the bounds of the container,
182 // go to a new bar after processing the current bar
183 if (currentBarSize + childSize > workingSize) {
184
185 currentBarSize -= spacingBetweenElements;
186
187 // Process current bar elements positioning
188 if (!layoutInput) {
189 if (startAxis == Axis.Horizontal) {
190 float newOffset = CalculateRowVerticalOffset (groupSize, offset, currentBarSpace);
191 LayoutRow (_itemList, currentBarSize, currentBarSpace, workingSize, padding.left, newOffset, axis);
192 } else if (startAxis == Axis.Vertical) {
193 float newOffset = CalculateColHorizontalOffset (groupSize, offset, currentBarSpace);
194 LayoutCol (_itemList, currentBarSpace, currentBarSize, workingSize, newOffset, padding.top, axis);
195 }
196 }
197
198 // Clear existing bar
199 _itemList.Clear ();
200
201 // Add the current bar space to total barSpace accumulator, and reset to 0 for the next row
202 offset += currentBarSpace;
203 offset += spacingBetweenBars;
204
205 currentBarSpace = 0;
206 currentBarSize = 0;
207
208 }
209
210 currentBarSize += childSize;
211 _itemList.Add (child);
212
213 // We need the largest element height to determine the starting position of the next line
214 if (childOtherSize > currentBarSpace) {
215 currentBarSpace = childOtherSize;
216 }
217
218 // Don't do this for the last one
219 if (i < rectChildren.Count - 1){
220 currentBarSize += spacingBetweenElements;
221 }
222
223 }
224
225 // Layout the final bar
226 if (!layoutInput) {
227 if (startAxis == Axis.Horizontal) {
228 float newOffset = CalculateRowVerticalOffset (groupHeight, offset, currentBarSpace);
229 currentBarSize -= spacingBetweenElements;
230 LayoutRow (_itemList, currentBarSize, currentBarSpace, workingSize - (ChildForceExpandWidth ? 0 : spacingBetweenElements), padding.left, newOffset, axis);
231 }else if (startAxis == Axis.Vertical) {
232 float newOffset = CalculateColHorizontalOffset(groupWidth, offset, currentBarSpace);
233 currentBarSize -= spacingBetweenElements;
234 LayoutCol(_itemList, currentBarSpace, currentBarSize, workingSize - (ChildForceExpandHeight ? 0 : spacingBetweenElements), newOffset, padding.top, axis);
235 }
236 }
237
238 _itemList.Clear();
239
240 // Add the last bar space to the barSpace accumulator
241 offset += currentBarSpace;
242 offset += counterOffset;
243
244 if (layoutInput) {
245 SetLayoutInputForAxis(offset, offset, -1, axis);
246 }
247 return offset;
248 }
249
250 private float CalculateRowVerticalOffset(float groupHeight, float yOffset, float currentRowHeight)
251 {
252 if (IsLowerAlign) {
253 return groupHeight - yOffset - currentRowHeight;
254 } else if (IsMiddleAlign) {
255 return groupHeight * 0.5f - _layoutHeight * 0.5f + yOffset;
256 } else {
257 return yOffset;
258 }
259 }
260
261 private float CalculateColHorizontalOffset(float groupWidth, float xOffset, float currentColWidth)
262 {
263 if (IsRightAlign) {
264 return groupWidth - xOffset - currentColWidth;
265 } else if (IsCenterAlign) {
266 return groupWidth * 0.5f - _layoutWidth * 0.5f + xOffset;
267 } else {
268 return xOffset;
269 }
270 }
271
272 protected void LayoutRow(IList<RectTransform> contents, float rowWidth, float rowHeight, float maxWidth, float xOffset, float yOffset, int axis)
273 {
274 var xPos = xOffset;
275
277 xPos += (maxWidth - rowWidth) * 0.5f;
278 } else if (!ChildForceExpandWidth && IsRightAlign) {
279 xPos += (maxWidth - rowWidth);
280 }
281
282 var extraWidth = 0f;
283 var extraSpacing = 0f;
284
286 extraWidth = (maxWidth - rowWidth)/_itemList.Count;
287 }
288 else if (ExpandHorizontalSpacing) {
289 extraSpacing = (maxWidth - rowWidth)/(_itemList.Count - 1);
290 if (_itemList.Count > 1) {
291 if (IsCenterAlign) {
292 xPos -= extraSpacing * 0.5f * (_itemList.Count - 1);
293 } else if (IsRightAlign) {
294 xPos -= extraSpacing * (_itemList.Count - 1);
295 }
296 }
297 }
298
299 for (var j = 0; j < _itemList.Count; j++) {
300
301 var index = IsLowerAlign ? _itemList.Count - 1 - j : j;
302
303 var rowChild = _itemList[index];
304
305 var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0) + extraWidth;
306 var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1);
307
309 rowChildHeight = rowHeight;
310
311 rowChildWidth = Mathf.Min(rowChildWidth, maxWidth);
312
313 var yPos = yOffset;
314
315 if (IsMiddleAlign) {
316 yPos += (rowHeight - rowChildHeight) * 0.5f;
317 } else if (IsLowerAlign) {
318 yPos += (rowHeight - rowChildHeight);
319 }
320
321 if (ExpandHorizontalSpacing && j > 0) {
322 xPos += extraSpacing;
323 }
324
325 if (axis == 0) {
326 SetChildAlongAxis (rowChild, 0, xPos, rowChildWidth);
327 } else {
328 SetChildAlongAxis (rowChild, 1, yPos, rowChildHeight);
329 }
330
331 // Don't do horizontal spacing for the last one
332 if (j < _itemList.Count - 1) {
333 xPos += rowChildWidth + SpacingX;
334 }
335 }
336 }
337
338 protected void LayoutCol(IList<RectTransform> contents, float colWidth, float colHeight, float maxHeight, float xOffset, float yOffset, int axis)
339 {
340 var yPos = yOffset;
341
343 yPos += (maxHeight - colHeight) * 0.5f;
344 } else if (!ChildForceExpandHeight && IsLowerAlign) {
345 yPos += (maxHeight - colHeight);
346 }
347
348 var extraHeight = 0f;
349 var extraSpacing = 0f;
350
352 extraHeight = (maxHeight - colHeight)/_itemList.Count;
353 }
354 else if (ExpandHorizontalSpacing) {
355 extraSpacing = (maxHeight - colHeight)/(_itemList.Count - 1);
356 if (_itemList.Count > 1) {
357 if (IsMiddleAlign) {
358 yPos -= extraSpacing * 0.5f * (_itemList.Count - 1);
359 } else if (IsLowerAlign) {
360 yPos -= extraSpacing * (_itemList.Count - 1);
361 }
362 }
363 }
364
365 for (var j = 0; j < _itemList.Count; j++) {
366
367 var index = IsRightAlign ? _itemList.Count - 1 - j : j;
368
369 var rowChild = _itemList[index];
370
371 var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0) ;
372 var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1) + extraHeight;
373
375 rowChildWidth = colWidth;
376 }
377
378 rowChildHeight = Mathf.Min(rowChildHeight, maxHeight);
379
380 var xPos = xOffset;
381
382 if (IsCenterAlign) {
383 xPos += (colWidth - rowChildWidth) * 0.5f;
384 } else if (IsRightAlign) {
385 xPos += (colWidth - rowChildWidth);
386 }
387
388 //
389 if (ExpandHorizontalSpacing && j > 0) {
390 yPos += extraSpacing;
391 }
392
393 if (axis == 0) {
394 SetChildAlongAxis (rowChild, 0, xPos, rowChildWidth);
395 } else {
396 SetChildAlongAxis (rowChild, 1, yPos, rowChildHeight);
397 }
398
399 // Don't do vertical spacing for the last one
400 if (j < _itemList.Count - 1) {
401 yPos += rowChildHeight + SpacingY;
402 }
403 }
404 }
405
407 {
408 var max = 0f;
409 for (var i = 0; i < rectChildren.Count; i++) {
410 var w = LayoutUtility.GetMinWidth(rectChildren[i]);
411
412 max = Mathf.Max(w, max);
413 }
414 return max;
415 }
416
418 {
419 var max = 0f;
420 for (var i = 0; i < rectChildren.Count; i++) {
421 var w = LayoutUtility.GetMinHeight(rectChildren[i]);
422
423 max = Mathf.Max(w, max);
424 }
425 return max;
426 }
427 }
428}
Layout Group controller that arranges children in bars, fitting as many on a line until total size ex...
float SetLayout(int axis, bool layoutInput)
Main layout method
void LayoutRow(IList< RectTransform > contents, float rowWidth, float rowHeight, float maxWidth, float xOffset, float yOffset, int axis)
void LayoutCol(IList< RectTransform > contents, float colWidth, float colHeight, float maxHeight, float xOffset, float yOffset, int axis)
Credit Erdener Gonenc - @PixelEnvision.