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 items = new List(); [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 callbacks = new List(); public List 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().OnClick.AddListener(unityAction); item.ContentObjectRect = item.ContentObject.transform as RectTransform; item.ContentLayoutElement = item.ContentObject.GetComponent() ?? item.ContentObject.AddComponent(); 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(); 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()); } 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); } } }