132 lines
6.1 KiB
C#
132 lines
6.1 KiB
C#
using System;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using TMPro;
|
||
using BaseGames.UI;
|
||
using BaseGames.Localization;
|
||
|
||
namespace BaseGames.UI.Menus
|
||
{
|
||
/// <summary>
|
||
/// 通用模态确认对话框(是/否):用于删除 / 覆盖 / 退出 / 传送等确认场景。
|
||
///
|
||
/// <para>双调用模式(同类不同实例各用其一,互不冲突):</para>
|
||
/// <list type="bullet">
|
||
/// <item><b>导航器 async(主菜单)</b>:<see cref="ShowAsync"/> 经 <see cref="IUINavigator"/> 压栈,
|
||
/// 栈式回退、ESC 取消、焦点恢复统一由导航器负责。</item>
|
||
/// <item><b>回调 legacy(游戏内地图传送等尚未接入导航器的场景)</b>:<see cref="Show"/> 本地 SetActive
|
||
/// 显隐 + onConfirm/onCancel 回调,不依赖导航器。</item>
|
||
/// </list>
|
||
///
|
||
/// 标题 / 正文 / 按钮文案走本地化键(<see cref="LocalizationManager"/>);传 null 保留 Inspector 原文。
|
||
/// 默认焦点置于"取消",防手柄/键盘连按误触破坏性确认。
|
||
/// </summary>
|
||
public class ConfirmDialogController : UIResultPanel<bool>
|
||
{
|
||
[Header("文本")]
|
||
[SerializeField] private TMP_Text _titleText;
|
||
[SerializeField] private TMP_Text _bodyText;
|
||
[Tooltip("确认按钮标签(可选,传 confirmKey 时覆盖)")]
|
||
[SerializeField] private TMP_Text _confirmLabel;
|
||
[Tooltip("取消按钮标签(可选,传 cancelKey 时覆盖)")]
|
||
[SerializeField] private TMP_Text _cancelLabel;
|
||
|
||
[Header("按钮")]
|
||
[SerializeField] private Button _btnConfirm;
|
||
[SerializeField] private Button _btnCancel;
|
||
|
||
// 取消 / ESC / 销毁默认结果:否。
|
||
protected override bool CancelResult => false;
|
||
|
||
// legacy 回调模式状态
|
||
private Action _legacyConfirm;
|
||
private Action _legacyCancel;
|
||
private bool _legacyMode;
|
||
|
||
private void Awake()
|
||
{
|
||
_btnConfirm?.onClick.AddListener(() => OnButton(true));
|
||
_btnCancel? .onClick.AddListener(() => OnButton(false));
|
||
// 不在此 SetActive(false):面板初始由场景/脚手架序列化为隐藏,激活完全交给导航器
|
||
// (对象初始 inactive 时 Awake 会被推迟到首次激活才执行,若在此关闭会立刻自我隐藏)。
|
||
}
|
||
|
||
/// <summary>默认焦点:取消按钮(破坏性操作安全默认)。</summary>
|
||
protected override GameObject ResolveFirstSelected()
|
||
=> _btnCancel != null ? _btnCancel.gameObject
|
||
: _btnConfirm != null ? _btnConfirm.gameObject : null;
|
||
|
||
// ── 导航器 async 路径(主菜单)────────────────────────────────────────
|
||
/// <summary>弹出确认框并等待结果(true=确认 / false=取消)。由导航器压栈管理。</summary>
|
||
public Task<bool> ShowAsync(string titleKey, string bodyKey, CancellationToken ct = default,
|
||
string confirmKey = null, string cancelKey = null)
|
||
{
|
||
_legacyMode = false;
|
||
ApplyText(titleKey, bodyKey, confirmKey, cancelKey);
|
||
|
||
var nav = GetService<IUINavigator>();
|
||
if (nav == null)
|
||
{
|
||
Debug.LogError("[ConfirmDialog] 未找到 IUINavigator 服务,无法以 async 模式弹出。", this);
|
||
return Task.FromResult(false);
|
||
}
|
||
return nav.PushForResultAsync<bool>(this, ct);
|
||
}
|
||
|
||
// ── legacy 回调路径(游戏内尚未接入导航器的调用方)──────────────────
|
||
/// <summary>弹出确认框(回调式,本地显隐,不走导航器)。</summary>
|
||
public void Show(string titleKey, string bodyKey, Action onConfirm, Action onCancel = null,
|
||
string confirmKey = null, string cancelKey = null)
|
||
{
|
||
_legacyMode = true;
|
||
_legacyConfirm = onConfirm;
|
||
_legacyCancel = onCancel;
|
||
ApplyText(titleKey, bodyKey, confirmKey, cancelKey);
|
||
gameObject.SetActive(true); // OnEnable 经 UIPanelBase 自动聚焦取消按钮
|
||
}
|
||
|
||
/// <summary>外部强制关闭(仅 legacy 模式有效)。不触发任何回调。</summary>
|
||
public void Close()
|
||
{
|
||
if (!_legacyMode) return;
|
||
_legacyConfirm = null;
|
||
_legacyCancel = null;
|
||
gameObject.SetActive(false);
|
||
}
|
||
|
||
// ── 按钮 ──────────────────────────────────────────────────────────────
|
||
private void OnButton(bool confirmed)
|
||
{
|
||
if (_legacyMode)
|
||
{
|
||
var cb = confirmed ? _legacyConfirm : _legacyCancel;
|
||
_legacyConfirm = null;
|
||
_legacyCancel = null;
|
||
gameObject.SetActive(false);
|
||
cb?.Invoke();
|
||
}
|
||
else
|
||
{
|
||
Complete(confirmed); // 设置结果 + 由导航器出栈
|
||
}
|
||
}
|
||
|
||
// ── 工具 ──────────────────────────────────────────────────────────────
|
||
private void ApplyText(string titleKey, string bodyKey, string confirmKey, string cancelKey)
|
||
{
|
||
if (_titleText != null && titleKey != null) _titleText.text = Loc(titleKey);
|
||
if (_bodyText != null && bodyKey != null) _bodyText.text = Loc(bodyKey);
|
||
if (_confirmLabel != null && confirmKey != null) _confirmLabel.text = Loc(confirmKey);
|
||
if (_cancelLabel != null && cancelKey != null) _cancelLabel.text = Loc(cancelKey);
|
||
}
|
||
|
||
private static string Loc(string key)
|
||
{
|
||
string s = LocalizationManager.Get(key, LocalizationTable.UI);
|
||
return string.IsNullOrEmpty(s) ? key : s;
|
||
}
|
||
}
|
||
}
|