Files
ichni_Official/Assets/Scripts/UI/Elements/SelectionBox.cs

115 lines
4.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using DG.Tweening;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
namespace Ichni.Menu.UI
{
/// <summary>
/// 可复用的多选弹窗元素。调用方提供标题、内容与任意数量的 <see cref="SelectionBoxOption"/>
/// 本类动态实例化对应按钮,并确保一次显示最多只会执行一个选项回调。
/// </summary>
public class SelectionBox : MonoBehaviour
{
[Header("Selection Box")]
public CanvasGroup canvasGroup;
public TMP_Text titleText;
public TMP_Text contentText;
public RectTransform optionContainer;
public SelectionBoxButton optionButtonPrefab;
private readonly List<SelectionBoxButton> _generatedButtons = new();
private bool _isResolved;
/// <summary>
/// 选项被选择并且自身淡出结束后触发。页面容器使用它销毁本次生成的 SelectionBox。
/// </summary>
public event Action<SelectionBox> Closed;
private void Awake()
{
canvasGroup ??= GetComponent<CanvasGroup>();
}
/// <summary>
/// 根据运行时选项初始化弹窗。至少需要一个选项;失败时返回 false且不会显示半配置的弹窗。
/// </summary>
public bool SetUp(string title, string content, IReadOnlyList<SelectionBoxOption> options)
{
if (canvasGroup == null || optionContainer == null || optionButtonPrefab == null ||
options == null || options.Count == 0)
{
Debug.LogWarning("[SelectionBox] 缺少 CanvasGroup、选项容器、选项按钮预制体或未提供任何选项。");
return false;
}
ClearGeneratedButtons();
_isResolved = false;
if (titleText != null) titleText.text = title ?? string.Empty;
if (contentText != null) contentText.text = content ?? string.Empty;
foreach (SelectionBoxOption option in options)
{
SelectionBoxButton button = Instantiate(optionButtonPrefab, optionContainer);
_generatedButtons.Add(button);
button.SetUp(option.label, () => Select(option.action));
}
gameObject.SetActive(true);
canvasGroup.alpha = 0f;
canvasGroup.interactable = true;
canvasGroup.blocksRaycasts = true;
canvasGroup.DOFade(1f, 0.2f).Play();
return true;
}
private void Select(UnityAction action)
{
if (_isResolved) return;
_isResolved = true;
// 先禁用所有输入,再执行回调。回调可以触发存档、场景切换或打开下一层 UI
// 因而不能让同一帧内的其它按钮再次触发。
canvasGroup.interactable = false;
canvasGroup.blocksRaycasts = false;
foreach (SelectionBoxButton button in _generatedButtons)
{
if (button?.button != null)
button.button.interactable = false;
}
action?.Invoke();
canvasGroup.DOFade(0f, 0.2f).OnComplete(() => Closed?.Invoke(this)).Play();
}
private void ClearGeneratedButtons()
{
foreach (SelectionBoxButton button in _generatedButtons)
{
if (button != null)
Destroy(button.gameObject);
}
_generatedButtons.Clear();
}
}
/// <summary>
/// SelectionBox 的单个运行时选项。它不序列化到场景或存档;显示时由调用方临时创建。
/// </summary>
public readonly struct SelectionBoxOption
{
public readonly string label;
public readonly UnityAction action;
public SelectionBoxOption(string label, UnityAction action)
{
this.label = label;
this.action = action;
}
}
}