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; | |||
|  |         } | |||
|  |     } | |||
|  | } |