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