Files
XCEngine/docs/api/XCEngine/Editor/Actions/HierarchyActionRouter/HierarchyActionRouter.md

7.1 KiB
Raw Blame History

HierarchyActionRouter

命名空间: XCEngine::Editor::Actions

类型: header-helper

源文件: editor/src/Actions/HierarchyActionRouter.h

描述: Hierarchy 面板的交互路由助手,负责把层级树中的点击、重命名、拖放、上下文菜单和创建动作转成统一的 editor command / event 流。

概述

HierarchyActionRouter 不是独立的运行时对象,而是一组 inline helper。
它的意义在于把 Hierarchy 这种“高交互密度、低业务独立性”的逻辑,从面板渲染代码中拆出来。

当前它负责的事情主要有:

  • 实体选择与背景点击清空选择。
  • 发布重命名请求,并提交最终名称。
  • 处理实体拖拽重挂接。
  • 绘制背景菜单、实体菜单和排序选项弹窗。
  • 调用 EntityCommands 创建、复制、粘贴、删除、重复和重挂接实体。

这类拆法很符合商业编辑器的做法。Hierarchy 面板应该主要关心“树怎么画”,而不是“菜单项怎么执行”。

前置知识

当前 Hierarchy 相关调用链大致是:

HierarchyPanel 采样 ImGui 交互 -> HierarchyActionRouter 做输入解释与路由 -> EntityCommands 修改场景 / 撤销栈 -> SelectionManagerEventBus 推进后续 UI 同步。

这条链路里:

  • 面板负责显示。
  • router 负责交互语义。
  • command 层负责真正修改场景。

这就是典型的“展示层 / 交互层 / 命令层”分离。

主要职责

职责 代表入口 说明
选择路由 HandleHierarchySelectionClick 单击实体时更新当前选择。
重命名请求 RequestEntityRename / CommitEntityRename 通过事件请求进入重命名,再调用命令层提交名称。
背景交互 HandleHierarchyBackgroundPrimaryClick / DrawHierarchyBackgroundInteraction 清空选择、接收拖放到根节点。
右键菜单 RequestHierarchyBackgroundContextPopup / DrawHierarchyContextActions 组装创建、删除、复制、粘贴等菜单项。
创建实体 DrawHierarchyCreateActions 调用命令层创建空对象、相机、灯光和内置 primitive。
拖拽重挂接 BeginHierarchyEntityDrag / AcceptHierarchyEntityDrop 在实体之间或根节点上完成重挂接。
排序菜单 DrawHierarchySortOptionsPopup 绘制排序选项 popup。

当前实现行为

选择与重命名

  • HandleHierarchySelectionClick 当前支持两种模式:
    • 普通点击: SetSelectedEntity(entityId)
    • additive 点击: 仅在未选中时 AddToSelection(entityId)
  • 当前 additive 语义更接近“增量加入”,不是完整的 Ctrl-toggle 逻辑。

重命名分两步:

  1. RequestEntityRename(context, gameObject) 发布 EntityRenameRequestedEvent
  2. CommitEntityRename(context, entityId, newName) 校验实体存在且名称非空,再调用 EntityCommands

这种拆法的意义在于:
Hierarchy 行内编辑、菜单栏 Rename、快捷键 Rename 都可以走同一条“先请求、后提交”的链路。

背景交互与 action route

HandleHierarchyBackgroundPrimaryClickDrawHierarchyBackgroundInteraction 会在以下条件下清空选择:

  • 鼠标在窗口内。
  • 当前没有悬停在具体 item 上。
  • 当前不处于 rename 编辑状态。

DrawHierarchyBackgroundInteraction 还会创建一个不可见交互面,并允许把实体直接拖回根节点。这一点很像 Unity 的 Hierarchy 根级拖放体验。

拖拽载荷与重挂接

当前拖拽载荷类型固定为:

  • "ENTITY_PTR"

BeginHierarchyEntityDrag 会把 GameObject* 直接写入 ImGui drag payload。
AcceptHierarchyEntityDrop / AcceptHierarchyEntityDropToRoot 在 drop 时读取这个指针,并调用:

  • Commands::CanReparentEntity(...)
  • Commands::ReparentEntityPreserveWorldTransform(...)

所以当前 router 的重挂接语义不是“只改 parent 指针”,而是“尽量保持世界空间位置、旋转和缩放不跳变”。

上下文菜单与创建动作

DrawHierarchyContextActions(...) 当前统一拼装了:

  • Detach
  • Rename
  • Delete
  • Copy
  • Paste
  • Duplicate
  • Create 子菜单

其中 DrawHierarchyCreateActions(...) 已经不只是创建“命名好的空物体”。
按当前 EntityCommands 实现:

  • CreateCameraEntity 会附加 CameraComponent
  • CreateLightEntity 会附加 LightComponent
  • CreatePrimitiveEntity 会附加 MeshFilterComponentMeshRendererComponent
  • primitive 还会绑定内置 mesh 路径和默认 primitive material

这点和旧文档结论不同当前实现已经更接近商业引擎里“Create 3D Object” 的真实行为。

Popup 诊断日志

文件中还有一组 TraceHierarchyPopup(...) 调试日志 helper。
它们会在背景右键菜单、实体右键菜单和 Create 子菜单的打开 / 关闭过程中写日志,便于定位 ImGui popup 状态错乱问题。

设计说明

Hierarchy 交互往往是编辑器里最容易失控的一块,因为它同时牵涉:

  • 选择
  • 多级树展开
  • 内联重命名
  • 拖放
  • 上下文菜单
  • 创建 / 删除 / 复制 / 粘贴

如果这些逻辑全部堆在 HierarchyPanel.cpp,后续一加多选、过滤、排序或者 prefab 支持,文件会很快失去可维护性。

当前拆成 router 的好处很实际:

  • HierarchyPanel 仍然能保持“绘树”为主。
  • 菜单、拖放、重命名可以复用同一套命令层。
  • 快捷键、菜单栏、右键菜单都能共用行为定义。

这就是商业编辑器常见的“thin panel, rich routing, centralized commands” 模式。

测试与验证

当前与该文件直接相关的行为,至少有以下测试锚点:

  • HierarchyRouteExecutesCopyPasteDuplicateDeleteAndRename
  • HierarchyRouterRenameHelpersPublishAndCommit
  • HierarchyItemContextRequestSelectsEntityAndStoresPopupTarget
  • ReparentPreserveWorldTransformKeepsWorldPose

这些测试位于 tests/Editor/test_action_routing.cpp,覆盖了 rename 请求、上下文目标记录、复制粘贴和保持世界变换的重挂接语义。

当前限制

  • 暂不支持多实体拖拽、范围选择或框选。
  • additive 选择不是完整 Ctrl-toggle / Shift-range 模型。
  • 拖拽 payload 使用原始 GameObject*,应视为单进程、单帧 UI 交互协议,而不是可持久化数据格式。
  • popup 诊断日志当前直接写常规日志通道,调试时有帮助,但正式产品里可能需要更细粒度的 trace 控制。

相关文档