Layout changes to support Vertical Flow
parent
1f46a4ca5f
commit
dcff7e29a3
|
@ -3,52 +3,66 @@
|
|||
/// Example http://forum.unity3d.com/threads/flowlayoutgroup.296709/
|
||||
/// Update by Martin Sharkbomb - http://forum.unity3d.com/threads/flowlayoutgroup.296709/#post-1977028
|
||||
/// Last item alignment fix by Vicente Russo - https://bitbucket.org/ddreaper/unity-ui-extensions/issues/22/flow-layout-group-align
|
||||
/// Vertical Flow by Ramon Molossi
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Layout Group controller that arranges children in rows, fitting as many on a line until total width exceeds parent bounds
|
||||
/// </summary>
|
||||
[AddComponentMenu("Layout/Extensions/Flow Layout Group")]
|
||||
/// <summary>
|
||||
/// Layout Group controller that arranges children in bars, fitting as many on a line until total size exceeds parent bounds
|
||||
/// </summary>
|
||||
[AddComponentMenu("Layout/Extensions/Flow Layout Group")]
|
||||
public class FlowLayoutGroup : LayoutGroup
|
||||
{
|
||||
public enum Axis { Horizontal = 0, Vertical = 1 }
|
||||
|
||||
public float SpacingX = 0f;
|
||||
public float SpacingY = 0f;
|
||||
public bool ExpandHorizontalSpacing = false;
|
||||
|
||||
|
||||
public bool ChildForceExpandWidth = false;
|
||||
public bool ChildForceExpandHeight = false;
|
||||
|
||||
public bool invertOrder = false;
|
||||
private float _layoutHeight;
|
||||
|
||||
private float _layoutWidth;
|
||||
|
||||
[SerializeField] protected Axis m_StartAxis = Axis.Horizontal;
|
||||
public Axis startAxis { get { return m_StartAxis; } set { SetProperty(ref m_StartAxis, value); } }
|
||||
|
||||
public override void CalculateLayoutInputHorizontal()
|
||||
{
|
||||
|
||||
base.CalculateLayoutInputHorizontal();
|
||||
|
||||
var minWidth = GetGreatestMinimumChildWidth() + padding.left + padding.right;
|
||||
|
||||
SetLayoutInputForAxis(minWidth, -1, -1, 0);
|
||||
|
||||
if (startAxis == Axis.Horizontal) {
|
||||
base.CalculateLayoutInputHorizontal ();
|
||||
var minWidth = GetGreatestMinimumChildWidth () + padding.left + padding.right;
|
||||
SetLayoutInputForAxis (minWidth, -1, -1, 0);
|
||||
} else {
|
||||
_layoutWidth = SetLayout (0, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override void SetLayoutHorizontal()
|
||||
{
|
||||
SetLayout(rectTransform.rect.width, 0, false);
|
||||
SetLayout(0, false);
|
||||
}
|
||||
|
||||
|
||||
public override void SetLayoutVertical()
|
||||
{
|
||||
SetLayout(rectTransform.rect.width, 1, false);
|
||||
SetLayout(1, false);
|
||||
}
|
||||
|
||||
|
||||
public override void CalculateLayoutInputVertical()
|
||||
{
|
||||
_layoutHeight = SetLayout(rectTransform.rect.width, 1, true);
|
||||
if (startAxis == Axis.Horizontal) {
|
||||
_layoutHeight = SetLayout (1, true);
|
||||
} else {
|
||||
base.CalculateLayoutInputHorizontal ();
|
||||
var minHeight = GetGreatestMinimumChildHeigth () + padding.bottom + padding.top;
|
||||
SetLayoutInputForAxis (minHeight, -1, -1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected bool IsCenterAlign
|
||||
{
|
||||
get
|
||||
|
@ -57,7 +71,7 @@ namespace UnityEngine.UI.Extensions
|
|||
childAlignment == TextAnchor.UpperCenter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected bool IsRightAlign
|
||||
{
|
||||
get
|
||||
|
@ -66,7 +80,7 @@ namespace UnityEngine.UI.Extensions
|
|||
childAlignment == TextAnchor.UpperRight;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected bool IsMiddleAlign
|
||||
{
|
||||
get
|
||||
|
@ -75,7 +89,7 @@ namespace UnityEngine.UI.Extensions
|
|||
childAlignment == TextAnchor.MiddleCenter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected bool IsLowerAlign
|
||||
{
|
||||
get
|
||||
|
@ -84,192 +98,331 @@ namespace UnityEngine.UI.Extensions
|
|||
childAlignment == TextAnchor.LowerCenter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Holds the rects that will make up the current row being processed
|
||||
/// Holds the rects that will make up the current bar being processed
|
||||
/// </summary>
|
||||
private readonly IList<RectTransform> _rowList = new List<RectTransform>();
|
||||
|
||||
private readonly IList<RectTransform> _itemList = new List<RectTransform>();
|
||||
|
||||
/// <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)
|
||||
public float SetLayout(int axis, bool layoutInput)
|
||||
{
|
||||
//container height and width
|
||||
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 ? (float)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 width - padding
|
||||
childWidth = Mathf.Min(childWidth, workingWidth);
|
||||
|
||||
// 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) {
|
||||
|
||||
currentRowWidth -= SpacingX;
|
||||
|
||||
// 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 += SpacingY;
|
||||
|
||||
currentRowHeight = 0;
|
||||
currentRowWidth = 0;
|
||||
|
||||
var groupWidth = rectTransform.rect.width;
|
||||
|
||||
float spacingBetweenBars = 0;
|
||||
float spacingBetweenElements = 0;
|
||||
float offset = 0;
|
||||
float counterOffset = 0;
|
||||
float groupSize = 0;
|
||||
float workingSize = 0;
|
||||
if (startAxis == Axis.Horizontal) {
|
||||
groupSize = groupHeight;
|
||||
workingSize = groupWidth - padding.left - padding.right;
|
||||
if (IsLowerAlign) {
|
||||
offset = (float)padding.bottom;
|
||||
counterOffset = (float)padding.top;
|
||||
} else {
|
||||
offset = (float)padding.top;
|
||||
counterOffset = (float)padding.bottom;
|
||||
}
|
||||
|
||||
currentRowWidth += childWidth;
|
||||
_rowList.Add(child);
|
||||
|
||||
spacingBetweenBars = SpacingY;
|
||||
spacingBetweenElements = SpacingX;
|
||||
} else if (startAxis == Axis.Vertical) {
|
||||
groupSize = groupWidth;
|
||||
workingSize = groupHeight - padding.top - padding.bottom;
|
||||
if (IsRightAlign) {
|
||||
offset = (float)padding.right;
|
||||
counterOffset = (float)padding.left;
|
||||
} else {
|
||||
offset = (float)padding.left;
|
||||
counterOffset = (float)padding.right;
|
||||
}
|
||||
spacingBetweenBars = SpacingX;
|
||||
spacingBetweenElements = SpacingY;
|
||||
}
|
||||
|
||||
var currentBarSize = 0f;
|
||||
var currentBarSpace = 0f;
|
||||
|
||||
for (var i = 0; i < rectChildren.Count; i++) {
|
||||
|
||||
int index = i;
|
||||
var child = rectChildren [index];
|
||||
float childSize = 0;
|
||||
float childOtherSize = 0;
|
||||
//get height and width of elements.
|
||||
if (startAxis == Axis.Horizontal) {
|
||||
if (invertOrder) {
|
||||
index = IsLowerAlign ? rectChildren.Count - 1 - i : i;
|
||||
}
|
||||
child = rectChildren [index];
|
||||
childSize = LayoutUtility.GetPreferredSize (child, 0);
|
||||
childSize = Mathf.Min (childSize, workingSize);
|
||||
childOtherSize = LayoutUtility.GetPreferredSize (child, 1);
|
||||
childOtherSize = Mathf.Min (childOtherSize, workingSize);
|
||||
} else if (startAxis == Axis.Vertical) {
|
||||
if (invertOrder) {
|
||||
index = IsRightAlign ? rectChildren.Count - 1 - i : i;
|
||||
}
|
||||
child = rectChildren [index];
|
||||
childSize = LayoutUtility.GetPreferredSize (child, 1);
|
||||
childSize = Mathf.Min (childSize, workingSize);
|
||||
childOtherSize = LayoutUtility.GetPreferredSize (child, 0);
|
||||
childOtherSize = Mathf.Min (childOtherSize, workingSize);
|
||||
}
|
||||
|
||||
// If adding this element would exceed the bounds of the container,
|
||||
// go to a new bar after processing the current bar
|
||||
if (currentBarSize + childSize > workingSize) {
|
||||
|
||||
currentBarSize -= spacingBetweenElements;
|
||||
|
||||
// Process current bar elements positioning
|
||||
if (!layoutInput) {
|
||||
if (startAxis == Axis.Horizontal) {
|
||||
float newOffset = CalculateRowVerticalOffset (groupSize, offset, currentBarSpace);
|
||||
LayoutRow (_itemList, currentBarSize, currentBarSpace, workingSize, padding.left, newOffset, axis);
|
||||
} else if (startAxis == Axis.Vertical) {
|
||||
float newOffset = CalculateColHorizontalOffset (groupSize, offset, currentBarSpace);
|
||||
LayoutCol (_itemList, currentBarSpace, currentBarSize, workingSize, newOffset, padding.top, axis);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear existing bar
|
||||
_itemList.Clear ();
|
||||
|
||||
// Add the current bar space to total barSpace accumulator, and reset to 0 for the next row
|
||||
offset += currentBarSpace;
|
||||
offset += spacingBetweenBars;
|
||||
|
||||
currentBarSpace = 0;
|
||||
currentBarSize = 0;
|
||||
|
||||
}
|
||||
|
||||
currentBarSize += childSize;
|
||||
_itemList.Add (child);
|
||||
|
||||
// We need the largest element height to determine the starting position of the next line
|
||||
if (childHeight > currentRowHeight) {
|
||||
currentRowHeight = childHeight;
|
||||
if (childOtherSize > currentBarSpace) {
|
||||
currentBarSpace = childOtherSize;
|
||||
}
|
||||
|
||||
// Don't do this for the last one
|
||||
if (i < rectChildren.Count - 1 )
|
||||
currentRowWidth += SpacingX;
|
||||
if (i < rectChildren.Count - 1){
|
||||
currentBarSize += spacingBetweenElements;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Layout the final bar
|
||||
if (!layoutInput) {
|
||||
var h = CalculateRowVerticalOffset(groupHeight, yOffset, currentRowHeight);
|
||||
currentRowWidth -= SpacingX;
|
||||
// Layout the final row
|
||||
LayoutRow(_rowList, currentRowWidth, currentRowHeight, workingWidth - (_rowList.Count > 1 ? SpacingX : 0), padding.left, h, axis);
|
||||
if (startAxis == Axis.Horizontal) {
|
||||
float newOffset = CalculateRowVerticalOffset (groupHeight, offset, currentBarSpace);
|
||||
currentBarSize -= spacingBetweenElements;
|
||||
LayoutRow (_itemList, currentBarSize, currentBarSpace, workingSize - (ChildForceExpandWidth ? 0 : spacingBetweenElements), padding.left, newOffset, axis);
|
||||
}else if (startAxis == Axis.Vertical) {
|
||||
float newOffset = CalculateColHorizontalOffset(groupWidth, offset, currentBarSpace);
|
||||
currentBarSize -= spacingBetweenElements;
|
||||
LayoutCol(_itemList, currentBarSpace, currentBarSize, workingSize - (ChildForceExpandHeight ? 0 : spacingBetweenElements), newOffset, padding.top, axis);
|
||||
}
|
||||
}
|
||||
|
||||
_rowList.Clear();
|
||||
|
||||
// Add the last rows height to the height accumulator
|
||||
yOffset += currentRowHeight;
|
||||
yOffset += IsLowerAlign ? padding.top : padding.bottom;
|
||||
|
||||
|
||||
_itemList.Clear();
|
||||
|
||||
// Add the last bar space to the barSpace accumulator
|
||||
offset += currentBarSpace;
|
||||
offset += counterOffset;
|
||||
|
||||
if (layoutInput) {
|
||||
|
||||
if(axis == 1)
|
||||
SetLayoutInputForAxis(yOffset, yOffset, -1, axis);
|
||||
|
||||
SetLayoutInputForAxis(offset, offset, -1, axis);
|
||||
}
|
||||
|
||||
return yOffset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
private float CalculateRowVerticalOffset(float groupHeight, float yOffset, float currentRowHeight)
|
||||
{
|
||||
float h;
|
||||
|
||||
if (IsLowerAlign) {
|
||||
h = groupHeight - yOffset - currentRowHeight;
|
||||
return groupHeight - yOffset - currentRowHeight;
|
||||
} else if (IsMiddleAlign) {
|
||||
h = groupHeight*0.5f - _layoutHeight * 0.5f + yOffset;
|
||||
return groupHeight * 0.5f - _layoutHeight * 0.5f + yOffset;
|
||||
} else {
|
||||
h = yOffset;
|
||||
return yOffset;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
private float CalculateColHorizontalOffset(float groupWidth, float xOffset, float currentColWidth)
|
||||
{
|
||||
if (IsRightAlign) {
|
||||
return groupWidth - xOffset - currentColWidth;
|
||||
} else if (IsCenterAlign) {
|
||||
return groupWidth * 0.5f - _layoutWidth * 0.5f + xOffset;
|
||||
} else {
|
||||
return xOffset;
|
||||
}
|
||||
}
|
||||
|
||||
protected void LayoutRow(IList<RectTransform> contents, float rowWidth, float rowHeight, float maxWidth, float xOffset, float yOffset, int axis)
|
||||
{
|
||||
var xPos = xOffset;
|
||||
|
||||
if (!ChildForceExpandWidth && IsCenterAlign)
|
||||
|
||||
if (!ChildForceExpandWidth && IsCenterAlign) {
|
||||
xPos += (maxWidth - rowWidth) * 0.5f;
|
||||
else if (!ChildForceExpandWidth && IsRightAlign)
|
||||
} else if (!ChildForceExpandWidth && IsRightAlign) {
|
||||
xPos += (maxWidth - rowWidth);
|
||||
|
||||
}
|
||||
|
||||
var extraWidth = 0f;
|
||||
var extraSpacing = 0f;
|
||||
|
||||
if (ChildForceExpandWidth) {
|
||||
extraWidth = (maxWidth - rowWidth)/_rowList.Count;
|
||||
extraWidth = (maxWidth - rowWidth)/_itemList.Count;
|
||||
}
|
||||
else if (ExpandHorizontalSpacing) {
|
||||
extraSpacing = (maxWidth - rowWidth)/(_rowList.Count - 1);
|
||||
if (_rowList.Count > 1) {
|
||||
if (IsCenterAlign)
|
||||
xPos -= extraSpacing * 0.5f * (_rowList.Count - 1);
|
||||
else if (IsRightAlign)
|
||||
xPos -= extraSpacing * (_rowList.Count - 1);
|
||||
extraSpacing = (maxWidth - rowWidth)/(_itemList.Count - 1);
|
||||
if (_itemList.Count > 1) {
|
||||
if (IsCenterAlign) {
|
||||
xPos -= extraSpacing * 0.5f * (_itemList.Count - 1);
|
||||
} else if (IsRightAlign) {
|
||||
xPos -= extraSpacing * (_itemList.Count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < _rowList.Count; j++) {
|
||||
|
||||
var index = IsLowerAlign ? _rowList.Count - 1 - j : j;
|
||||
|
||||
var rowChild = _rowList[index];
|
||||
|
||||
|
||||
for (var j = 0; j < _itemList.Count; j++) {
|
||||
|
||||
var index = IsLowerAlign ? _itemList.Count - 1 - j : j;
|
||||
|
||||
var rowChild = _itemList[index];
|
||||
|
||||
var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0) + extraWidth;
|
||||
var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1);
|
||||
|
||||
|
||||
if (ChildForceExpandHeight)
|
||||
rowChildHeight = rowHeight;
|
||||
|
||||
|
||||
rowChildWidth = Mathf.Min(rowChildWidth, maxWidth);
|
||||
|
||||
|
||||
var yPos = yOffset;
|
||||
|
||||
if (IsMiddleAlign)
|
||||
if (IsMiddleAlign) {
|
||||
yPos += (rowHeight - rowChildHeight) * 0.5f;
|
||||
else if (IsLowerAlign)
|
||||
} else if (IsLowerAlign) {
|
||||
yPos += (rowHeight - rowChildHeight);
|
||||
}
|
||||
|
||||
//
|
||||
if (ExpandHorizontalSpacing && j > 0)
|
||||
if (ExpandHorizontalSpacing && j > 0) {
|
||||
xPos += extraSpacing;
|
||||
}
|
||||
|
||||
if (axis == 0)
|
||||
SetChildAlongAxis(rowChild, 0, xPos, rowChildWidth);
|
||||
else
|
||||
SetChildAlongAxis(rowChild, 1, yPos, rowChildHeight);
|
||||
if (axis == 0) {
|
||||
SetChildAlongAxis (rowChild, 0, xPos, rowChildWidth);
|
||||
} else {
|
||||
SetChildAlongAxis (rowChild, 1, yPos, rowChildHeight);
|
||||
}
|
||||
|
||||
// Don't do horizontal spacing for the last one
|
||||
if (j < _rowList.Count - 1 )
|
||||
if (j < _itemList.Count - 1) {
|
||||
xPos += rowChildWidth + SpacingX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void LayoutCol(IList<RectTransform> contents, float colWidth, float colHeight, float maxHeight, float xOffset, float yOffset, int axis)
|
||||
{
|
||||
var yPos = yOffset;
|
||||
|
||||
if (!ChildForceExpandHeight && IsMiddleAlign) {
|
||||
yPos += (maxHeight - colHeight) * 0.5f;
|
||||
} else if (!ChildForceExpandHeight && IsLowerAlign) {
|
||||
yPos += (maxHeight - colHeight);
|
||||
}
|
||||
|
||||
var extraHeight = 0f;
|
||||
var extraSpacing = 0f;
|
||||
|
||||
if (ChildForceExpandHeight) {
|
||||
extraHeight = (maxHeight - colHeight)/_itemList.Count;
|
||||
}
|
||||
else if (ExpandHorizontalSpacing) {
|
||||
extraSpacing = (maxHeight - colHeight)/(_itemList.Count - 1);
|
||||
if (_itemList.Count > 1) {
|
||||
if (IsMiddleAlign) {
|
||||
yPos -= extraSpacing * 0.5f * (_itemList.Count - 1);
|
||||
} else if (IsLowerAlign) {
|
||||
yPos -= extraSpacing * (_itemList.Count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < _itemList.Count; j++) {
|
||||
|
||||
var index = IsRightAlign ? _itemList.Count - 1 - j : j;
|
||||
|
||||
var rowChild = _itemList[index];
|
||||
|
||||
var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0) ;
|
||||
var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1) + extraHeight;
|
||||
|
||||
if (ChildForceExpandWidth) {
|
||||
rowChildWidth = colWidth;
|
||||
}
|
||||
|
||||
rowChildHeight = Mathf.Min(rowChildHeight, maxHeight);
|
||||
|
||||
var xPos = xOffset;
|
||||
|
||||
if (IsCenterAlign) {
|
||||
xPos += (colWidth - rowChildWidth) * 0.5f;
|
||||
} else if (IsRightAlign) {
|
||||
xPos += (colWidth - rowChildWidth);
|
||||
}
|
||||
|
||||
//
|
||||
if (ExpandHorizontalSpacing && j > 0) {
|
||||
yPos += extraSpacing;
|
||||
}
|
||||
|
||||
if (axis == 0) {
|
||||
SetChildAlongAxis (rowChild, 0, xPos, rowChildWidth);
|
||||
} else {
|
||||
SetChildAlongAxis (rowChild, 1, yPos, rowChildHeight);
|
||||
}
|
||||
|
||||
// Don't do vertical spacing for the last one
|
||||
if (j < _itemList.Count - 1) {
|
||||
yPos += rowChildHeight + SpacingY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public float GetGreatestMinimumChildHeigth()
|
||||
{
|
||||
var max = 0f;
|
||||
for (var i = 0; i < rectChildren.Count; i++) {
|
||||
var w = LayoutUtility.GetMinHeight(rectChildren[i]);
|
||||
|
||||
max = Mathf.Max(w, max);
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue