mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-25 13:33:28 +08:00
feat(ui): 添加UI层级管理和Godot平台实现
- 在IUiRoot接口中添加Z-order控制和页面层级管理功能 - 实现Godot平台的UiRoot,支持UI页面的添加、移除和层级排序 - 添加UiLayer枚举定义不同UI层级(Page、Overlay、Modal、Toast、Topmost) - 在IUiRouter中扩展层级管理方法,支持指定层级显示UI - 实现UiRouterBase中的层级管理逻辑,包括显示、隐藏、清空等操作 - 添加对GodotSharp包的引用以支持Godot平台功能
This commit is contained in:
parent
760cc71985
commit
c9f01f5877
33
GFramework.Game.Abstractions/enums/UiLayer.cs
Normal file
33
GFramework.Game.Abstractions/enums/UiLayer.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
namespace GFramework.Game.Abstractions.enums;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UI层级枚举,定义UI界面的显示层级
|
||||||
|
/// 用于管理不同类型的UI在屏幕上的显示顺序
|
||||||
|
/// </summary>
|
||||||
|
public enum UiLayer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 页面层,使用栈管理UI的切换
|
||||||
|
/// </summary>
|
||||||
|
Page = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 浮层,用于覆盖层、对话框等
|
||||||
|
/// </summary>
|
||||||
|
Overlay = 10,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模态层,会阻挡下层交互,带有遮罩效果
|
||||||
|
/// </summary>
|
||||||
|
Modal = 20,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 提示层,用于轻量提示如toast消息、loading指示器等
|
||||||
|
/// </summary>
|
||||||
|
Toast = 30,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 顶层,用于系统级弹窗、全屏加载等
|
||||||
|
/// </summary>
|
||||||
|
Topmost = 40
|
||||||
|
}
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace GFramework.Game.Abstractions.ui;
|
namespace GFramework.Game.Abstractions.ui;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -11,9 +13,34 @@ public interface IUiRoot
|
|||||||
/// <param name="child">要添加的UI页面子节点</param>
|
/// <param name="child">要添加的UI页面子节点</param>
|
||||||
void AddUiPage(IUiPageBehavior child);
|
void AddUiPage(IUiPageBehavior child);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 向UI根节点添加子页面到指定层级
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="child">要添加的UI页面子节点</param>
|
||||||
|
/// <param name="zOrder">Z-order值,用于控制UI显示层级</param>
|
||||||
|
void AddUiPage(IUiPageBehavior child, int zOrder = 0);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从UI根节点移除子页面
|
/// 从UI根节点移除子页面
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="child">要移除的UI页面子节点</param>
|
/// <param name="child">要移除的UI页面子节点</param>
|
||||||
void RemoveUiPage(IUiPageBehavior child);
|
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();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 强制刷新UI层级排序
|
||||||
|
/// </summary>
|
||||||
|
void RefreshLayerOrder();
|
||||||
}
|
}
|
||||||
@ -114,4 +114,58 @@ public interface IUiRouter : ISystem
|
|||||||
/// 判断指定UI是否存在于UI栈中
|
/// 判断指定UI是否存在于UI栈中
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool Contains(string uiKey);
|
bool Contains(string uiKey);
|
||||||
|
|
||||||
|
#region 层级管理
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在指定层级显示UI(非栈管理)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uiKey">UI标识符</param>
|
||||||
|
/// <param name="layer">UI层级</param>
|
||||||
|
/// <param name="param">进入参数</param>
|
||||||
|
/// <param name="instancePolicy">实例策略</param>
|
||||||
|
void Show(
|
||||||
|
string uiKey,
|
||||||
|
UiLayer layer,
|
||||||
|
IUiPageEnterParam? param = null,
|
||||||
|
UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在指定层级显示UI(基于实例)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="page">UI页面实例</param>
|
||||||
|
/// <param name="layer">UI层级</param>
|
||||||
|
void Show(IUiPageBehavior page, UiLayer layer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 隐藏指定层级的UI
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uiKey">UI标识符</param>
|
||||||
|
/// <param name="layer">UI层级</param>
|
||||||
|
/// <param name="destroy">是否销毁实例</param>
|
||||||
|
void Hide(string uiKey, UiLayer layer, bool destroy = false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清空指定层级的所有UI
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="layer">UI层级</param>
|
||||||
|
/// <param name="destroy">是否销毁实例</param>
|
||||||
|
void ClearLayer(UiLayer layer, bool destroy = false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定层级的UI实例
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uiKey">UI标识符</param>
|
||||||
|
/// <param name="layer">UI层级</param>
|
||||||
|
/// <returns>UI实例,不存在则返回null</returns>
|
||||||
|
IUiPageBehavior? GetFromLayer(string uiKey, UiLayer layer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断指定层级是否有UI显示
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="layer">UI层级</param>
|
||||||
|
/// <returns>是否有UI显示</returns>
|
||||||
|
bool HasVisibleInLayer(UiLayer layer);
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@ -4,6 +4,7 @@ using GFramework.Core.logging;
|
|||||||
using GFramework.Core.system;
|
using GFramework.Core.system;
|
||||||
using GFramework.Game.Abstractions.enums;
|
using GFramework.Game.Abstractions.enums;
|
||||||
using GFramework.Game.Abstractions.ui;
|
using GFramework.Game.Abstractions.ui;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace GFramework.Game.ui;
|
namespace GFramework.Game.ui;
|
||||||
|
|
||||||
@ -24,6 +25,11 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Stack<IUiPageBehavior> _stack = new();
|
private readonly Stack<IUiPageBehavior> _stack = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 层级管理(非栈层级),用于Overlay、Modal、Toast等浮层
|
||||||
|
/// </summary>
|
||||||
|
private readonly Dictionary<UiLayer, Dictionary<string, IUiPageBehavior>> _layers = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// UI工厂实例,用于创建UI相关的对象
|
/// UI工厂实例,用于创建UI相关的对象
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -434,4 +440,135 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
|
|||||||
while (_stack.Count > 0)
|
while (_stack.Count > 0)
|
||||||
DoPopInternal(policy);
|
DoPopInternal(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region 层级管理
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在指定层级显示UI(非栈管理)
|
||||||
|
/// </summary>
|
||||||
|
public void Show(
|
||||||
|
string uiKey,
|
||||||
|
Game.Abstractions.enums.UiLayer layer,
|
||||||
|
IUiPageEnterParam? param = null,
|
||||||
|
UiInstancePolicy instancePolicy = UiInstancePolicy.Reuse)
|
||||||
|
{
|
||||||
|
if (layer == Game.Abstractions.enums.UiLayer.Page)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Use Push() for Page layer");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化层级字典
|
||||||
|
if (!_layers.ContainsKey(layer))
|
||||||
|
_layers[layer] = new Dictionary<string, IUiPageBehavior>();
|
||||||
|
|
||||||
|
var layerDict = _layers[layer];
|
||||||
|
|
||||||
|
// 检查是否已存在
|
||||||
|
if (layerDict.TryGetValue(uiKey, out var existing))
|
||||||
|
{
|
||||||
|
Log.Debug("UI already visible in layer: {0}, layer={1}", uiKey, layer);
|
||||||
|
existing.OnEnter(param);
|
||||||
|
existing.OnShow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取或创建实例
|
||||||
|
var page = _factory.GetOrCreate(uiKey, instancePolicy);
|
||||||
|
layerDict[uiKey] = page;
|
||||||
|
|
||||||
|
// 添加到UiRoot,传入层级Z-order
|
||||||
|
_uiRoot.AddUiPage(page, (int)layer);
|
||||||
|
|
||||||
|
page.OnEnter(param);
|
||||||
|
page.OnShow();
|
||||||
|
|
||||||
|
Log.Debug("Show UI in layer: {0}, layer={1}", uiKey, layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在指定层级显示UI(基于实例)
|
||||||
|
/// </summary>
|
||||||
|
public void Show(IUiPageBehavior page, UiLayer layer)
|
||||||
|
{
|
||||||
|
if (layer == UiLayer.Page)
|
||||||
|
throw new ArgumentException("Use Push() for Page layer");
|
||||||
|
|
||||||
|
var uiKey = page.Key;
|
||||||
|
|
||||||
|
if (!_layers.ContainsKey(layer))
|
||||||
|
_layers[layer] = new Dictionary<string, IUiPageBehavior>();
|
||||||
|
|
||||||
|
_layers[layer][uiKey] = page;
|
||||||
|
_uiRoot.AddUiPage(page, (int)layer);
|
||||||
|
page.OnShow();
|
||||||
|
|
||||||
|
Log.Debug("Show existing UI instance in layer: {0}, layer={1}", uiKey, layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 隐藏指定层级的UI
|
||||||
|
/// </summary>
|
||||||
|
public void Hide(string uiKey, Game.Abstractions.enums.UiLayer layer, bool destroy = false)
|
||||||
|
{
|
||||||
|
if (!_layers.TryGetValue(layer, out var layerDict))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!layerDict.TryGetValue(uiKey, out var page))
|
||||||
|
return;
|
||||||
|
|
||||||
|
page.OnExit();
|
||||||
|
page.OnHide();
|
||||||
|
|
||||||
|
if (destroy)
|
||||||
|
{
|
||||||
|
_uiRoot.RemoveUiPage(page);
|
||||||
|
layerDict.Remove(uiKey);
|
||||||
|
Log.Debug("Hide & Destroy UI from layer: {0}, layer={1}", uiKey, layer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_uiRoot.RemoveUiPage(page);
|
||||||
|
_factory.Recycle(page);
|
||||||
|
layerDict.Remove(uiKey);
|
||||||
|
Log.Debug("Hide & Cache UI from layer: {0}, layer={1}", uiKey, layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清空指定层级的所有UI
|
||||||
|
/// </summary>
|
||||||
|
public void ClearLayer(Game.Abstractions.enums.UiLayer layer, bool destroy = false)
|
||||||
|
{
|
||||||
|
if (!_layers.TryGetValue(layer, out var layerDict))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var keys = layerDict.Keys.ToArray();
|
||||||
|
foreach (var key in keys)
|
||||||
|
{
|
||||||
|
Hide(key, layer, destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Debug("Cleared layer: {0}, destroyed={1}", layer, destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定层级的UI实例
|
||||||
|
/// </summary>
|
||||||
|
public IUiPageBehavior? GetFromLayer(string uiKey, UiLayer layer)
|
||||||
|
{
|
||||||
|
return _layers.TryGetValue(layer, out var layerDict) &&
|
||||||
|
layerDict.TryGetValue(uiKey, out var page)
|
||||||
|
? page
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断指定层级是否有UI显示
|
||||||
|
/// </summary>
|
||||||
|
public bool HasVisibleInLayer(UiLayer layer)
|
||||||
|
{
|
||||||
|
return _layers.TryGetValue(layer, out var layerDict) && layerDict.Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>GeWuYou.$(AssemblyName)</PackageId>
|
<PackageId>GeWuYou.$(AssemblyName)</PackageId>
|
||||||
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Godot.SourceGenerators" Version="4.5.1" PrivateAssets="all"/>
|
<PackageReference Include="Godot.SourceGenerators" Version="4.5.1" PrivateAssets="all"/>
|
||||||
|
<PackageReference Include="GodotSharp" Version="4.5.1"/>
|
||||||
<PackageReference Include="GodotSharpEditor" Version="4.5.1" PrivateAssets="all"/>
|
<PackageReference Include="GodotSharpEditor" Version="4.5.1" PrivateAssets="all"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
123
GFramework.Godot/ui/GodotUiRoot.cs
Normal file
123
GFramework.Godot/ui/GodotUiRoot.cs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
using GFramework.Game.Abstractions.ui;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace GFramework.Godot.ui;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Godot平台的UI根节点实现
|
||||||
|
/// 用于管理UI页面的添加、移除和层级排序
|
||||||
|
/// </summary>
|
||||||
|
public partial class GodotUiRoot : Node, IUiRoot
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// UI节点的父容器,所有UI页面都添加到这个节点下
|
||||||
|
/// </summary>
|
||||||
|
private Node? _uiContainer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UI页面的追踪字典,记录每个页面的节点
|
||||||
|
/// </summary>
|
||||||
|
private readonly Dictionary<IUiPageBehavior, Node> _pageNodes = new();
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
// 创建UI容器节点
|
||||||
|
_uiContainer = new Node
|
||||||
|
{
|
||||||
|
Name = "UiContainer"
|
||||||
|
};
|
||||||
|
|
||||||
|
AddChild(_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.ContainsKey(child))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_pageNodes[child] = node;
|
||||||
|
|
||||||
|
if (node.GetParent() != _uiContainer)
|
||||||
|
{
|
||||||
|
_uiContainer.AddChild(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 向UI根节点添加子页面到指定层级
|
||||||
|
/// </summary>
|
||||||
|
public void AddUiPage(IUiPageBehavior child, int zOrder = 0)
|
||||||
|
{
|
||||||
|
AddUiPage(child);
|
||||||
|
SetZOrder(child, zOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从UI根节点移除子页面
|
||||||
|
/// </summary>
|
||||||
|
public void RemoveUiPage(IUiPageBehavior child)
|
||||||
|
{
|
||||||
|
if (!_pageNodes.TryGetValue(child, out var node))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_pageNodes.Remove(child);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 强制刷新UI层级排序
|
||||||
|
/// </summary>
|
||||||
|
public void RefreshLayerOrder()
|
||||||
|
{
|
||||||
|
if (_uiContainer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_uiContainer.MoveChild(_uiContainer.GetChild(0), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从页面行为获取对应的节点
|
||||||
|
/// </summary>
|
||||||
|
private static Node? GetNodeFromPage(IUiPageBehavior page)
|
||||||
|
{
|
||||||
|
return page.View as Node;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user