From 35a06d25657d10539f38367a162e06a289bf500d Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:28:15 +0800 Subject: [PATCH] =?UTF-8?q?refactor(ui):=20=E5=B0=86CanvasItemUiPageBehavi?= =?UTF-8?q?or=E9=87=8D=E6=9E=84=E4=B8=BA=E5=88=86=E5=B1=82=E8=A1=8C?= =?UTF-8?q?=E4=B8=BA=E5=9F=BA=E7=B1=BB=E5=92=8C=E5=B7=A5=E5=8E=82=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除原有的CanvasItemUiPageBehavior类 - 创建CanvasItemUiPageBehaviorBase抽象基类,统一管理生命周期逻辑 - 实现PageLayerUiPageBehavior处理页面层UI行为 - 实现OverlayLayerUiPageBehavior处理覆盖层UI行为 - 实现ModalLayerUiPageBehavior处理模态层UI行为 - 实现ToastLayerUiPageBehavior处理Toast层UI行为 - 实现TopmostLayerUiPageBehavior处理顶层UI行为 - 创建UiPageBehaviorFactory工厂类按层级创建对应的行为实例 - 添加Apache License 2.0版权声明到所有新文件 --- .../ui/CanvasItemUiPageBehavior.cs | 134 ------------- .../ui/CanvasItemUiPageBehaviorBase.cs | 185 ++++++++++++++++++ .../ui/ModalLayerUiPageBehavior.cs | 46 +++++ .../ui/OverlayLayerUiPageBehavior.cs | 68 +++++++ .../ui/PageLayerUiPageBehavior.cs | 53 +++++ .../ui/ToastLayerUiPageBehavior.cs | 52 +++++ .../ui/TopmostLayerUiPageBehavior.cs | 46 +++++ GFramework.Godot/ui/UiPageBehaviorFactory.cs | 46 +++++ 8 files changed, 496 insertions(+), 134 deletions(-) delete mode 100644 GFramework.Godot/ui/CanvasItemUiPageBehavior.cs create mode 100644 GFramework.Godot/ui/CanvasItemUiPageBehaviorBase.cs create mode 100644 GFramework.Godot/ui/ModalLayerUiPageBehavior.cs create mode 100644 GFramework.Godot/ui/OverlayLayerUiPageBehavior.cs create mode 100644 GFramework.Godot/ui/PageLayerUiPageBehavior.cs create mode 100644 GFramework.Godot/ui/ToastLayerUiPageBehavior.cs create mode 100644 GFramework.Godot/ui/TopmostLayerUiPageBehavior.cs create mode 100644 GFramework.Godot/ui/UiPageBehaviorFactory.cs diff --git a/GFramework.Godot/ui/CanvasItemUiPageBehavior.cs b/GFramework.Godot/ui/CanvasItemUiPageBehavior.cs deleted file mode 100644 index 62700fd..0000000 --- a/GFramework.Godot/ui/CanvasItemUiPageBehavior.cs +++ /dev/null @@ -1,134 +0,0 @@ -using GFramework.Game.Abstractions.enums; -using GFramework.Game.Abstractions.ui; -using GFramework.Godot.extensions; -using Godot; - -namespace GFramework.Godot.ui; - -/// -/// 控制 UI 页面行为的泛型行为类, -/// 支持所有继承自 CanvasItem 的节点 -/// -/// CanvasItem 类型的视图节点 -public class CanvasItemUiPageBehavior(T owner, string key) : IUiPageBehavior - where T : CanvasItem -{ - private readonly IUiPage? _page = owner as IUiPage; - - /// - /// 获取当前UI层的类型。 - /// 返回值表示当前页面所属的UI层级,此处固定返回UiLayer.Page。 - /// - public UiLayer Layer => UiLayer.Page; - - /// - /// 判断当前Page是否允许重入。 - /// 返回值为false,表示该Page不支持重入操作。 - /// - public bool IsReentrant => false; - - - /// - /// 获取页面视图对象 - /// - /// 返回与当前实例关联的视图对象 - public object View => owner; - - /// - /// 获取当前实例的键值标识符 - /// - /// 返回用于标识当前实例的键字符串 - public string Key => key; - - - /// - /// 获取页面是否存活状态 - /// - /// 如果页面节点有效则返回true,否则返回false - public bool IsAlive => owner.IsValidNode(); - - /// - /// 获取页面是否可见状态 - /// - /// 如果页面可见则返回true,否则返回false - public bool IsVisible => owner.Visible; - - /// - /// 页面进入时调用 - /// - /// 页面进入参数 - public void OnEnter(IUiPageEnterParam? param) - { - _page?.OnEnter(param); - } - - /// - /// 页面退出时调用 - /// - public void OnExit() - { - _page?.OnExit(); - owner.QueueFreeX(); - } - - /// - /// 页面暂停时调用 - /// - public void OnPause() - { - _page?.OnPause(); - - // 暂停节点的处理、物理处理和输入处理 - if (!BlocksInput) return; - owner.SetProcess(false); - owner.SetPhysicsProcess(false); - owner.SetProcessInput(false); - } - - /// - /// 页面恢复时调用 - /// - public void OnResume() - { - if (owner.IsInvalidNode()) - { - return; - } - - _page?.OnResume(); - - // 恢复节点的处理、物理处理和输入处理 - owner.SetProcess(true); - owner.SetPhysicsProcess(true); - owner.SetProcessInput(true); - } - - /// - /// 页面隐藏时调用 - /// - public void OnHide() - { - _page?.OnHide(); - owner.Hide(); - } - - /// - /// 页面显示时调用 - /// - public void OnShow() - { - _page?.OnShow(); - owner.Show(); - OnResume(); - } - - /// - /// 获取或设置页面是否为模态对话框 - /// - public bool IsModal { get; set; } - - /// - /// 获取或设置页面是否阻止输入 - /// - public bool BlocksInput { get; set; } = true; -} \ No newline at end of file diff --git a/GFramework.Godot/ui/CanvasItemUiPageBehaviorBase.cs b/GFramework.Godot/ui/CanvasItemUiPageBehaviorBase.cs new file mode 100644 index 0000000..0c42228 --- /dev/null +++ b/GFramework.Godot/ui/CanvasItemUiPageBehaviorBase.cs @@ -0,0 +1,185 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using GFramework.Game.Abstractions.enums; +using GFramework.Game.Abstractions.ui; +using GFramework.Godot.extensions; +using Godot; + +namespace GFramework.Godot.ui; + +/// +/// UI 页面行为基类,封装通用的生命周期管理逻辑。 +/// 提供对 CanvasItem 类型视图节点的统一管理,包括显示、隐藏、进入、退出等操作。 +/// +/// CanvasItem 类型的视图节点。 +public abstract class CanvasItemUiPageBehaviorBase : IUiPageBehavior + where T : CanvasItem +{ + /// + /// UI 的唯一标识键。 + /// + private readonly string _key; + + /// + /// IUiPage 接口引用(如果视图实现了该接口)。 + /// + private readonly IUiPage? _page; + + /// + /// 视图节点的所有者实例。 + /// + protected readonly T Owner; + + /// + /// 初始化 CanvasItemUiPageBehaviorBase 实例。 + /// + /// 视图节点的所有者实例。 + /// UI 的唯一标识键。 + protected CanvasItemUiPageBehaviorBase(T owner, string key) + { + Owner = owner; + _key = key; + _page = owner as IUiPage; + } + + #region 抽象属性 - 子类必须实现 + + /// + /// 获取 UI 所属的层级。 + /// 由子类指定具体值。 + /// + public abstract UiLayer Layer { get; } + + /// + /// 获取是否支持重入。 + /// 由子类指定具体值。 + /// + public abstract bool IsReentrant { get; } + + /// + /// 获取是否为模态窗口。 + /// 由子类指定默认值。 + /// + public abstract bool IsModal { get; } + + /// + /// 获取是否阻止下层输入。 + /// 由子类指定默认值。 + /// + public abstract bool BlocksInput { get; } + + #endregion + + #region 基础属性 + + /// + /// 获取视图节点实例。 + /// + public object View => Owner; + + /// + /// 获取 UI 的唯一标识键。 + /// + public string Key => _key; + + /// + /// 获取视图节点是否有效。 + /// + public bool IsAlive => Owner.IsValidNode(); + + /// + /// 获取视图节点是否可见。 + /// + public bool IsVisible => Owner.Visible; + + #endregion + + #region 生命周期管理 + + /// + /// 当 UI 进入时调用。 + /// 默认调用 IUiPage 接口的 OnEnter 方法(如果存在)。 + /// + /// 进入参数。 + public virtual void OnEnter(IUiPageEnterParam? param) + { + _page?.OnEnter(param); + } + + /// + /// 当 UI 退出时调用。 + /// 默认调用 IUiPage 接口的 OnExit 方法(如果存在),并释放视图节点。 + /// + public virtual void OnExit() + { + _page?.OnExit(); + Owner.QueueFreeX(); + } + + /// + /// 当 UI 暂停时调用。 + /// 默认调用 IUiPage 接口的 OnPause 方法(如果存在),并根据 BlocksInput 决定是否暂停处理逻辑。 + /// + public virtual void OnPause() + { + _page?.OnPause(); + + // 只有阻止输入的 UI 才需要暂停处理 + if (!BlocksInput) return; + + Owner.SetProcess(false); + Owner.SetPhysicsProcess(false); + Owner.SetProcessInput(false); + } + + /// + /// 当 UI 恢复时调用。 + /// 默认调用 IUiPage 接口的 OnResume 方法(如果存在),并恢复处理逻辑。 + /// + public virtual void OnResume() + { + if (Owner.IsInvalidNode()) + return; + + _page?.OnResume(); + + // 恢复处理 + Owner.SetProcess(true); + Owner.SetPhysicsProcess(true); + Owner.SetProcessInput(true); + } + + /// + /// 当 UI 隐藏时调用。 + /// 默认调用 IUiPage 接口的 OnHide 方法(如果存在),并隐藏视图节点。 + /// + public virtual void OnHide() + { + _page?.OnHide(); + Owner.Hide(); + } + + /// + /// 当 UI 显示时调用。 + /// 默认调用 IUiPage 接口的 OnShow 方法(如果存在),并显示视图节点,同时触发恢复逻辑。 + /// + public virtual void OnShow() + { + _page?.OnShow(); + Owner.Show(); + OnResume(); + } + + #endregion +} \ No newline at end of file diff --git a/GFramework.Godot/ui/ModalLayerUiPageBehavior.cs b/GFramework.Godot/ui/ModalLayerUiPageBehavior.cs new file mode 100644 index 0000000..03a4704 --- /dev/null +++ b/GFramework.Godot/ui/ModalLayerUiPageBehavior.cs @@ -0,0 +1,46 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using GFramework.Game.Abstractions.enums; +using Godot; + +namespace GFramework.Godot.ui; + +/// +/// 模态层 UI 行为 - 可重入但需谨慎,带遮罩阻止下层交互 +/// +public class ModalLayerUiPageBehavior(T owner, string key) : CanvasItemUiPageBehaviorBase(owner, key) + where T : CanvasItem +{ + public override UiLayer Layer => UiLayer.Modal; + public override bool IsReentrant => true; // ✅ 支持重入(如多层确认弹窗) + public override bool IsModal => true; // 模态窗口 + public override bool BlocksInput => true; // 必须阻止下层交互 + + /// + /// 模态窗口显示时,可以添加遮罩逻辑 + /// + public override void OnShow() + { + base.OnShow(); + // TODO: 可在此添加半透明遮罩层 + // AddModalMask(); + } + + public override void OnHide() + { + // TODO: 移除遮罩层 + // RemoveModalMask(); + base.OnHide(); + } +} \ No newline at end of file diff --git a/GFramework.Godot/ui/OverlayLayerUiPageBehavior.cs b/GFramework.Godot/ui/OverlayLayerUiPageBehavior.cs new file mode 100644 index 0000000..c63cfc9 --- /dev/null +++ b/GFramework.Godot/ui/OverlayLayerUiPageBehavior.cs @@ -0,0 +1,68 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using GFramework.Game.Abstractions.enums; +using GFramework.Game.Abstractions.ui; +using Godot; + +namespace GFramework.Godot.ui; + +/// +/// 浮层 UI 行为类,用于管理覆盖层、对话框等 UI 元素。 +/// 该行为支持可重入性,适用于需要叠加显示的场景。 +/// +/// UI 元素的类型,必须继承自 CanvasItem。 +public class OverlayLayerUiPageBehavior : CanvasItemUiPageBehaviorBase + where T : CanvasItem +{ + /// + /// 初始化 OverlayLayerUiPageBehavior 实例。 + /// + /// 关联的 UI 元素实例。 + /// 用于标识该行为的唯一键。 + public OverlayLayerUiPageBehavior(T owner, string key) : base(owner, key) + { + } + + /// + /// 获取当前 UI 行为所属的层级,固定为浮层(Overlay)。 + /// + public override UiLayer Layer => UiLayer.Overlay; + + /// + /// 指示该行为是否支持可重入性,始终返回 true。 + /// + public override bool IsReentrant => true; + + /// + /// 指示该行为是否为模态行为,始终返回 false。 + /// + public override bool IsModal => false; + + /// + /// 指示该行为是否会阻塞输入,始终返回 false。 + /// + public override bool BlocksInput => false; + + /// + /// 当浮层被暂停时调用此方法。 + /// 浮层在暂停时不中断处理逻辑(如动画等),仅触发业务层的 OnPause 方法。 + /// + public override void OnPause() + { + // 浮层不暂停处理,保持动画和交互 + // 只调用业务层的 OnPause + if (Owner is IUiPage page) + page.OnPause(); + } +} \ No newline at end of file diff --git a/GFramework.Godot/ui/PageLayerUiPageBehavior.cs b/GFramework.Godot/ui/PageLayerUiPageBehavior.cs new file mode 100644 index 0000000..b91c868 --- /dev/null +++ b/GFramework.Godot/ui/PageLayerUiPageBehavior.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using GFramework.Game.Abstractions.enums; +using Godot; + +namespace GFramework.Godot.ui; + +/// +/// 页面层 UI 行为类,用于实现栈式管理的页面行为。 +/// 此类继承自 CanvasItemUiPageBehaviorBase,提供页面层级的 UI 控制逻辑。 +/// 特性包括:不可重入、非模态、阻塞输入。 +/// +/// 泛型参数,表示拥有此行为的 CanvasItem 类型。 +/// 拥有此行为的 CanvasItem 实例。 +/// 用于标识此行为的唯一键。 +public class PageLayerUiPageBehavior(T owner, string key) : CanvasItemUiPageBehaviorBase(owner, key) + where T : CanvasItem +{ + /// + /// 获取当前 UI 行为所属的层级。 + /// 返回值固定为 UiLayer.Page,表示页面层级。 + /// + public override UiLayer Layer => UiLayer.Page; + + /// + /// 指示当前 UI 行为是否可重入。 + /// 返回值为 false,表示不可重入。 + /// + public override bool IsReentrant => false; + + /// + /// 指示当前 UI 行为是否为模态。 + /// 返回值为 false,表示非模态。 + /// + public override bool IsModal => false; + + /// + /// 指示当前 UI 行为是否会阻塞输入。 + /// 返回值为 true,表示会阻塞输入。 + /// + public override bool BlocksInput => true; +} \ No newline at end of file diff --git a/GFramework.Godot/ui/ToastLayerUiPageBehavior.cs b/GFramework.Godot/ui/ToastLayerUiPageBehavior.cs new file mode 100644 index 0000000..a8a12e7 --- /dev/null +++ b/GFramework.Godot/ui/ToastLayerUiPageBehavior.cs @@ -0,0 +1,52 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using GFramework.Game.Abstractions.enums; +using Godot; + +namespace GFramework.Godot.ui; + +/// +/// 表示一个用于管理Toast层UI页面行为的泛型类。 +/// 此类继承自CanvasItemUiPageBehaviorBase,专门用于处理Toast类型的UI层逻辑。 +/// +/// 指定的CanvasItem类型,表示此类的所有者。 +/// 当前UI页面行为的所有者对象,必须是CanvasItem的实例。 +/// 用于标识此UI页面行为的唯一键值。 +public class ToastLayerUiPageBehavior(T owner, string key) : CanvasItemUiPageBehaviorBase(owner, key) + where T : CanvasItem +{ + /// + /// 获取当前UI页面行为所属的UI层类型。 + /// 对于Toast层,此属性始终返回UiLayer.Toast。 + /// + public override UiLayer Layer => UiLayer.Toast; + + /// + /// 指示此UI页面行为是否支持重入(即是否允许在已激活状态下再次被调用)。 + /// Toast层通常允许多次触发,因此此属性返回true。 + /// + public override bool IsReentrant => true; + + /// + /// 指示此UI页面行为是否为模态(即是否会阻止用户与其他UI交互)。 + /// Toast层通常是非模态的,因此此属性返回false。 + /// + public override bool IsModal => false; + + /// + /// 指示此UI页面行为是否会阻塞用户输入。 + /// Toast层通常不会阻塞输入,因此此属性返回false。 + /// + public override bool BlocksInput => false; +} \ No newline at end of file diff --git a/GFramework.Godot/ui/TopmostLayerUiPageBehavior.cs b/GFramework.Godot/ui/TopmostLayerUiPageBehavior.cs new file mode 100644 index 0000000..69eeed1 --- /dev/null +++ b/GFramework.Godot/ui/TopmostLayerUiPageBehavior.cs @@ -0,0 +1,46 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using GFramework.Game.Abstractions.enums; +using Godot; + +namespace GFramework.Godot.ui; + +/// +/// 顶层 UI 行为 - 不可重入,最高优先级,用于系统级弹窗 +/// +public class TopmostLayerUiPageBehavior(T owner, string key) : CanvasItemUiPageBehaviorBase(owner, key) + where T : CanvasItem +{ + public override UiLayer Layer => UiLayer.Topmost; + public override bool IsReentrant => false; // ❌ 顶层不支持重入 + public override bool IsModal => true; // 顶层通常是模态的 + public override bool BlocksInput => true; // 必须阻止所有下层交互 + + /// + /// 顶层显示时,可以禁用所有下层 UI + /// + public override void OnShow() + { + base.OnShow(); + // TODO: 可在此禁用其他所有层级 + // DisableAllLowerLayers(); + } + + public override void OnHide() + { + // TODO: 恢复其他层级 + // EnableAllLowerLayers(); + base.OnHide(); + } +} \ No newline at end of file diff --git a/GFramework.Godot/ui/UiPageBehaviorFactory.cs b/GFramework.Godot/ui/UiPageBehaviorFactory.cs new file mode 100644 index 0000000..965005b --- /dev/null +++ b/GFramework.Godot/ui/UiPageBehaviorFactory.cs @@ -0,0 +1,46 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using GFramework.Game.Abstractions.enums; +using GFramework.Game.Abstractions.ui; +using Godot; + +namespace GFramework.Godot.ui; + +/// +/// UI 页面行为工厂类,根据层级创建对应的 Behavior 实例 +/// +public static class UiPageBehaviorFactory +{ + /// + /// 创建指定层级的 UI 页面行为实例 + /// + /// CanvasItem 类型 + /// 视图节点 + /// UI 标识键 + /// 目标层级 + /// 对应层级的 IUiPageBehavior 实例 + public static IUiPageBehavior Create(T owner, string key, UiLayer layer) + where T : CanvasItem + { + return layer switch + { + UiLayer.Page => new PageLayerUiPageBehavior(owner, key), + UiLayer.Overlay => new OverlayLayerUiPageBehavior(owner, key), + UiLayer.Modal => new ModalLayerUiPageBehavior(owner, key), + UiLayer.Toast => new ToastLayerUiPageBehavior(owner, key), + UiLayer.Topmost => new TopmostLayerUiPageBehavior(owner, key), + _ => throw new ArgumentException($"Unsupported UI layer: {layer}") + }; + } +} \ No newline at end of file