305 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
			
		
		
	
	
			305 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
| 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;
 | |
|         }
 | |
|     }
 | |
| }
 |