refactor(ui): 移除UI根接口中的Z序管理方法并优化文档注释

- 从IUiRoot接口中移除SetZOrder和GetVisiblePages方法
- 删除GodotUiRoot类的完整实现文件
- 为ModalLayerUiPageBehavior类添加详细的XML文档注释
- 为TopmostLayerUiPageBehavior类添加详细的XML文档注释
- 优化模态层和顶层UI行为类的属性文档说明
- [release ci]
This commit is contained in:
GeWuYou 2026-02-07 21:51:01 +08:00
parent 35a06d2565
commit 43eacc1e4a
4 changed files with 45 additions and 181 deletions

View File

@ -26,17 +26,4 @@ public interface IUiRoot
/// </summary>
/// <param name="child">要移除的UI页面子节点</param>
void RemoveUiPage(IUiPageBehavior child);
/// <summary>
/// 设置页面的Z-order层级顺序
/// </summary>
/// <param name="page">UI页面</param>
/// <param name="zOrder">Z-order值</param>
void SetZOrder(IUiPageBehavior page, int zOrder);
/// <summary>
/// 获取当前所有显示的页面
/// </summary>
/// <returns>所有显示的页面列表</returns>
IReadOnlyList<IUiPageBehavior> GetVisiblePages();
}

View File

@ -1,134 +0,0 @@
using GFramework.Game.Abstractions.enums;
using GFramework.Game.Abstractions.ui;
using Godot;
namespace GFramework.Godot.ui;
/// <summary>
/// Godot平台的UI根节点实现
/// 用于管理UI页面的添加、移除和层级排序
/// </summary>
public partial class GodotUiRoot(IReadOnlyDictionary<UiLayer, int>? layerZOrderMap) : Node, IUiRoot
{
/// <summary>
/// UI层级与Z轴顺序的映射表定义了不同UI层的渲染优先级
/// </summary>
/// <remarks>
/// 默认层级映射关系:
/// - Page: 0 (基础页面层)
/// - Overlay: 100 (覆盖层)
/// - Modal: 200 (模态窗口层)
/// - Toast: 300 (提示消息层)
/// - Topmost: 400 (最顶层)
/// </remarks>
private readonly IReadOnlyDictionary<UiLayer, int> _layerZOrderMap = layerZOrderMap ??
new Dictionary<UiLayer, int>
{
{ UiLayer.Page, 0 },
{ UiLayer.Overlay, 100 },
{ UiLayer.Modal, 200 },
{ UiLayer.Toast, 300 },
{ UiLayer.Topmost, 400 }
};
/// <summary>
/// UI页面的追踪字典记录每个页面的节点
/// </summary>
private readonly Dictionary<IUiPageBehavior, Node> _pageNodes = new();
/// <summary>
/// UI节点的父容器所有UI页面都添加到这个节点下
/// </summary>
private Node? _uiContainer;
/// <summary>
/// 向UI根节点添加子页面
/// </summary>
public void AddUiPage(IUiPageBehavior child)
{
if (_uiContainer == null)
throw new InvalidOperationException("UiContainer is not initialized");
var node = GetNodeFromPage(child);
if (node == null)
throw new InvalidOperationException($"Page node is null: {child.Key}");
if (!_pageNodes.TryAdd(child, node))
return;
if (node.GetParent() != _uiContainer) _uiContainer.AddChild(node);
}
/// <summary>
/// 向UI根节点添加子页面到指定层级
/// </summary>
public void AddUiPage(
IUiPageBehavior child,
UiLayer layer,
int orderInLayer = 0)
{
AddUiPage(child);
var z = GetBaseZOrder(layer) + orderInLayer;
SetZOrder(child, z);
}
/// <summary>
/// 从UI根节点移除子页面
/// </summary>
public void RemoveUiPage(IUiPageBehavior child)
{
if (!_pageNodes.Remove(child, out var node))
return;
if (_uiContainer != null && node.GetParent() == _uiContainer) _uiContainer.RemoveChild(node);
}
/// <summary>
/// 设置页面的Z-order层级顺序
/// </summary>
public void SetZOrder(IUiPageBehavior page, int zOrder)
{
if (!_pageNodes.TryGetValue(page, out var node))
return;
if (node is CanvasItem canvasItem) canvasItem.ZIndex = zOrder;
}
/// <summary>
/// 获取当前所有显示的页面
/// </summary>
public IReadOnlyList<IUiPageBehavior> GetVisiblePages()
{
return _pageNodes.Keys.ToList().AsReadOnly();
}
public override void _Ready()
{
// 创建UI容器节点
_uiContainer = new Node
{
Name = "UiContainer"
};
AddChild(_uiContainer);
}
/// <summary>
/// 从页面行为获取对应的节点
/// </summary>
private static Node? GetNodeFromPage(IUiPageBehavior page)
{
return page.View as Node;
}
private int GetBaseZOrder(UiLayer layer)
{
return !_layerZOrderMap.TryGetValue(layer, out var z)
? throw new ArgumentOutOfRangeException(nameof(layer), layer, null)
: z;
}
}

View File

@ -17,30 +17,35 @@ using Godot;
namespace GFramework.Godot.ui;
/// <summary>
/// 模态层 UI 行为 - 可重入但需谨慎,带遮罩阻止下层交互
/// 模态层 UI 行为类,用于管理模态界面的行为。
/// 此类继承自 CanvasItemUiPageBehaviorBase提供模态层特有的功能
/// - 支持可重入IsReentrant = true
/// - 带有遮罩以阻止下层交互BlocksInput = true
/// - 属于模态层级Layer = UiLayer.Modal
/// </summary>
/// <typeparam name="T">拥有者类型,必须是 CanvasItem 的子类</typeparam>
/// <param name="owner">当前行为的拥有者对象</param>
/// <param name="key">用于标识此行为的键值</param>
public class ModalLayerUiPageBehavior<T>(T owner, string key) : CanvasItemUiPageBehaviorBase<T>(owner, key)
where T : CanvasItem
{
/// <summary>
/// 获取当前 UI 所属的层级,此处固定为模态层。
/// </summary>
public override UiLayer Layer => UiLayer.Modal;
public override bool IsReentrant => true; // ✅ 支持重入(如多层确认弹窗)
public override bool IsModal => true; // 模态窗口
public override bool BlocksInput => true; // 必须阻止下层交互
/// <summary>
/// 模态窗口显示时,可以添加遮罩逻辑
/// 指示当前 UI 是否支持可重入。设置为 true 表示允许重复进入同一界面。
/// </summary>
public override void OnShow()
{
base.OnShow();
// TODO: 可在此添加半透明遮罩层
// AddModalMask();
}
public override bool IsReentrant => true;
public override void OnHide()
{
// TODO: 移除遮罩层
// RemoveModalMask();
base.OnHide();
}
/// <summary>
/// 指示当前 UI 是否为模态界面。设置为 true 表示该界面会阻止用户与下层界面交互。
/// </summary>
public override bool IsModal => true;
/// <summary>
/// 指示当前 UI 是否阻止输入事件传递到下层界面。设置为 true 表示启用遮罩功能。
/// </summary>
public override bool BlocksInput => true;
}

View File

@ -17,30 +17,36 @@ using Godot;
namespace GFramework.Godot.ui;
/// <summary>
/// 顶层 UI 行为 - 不可重入,最高优先级,用于系统级弹窗
/// 顶层 UI 行为类,继承自 CanvasItemUiPageBehaviorBase。
/// 此类用于实现系统级弹窗行为,具有不可重入、最高优先级和模态特性。
/// </summary>
/// <typeparam name="T">泛型参数,表示拥有此行为的 CanvasItem 类型。</typeparam>
/// <param name="owner">拥有此行为的 CanvasItem 实例。</param>
/// <param name="key">用于标识此行为的唯一键。</param>
public class TopmostLayerUiPageBehavior<T>(T owner, string key) : CanvasItemUiPageBehaviorBase<T>(owner, key)
where T : CanvasItem
{
/// <summary>
/// 获取当前 UI 行为所在的层级。
/// 返回值为 UiLayer.Topmost表示该行为位于最顶层。
/// </summary>
public override UiLayer Layer => UiLayer.Topmost;
public override bool IsReentrant => false; // ❌ 顶层不支持重入
public override bool IsModal => true; // 顶层通常是模态的
public override bool BlocksInput => true; // 必须阻止所有下层交互
/// <summary>
/// 顶层显示时,可以禁用所有下层 UI
/// 指示此行为是否可重入。
/// 返回值为 false表示不可重入。
/// </summary>
public override void OnShow()
{
base.OnShow();
// TODO: 可在此禁用其他所有层级
// DisableAllLowerLayers();
}
public override bool IsReentrant => false;
public override void OnHide()
{
// TODO: 恢复其他层级
// EnableAllLowerLayers();
base.OnHide();
}
/// <summary>
/// 指示此行为是否为模态行为。
/// 返回值为 true表示为模态行为会阻止其他交互。
/// </summary>
public override bool IsModal => true;
/// <summary>
/// 指示此行为是否会阻塞输入。
/// 返回值为 true表示会阻塞用户输入。
/// </summary>
public override bool BlocksInput => true;
}