Files
zeling_v2/Assets/_Game/Scripts/UI/Menus/ConfirmDialogController.cs
2026-06-07 11:49:55 +08:00

132 lines
6.1 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.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;
}
}
}