This commit is contained in:
2026-06-07 11:49:55 +08:00
parent ff0f3bde54
commit 1897658a00
98 changed files with 9903 additions and 13907 deletions

View File

@@ -0,0 +1,60 @@
using System.Threading;
using System.Threading.Tasks;
namespace BaseGames.UI
{
/// <summary>
/// 返回结果的模态面板基类(确认框、模式 / 难度选择等)。
///
/// <para>用法(调用方走线性 await无回调嵌套</para>
/// <code>
/// bool ok = await _confirmDialog.ShowAsync("CONFIRM_OVERWRITE_TITLE", "CONFIRM_OVERWRITE_BODY", ct);
/// if (!ok) return;
/// </code>
///
/// <para>结果通道由 <see cref="TaskCompletionSource{T}"/> 承载,并对所有"提前结束"路径兜底
/// ESC 取消出栈、外部强关、所属场景卸载、CancellationToken 取消),保证 await 绝不悬挂。</para>
/// </summary>
public abstract class UIResultPanel<T> : UIPanelBase
{
private TaskCompletionSource<T> _tcs;
private CancellationTokenRegistration _ctReg;
/// <summary>取消 / 默认结果ESC、返回、销毁、场景卸载、ct 取消时以此完成。</summary>
protected abstract T CancelResult { get; }
/// <summary>由导航器 <see cref="IUINavigator.PushForResultAsync{T}"/> 调用:开启一轮结果等待。</summary>
internal Task<T> BeginResult(CancellationToken ct)
{
// 复用面板:若上一轮仍未决,先以默认值收口,避免句柄泄漏。
ResolvePending();
_tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
_ctReg = ct.CanBeCanceled ? ct.Register(ResolvePending) : default;
return _tcs.Task;
}
/// <summary>具体按钮回调:以 <paramref name="result"/> 完成并出栈自身。</summary>
protected void Complete(T result)
{
var tcs = _tcs;
if (tcs != null && tcs.TrySetResult(result))
{
_ctReg.Dispose();
GetService<IUINavigator>()?.Pop(); // 弹出自己(栈顶)
}
}
/// <summary>以取消默认值收口未决结果(幂等)。</summary>
private void ResolvePending()
{
var tcs = _tcs;
if (tcs != null && tcs.TrySetResult(CancelResult))
_ctReg.Dispose();
}
// 出栈(含 ESC 取消)会 SetActive(false) → OnDisable → OnPanelClose
// 场景卸载 / 销毁同样经 OnDisable。统一在此兜底覆盖所有提前结束路径。
protected override void OnPanelClose() => ResolvePending();
}
}