Files
Convention-Unity/Convention/[Visual]/UIComponent/ModernUIDropdown.cs
2025-07-21 15:58:52 +08:00

413 lines
13 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using static Convention.ProjectContextLabelAttribute;
namespace Convention.WindowsUI
{
public class ModernUIDropdown : WindowUIModule, IActionInvoke<string>, ITitle
{
// Resources
[Resources, OnlyNotNullMode] public GameObject triggerObject;
[Resources, OnlyNotNullMode] public Transform itemParent;
[Resources, OnlyNotNullMode] public GameObject itemPrefab;
[Resources, OnlyNotNullMode] public GameObject scrollbar;
private VerticalLayoutGroup itemList;
private Transform currentListParent;
[Resources, WhenAttribute.Is(nameof(isListItem), true)] public Transform listParent;
private Animator dropdownAnimator;
[Resources, OnlyNotNullMode] public TextMeshProUGUI m_Title;
private Button m_RawButton;
// Settings
[Setting] public bool enableTrigger = true;
[Setting] public bool enableScrollbar = true;
[Setting] public bool setHighPriorty = true;
[Setting] public bool outOnPointerExit = false;
[Setting] public bool isListItem = false;
[Setting] public AnimationType animationType = AnimationType.FADING;
[Setting] public UnityEvent<string> OnSelect = new();
[Setting] public bool isMutiSelect = false;
// Items
[Content] public List<Item> dropdownItems = new();
// Other variables
bool isOn;
[Content] public int siblingIndex = 0;
private bool m_interactable = true;
public bool interactable
{
get => m_interactable;
set
{
if (m_interactable != value)
{
m_interactable = value;
RefreshImmediate();
}
}
}
public string title { get => m_Title.text; set => m_Title.text = value; }
public enum AnimationType
{
None,
FADING,
SLIDING,
STYLISH
}
[System.Serializable]
public class Item
{
public string itemName = "Dropdown Item";
[HideInInspector] private Toggle m_ToggleItem;
public Toggle ToggleItem
{
get => m_ToggleItem;
set
{
if (m_ToggleItem != value)
{
m_ToggleItem = value;
if (m_ToggleItem != null)
m_ToggleItem.isOn = lazy_isOn;
lazy_isOn = false;
}
}
}
public UnityEvent<bool> toggleEvents = new();
private bool lazy_isOn = false;
public bool isOn
{
get => ToggleItem == null ? lazy_isOn : ToggleItem.isOn;
set
{
if (ToggleItem == null)
lazy_isOn = value;
else
ToggleItem.SetIsOnWithoutNotify(value);
}
}
}
private void Reset()
{
triggerObject = transform.Find("Trigger").gameObject;
}
[Resources] public RectTransform ResizeBroadcastRect;
public IEnumerator ResizeBroadcast()
{
if (ResizeBroadcastRect == null)
yield break;
for (int i = 0; i < 60; i++)
{
RectTransformExtension.AdjustSizeToContainsChilds(ResizeBroadcastRect);
yield return null;
}
}
private void Start()
{
m_RawButton = GetComponent<Button>();
try
{
dropdownAnimator = this.GetComponent<Animator>();
itemList = itemParent.GetComponent<VerticalLayoutGroup>();
RefreshImmediate();
currentListParent = transform.parent;
}
catch
{
DebugError("Dropdown", "Cannot initalize the object", ContextLabelType.Resources, this);
}
if (enableScrollbar == true)
{
itemList.padding.right = 25;
scrollbar.SetActive(true);
}
else
{
itemList.padding.right = 8;
Destroy(scrollbar);
}
if (setHighPriorty == true)
transform.SetAsLastSibling();
}
[Content, OnlyPlayMode]
public void RefreshImmediate()
{
foreach (Transform child in itemParent)
if (child.gameObject != itemPrefab)
GameObject.Destroy(child.gameObject);
for (int i = 0; i < dropdownItems.Count; ++i)
{
Item current = dropdownItems[i];
GameObject go = Instantiate(itemPrefab, itemParent);
go.SetActive(true);
go.GetComponentInChildren<TMP_Text>().text = current.itemName;
Toggle itemToggle = go.GetComponent<Toggle>();
current.ToggleItem = itemToggle;
itemToggle.isOn = false;
itemToggle.interactable = interactable;
}
foreach (var current in dropdownItems)
{
var itemToggle = current.ToggleItem;
itemToggle.onValueChanged.AddListener(GenerateCallback(current));
}
currentListParent = transform.parent;
UnityAction<bool> GenerateCallback(Item current)
{
void Callback(bool T)
{
if (isMutiSelect)
{
int selectCount = 0;
Item selectedLast = null;
foreach (var item in dropdownItems)
{
if (item.isOn)
{
selectCount++;
selectedLast = item;
}
}
if (selectCount == 0)
this.title = "Empty";
else if (selectCount == 1)
this.title = selectedLast.itemName;
else if (selectCount == dropdownItems.Count)
this.title = "Every";
else
this.title = "Muti";
}
else
{
if (T)
{
foreach (var item in dropdownItems)
{
if (current != item)
item.isOn = false;
}
this.title = current.itemName;
}
else
{
dropdownItems[0].isOn = true;
this.title = dropdownItems[0].itemName;
}
}
current.toggleEvents.Invoke(T);
OnSelect.Invoke(current.itemName);
}
return Callback;
}
}
public void Animate()
{
if (isOn == false && animationType == AnimationType.FADING)
{
dropdownAnimator.Play("Fading In");
isOn = true;
if (isListItem == true)
{
siblingIndex = transform.GetSiblingIndex();
gameObject.transform.SetParent(listParent, true);
}
}
else if (isOn == true && animationType == AnimationType.FADING)
{
dropdownAnimator.Play("Fading Out");
isOn = false;
if (isListItem == true)
{
gameObject.transform.SetParent(currentListParent, true);
gameObject.transform.SetSiblingIndex(siblingIndex);
}
}
else if (isOn == false && animationType == AnimationType.SLIDING)
{
dropdownAnimator.Play("Sliding In");
isOn = true;
if (isListItem == true)
{
siblingIndex = transform.GetSiblingIndex();
gameObject.transform.SetParent(listParent, true);
}
}
else if (isOn == true && animationType == AnimationType.SLIDING)
{
dropdownAnimator.Play("Sliding Out");
isOn = false;
if (isListItem == true)
{
gameObject.transform.SetParent(currentListParent, true);
gameObject.transform.SetSiblingIndex(siblingIndex);
}
}
else if (isOn == false && animationType == AnimationType.STYLISH)
{
dropdownAnimator.Play("Stylish In");
isOn = true;
if (isListItem == true)
{
siblingIndex = transform.GetSiblingIndex();
gameObject.transform.SetParent(listParent, true);
}
}
else if (isOn == true && animationType == AnimationType.STYLISH)
{
dropdownAnimator.Play("Stylish Out");
isOn = false;
if (isListItem == true)
{
gameObject.transform.SetParent(currentListParent, true);
gameObject.transform.SetSiblingIndex(siblingIndex);
}
}
StopCoroutine(nameof(ResizeBroadcast));
StartCoroutine(nameof(ResizeBroadcast));
if (enableTrigger == true && isOn == false)
triggerObject.SetActive(false);
else if (enableTrigger == true && isOn == true)
triggerObject.SetActive(true);
if (outOnPointerExit == true)
triggerObject.SetActive(false);
if (setHighPriorty == true)
transform.SetAsLastSibling();
}
public void OnPointerExit(PointerEventData eventData)
{
if (outOnPointerExit == true)
{
if (isOn == true)
{
Animate();
isOn = false;
}
if (isListItem == true)
gameObject.transform.SetParent(currentListParent, true);
}
}
public void UpdateValues()
{
if (enableScrollbar == true)
{
itemList.padding.right = 25;
scrollbar.SetActive(true);
}
else
{
itemList.padding.right = 8;
scrollbar.SetActive(false);
}
}
public Item CreateOption(string name, params UnityAction<bool>[] actions)
{
Item item = new()
{
itemName = name,
toggleEvents = ConventionUtility.WrapperAction2Event(actions)
};
dropdownItems.Add(item);
return item;
}
public void RemoveOption(params Item[] items)
{
foreach (var item in items)
{
dropdownItems.RemoveAll(T => T == item);
}
RefreshImmediate();
}
[Content, OnlyPlayMode]
public void ClearOptions()
{
dropdownItems.Clear();
RefreshImmediate();
}
public void Select(string option)
{
var target = dropdownItems.FirstOrDefault(T => T.itemName == option);
if (target != default)
{
target.ToggleItem.isOn = true;
title = option;
}
}
public IActionInvoke<string> AddListener(params UnityAction<string>[] action)
{
foreach (var item in action)
{
OnSelect.AddListener(item);
}
return this;
}
public IActionInvoke<string> RemoveListener(params UnityAction<string>[] action)
{
foreach (var item in action)
{
OnSelect.RemoveListener(item);
}
return this;
}
public IActionInvoke<string> RemoveAllListeners()
{
OnSelect.RemoveAllListeners();
return this;
}
}
}