MeowmentDebugTool/Packages/com.bywaystudios.meowmentdebugtool/Runtime/WrapLayoutGroup.cs
2026-03-26 14:32:41 +08:00

190 lines
5.9 KiB
C#

using UnityEngine;
using UnityEngine.UI;
namespace MeowmentDebugTool
{
/// <summary>
/// 简单的自动换行布局,用于运行时 Tab 栏。
/// </summary>
public class WrapLayoutGroup : LayoutGroup
{
[SerializeField] private float spacing = 8f;
[SerializeField] private float lineSpacing = 8f;
[SerializeField] private bool childControlWidth = false;
[SerializeField] private bool childControlHeight = false;
public float Spacing
{
get => spacing;
set => spacing = value;
}
public float LineSpacing
{
get => lineSpacing;
set => lineSpacing = value;
}
public override void CalculateLayoutInputHorizontal()
{
base.CalculateLayoutInputHorizontal();
float totalMinWidth = padding.horizontal;
float totalPreferredWidth = padding.horizontal;
for (int i = 0; i < rectChildren.Count; i++)
{
RectTransform child = rectChildren[i];
totalMinWidth += GetChildWidth(child);
totalPreferredWidth += GetChildWidth(child);
if (i < rectChildren.Count - 1)
{
totalMinWidth += spacing;
totalPreferredWidth += spacing;
}
}
SetLayoutInputForAxis(totalMinWidth, totalPreferredWidth, -1f, 0);
}
public override void CalculateLayoutInputVertical()
{
float requiredHeight = CalculateRequiredHeight(GetUsableWidth());
SetLayoutInputForAxis(requiredHeight, requiredHeight, -1f, 1);
}
public override void SetLayoutHorizontal()
{
SetChildrenAlongBothAxis();
}
public override void SetLayoutVertical()
{
SetChildrenAlongBothAxis();
}
private float GetContainerWidth()
{
float selfWidth = rectTransform.rect.width;
float parentWidth = 0f;
if (rectTransform.parent is RectTransform parentRect)
{
parentWidth = parentRect.rect.width;
if (parentWidth > 0f)
{
if (selfWidth > 0f)
{
return Mathf.Min(selfWidth, parentWidth);
}
return parentWidth;
}
}
if (selfWidth > 0f)
{
return selfWidth;
}
return float.MaxValue;
}
private float GetUsableWidth()
{
float width = GetContainerWidth() - padding.horizontal;
return width > 0f ? width : float.MaxValue;
}
private float CalculateRequiredHeight(float usableWidth)
{
if (rectChildren.Count == 0)
{
return padding.vertical;
}
float currentLineWidth = 0f;
float currentLineHeight = 0f;
float totalHeight = padding.vertical;
for (int i = 0; i < rectChildren.Count; i++)
{
RectTransform child = rectChildren[i];
float childWidth = GetChildWidth(child);
float childHeight = GetChildHeight(child);
bool needWrap = currentLineWidth > 0f && currentLineWidth + spacing + childWidth > usableWidth;
if (needWrap)
{
totalHeight += currentLineHeight + lineSpacing;
currentLineWidth = 0f;
currentLineHeight = 0f;
}
if (currentLineWidth > 0f)
{
currentLineWidth += spacing;
}
currentLineWidth += childWidth;
currentLineHeight = Mathf.Max(currentLineHeight, childHeight);
}
totalHeight += currentLineHeight;
return totalHeight;
}
private void SetChildrenAlongBothAxis()
{
float usableWidth = GetUsableWidth();
float containerRight = padding.left + usableWidth;
float x = padding.left;
float y = padding.top;
float currentLineHeight = 0f;
for (int i = 0; i < rectChildren.Count; i++)
{
RectTransform child = rectChildren[i];
float childWidth = GetChildWidth(child);
float childHeight = GetChildHeight(child);
bool needWrap = x > padding.left && x + childWidth > containerRight;
if (needWrap)
{
x = padding.left;
y += currentLineHeight + lineSpacing;
currentLineHeight = 0f;
}
SetChildAlongAxis(child, 0, x, childWidth);
SetChildAlongAxis(child, 1, y, childHeight);
x += childWidth + spacing;
currentLineHeight = Mathf.Max(currentLineHeight, childHeight);
}
}
private float GetChildWidth(RectTransform child)
{
return childControlWidth ? LayoutUtility.GetPreferredWidth(child) : GetPreferredOrRectWidth(child);
}
private float GetChildHeight(RectTransform child)
{
return childControlHeight ? LayoutUtility.GetPreferredHeight(child) : GetPreferredOrRectHeight(child);
}
private static float GetPreferredOrRectWidth(RectTransform child)
{
float preferredWidth = LayoutUtility.GetPreferredWidth(child);
return preferredWidth > 0f ? preferredWidth : child.rect.width;
}
private static float GetPreferredOrRectHeight(RectTransform child)
{
float preferredHeight = LayoutUtility.GetPreferredHeight(child);
return preferredHeight > 0f ? preferredHeight : child.rect.height;
}
}
}