Files
aibis-dream/Assets/Plugins/CleanFlatUI/Scripts/PopupMenu/PopupMenu.cs
T
2024-10-10 12:46:34 +08:00

580 lines
19 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using TMPro;
namespace RainbowArt.CleanFlatUI
{
[ExecuteAlways]
public class PopupMenu : MonoBehaviour, IPointerDownHandler
{
protected internal class PopupMenuItem : MonoBehaviour, IPointerEnterHandler
{
public TextMeshProUGUI itemText;
public Image itemImage;
public Image itemLine;
public Button button;
public virtual void OnPointerEnter(PointerEventData eventData)
{
EventSystem.current.SetSelectedGameObject(gameObject);
}
}
[SerializeField]
GameObject itemTemplate;
[SerializeField]
TextMeshProUGUI itemText;
[SerializeField]
Image itemImage;
[SerializeField]
Image itemLine;
[SerializeField]
Animator animator;
[SerializeField]
RectOffset padding = new RectOffset();
[SerializeField]
float spacing = 2;
public enum Origin
{
TopStart = 0,
TopCenter,
TopEnd,
BottomStart,
BottomCenter,
BottomEnd,
LeftStart,
LeftCenter,
LeftEnd,
RightStart,
RightCenter,
RightEnd
}
[SerializeField]
Origin origin = Origin.BottomStart;
[Serializable]
public class OptionItem
{
public string text;
public Sprite icon;
public OptionItem()
{
}
public OptionItem(string newText)
{
text = newText;
}
public OptionItem(Sprite newImage)
{
icon = newImage;
}
public OptionItem(string newText, Sprite newImage)
{
text = newText;
icon = newImage;
}
}
[SerializeField]
List<OptionItem> options = new List<OptionItem>();
[Serializable]
public class PopupMenuEvent: UnityEvent<int> { }
[SerializeField]
PopupMenuEvent onValueChanged = new PopupMenuEvent();
List<PopupMenuItem> menuItems = new List<PopupMenuItem>();
GameObject clickerBlocker;
IEnumerator diableCoroutine;
float disableTime = 0.15f;
uint distance = 10;
public RectOffset Padding
{
get => padding;
set
{
padding = value;
}
}
public float Spacing
{
get => spacing;
set
{
spacing = value;
}
}
public PopupMenuEvent OnValueChanged
{
get => onValueChanged;
set
{
onValueChanged = value;
}
}
public void ShowPopupMenu(Vector3 position, float width, float height)
{
if(options.Count > 0)
{
gameObject.SetActive(true);
DestroyAllMenuItems();
DestroyClickBlocker();
SetupTemplate();
CreateAllMenuItems(options);
UpdatePosition(position,width,height);
CreateClickBlocker();
PlayAnimation(true);
}
}
public void AddOptions(List<OptionItem> optionList)
{
options.AddRange(optionList);
}
public void AddOptions(List<string> optionList)
{
for (int i = 0; i < optionList.Count; i++)
{
options.Add(new OptionItem(optionList[i]));
}
}
public void AddOptions(List<Sprite> optionList)
{
for (int i = 0; i < optionList.Count; i++)
{
options.Add(new OptionItem(optionList[i]));
}
}
public void ClearOptions()
{
options.Clear();
}
void SetupTemplate()
{
PopupMenuItem menuItem = itemTemplate.GetComponent<PopupMenuItem>();
if (menuItem == null)
{
menuItem = itemTemplate.AddComponent<PopupMenuItem>();
menuItem.itemText = itemText;
menuItem.itemImage = itemImage;
menuItem.itemLine = itemLine;
menuItem.button = itemTemplate.GetComponent<Button>();
}
itemTemplate.SetActive(false);
}
void CreateAllMenuItems(List<OptionItem> options)
{
float itemWidth = itemTemplate.GetComponent<RectTransform>().rect.width;
RectTransform templateParentTransform = itemTemplate.transform.parent as RectTransform;
int dataCount = options.Count;
float curY = -padding.top;
for (int i = 0; i < dataCount; ++i)
{
OptionItem itemData = options[i];
int index = i;
GameObject go = Instantiate(itemTemplate) as GameObject;
go.transform.SetParent(itemTemplate.gameObject.transform.parent, false);
go.transform.localPosition = Vector3.zero;
go.transform.localEulerAngles = Vector3.zero;
go.SetActive(true);
go.name = "MenuItem" + i;
PopupMenuItem curMenuItem = go.GetComponent<PopupMenuItem>();
menuItems.Add(curMenuItem);
curMenuItem.itemText.text = itemData.text;
if(itemData.icon == null)
{
curMenuItem.itemImage.gameObject.SetActive(false);
curMenuItem.itemImage.sprite = null;
}
else
{
curMenuItem.itemImage.gameObject.SetActive(true);
curMenuItem.itemImage.sprite = itemData.icon;
}
if(curMenuItem.itemLine != null)
{
if(i == (dataCount-1))
{
curMenuItem.itemLine.gameObject.SetActive(false);
}
else
{
curMenuItem.itemLine.gameObject.SetActive(true);
}
}
curMenuItem.button.onClick.RemoveAllListeners();
curMenuItem.button.onClick.AddListener(delegate { OnItemClicked(index); });
RectTransform curRectTransform = go.GetComponent<RectTransform>();
curRectTransform.anchoredPosition3D = new Vector3(padding.left, curY, 0);
float curItemHeight = curRectTransform.rect.height;
curY = curY - curItemHeight;
if (i < (dataCount - 1))
{
curY = curY - spacing;
}
}
gameObject.GetComponent<RectTransform>().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, Mathf.Abs(curY) + padding.bottom);
gameObject.GetComponent<RectTransform>().SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, itemWidth + padding.left+padding.right);
}
Canvas GetRootCanvas()
{
List<Canvas> list = new List<Canvas>();
gameObject.GetComponentsInParent(false, list);
if (list.Count == 0)
{
return null;
}
var listCount = list.Count;
Canvas rootCanvas = list[listCount - 1];
for (int i = 0; i < listCount; i++)
{
if (list[i].isRootCanvas || list[i].overrideSorting)
{
rootCanvas = list[i];
break;
}
}
return rootCanvas;
}
RectTransform GetRootCanvasRectTrans()
{
Canvas rootCanvas = GetRootCanvas();
if (rootCanvas == null)
{
return null;
}
return rootCanvas.GetComponent<RectTransform>();
}
void CreateClickBlocker()
{
Canvas rootCanvas = GetRootCanvas();
if(rootCanvas == null)
{
return;
}
clickerBlocker = new GameObject("ClickBlocker");
RectTransform blockerRect = clickerBlocker.AddComponent<RectTransform>();
blockerRect.anchorMin = new Vector2(0.5f, 0.5f);
blockerRect.anchorMax = new Vector2(0.5f, 0.5f);
blockerRect.pivot = new Vector2(0.5f, 0.5f);
Image blockerImage = clickerBlocker.AddComponent<Image>();
blockerImage.color = Color.clear;
RectTransform rootCanvasRect = rootCanvas.GetComponent<RectTransform>();
float rootCanvasWidth = rootCanvasRect.rect.width;
float rootCanvasHeight = rootCanvasRect.rect.height;
blockerRect.SetParent(rootCanvas.transform, false);
blockerRect.localPosition = Vector3.zero;
blockerRect.SetParent(gameObject.transform, true);
blockerRect.SetAsFirstSibling();
blockerRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, rootCanvasHeight);
blockerRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, rootCanvasWidth);
}
public void OnPointerDown(PointerEventData eventData)
{
HidePopupMenu(true);
}
void OnItemClicked(int index)
{
onValueChanged.Invoke(index);
HidePopupMenu(true);
}
void DestroyAllMenuItems()
{
var itemsCount = menuItems.Count;
for (int i = 0; i < itemsCount; i++)
{
if (menuItems[i] != null)
{
Destroy(menuItems[i].gameObject);
}
}
menuItems.Clear();
}
void DestroyClickBlocker()
{
if(clickerBlocker != null)
{
Destroy(clickerBlocker);
clickerBlocker = null;
}
}
public void HidePopupMenu(bool playAnim)
{
if (diableCoroutine != null)
{
StopCoroutine(diableCoroutine);
diableCoroutine = null;
}
if(playAnim)
{
PlayAnimation(false);
diableCoroutine = DisableTransition();
StartCoroutine(diableCoroutine);
}
else
{
if (animator != null)
{
animator.enabled = false;
animator.gameObject.transform.localScale = Vector3.one;
animator.gameObject.transform.localEulerAngles = Vector3.zero;
}
gameObject.SetActive(false);
DestroyAllMenuItems();
DestroyClickBlocker();
}
}
IEnumerator DisableTransition()
{
yield return new WaitForSeconds(disableTime);
gameObject.SetActive(false);
DestroyAllMenuItems();
}
public bool IsShowing()
{
return gameObject.activeSelf;
}
void UpdatePosition(Vector3 position, float uiWidth, float uiHeight)
{
RectTransform selfRect = GetComponent<RectTransform>();
float width = selfRect.rect.width;
float height = selfRect.rect.height;
float selfPosX = position.x;
float selfPosY = position.y;
switch (origin)
{
case Origin.BottomStart:
{
selfPosY = position.y - distance;
break;
}
case Origin.BottomCenter:
{
selfPosX = position.x + uiWidth/2 - width/2;
selfPosY = position.y - distance;
break;
}
case Origin.BottomEnd:
{
selfPosX = position.x + uiWidth - width;
selfPosY = position.y - distance;
break;
}
case Origin.TopStart:
{
selfPosY = position.y + uiHeight + height + distance;
break;
}
case Origin.TopCenter:
{
selfPosX = position.x + uiWidth/2 - width/2;
selfPosY = position.y + uiHeight + height + distance;
break;
}
case Origin.TopEnd:
{
selfPosX = position.x + uiWidth - width;
selfPosY = position.y + uiHeight + height + distance;
break;
}
case Origin.LeftStart:
{
selfPosX = position.x - width - distance;
selfPosY = position.y + uiHeight;
break;
}
case Origin.LeftCenter:
{
selfPosX = position.x - width - distance;
selfPosY = position.y + uiHeight/2 + height/2;
break;
}
case Origin.LeftEnd:
{
selfPosX = position.x - width - distance;
selfPosY = position.y + height;
break;
}
case Origin.RightStart:
{
selfPosX = position.x + uiWidth + distance;
selfPosY = position.y + uiHeight;
break;
}
case Origin.RightCenter:
{
selfPosX = position.x + uiWidth + distance;
selfPosY = position.y + uiHeight/2 + height/2;
break;
}
case Origin.RightEnd:
{
selfPosX = position.x + uiWidth + distance;
selfPosY = position.y + height;
break;
}
}
Vector3 selfPos = new Vector3(selfPosX,selfPosY,0);
selfRect.anchoredPosition3D = selfPos;
}
void PlayAnimation(bool bShow)
{
if (animator != null)
{
animator.enabled = false;
animator.gameObject.transform.localScale = Vector3.one;
animator.gameObject.transform.localEulerAngles = Vector3.zero;
}
if (animator != null)
{
if(animator.enabled == false)
{
animator.enabled = true;
}
string animationStr = null;
if(bShow)
{
animationStr = "In Right Bottom";
switch (origin)
{
case Origin.BottomStart:
case Origin.RightStart:
{
break;
}
case Origin.TopStart:
case Origin.RightEnd:
{
animationStr = "In Right Top";
break;
}
case Origin.TopEnd:
case Origin.LeftEnd:
{
animationStr = "In Left Top";
break;
}
case Origin.BottomEnd:
case Origin.LeftStart:
{
animationStr = "In Left Bottom";
break;
}
case Origin.TopCenter:
{
animationStr = "In Top Middle";
break;
}
case Origin.BottomCenter:
{
animationStr = "In Bottom Middle";
break;
}
case Origin.LeftCenter:
{
animationStr = "In Left Middle";
break;
}
case Origin.RightCenter:
{
animationStr = "In Right Middle";
break;
}
}
}
else
{
animationStr = "Out Right Bottom";
switch (origin)
{
case Origin.BottomStart:
case Origin.RightStart:
{
break;
}
case Origin.TopStart:
case Origin.RightEnd:
{
animationStr = "Out Right Top";
break;
}
case Origin.TopEnd:
case Origin.LeftEnd:
{
animationStr = "Out Left Top";
break;
}
case Origin.BottomEnd:
case Origin.LeftStart:
{
animationStr = "Out Left Bottom";
break;
}
case Origin.TopCenter:
{
animationStr = "Out Top Middle";
break;
}
case Origin.BottomCenter:
{
animationStr = "Out Bottom Middle";
break;
}
case Origin.LeftCenter:
{
animationStr = "Out Left Middle";
break;
}
case Origin.RightCenter:
{
animationStr = "Out Right Middle";
break;
}
}
}
animator.Play(animationStr,0,0);
}
}
}
}