You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CrowdControl/Assets/3rd/StompyRobot/SRF/Scripts/UI/Layout/FlowLayoutGroup.cs

305 lines
9.7 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;
}
}
}