252 lines
5.8 KiB
C#
252 lines
5.8 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using UnityEngine.Serialization;
|
|
using UnityEngine.UI;
|
|
|
|
namespace UIWidgets
|
|
{
|
|
[AddComponentMenu("UI/Accordion", 350)]
|
|
public class Accordion : MonoBehaviour
|
|
{
|
|
[FormerlySerializedAs("Items")]
|
|
[SerializeField]
|
|
protected List<AccordionItem> items = new List<AccordionItem>();
|
|
|
|
[SerializeField]
|
|
public bool OnlyOneOpen = true;
|
|
|
|
[SerializeField]
|
|
public bool Animate = true;
|
|
|
|
[SerializeField]
|
|
public float AnimationDuration = 0.5f;
|
|
|
|
[SerializeField]
|
|
public AccordionDirection Direction = AccordionDirection.Vertical;
|
|
|
|
[SerializeField]
|
|
public AccordionEvent OnToggleItem = new AccordionEvent();
|
|
|
|
protected List<UnityAction> callbacks = new List<UnityAction>();
|
|
|
|
public List<AccordionItem> Items
|
|
{
|
|
get
|
|
{
|
|
return items;
|
|
}
|
|
protected set
|
|
{
|
|
if (items != null)
|
|
{
|
|
RemoveCallbacks();
|
|
}
|
|
items = value;
|
|
if (items != null)
|
|
{
|
|
AddCallbacks();
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void Start()
|
|
{
|
|
AddCallbacks();
|
|
UpdateLayout();
|
|
}
|
|
|
|
protected virtual void AddCallback(AccordionItem item)
|
|
{
|
|
if (item.Open)
|
|
{
|
|
Open(item, false);
|
|
}
|
|
else
|
|
{
|
|
Close(item, false);
|
|
}
|
|
UnityAction unityAction = delegate
|
|
{
|
|
ToggleItem(item);
|
|
};
|
|
item.ToggleObject.AddComponent<AccordionItemComponent>().OnClick.AddListener(unityAction);
|
|
item.ContentObjectRect = item.ContentObject.transform as RectTransform;
|
|
item.ContentLayoutElement = item.ContentObject.GetComponent<LayoutElement>() ?? item.ContentObject.AddComponent<LayoutElement>();
|
|
item.ContentObjectHeight = item.ContentObjectRect.rect.height;
|
|
if (item.ContentObjectHeight == 0f)
|
|
{
|
|
item.ContentObjectHeight = item.ContentLayoutElement.preferredHeight;
|
|
}
|
|
item.ContentObjectWidth = item.ContentObjectRect.rect.width;
|
|
if (item.ContentObjectWidth == 0f)
|
|
{
|
|
item.ContentObjectWidth = item.ContentLayoutElement.preferredWidth;
|
|
}
|
|
callbacks.Add(unityAction);
|
|
}
|
|
|
|
protected virtual void AddCallbacks()
|
|
{
|
|
Items.ForEach(AddCallback);
|
|
}
|
|
|
|
protected virtual void RemoveCallback(AccordionItem item, int index)
|
|
{
|
|
if (item != null && !(item.ToggleObject == null))
|
|
{
|
|
AccordionItemComponent component = item.ToggleObject.GetComponent<AccordionItemComponent>();
|
|
if (component != null && index < callbacks.Count)
|
|
{
|
|
component.OnClick.RemoveListener(callbacks[index]);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void RemoveCallbacks()
|
|
{
|
|
Items.ForEach(RemoveCallback);
|
|
callbacks.Clear();
|
|
}
|
|
|
|
protected virtual void OnDestroy()
|
|
{
|
|
RemoveCallbacks();
|
|
}
|
|
|
|
public virtual void ToggleItem(AccordionItem item)
|
|
{
|
|
if (item.Open)
|
|
{
|
|
if (!OnlyOneOpen)
|
|
{
|
|
Close(item);
|
|
}
|
|
return;
|
|
}
|
|
if (OnlyOneOpen)
|
|
{
|
|
Items.Where(IsOpen).ForEach(Close);
|
|
}
|
|
Open(item);
|
|
}
|
|
|
|
public virtual void Open(AccordionItem item)
|
|
{
|
|
if (!item.Open)
|
|
{
|
|
if (OnlyOneOpen)
|
|
{
|
|
Items.Where(IsOpen).ForEach(Close);
|
|
}
|
|
Open(item, Animate);
|
|
}
|
|
}
|
|
|
|
public virtual void Close(AccordionItem item)
|
|
{
|
|
if (item.Open)
|
|
{
|
|
Close(item, Animate);
|
|
}
|
|
}
|
|
|
|
protected bool IsOpen(AccordionItem item)
|
|
{
|
|
return item.Open;
|
|
}
|
|
|
|
protected bool IsHorizontal()
|
|
{
|
|
return Direction == AccordionDirection.Horizontal;
|
|
}
|
|
|
|
protected virtual void Open(AccordionItem item, bool animate)
|
|
{
|
|
if (item.CurrentCorutine != null)
|
|
{
|
|
StopCoroutine(item.CurrentCorutine);
|
|
if (IsHorizontal())
|
|
{
|
|
item.ContentObjectRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, item.ContentObjectWidth);
|
|
item.ContentLayoutElement.preferredWidth = item.ContentObjectWidth;
|
|
}
|
|
else
|
|
{
|
|
item.ContentObjectRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, item.ContentObjectHeight);
|
|
item.ContentLayoutElement.preferredHeight = item.ContentObjectHeight;
|
|
}
|
|
item.ContentObject.SetActive(false);
|
|
}
|
|
if (animate)
|
|
{
|
|
item.CurrentCorutine = StartCoroutine(OpenCorutine(item));
|
|
}
|
|
else
|
|
{
|
|
item.ContentObject.SetActive(true);
|
|
OnToggleItem.Invoke(item);
|
|
}
|
|
item.ContentObject.SetActive(true);
|
|
item.Open = true;
|
|
}
|
|
|
|
protected virtual void Close(AccordionItem item, bool animate)
|
|
{
|
|
if (item.CurrentCorutine != null)
|
|
{
|
|
StopCoroutine(item.CurrentCorutine);
|
|
item.ContentObject.SetActive(true);
|
|
if (IsHorizontal())
|
|
{
|
|
item.ContentObjectRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, item.ContentObjectWidth);
|
|
item.ContentLayoutElement.preferredWidth = item.ContentObjectWidth;
|
|
}
|
|
else
|
|
{
|
|
item.ContentObjectRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, item.ContentObjectHeight);
|
|
item.ContentLayoutElement.preferredHeight = item.ContentObjectHeight;
|
|
}
|
|
}
|
|
if (item.ContentObjectRect != null)
|
|
{
|
|
item.ContentObjectHeight = item.ContentObjectRect.rect.height;
|
|
item.ContentObjectWidth = item.ContentObjectRect.rect.width;
|
|
}
|
|
if (animate)
|
|
{
|
|
item.CurrentCorutine = StartCoroutine(HideCorutine(item));
|
|
return;
|
|
}
|
|
item.ContentObject.SetActive(false);
|
|
item.Open = false;
|
|
OnToggleItem.Invoke(item);
|
|
}
|
|
|
|
protected virtual IEnumerator OpenCorutine(AccordionItem item)
|
|
{
|
|
item.ContentObject.SetActive(true);
|
|
item.Open = true;
|
|
yield return StartCoroutine(Animations.Open(item.ContentObjectRect, AnimationDuration, IsHorizontal()));
|
|
UpdateLayout();
|
|
OnToggleItem.Invoke(item);
|
|
}
|
|
|
|
protected void UpdateLayout()
|
|
{
|
|
Utilites.UpdateLayout(GetComponent<LayoutGroup>());
|
|
}
|
|
|
|
protected virtual IEnumerator HideCorutine(AccordionItem item)
|
|
{
|
|
yield return StartCoroutine(Animations.Collapse(item.ContentObjectRect, AnimationDuration, IsHorizontal()));
|
|
item.Open = false;
|
|
item.ContentObject.SetActive(false);
|
|
UpdateLayout();
|
|
OnToggleItem.Invoke(item);
|
|
}
|
|
}
|
|
}
|