Layout changes to support Vertical Flow

release
ramonmolossi 2017-09-08 21:14:58 -03:00
parent 1f46a4ca5f
commit dcff7e29a3
1 changed files with 298 additions and 145 deletions

View File

@ -3,50 +3,64 @@
/// Example http://forum.unity3d.com/threads/flowlayoutgroup.296709/ /// Example http://forum.unity3d.com/threads/flowlayoutgroup.296709/
/// Update by Martin Sharkbomb - http://forum.unity3d.com/threads/flowlayoutgroup.296709/#post-1977028 /// 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 /// 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; using System.Collections.Generic;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
/// <summary> /// <summary>
/// Layout Group controller that arranges children in rows, fitting as many on a line until total width exceeds parent bounds /// Layout Group controller that arranges children in bars, fitting as many on a line until total size exceeds parent bounds
/// </summary> /// </summary>
[AddComponentMenu("Layout/Extensions/Flow Layout Group")] [AddComponentMenu("Layout/Extensions/Flow Layout Group")]
public class FlowLayoutGroup : LayoutGroup public class FlowLayoutGroup : LayoutGroup
{ {
public enum Axis { Horizontal = 0, Vertical = 1 }
public float SpacingX = 0f; public float SpacingX = 0f;
public float SpacingY = 0f; public float SpacingY = 0f;
public bool ExpandHorizontalSpacing = false; public bool ExpandHorizontalSpacing = false;
public bool ChildForceExpandWidth = false; public bool ChildForceExpandWidth = false;
public bool ChildForceExpandHeight = false; public bool ChildForceExpandHeight = false;
public bool invertOrder = false;
private float _layoutHeight; 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() public override void CalculateLayoutInputHorizontal()
{ {
if (startAxis == Axis.Horizontal) {
base.CalculateLayoutInputHorizontal(); base.CalculateLayoutInputHorizontal ();
var minWidth = GetGreatestMinimumChildWidth () + padding.left + padding.right;
var minWidth = GetGreatestMinimumChildWidth() + padding.left + padding.right; SetLayoutInputForAxis (minWidth, -1, -1, 0);
} else {
SetLayoutInputForAxis(minWidth, -1, -1, 0); _layoutWidth = SetLayout (0, true);
}
} }
public override void SetLayoutHorizontal() public override void SetLayoutHorizontal()
{ {
SetLayout(rectTransform.rect.width, 0, false); SetLayout(0, false);
} }
public override void SetLayoutVertical() public override void SetLayoutVertical()
{ {
SetLayout(rectTransform.rect.width, 1, false); SetLayout(1, false);
} }
public override void CalculateLayoutInputVertical() 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 protected bool IsCenterAlign
@ -86,9 +100,9 @@ namespace UnityEngine.UI.Extensions
} }
/// <summary> /// <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> /// </summary>
private readonly IList<RectTransform> _rowList = new List<RectTransform>(); private readonly IList<RectTransform> _itemList = new List<RectTransform>();
/// <summary> /// <summary>
/// Main layout method /// Main layout method
@ -96,138 +110,197 @@ namespace UnityEngine.UI.Extensions
/// <param name="width">Width to calculate the layout with</param> /// <param name="width">Width to calculate the layout with</param>
/// <param name="axis">0 for horizontal axis, 1 for vertical</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> /// <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; var groupHeight = rectTransform.rect.height;
var groupWidth = rectTransform.rect.width;
// Width that is available after padding is subtracted float spacingBetweenBars = 0;
var workingWidth = rectTransform.rect.width - padding.left - padding.right; 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;
}
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;
}
// Accumulates the total height of the rows, including spacing and padding. var currentBarSize = 0f;
var yOffset = IsLowerAlign ? (float)padding.bottom : (float)padding.top; var currentBarSpace = 0f;
var currentRowWidth = 0f;
var currentRowHeight = 0f;
for (var i = 0; i < rectChildren.Count; i++) { for (var i = 0; i < rectChildren.Count; i++) {
// LowerAlign works from back to front int index = i;
var index = IsLowerAlign ? rectChildren.Count - 1 - i : 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);
}
var child = rectChildren[index]; // 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) {
var childWidth = LayoutUtility.GetPreferredSize(child, 0); currentBarSize -= spacingBetweenElements;
var childHeight = LayoutUtility.GetPreferredSize(child, 1);
// Max child width is layout group width - padding // Process current bar elements positioning
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) { 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);
}
}
var h = CalculateRowVerticalOffset(groupHeight, yOffset, currentRowHeight); // Clear existing bar
LayoutRow(_rowList, currentRowWidth, currentRowHeight, workingWidth, padding.left, h, axis); _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;
} }
// Clear existing row currentBarSize += childSize;
_rowList.Clear(); _itemList.Add (child);
// 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;
}
currentRowWidth += childWidth;
_rowList.Add(child);
// We need the largest element height to determine the starting position of the next line // We need the largest element height to determine the starting position of the next line
if (childHeight > currentRowHeight) { if (childOtherSize > currentBarSpace) {
currentRowHeight = childHeight; currentBarSpace = childOtherSize;
} }
// Don't do this for the last one // Don't do this for the last one
if (i < rectChildren.Count - 1 ) if (i < rectChildren.Count - 1){
currentRowWidth += SpacingX; currentBarSize += spacingBetweenElements;
} }
}
// Layout the final bar
if (!layoutInput) { if (!layoutInput) {
var h = CalculateRowVerticalOffset(groupHeight, yOffset, currentRowHeight); if (startAxis == Axis.Horizontal) {
currentRowWidth -= SpacingX; float newOffset = CalculateRowVerticalOffset (groupHeight, offset, currentBarSpace);
// Layout the final row currentBarSize -= spacingBetweenElements;
LayoutRow(_rowList, currentRowWidth, currentRowHeight, workingWidth - (_rowList.Count > 1 ? SpacingX : 0), padding.left, h, axis); 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(); _itemList.Clear();
// Add the last rows height to the height accumulator // Add the last bar space to the barSpace accumulator
yOffset += currentRowHeight; offset += currentBarSpace;
yOffset += IsLowerAlign ? padding.top : padding.bottom; offset += counterOffset;
if (layoutInput) { if (layoutInput) {
SetLayoutInputForAxis(offset, offset, -1, axis);
if(axis == 1)
SetLayoutInputForAxis(yOffset, yOffset, -1, axis);
} }
return offset;
return yOffset;
} }
private float CalculateRowVerticalOffset(float groupHeight, float yOffset, float currentRowHeight) private float CalculateRowVerticalOffset(float groupHeight, float yOffset, float currentRowHeight)
{ {
float h;
if (IsLowerAlign) { if (IsLowerAlign) {
h = groupHeight - yOffset - currentRowHeight; return groupHeight - yOffset - currentRowHeight;
} else if (IsMiddleAlign) { } else if (IsMiddleAlign) {
h = groupHeight*0.5f - _layoutHeight * 0.5f + yOffset; return groupHeight * 0.5f - _layoutHeight * 0.5f + yOffset;
} else { } else {
h = yOffset; return yOffset;
}
}
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;
} }
return h;
} }
protected void LayoutRow(IList<RectTransform> contents, float rowWidth, float rowHeight, float maxWidth, float xOffset, float yOffset, int axis) protected void LayoutRow(IList<RectTransform> contents, float rowWidth, float rowHeight, float maxWidth, float xOffset, float yOffset, int axis)
{ {
var xPos = xOffset; var xPos = xOffset;
if (!ChildForceExpandWidth && IsCenterAlign) if (!ChildForceExpandWidth && IsCenterAlign) {
xPos += (maxWidth - rowWidth) * 0.5f; xPos += (maxWidth - rowWidth) * 0.5f;
else if (!ChildForceExpandWidth && IsRightAlign) } else if (!ChildForceExpandWidth && IsRightAlign) {
xPos += (maxWidth - rowWidth); xPos += (maxWidth - rowWidth);
}
var extraWidth = 0f; var extraWidth = 0f;
var extraSpacing = 0f; var extraSpacing = 0f;
if (ChildForceExpandWidth) { if (ChildForceExpandWidth) {
extraWidth = (maxWidth - rowWidth)/_rowList.Count; extraWidth = (maxWidth - rowWidth)/_itemList.Count;
} }
else if (ExpandHorizontalSpacing) { else if (ExpandHorizontalSpacing) {
extraSpacing = (maxWidth - rowWidth)/(_rowList.Count - 1); extraSpacing = (maxWidth - rowWidth)/(_itemList.Count - 1);
if (_rowList.Count > 1) { if (_itemList.Count > 1) {
if (IsCenterAlign) if (IsCenterAlign) {
xPos -= extraSpacing * 0.5f * (_rowList.Count - 1); xPos -= extraSpacing * 0.5f * (_itemList.Count - 1);
else if (IsRightAlign) } else if (IsRightAlign) {
xPos -= extraSpacing * (_rowList.Count - 1); xPos -= extraSpacing * (_itemList.Count - 1);
}
} }
} }
for (var j = 0; j < _rowList.Count; j++) { for (var j = 0; j < _itemList.Count; j++) {
var index = IsLowerAlign ? _rowList.Count - 1 - j : j; var index = IsLowerAlign ? _itemList.Count - 1 - j : j;
var rowChild = _rowList[index]; var rowChild = _itemList[index];
var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0) + extraWidth; var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0) + extraWidth;
var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1); var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1);
@ -239,36 +312,116 @@ namespace UnityEngine.UI.Extensions
var yPos = yOffset; var yPos = yOffset;
if (IsMiddleAlign) if (IsMiddleAlign) {
yPos += (rowHeight - rowChildHeight) * 0.5f; yPos += (rowHeight - rowChildHeight) * 0.5f;
else if (IsLowerAlign) } else if (IsLowerAlign) {
yPos += (rowHeight - rowChildHeight); yPos += (rowHeight - rowChildHeight);
}
// if (ExpandHorizontalSpacing && j > 0) {
if (ExpandHorizontalSpacing && j > 0)
xPos += extraSpacing; xPos += extraSpacing;
}
if (axis == 0) if (axis == 0) {
SetChildAlongAxis(rowChild, 0, xPos, rowChildWidth); SetChildAlongAxis (rowChild, 0, xPos, rowChildWidth);
else } else {
SetChildAlongAxis(rowChild, 1, yPos, rowChildHeight); SetChildAlongAxis (rowChild, 1, yPos, rowChildHeight);
}
// Don't do horizontal spacing for the last one // Don't do horizontal spacing for the last one
if (j < _rowList.Count - 1 ) if (j < _itemList.Count - 1) {
xPos += rowChildWidth + SpacingX; 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() public float GetGreatestMinimumChildWidth()
{ {
var max = 0f; var max = 0f;
for (var i = 0; i < rectChildren.Count; i++) { for (var i = 0; i < rectChildren.Count; i++) {
var w = LayoutUtility.GetMinWidth(rectChildren[i]); var w = LayoutUtility.GetMinWidth(rectChildren[i]);
max = Mathf.Max(w, max); 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; return max;
} }
} }