137 lines
4.7 KiB
C#
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);
|
|
}
|
|
}
|