This repository has been archived on 2025-08-02. You can view files and clone it, but cannot push or open issues or pull requests.
Files
Cryville.EEW.Unity/Assets/Cryville.Common/Unity/UI/DockLayoutGroup.cs
2025-02-14 16:06:00 +08:00

137 lines
4.7 KiB
C#

using UnityEngine;
using UnityEngine.UI;
namespace Cryville.Common.Unity.UI {
/// <summary>
/// A <see cref="LayoutGroup" /> that docks its first child element to one side.
/// </summary>
public abstract class DockLayoutGroup : LayoutGroup {
/// <summary>
/// The dock side.
/// </summary>
public enum Side {
/// <summary>
/// Top.
/// </summary>
Top = 0,
/// <summary>
/// Right.
/// </summary>
Right = 1,
/// <summary>
/// Bottom.
/// </summary>
Bottom = 2,
/// <summary>
/// Left.
/// </summary>
Left = 3,
}
[SerializeField]
[Tooltip("The docking side of the first child element.")]
private Side m_side;
/// <summary>
/// The docking side of the first child element.
/// </summary>
public Side DockSide {
get { return m_side; }
set { SetProperty(ref m_side, value); }
}
[SerializeField]
[Tooltip("The slide index. The children slide along the cross axis.")]
private float m_slideIndex;
/// <summary>
/// The slide index. The children slide along the axis.
/// </summary>
public float SlideIndex {
get { return m_slideIndex; }
set { SetProperty(ref m_slideIndex, value); }
}
/// <inheritdoc />
public sealed override void CalculateLayoutInputHorizontal() { base.CalculateLayoutInputHorizontal(); CalcAlongAxis(0); }
/// <inheritdoc />
public sealed override void CalculateLayoutInputVertical() { CalcAlongAxis(1); }
/// <inheritdoc />
public sealed override void SetLayoutHorizontal() { SetChildrenAlongAxis(0); }
/// <inheritdoc />
public sealed override void SetLayoutVertical() { SetChildrenAlongAxis(1); }
private void CalcAlongAxis(int axis) {
int isHorizontal = (int)m_side & 1;
if ((isHorizontal ^ axis) == 1) {
SetLayoutInputForAxis(0, 0, 1, axis); // TODO
}
else {
float padding = isHorizontal == 0 ? m_Padding.horizontal : m_Padding.vertical;
float totalMin = 0, totalPreferred = 0, totalFlexible = 0;
for (int i = 0; i < rectChildren.Count; i++) {
GetChildSizes(rectChildren[i], axis, out float min, out float preferred, out float flexible);
if (min > totalMin) totalMin = min;
if (preferred > totalPreferred) totalPreferred = preferred;
if (flexible > totalFlexible) totalFlexible = flexible;
}
SetLayoutInputForAxis(totalMin + padding, totalPreferred + padding, totalFlexible, axis);
}
}
private void GetChildSizes(RectTransform child, int axis, out float min, out float preferred, out float flexible) {
min = LayoutUtility.GetMinSize(child, axis);
preferred = LayoutUtility.GetPreferredSize(child, axis);
flexible = LayoutUtility.GetFlexibleSize(child, axis);
}
private float GetSlidePosition(float groupHeight, float dockHeight) {
bool d = Mathf.FloorToInt(m_slideIndex - Mathf.Floor(m_slideIndex / 2) * 2) == 0;
int l = Mathf.FloorToInt(m_slideIndex / 2);
float p = m_slideIndex - Mathf.Floor(m_slideIndex);
if (d) return l * groupHeight + p * dockHeight;
else return l * groupHeight + dockHeight + p * (groupHeight - dockHeight);
}
private void SetChildrenAlongAxis(int axis) {
int isHorizontal = (int)m_side & 1;
bool isReversed = m_side == Side.Right || m_side == Side.Bottom;
var rect = rectTransform.rect;
if ((isHorizontal ^ axis) == 1) {
float p0 = isHorizontal == 1 ? m_Padding.left : m_Padding.top;
float p1 = isHorizontal == 1 ? m_Padding.right : m_Padding.bottom;
var gs = rect.size - new Vector2(m_Padding.horizontal, m_Padding.vertical);
if (isHorizontal == 0) gs = new Vector2(gs.y, gs.x);
if (rectChildren.Count == 1) {
SetChildAlongAxis(rectChildren[0], axis, p0, gs.x);
}
else {
float s1 = GetDockElementSize(gs);
float s0 = GetSlidePosition(gs.x, s1);
float a1 = (isHorizontal == 0 ? rect.height : rect.width) - p0 - p1;
for (int i = 0; i < rectChildren.Count; i++) {
var c = rectChildren[i];
bool d = i % 2 == 0;
int l = i / 2;
if (isReversed)
SetChildAlongAxis(c, axis, (d ? a1 - s1 + p0 : p0) - a1 * l + s0, d ? s1 : a1 - s1);
else
SetChildAlongAxis(c, axis, (d ? p0 : s1 + p0) - s0 + a1 * l, d ? s1 : a1 - s1);
}
}
}
else {
float p0 = isHorizontal == 0 ? m_Padding.left : m_Padding.top;
float p1 = isHorizontal == 0 ? m_Padding.right : m_Padding.bottom;
var height = (isHorizontal == 1 ? rect.height : rect.width) - p0 - p1;
for (int i = 0; i < rectChildren.Count; i++) {
SetChildAlongAxis(rectChildren[i], axis, p0, height);
}
}
}
/// <summary>
/// Gets the length of the first child element along the axis.
/// </summary>
/// <param name="groupSize">The size of the layout group.</param>
/// <returns></returns>
protected abstract float GetDockElementSize(Vector2 groupSize);
}
}