docs: sync api and planning docs
This commit is contained in:
@@ -6,46 +6,161 @@
|
||||
|
||||
**源文件**: `editor/src/Actions/HierarchyActionRouter.h`
|
||||
|
||||
**描述**: 封装 Hierarchy 面板中的选中、重命名、拖拽重挂接、排序和上下文菜单动作。
|
||||
**描述**: Hierarchy 面板的交互路由助手,负责把层级树中的点击、重命名、拖放、上下文菜单和创建动作转成统一的 editor command / event 流。
|
||||
|
||||
## 概述
|
||||
|
||||
`HierarchyActionRouter` 是当前实体层级树交互的主要承载者。它把 `HierarchyPanel.cpp` 中最容易膨胀的逻辑拆成了几类可复用动作:
|
||||
`HierarchyActionRouter` 不是独立的运行时对象,而是一组 `inline` helper。
|
||||
它的意义在于把 Hierarchy 这种“高交互密度、低业务独立性”的逻辑,从面板渲染代码中拆出来。
|
||||
|
||||
- 选择与背景点击清空选择
|
||||
- 实体重命名请求与提交
|
||||
- 拖拽实体到其他父节点或根节点
|
||||
- 创建 / 删除 / 复制 / 粘贴 / 重复实体
|
||||
- 排序选项弹窗
|
||||
当前它负责的事情主要有:
|
||||
|
||||
## 当前实现
|
||||
- 实体选择与背景点击清空选择。
|
||||
- 发布重命名请求,并提交最终名称。
|
||||
- 处理实体拖拽重挂接。
|
||||
- 绘制背景菜单、实体菜单和排序选项弹窗。
|
||||
- 调用 [EntityCommands](../../Commands/EntityCommands/EntityCommands.md) 创建、复制、粘贴、删除、重复和重挂接实体。
|
||||
|
||||
- 拖拽载荷类型固定为字符串 `"ENTITY_PTR"`
|
||||
- `HandleHierarchySelectionClick` 只实现了单选和 Ctrl 增选
|
||||
- `CommitEntityRename` 会先验证实体存在且名字非空,再调用 [`RenameEntity`](../../Commands/EntityCommands/EntityCommands.md)
|
||||
- 拖拽重挂接时会调用 `Commands::CanReparentEntity` 和 `ReparentEntityPreserveWorldTransform`
|
||||
- 背景右键菜单可以直接创建空物体、Camera、Light,以及命名为 `Cube` / `Sphere` / `Plane` 的空实体
|
||||
这类拆法很符合商业编辑器的做法。Hierarchy 面板应该主要关心“树怎么画”,而不是“菜单项怎么执行”。
|
||||
|
||||
## 前置知识
|
||||
|
||||
当前 Hierarchy 相关调用链大致是:
|
||||
|
||||
`HierarchyPanel` 采样 ImGui 交互 -> `HierarchyActionRouter` 做输入解释与路由 -> [EntityCommands](../../Commands/EntityCommands/EntityCommands.md) 修改场景 / 撤销栈 -> [SelectionManager](../../Core/ISelectionManager/ISelectionManager.md) 与 [EventBus](../../Core/EventBus/EventBus.md) 推进后续 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](../../Commands/EntityCommands/EntityCommands.md)。
|
||||
|
||||
这种拆法的意义在于:
|
||||
Hierarchy 行内编辑、菜单栏 Rename、快捷键 Rename 都可以走同一条“先请求、后提交”的链路。
|
||||
|
||||
### 背景交互与 action route
|
||||
|
||||
`HandleHierarchyBackgroundPrimaryClick` 和 `DrawHierarchyBackgroundInteraction` 会在以下条件下清空选择:
|
||||
|
||||
- 鼠标在窗口内。
|
||||
- 当前没有悬停在具体 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](../../Commands/EntityCommands/EntityCommands.md) 实现:
|
||||
|
||||
- `CreateCameraEntity` 会附加 `CameraComponent`
|
||||
- `CreateLightEntity` 会附加 `LightComponent`
|
||||
- `CreatePrimitiveEntity` 会附加 `MeshFilterComponent` 与 `MeshRendererComponent`
|
||||
- primitive 还会绑定内置 mesh 路径和默认 primitive material
|
||||
|
||||
这点和旧文档结论不同,当前实现已经更接近商业引擎里“Create 3D Object” 的真实行为。
|
||||
|
||||
### Popup 诊断日志
|
||||
|
||||
文件中还有一组 `TraceHierarchyPopup(...)` 调试日志 helper。
|
||||
它们会在背景右键菜单、实体右键菜单和 `Create` 子菜单的打开 / 关闭过程中写日志,便于定位 ImGui popup 状态错乱问题。
|
||||
|
||||
## 设计说明
|
||||
|
||||
这类层级树交互通常非常碎,如果全写在面板类里,后续一加多选、拖放规则或菜单项就会迅速失控。
|
||||
把交互逻辑集中在 router 里有三个好处:
|
||||
Hierarchy 交互往往是编辑器里最容易失控的一块,因为它同时牵涉:
|
||||
|
||||
- 面板类仍然只负责树结构遍历和 UI 布局
|
||||
- 所有入口都能复用同一套实体命令
|
||||
- 可以明确把“层级树交互规则”与“SceneManager 真正修改场景”的职责隔开
|
||||
- 选择
|
||||
- 多级树展开
|
||||
- 内联重命名
|
||||
- 拖放
|
||||
- 上下文菜单
|
||||
- 创建 / 删除 / 复制 / 粘贴
|
||||
|
||||
这和 Unity Hierarchy 的设计也比较接近:Hierarchy 面板只是入口,真正的数据修改要落到场景编辑命令层。
|
||||
如果这些逻辑全部堆在 `HierarchyPanel.cpp`,后续一加多选、过滤、排序或者 prefab 支持,文件会很快失去可维护性。
|
||||
|
||||
当前拆成 router 的好处很实际:
|
||||
|
||||
- `HierarchyPanel` 仍然能保持“绘树”为主。
|
||||
- 菜单、拖放、重命名可以复用同一套命令层。
|
||||
- 快捷键、菜单栏、右键菜单都能共用行为定义。
|
||||
|
||||
这就是商业编辑器常见的“thin panel, rich routing, centralized commands” 模式。
|
||||
|
||||
## 测试与验证
|
||||
|
||||
当前与该文件直接相关的行为,至少有以下测试锚点:
|
||||
|
||||
- `HierarchyRouteExecutesCopyPasteDuplicateDeleteAndRename`
|
||||
- `HierarchyRouterRenameHelpersPublishAndCommit`
|
||||
- `HierarchyItemContextRequestSelectsEntityAndStoresPopupTarget`
|
||||
- `ReparentPreserveWorldTransformKeepsWorldPose`
|
||||
|
||||
这些测试位于 `tests/Editor/test_action_routing.cpp`,覆盖了 rename 请求、上下文目标记录、复制粘贴和保持世界变换的重挂接语义。
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 暂不支持多实体批量拖拽
|
||||
- 只有 `Ctrl` 增选,没有框选、Shift 范围选中等高级交互
|
||||
- `Cube` / `Sphere` / `Plane` 当前只是命名约定,不会自动附加网格或渲染组件
|
||||
- 暂不支持多实体拖拽、范围选择或框选。
|
||||
- additive 选择不是完整 Ctrl-toggle / Shift-range 模型。
|
||||
- 拖拽 payload 使用原始 `GameObject*`,应视为单进程、单帧 UI 交互协议,而不是可持久化数据格式。
|
||||
- popup 诊断日志当前直接写常规日志通道,调试时有帮助,但正式产品里可能需要更细粒度的 trace 控制。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Actions](../Actions.md)
|
||||
- [HierarchyPanel](../../panels/HierarchyPanel/HierarchyPanel.md)
|
||||
- [EntityCommands](../../Commands/EntityCommands/EntityCommands.md)
|
||||
- [EditorEvents](../../Core/EditorEvents/EditorEvents.md)
|
||||
- [EventBus](../../Core/EventBus/EventBus.md)
|
||||
- [EditorActionRoute](../../Core/EditorActionRoute/EditorActionRoute.md)
|
||||
- [Widgets](../../UI/Widgets/Widgets.md)
|
||||
|
||||
Reference in New Issue
Block a user