| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- namespace SRF.UI.Layout
- {
- using System.Collections.Generic;
- using Internal;
- using UnityEngine;
- using UnityEngine.UI;
- /// <summary>
- /// Layout Group controller that arranges children in rows, fitting as many on a line until total width exceeds parent
- /// bounds
- /// </summary>
- [AddComponentMenu(ComponentMenuPaths.FlowLayoutGroup)]
- public class FlowLayoutGroup : LayoutGroup
- {
- /// <summary>
- /// Holds the rects that will make up the current row being processed
- /// </summary>
- private readonly IList<RectTransform> _rowList = new List<RectTransform>();
- private float _layoutHeight;
- public bool ChildForceExpandHeight = false;
- public bool ChildForceExpandWidth = false;
- public float Spacing = 0f;
- protected bool IsCenterAlign
- {
- get
- {
- return childAlignment == TextAnchor.LowerCenter || childAlignment == TextAnchor.MiddleCenter ||
- childAlignment == TextAnchor.UpperCenter;
- }
- }
- protected bool IsRightAlign
- {
- get
- {
- return childAlignment == TextAnchor.LowerRight || childAlignment == TextAnchor.MiddleRight ||
- childAlignment == TextAnchor.UpperRight;
- }
- }
- protected bool IsMiddleAlign
- {
- get
- {
- return childAlignment == TextAnchor.MiddleLeft || childAlignment == TextAnchor.MiddleRight ||
- childAlignment == TextAnchor.MiddleCenter;
- }
- }
- protected bool IsLowerAlign
- {
- get
- {
- return childAlignment == TextAnchor.LowerLeft || childAlignment == TextAnchor.LowerRight ||
- childAlignment == TextAnchor.LowerCenter;
- }
- }
- public override void CalculateLayoutInputHorizontal()
- {
- base.CalculateLayoutInputHorizontal();
- var minWidth = GetGreatestMinimumChildWidth() + padding.left + padding.right;
- SetLayoutInputForAxis(minWidth, -1, -1, 0);
- }
- public override void SetLayoutHorizontal()
- {
- SetLayout(rectTransform.rect.width, 0, false);
- }
- public override void SetLayoutVertical()
- {
- SetLayout(rectTransform.rect.width, 1, false);
- }
- public override void CalculateLayoutInputVertical()
- {
- _layoutHeight = SetLayout(rectTransform.rect.width, 1, true);
- }
- /// <summary>
- /// Main layout method
- /// </summary>
- /// <param name="width">Width to calculate the layout with</param>
- /// <param name="axis">0 for horizontal axis, 1 for vertical</param>
- /// <param name="layoutInput">If true, sets the layout input for the axis. If false, sets child position for axis</param>
- public float SetLayout(float width, int axis, bool layoutInput)
- {
- var groupHeight = rectTransform.rect.height;
- // Width that is available after padding is subtracted
- var workingWidth = rectTransform.rect.width - padding.left - padding.right;
- // Accumulates the total height of the rows, including spacing and padding.
- var yOffset = IsLowerAlign ? padding.bottom : (float)padding.top;
- var currentRowWidth = 0f;
- var currentRowHeight = 0f;
- for (var i = 0; i < rectChildren.Count; i++)
- {
- // LowerAlign works from back to front
- var index = IsLowerAlign ? rectChildren.Count - 1 - i : i;
- var child = rectChildren[index];
- var childWidth = LayoutUtility.GetPreferredSize(child, 0);
- var childHeight = LayoutUtility.GetPreferredSize(child, 1);
- // Max child width is layout group with - padding
- childWidth = Mathf.Min(childWidth, workingWidth);
- // Apply spacing if not the first element in a row
- if (_rowList.Count > 0)
- {
- currentRowWidth += Spacing;
- }
- // If adding this element would exceed the bounds of the row,
- // go to a new line after processing the current row
- if (currentRowWidth + childWidth > workingWidth)
- {
- // Undo spacing addition if we're moving to a new line (Spacing is not applied on edges)
- currentRowWidth -= Spacing;
- // Process current row elements positioning
- if (!layoutInput)
- {
- var h = CalculateRowVerticalOffset(groupHeight, yOffset, currentRowHeight);
- LayoutRow(_rowList, currentRowWidth, currentRowHeight, workingWidth, padding.left, h, axis);
- }
- // Clear existing row
- _rowList.Clear();
- // Add the current row height to total height accumulator, and reset to 0 for the next row
- yOffset += currentRowHeight;
- yOffset += Spacing;
- currentRowHeight = 0;
- currentRowWidth = 0;
- }
- currentRowWidth += childWidth;
- _rowList.Add(child);
- // We need the largest element height to determine the starting position of the next line
- if (childHeight > currentRowHeight)
- {
- currentRowHeight = childHeight;
- }
- }
- if (!layoutInput)
- {
- var h = CalculateRowVerticalOffset(groupHeight, yOffset, currentRowHeight);
- // Layout the final row
- LayoutRow(_rowList, currentRowWidth, currentRowHeight, workingWidth, padding.left, h, axis);
- }
- _rowList.Clear();
- // Add the last rows height to the height accumulator
- yOffset += currentRowHeight;
- yOffset += IsLowerAlign ? padding.top : padding.bottom;
- if (layoutInput)
- {
- if (axis == 1)
- {
- SetLayoutInputForAxis(yOffset, yOffset, -1, axis);
- }
- }
- return yOffset;
- }
- private float CalculateRowVerticalOffset(float groupHeight, float yOffset, float currentRowHeight)
- {
- float h;
- if (IsLowerAlign)
- {
- h = groupHeight - yOffset - currentRowHeight;
- }
- else if (IsMiddleAlign)
- {
- h = groupHeight * 0.5f - _layoutHeight * 0.5f + yOffset;
- }
- else
- {
- h = yOffset;
- }
- return h;
- }
- protected void LayoutRow(IList<RectTransform> contents, float rowWidth, float rowHeight, float maxWidth,
- float xOffset, float yOffset, int axis)
- {
- var xPos = xOffset;
- if (!ChildForceExpandWidth && IsCenterAlign)
- {
- xPos += (maxWidth - rowWidth) * 0.5f;
- }
- else if (!ChildForceExpandWidth && IsRightAlign)
- {
- xPos += (maxWidth - rowWidth);
- }
- var extraWidth = 0f;
- if (ChildForceExpandWidth)
- {
- var flexibleChildCount = 0;
- for (var i = 0; i < _rowList.Count; i++)
- {
- if (LayoutUtility.GetFlexibleWidth(_rowList[i]) > 0f)
- {
- flexibleChildCount++;
- }
- }
- if (flexibleChildCount > 0)
- {
- extraWidth = (maxWidth - rowWidth) / flexibleChildCount;
- }
- }
- for (var j = 0; j < _rowList.Count; j++)
- {
- var index = IsLowerAlign ? _rowList.Count - 1 - j : j;
- var rowChild = _rowList[index];
- var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0);
- if (LayoutUtility.GetFlexibleWidth(rowChild) > 0f)
- {
- rowChildWidth += extraWidth;
- }
- var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1);
- if (ChildForceExpandHeight)
- {
- rowChildHeight = rowHeight;
- }
- rowChildWidth = Mathf.Min(rowChildWidth, maxWidth);
- var yPos = yOffset;
- if (IsMiddleAlign)
- {
- yPos += (rowHeight - rowChildHeight) * 0.5f;
- }
- else if (IsLowerAlign)
- {
- yPos += (rowHeight - rowChildHeight);
- }
- if (axis == 0)
- {
- #if UNITY_2019_1
- SetChildAlongAxis(rowChild, 0, 1f, xPos, rowChildWidth);
- #else
- SetChildAlongAxis(rowChild, 0, xPos, rowChildWidth);
- #endif
- }
- else
- {
- #if UNITY_2019_1
- SetChildAlongAxis(rowChild, 1, 1f, yPos, rowChildHeight);
- #else
- SetChildAlongAxis(rowChild, 1, yPos, rowChildHeight);
- #endif
- }
- xPos += rowChildWidth + Spacing;
- }
- }
- public float GetGreatestMinimumChildWidth()
- {
- var max = 0f;
- for (var i = 0; i < rectChildren.Count; i++)
- {
- var w = LayoutUtility.GetMinWidth(rectChildren[i]);
- max = Mathf.Max(w, max);
- }
- return max;
- }
- }
- }
|