mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +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;
|
||||
|
||||
/// <summary>
|
||||
@ -11,9 +13,34 @@ public interface IUiRoot
|
||||
/// <param name="child">要添加的UI页面子节点</param>
|
||||
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>
|
||||
/// 从UI根节点移除子页面
|
||||
/// </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();
|
||||
|
||||
/// <summary>
|
||||
/// 强制刷新UI层级排序
|
||||
/// </summary>
|
||||
void RefreshLayerOrder();
|
||||
}
|
||||
@ -114,4 +114,58 @@ public interface IUiRouter : ISystem
|
||||
/// 判断指定UI是否存在于UI栈中
|
||||
/// </summary>
|
||||
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.Game.Abstractions.enums;
|
||||
using GFramework.Game.Abstractions.ui;
|
||||
using System.Linq;
|
||||
|
||||
namespace GFramework.Game.ui;
|
||||
|
||||
@ -24,6 +25,11 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
|
||||
/// </summary>
|
||||
private readonly Stack<IUiPageBehavior> _stack = new();
|
||||
|
||||
/// <summary>
|
||||
/// 层级管理(非栈层级),用于Overlay、Modal、Toast等浮层
|
||||
/// </summary>
|
||||
private readonly Dictionary<UiLayer, Dictionary<string, IUiPageBehavior>> _layers = new();
|
||||
|
||||
/// <summary>
|
||||
/// UI工厂实例,用于创建UI相关的对象
|
||||
/// </summary>
|
||||
@ -434,4 +440,135 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter
|
||||
while (_stack.Count > 0)
|
||||
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>
|
||||
<PackageId>GeWuYou.$(AssemblyName)</PackageId>
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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"/>
|
||||
</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