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 { /// /// 通用模态确认对话框(是/否):用于删除 / 覆盖 / 退出 / 传送等确认场景。 /// /// 双调用模式(同类不同实例各用其一,互不冲突): /// /// 导航器 async(主菜单) 压栈, /// 栈式回退、ESC 取消、焦点恢复统一由导航器负责。 /// 回调 legacy(游戏内地图传送等尚未接入导航器的场景) 本地 SetActive /// 显隐 + onConfirm/onCancel 回调,不依赖导航器。 /// /// /// 标题 / 正文 / 按钮文案走本地化键();传 null 保留 Inspector 原文。 /// 默认焦点置于"取消",防手柄/键盘连按误触破坏性确认。 /// public class ConfirmDialogController : UIResultPanel { [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 会被推迟到首次激活才执行,若在此关闭会立刻自我隐藏)。 } /// 默认焦点:取消按钮(破坏性操作安全默认)。 protected override GameObject ResolveFirstSelected() => _btnCancel != null ? _btnCancel.gameObject : _btnConfirm != null ? _btnConfirm.gameObject : null; // ── 导航器 async 路径(主菜单)──────────────────────────────────────── /// 弹出确认框并等待结果(true=确认 / false=取消)。由导航器压栈管理。 public Task ShowAsync(string titleKey, string bodyKey, CancellationToken ct = default, string confirmKey = null, string cancelKey = null) { _legacyMode = false; ApplyText(titleKey, bodyKey, confirmKey, cancelKey); var nav = GetService(); if (nav == null) { Debug.LogError("[ConfirmDialog] 未找到 IUINavigator 服务,无法以 async 模式弹出。", this); return Task.FromResult(false); } return nav.PushForResultAsync(this, ct); } // ── legacy 回调路径(游戏内尚未接入导航器的调用方)────────────────── /// 弹出确认框(回调式,本地显隐,不走导航器)。 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 自动聚焦取消按钮 } /// 外部强制关闭(仅 legacy 模式有效)。不触发任何回调。 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; } } }