docs: sync api and planning docs

This commit is contained in:
2026-04-08 16:07:03 +08:00
parent 08c3278e10
commit 31756847ab
1826 changed files with 44502 additions and 29645 deletions

View File

@@ -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)