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