Resolves an issue where the last line in a flow layout group would overflow the rect bounds

Fixes - https://github.com/Unity-UI-Extensions/com.unity.uiextensions/issues/432
pull/435/head
SimonDarksideJ 2023-03-12 11:44:50 +00:00
parent 3e8eaa0643
commit 02da81ef28
1 changed files with 205 additions and 159 deletions

View File

@ -6,6 +6,7 @@
/// Vertical Flow by Ramon Molossi /// Vertical Flow by Ramon Molossi
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
@ -17,27 +18,32 @@ namespace UnityEngine.UI.Extensions
{ {
public enum Axis { Horizontal = 0, Vertical = 1 } public enum Axis { Horizontal = 0, Vertical = 1 }
public float SpacingX = 0f; private float _layoutHeight;
private float _layoutWidth;
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; public bool invertOrder = false;
private float _layoutHeight;
private float _layoutWidth;
[SerializeField] protected Axis m_StartAxis = Axis.Horizontal; [SerializeField]
public Axis startAxis { get { return m_StartAxis; } set { SetProperty(ref m_StartAxis, value); } } 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) { if (StartAxis == Axis.Horizontal)
base.CalculateLayoutInputHorizontal (); {
var minWidth = GetGreatestMinimumChildWidth () + padding.left + padding.right; base.CalculateLayoutInputHorizontal();
SetLayoutInputForAxis (minWidth, -1, -1, 0); var minWidth = GetGreatestMinimumChildWidth() + padding.left + padding.right;
} else { SetLayoutInputForAxis(minWidth, -1, -1, 0);
_layoutWidth = SetLayout (0, true); }
else
{
_layoutWidth = SetLayout(0, true);
} }
} }
@ -54,55 +60,42 @@ namespace UnityEngine.UI.Extensions
public override void CalculateLayoutInputVertical() public override void CalculateLayoutInputVertical()
{ {
if (startAxis == Axis.Horizontal) { if (StartAxis == Axis.Horizontal)
_layoutHeight = SetLayout (1, true); {
} else { _layoutHeight = SetLayout(1, true);
base.CalculateLayoutInputHorizontal (); }
var minHeight = GetGreatestMinimumChildHeigth () + padding.bottom + padding.top; else
SetLayoutInputForAxis (minHeight, -1, -1, 1); {
base.CalculateLayoutInputHorizontal();
var minHeight = GetGreatestMinimumChildHeigth() + padding.bottom + padding.top;
SetLayoutInputForAxis(minHeight, -1, -1, 1);
} }
} }
protected bool IsCenterAlign protected bool IsCenterAlign
{ {
get get => childAlignment == TextAnchor.LowerCenter || childAlignment == TextAnchor.MiddleCenter || childAlignment == TextAnchor.UpperCenter;
{
return childAlignment == TextAnchor.LowerCenter || childAlignment == TextAnchor.MiddleCenter ||
childAlignment == TextAnchor.UpperCenter;
}
} }
protected bool IsRightAlign protected bool IsRightAlign
{ {
get get => childAlignment == TextAnchor.LowerRight || childAlignment == TextAnchor.MiddleRight || childAlignment == TextAnchor.UpperRight;
{
return childAlignment == TextAnchor.LowerRight || childAlignment == TextAnchor.MiddleRight ||
childAlignment == TextAnchor.UpperRight;
}
} }
protected bool IsMiddleAlign protected bool IsMiddleAlign
{ {
get get => childAlignment == TextAnchor.MiddleLeft || childAlignment == TextAnchor.MiddleRight || childAlignment == TextAnchor.MiddleCenter;
{
return childAlignment == TextAnchor.MiddleLeft || childAlignment == TextAnchor.MiddleRight ||
childAlignment == TextAnchor.MiddleCenter;
}
} }
protected bool IsLowerAlign protected bool IsLowerAlign
{ {
get get => childAlignment == TextAnchor.LowerLeft || childAlignment == TextAnchor.LowerRight || childAlignment == TextAnchor.LowerCenter;
{
return childAlignment == TextAnchor.LowerLeft || childAlignment == TextAnchor.LowerRight ||
childAlignment == TextAnchor.LowerCenter;
}
} }
/// <summary> /// <summary>
/// Holds the rects that will make up the current bar being processed /// Holds the rects that will make up the current bar being processed
/// </summary> /// </summary>
private readonly IList<RectTransform> _itemList = new List<RectTransform>(); private readonly IList<RectTransform> _itemList = new List<RectTransform>();
/// <summary> /// <summary>
/// Main layout method /// Main layout method
@ -122,25 +115,34 @@ namespace UnityEngine.UI.Extensions
float counterOffset = 0; float counterOffset = 0;
float groupSize = 0; float groupSize = 0;
float workingSize = 0; float workingSize = 0;
if (startAxis == Axis.Horizontal) { if (StartAxis == Axis.Horizontal)
{
groupSize = groupHeight; groupSize = groupHeight;
workingSize = groupWidth - padding.left - padding.right; workingSize = groupWidth - padding.left - padding.right;
if (IsLowerAlign) { if (IsLowerAlign)
{
offset = (float)padding.bottom; offset = (float)padding.bottom;
counterOffset = (float)padding.top; counterOffset = (float)padding.top;
} else { }
else
{
offset = (float)padding.top; offset = (float)padding.top;
counterOffset = (float)padding.bottom; counterOffset = (float)padding.bottom;
} }
spacingBetweenBars = SpacingY; spacingBetweenBars = SpacingY;
spacingBetweenElements = SpacingX; spacingBetweenElements = SpacingX;
} else if (startAxis == Axis.Vertical) { }
else if (StartAxis == Axis.Vertical)
{
groupSize = groupWidth; groupSize = groupWidth;
workingSize = groupHeight - padding.top - padding.bottom; workingSize = groupHeight - padding.top - padding.bottom;
if (IsRightAlign) { if (IsRightAlign)
{
offset = (float)padding.right; offset = (float)padding.right;
counterOffset = (float)padding.left; counterOffset = (float)padding.left;
} else { }
else
{
offset = (float)padding.left; offset = (float)padding.left;
counterOffset = (float)padding.right; counterOffset = (float)padding.right;
} }
@ -151,50 +153,59 @@ namespace UnityEngine.UI.Extensions
var currentBarSize = 0f; var currentBarSize = 0f;
var currentBarSpace = 0f; var currentBarSpace = 0f;
for (var i = 0; i < rectChildren.Count; i++) { for (var i = 0; i < rectChildren.Count; i++)
{
int index = i; int index = i;
var child = rectChildren [index]; var child = rectChildren[index];
float childSize = 0; float childSize = 0;
float childOtherSize = 0; float childOtherSize = 0;
//get height and width of elements. //get height and width of elements.
if (startAxis == Axis.Horizontal) { if (StartAxis == Axis.Horizontal)
if (invertOrder) { {
index = IsLowerAlign ? rectChildren.Count - 1 - i : i; if (invertOrder)
{
index = IsLowerAlign ? rectChildren.Count - 1 - i : i;
} }
child = rectChildren [index]; child = rectChildren[index];
childSize = LayoutUtility.GetPreferredSize (child, 0); childSize = LayoutUtility.GetPreferredSize(child, 0);
childSize = Mathf.Min (childSize, workingSize); childSize = Mathf.Min(childSize, workingSize);
childOtherSize = LayoutUtility.GetPreferredSize (child, 1); childOtherSize = LayoutUtility.GetPreferredSize(child, 1);
} else if (startAxis == Axis.Vertical) { }
if (invertOrder) { else if (StartAxis == Axis.Vertical)
{
if (invertOrder)
{
index = IsRightAlign ? rectChildren.Count - 1 - i : i; index = IsRightAlign ? rectChildren.Count - 1 - i : i;
} }
child = rectChildren [index]; child = rectChildren[index];
childSize = LayoutUtility.GetPreferredSize (child, 1); childSize = LayoutUtility.GetPreferredSize(child, 1);
childSize = Mathf.Min (childSize, workingSize); childSize = Mathf.Min(childSize, workingSize);
childOtherSize = LayoutUtility.GetPreferredSize (child, 0); childOtherSize = LayoutUtility.GetPreferredSize(child, 0);
} }
// If adding this element would exceed the bounds of the container, // If adding this element would exceed the bounds of the container,
// go to a new bar after processing the current bar // go to a new bar after processing the current bar
if (currentBarSize + childSize > workingSize) { if (currentBarSize + childSize > workingSize)
{
currentBarSize -= spacingBetweenElements; currentBarSize -= spacingBetweenElements;
// Process current bar elements positioning // Process current bar elements positioning
if (!layoutInput) { if (!layoutInput)
if (startAxis == Axis.Horizontal) { {
float newOffset = CalculateRowVerticalOffset (groupSize, offset, currentBarSpace); if (StartAxis == Axis.Horizontal)
LayoutRow (_itemList, currentBarSize, currentBarSpace, workingSize, padding.left, newOffset, axis); {
} else if (startAxis == Axis.Vertical) { float newOffset = CalculateRowVerticalOffset(groupSize, offset, currentBarSpace);
float newOffset = CalculateColHorizontalOffset (groupSize, offset, currentBarSpace); LayoutRow(_itemList, currentBarSize, currentBarSpace, workingSize, padding.left, newOffset, axis);
LayoutCol (_itemList, currentBarSpace, currentBarSize, workingSize, newOffset, padding.top, axis); }
else if (StartAxis == Axis.Vertical)
{
float newOffset = CalculateColHorizontalOffset(groupSize, offset, currentBarSpace);
LayoutCol(_itemList, currentBarSpace, currentBarSize, workingSize, newOffset, padding.top, axis);
} }
} }
// Clear existing bar // Clear existing bar
_itemList.Clear (); _itemList.Clear();
// Add the current bar space to total barSpace accumulator, and reset to 0 for the next row // Add the current bar space to total barSpace accumulator, and reset to 0 for the next row
offset += currentBarSpace; offset += currentBarSpace;
@ -202,31 +213,28 @@ namespace UnityEngine.UI.Extensions
currentBarSpace = 0; currentBarSpace = 0;
currentBarSize = 0; currentBarSize = 0;
} }
currentBarSize += childSize; currentBarSize += childSize;
_itemList.Add (child); _itemList.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 (childOtherSize > currentBarSpace) { currentBarSpace = childOtherSize > currentBarSpace ? childOtherSize : currentBarSpace;
currentBarSpace = childOtherSize;
}
// Don't do this for the last one currentBarSize += spacingBetweenElements;
if (i < rectChildren.Count - 1){
currentBarSize += spacingBetweenElements;
}
} }
// Layout the final bar // Layout the final bar
if (!layoutInput) { if (!layoutInput)
if (startAxis == Axis.Horizontal) { {
float newOffset = CalculateRowVerticalOffset (groupHeight, offset, currentBarSpace); if (StartAxis == Axis.Horizontal)
{
float newOffset = CalculateRowVerticalOffset(groupHeight, offset, currentBarSpace);
currentBarSize -= spacingBetweenElements; currentBarSize -= spacingBetweenElements;
LayoutRow (_itemList, currentBarSize, currentBarSpace, workingSize, padding.left, newOffset, axis); LayoutRow(_itemList, currentBarSize, currentBarSpace, workingSize, padding.left, newOffset, axis);
}else if (startAxis == Axis.Vertical) { }
else if (StartAxis == Axis.Vertical)
{
float newOffset = CalculateColHorizontalOffset(groupWidth, offset, currentBarSpace); float newOffset = CalculateColHorizontalOffset(groupWidth, offset, currentBarSpace);
currentBarSize -= spacingBetweenElements; currentBarSize -= spacingBetweenElements;
LayoutCol(_itemList, currentBarSpace, currentBarSize, workingSize, newOffset, padding.top, axis); LayoutCol(_itemList, currentBarSpace, currentBarSize, workingSize, newOffset, padding.top, axis);
@ -239,7 +247,8 @@ namespace UnityEngine.UI.Extensions
offset += currentBarSpace; offset += currentBarSpace;
offset += counterOffset; offset += counterOffset;
if (layoutInput) { if (layoutInput)
{
SetLayoutInputForAxis(offset, offset, -1, axis); SetLayoutInputForAxis(offset, offset, -1, axis);
} }
return offset; return offset;
@ -247,87 +256,109 @@ namespace UnityEngine.UI.Extensions
private float CalculateRowVerticalOffset(float groupHeight, float yOffset, float currentRowHeight) private float CalculateRowVerticalOffset(float groupHeight, float yOffset, float currentRowHeight)
{ {
if (IsLowerAlign) { if (IsLowerAlign)
{
return groupHeight - yOffset - currentRowHeight; return groupHeight - yOffset - currentRowHeight;
} else if (IsMiddleAlign) {
return groupHeight * 0.5f - _layoutHeight * 0.5f + yOffset;
} else {
return yOffset;
} }
else if (IsMiddleAlign)
{
return groupHeight * 0.5f - _layoutHeight * 0.5f + yOffset;
}
return yOffset;
} }
private float CalculateColHorizontalOffset(float groupWidth, float xOffset, float currentColWidth) private float CalculateColHorizontalOffset(float groupWidth, float xOffset, float currentColWidth)
{ {
if (IsRightAlign) { if (IsRightAlign)
{
return groupWidth - xOffset - currentColWidth; return groupWidth - xOffset - currentColWidth;
} else if (IsCenterAlign) {
return groupWidth * 0.5f - _layoutWidth * 0.5f + xOffset;
} else {
return xOffset;
} }
else if (IsCenterAlign)
{
return groupWidth * 0.5f - _layoutWidth * 0.5f + xOffset;
}
return xOffset;
} }
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)/_itemList.Count; {
extraWidth = (maxWidth - rowWidth) / contents.Count;
} }
else if (ExpandHorizontalSpacing) { else if (ExpandHorizontalSpacing)
extraSpacing = (maxWidth - rowWidth)/(_itemList.Count - 1); {
if (_itemList.Count > 1) { extraSpacing = (maxWidth - rowWidth) / (contents.Count - 1);
if (IsCenterAlign) { if (contents.Count > 1)
xPos -= extraSpacing * 0.5f * (_itemList.Count - 1); {
} else if (IsRightAlign) { if (IsCenterAlign)
xPos -= extraSpacing * (_itemList.Count - 1); {
xPos -= extraSpacing * 0.5f * (contents.Count - 1);
}
else if (IsRightAlign)
{
xPos -= extraSpacing * (contents.Count - 1);
} }
} }
} }
for (var j = 0; j < _itemList.Count; j++) { for (var j = 0; j < contents.Count; j++)
{
var index = IsLowerAlign ? contents.Count - 1 - j : j;
var index = IsLowerAlign ? _itemList.Count - 1 - j : j; var rowChild = contents[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);
if (ChildForceExpandHeight) if (ChildForceExpandHeight)
{
rowChildHeight = rowHeight; rowChildHeight = rowHeight;
}
rowChildWidth = Mathf.Min(rowChildWidth, maxWidth); rowChildWidth = Mathf.Min(rowChildWidth, maxWidth);
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); {
} else { SetChildAlongAxis(rowChild, 0, xPos, rowChildWidth);
SetChildAlongAxis (rowChild, 1, yPos, rowChildHeight); }
else
{
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 < _itemList.Count - 1) { if (j < contents.Count - 1)
{
xPos += rowChildWidth + SpacingX; xPos += rowChildWidth + SpacingX;
} }
} }
@ -337,39 +368,49 @@ namespace UnityEngine.UI.Extensions
{ {
var yPos = yOffset; var yPos = yOffset;
if (!ChildForceExpandHeight && IsMiddleAlign) { if (!ChildForceExpandHeight && IsMiddleAlign)
{
yPos += (maxHeight - colHeight) * 0.5f; yPos += (maxHeight - colHeight) * 0.5f;
} else if (!ChildForceExpandHeight && IsLowerAlign) { }
else if (!ChildForceExpandHeight && IsLowerAlign)
{
yPos += (maxHeight - colHeight); yPos += (maxHeight - colHeight);
} }
var extraHeight = 0f; var extraHeight = 0f;
var extraSpacing = 0f; var extraSpacing = 0f;
if (ChildForceExpandHeight) { if (ChildForceExpandHeight)
extraHeight = (maxHeight - colHeight)/_itemList.Count; {
extraHeight = (maxHeight - colHeight) / contents.Count;
} }
else if (ExpandHorizontalSpacing) { else if (ExpandHorizontalSpacing)
extraSpacing = (maxHeight - colHeight)/(_itemList.Count - 1); {
if (_itemList.Count > 1) { extraSpacing = (maxHeight - colHeight) / (contents.Count - 1);
if (IsMiddleAlign) { if (contents.Count > 1)
yPos -= extraSpacing * 0.5f * (_itemList.Count - 1); {
} else if (IsLowerAlign) { if (IsMiddleAlign)
yPos -= extraSpacing * (_itemList.Count - 1); {
yPos -= extraSpacing * 0.5f * (contents.Count - 1);
}
else if (IsLowerAlign)
{
yPos -= extraSpacing * (contents.Count - 1);
} }
} }
} }
for (var j = 0; j < _itemList.Count; j++) { for (var j = 0; j < contents.Count; j++)
{
var index = IsRightAlign ? contents.Count - 1 - j : j;
var index = IsRightAlign ? _itemList.Count - 1 - j : j; var rowChild = contents[index];
var rowChild = _itemList[index]; var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0);
var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0) ;
var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1) + extraHeight; var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1) + extraHeight;
if (ChildForceExpandWidth) { if (ChildForceExpandWidth)
{
rowChildWidth = colWidth; rowChildWidth = colWidth;
} }
@ -377,25 +418,32 @@ namespace UnityEngine.UI.Extensions
var xPos = xOffset; var xPos = xOffset;
if (IsCenterAlign) { if (IsCenterAlign)
{
xPos += (colWidth - rowChildWidth) * 0.5f; xPos += (colWidth - rowChildWidth) * 0.5f;
} else if (IsRightAlign) { }
else if (IsRightAlign)
{
xPos += (colWidth - rowChildWidth); xPos += (colWidth - rowChildWidth);
} }
// if (ExpandHorizontalSpacing && j > 0)
if (ExpandHorizontalSpacing && j > 0) { {
yPos += extraSpacing; yPos += extraSpacing;
} }
if (axis == 0) { if (axis == 0)
SetChildAlongAxis (rowChild, 0, xPos, rowChildWidth); {
} else { SetChildAlongAxis(rowChild, 0, xPos, rowChildWidth);
SetChildAlongAxis (rowChild, 1, yPos, rowChildHeight); }
else
{
SetChildAlongAxis(rowChild, 1, yPos, rowChildHeight);
} }
// Don't do vertical spacing for the last one // Don't do vertical spacing for the last one
if (j < _itemList.Count - 1) { if (j < contents.Count - 1)
{
yPos += rowChildHeight + SpacingY; yPos += rowChildHeight + SpacingY;
} }
} }
@ -404,9 +452,9 @@ namespace UnityEngine.UI.Extensions
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; return max;
@ -415,20 +463,18 @@ namespace UnityEngine.UI.Extensions
public float GetGreatestMinimumChildHeigth() public float GetGreatestMinimumChildHeigth()
{ {
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.GetMinHeight(rectChildren[i]); var w = LayoutUtility.GetMinHeight(rectChildren[i]);
max = Mathf.Max(w, max); max = Mathf.Max(w, max);
} }
return max; return max;
} }
protected override void OnDisable() protected override void OnDisable()
{ {
m_Tracker.Clear(); m_Tracker.Clear();
LayoutRebuilder.MarkLayoutForRebuild(rectTransform); LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
} }
}
}
} }