Files
Cielonos/Assets/Scripts/SLSUtilities/UI/UIPageManager.cs
SoulliesOfficial 6d7ebc5825 Passion & UI
2026-06-12 17:11:39 -04:00

126 lines
4.8 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 SLSUtilities.General;
using UnityEngine;
namespace SLSUtilities.UI
{
/// <summary>
/// 栈式 UI 页面管理器。
/// 管理所有打开的 UIPageBase 页面,提供输入阻塞事件。
/// ESC 关闭栈顶由游戏层负责调用 CloseTopPage()。
/// 需要放置在场景中的 GameObject 上Singleton
/// </summary>
public class UIPageManager : Singleton<UIPageManager>
{
// ──────────────────── 动态页面 ────────────────────
[Header("Dynamic Pages")]
[Tooltip("确认 / 提示弹窗的 Prefab需挂载 ConfirmUIPage 组件)。")]
[SerializeField] private GameObject confirmPagePrefab;
[Tooltip("动态页面实例化的父节点(通常是 Canvas 下的某个 Transform。")]
[SerializeField] private Transform dynamicPageParent;
// ──────────────────── 页面栈 ────────────────────
private readonly List<UIPageBase> openPages = new List<UIPageBase>();
/// <summary>
/// 输入阻塞状态变化时触发。
/// true = 首个页面打开应阻塞游戏输入false = 所有页面已关闭(应恢复输入)。
/// 游戏层通过订阅此事件联动 isCursorLocked 等状态。
/// </summary>
public event Action<bool> OnInputBlockChanged;
/// <summary>当前是否有打开的页面。</summary>
public bool HasOpenPages => openPages.Count > 0;
/// <summary>当前栈顶页面,若无则返回 null。</summary>
public UIPageBase TopPage => openPages.Count > 0 ? openPages[^1] : null;
/// <summary>确认页面的 Prefab 引用(以 GameObject 形式暴露,避免跨 Assembly 类型依赖)。</summary>
public GameObject ConfirmPagePrefab => confirmPagePrefab;
// ──────────────────── 页面注册 ────────────────────
/// <summary>
/// 将页面注册到栈顶。若为首个页面则触发 OnInputBlockChanged(true)。
/// 由 UIPageBase.Open() 内部调用,不要手动调用。
/// </summary>
public void RegisterPage(UIPageBase page)
{
if (openPages.Contains(page)) return;
bool wasEmpty = openPages.Count == 0;
openPages.Add(page);
if (wasEmpty)
{
OnInputBlockChanged?.Invoke(true);
}
}
/// <summary>
/// 将页面从栈中移除。若栈清空则触发 OnInputBlockChanged(false)。
/// 由 UIPageBase.Close() 内部调用,不要手动调用。
/// </summary>
public void UnregisterPage(UIPageBase page)
{
if (!openPages.Remove(page)) return;
if (openPages.Count == 0)
{
OnInputBlockChanged?.Invoke(false);
}
}
// ──────────────────── 页面关闭 ────────────────────
/// <summary>
/// 关闭栈顶页面(若其允许 ESC 关闭)。由游戏层的 ESC 处理逻辑调用。
/// </summary>
/// <returns>true 表示成功关闭了一个页面。</returns>
public bool CloseTopPage()
{
if (openPages.Count == 0) return false;
UIPageBase top = openPages[^1];
if (top.CloseOnEsc)
{
top.Close();
return true;
}
return false;
}
/// <summary>关闭所有打开的页面。</summary>
public void CloseAllPages()
{
for (int i = openPages.Count - 1; i >= 0; i--)
{
openPages[i].Close();
}
}
// ──────────────────── 动态页面实例化 ────────────────────
/// <summary>
/// 在 <see cref="dynamicPageParent"/> 下实例化一个动态页面 Prefab。
/// 用于创建运行时弹窗(确认框、提示框等)。
/// </summary>
/// <param name="prefab">要实例化的 Prefab。</param>
/// <returns>实例化后的 GameObject。</returns>
public GameObject InstantiateDynamicPage(GameObject prefab)
{
if (prefab == null)
{
Debug.LogError("[UIPageManager] Cannot instantiate a null prefab.");
return null;
}
Transform parent = dynamicPageParent != null ? dynamicPageParent : transform;
return Instantiate(prefab, parent);
}
}
}