Document editor architecture finish state
This commit is contained in:
378
docs/plan/Editor架构说明.md
Normal file
378
docs/plan/Editor架构说明.md
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
# Editor 架构说明
|
||||||
|
|
||||||
|
## 1. 当前目标
|
||||||
|
|
||||||
|
当前这一轮 editor 重构,目标不是继续在各个 panel 上零散修 UI,而是先把 editor 自身的架构层次收稳:
|
||||||
|
|
||||||
|
- 把视觉样式、交互路由、编辑命令、dock 布局、面板壳层拆开。
|
||||||
|
- 把 `Hierarchy / Project / Inspector / Console / MenuBar` 统一到同一套 shared UI 和 action 语义上。
|
||||||
|
- 保持 `Scene / Game` 先作为空壳 panel,等待后续 `Viewport / RHI` 回归。
|
||||||
|
|
||||||
|
这意味着当前 editor 的重点是“编辑器外壳架构完整”,不是“运行时视口功能完整”。
|
||||||
|
|
||||||
|
## 2. 总体分层
|
||||||
|
|
||||||
|
当前 editor 推荐按下面的依赖方向理解:
|
||||||
|
|
||||||
|
`Application -> EditorLayer -> EditorWorkspace -> Panels -> Actions -> Commands -> Managers/Core`
|
||||||
|
|
||||||
|
同时还有一条横向的共享 UI 层:
|
||||||
|
|
||||||
|
`Panels / Actions / ComponentEditors -> UI`
|
||||||
|
|
||||||
|
以及一条 inspector 专用扩展链路:
|
||||||
|
|
||||||
|
`InspectorPanel -> ComponentEditorRegistry -> IComponentEditor`
|
||||||
|
|
||||||
|
允许依赖的基本原则:
|
||||||
|
|
||||||
|
- `UI` 只负责样式 token、共享控件、popup/property-grid 等表现层能力,不承担业务语义。
|
||||||
|
- `Actions` 负责把 button/menu/shortcut/context menu 这些 UI 意图转换为命令调用、事件请求或共享状态更新。
|
||||||
|
- `Commands` 负责真正修改 scene/project/component 数据,并处理 dirty/undo/selection 的业务边界。
|
||||||
|
- `Panels` 只保留最小的渲染壳层和少量局部瞬时状态,不直接堆业务逻辑。
|
||||||
|
- `Layout` 只负责 dockspace 和布局持久化,不处理 scene/project 业务。
|
||||||
|
- `Core/Managers` 提供 editor 运行时共享状态与数据入口。
|
||||||
|
|
||||||
|
不允许继续扩散的方向:
|
||||||
|
|
||||||
|
- 不要在 panel 里直接散落 scene/project 业务修改。
|
||||||
|
- 不要把菜单、快捷键、右键菜单逻辑分别复制到不同 panel。
|
||||||
|
- 不要把样式常量重新写回 panel 本地。
|
||||||
|
- 不要让 `UI` 反向依赖 `Commands` 或具体 panel。
|
||||||
|
|
||||||
|
## 3. 主要模块职责
|
||||||
|
|
||||||
|
### 3.1 Application / Platform
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
|
||||||
|
- `editor/src/Application.cpp`
|
||||||
|
- `editor/src/Platform/Win32EditorHost.h`
|
||||||
|
- `editor/src/Platform/D3D12WindowRenderer.h`
|
||||||
|
- `editor/src/UI/ImGuiSession.h`
|
||||||
|
- `editor/src/UI/ImGuiBackendBridge.h`
|
||||||
|
|
||||||
|
职责:
|
||||||
|
|
||||||
|
- 创建窗口、D3D12 renderer、ImGui session 与 backend bridge。
|
||||||
|
- 初始化 `EditorContext`。
|
||||||
|
- 监听 editor 级退出事件。
|
||||||
|
- 驱动 layer attach/detach/update/render 主循环。
|
||||||
|
- 更新窗口标题。
|
||||||
|
|
||||||
|
边界:
|
||||||
|
|
||||||
|
- 这里是 editor 的宿主壳层,不负责具体 panel 交互。
|
||||||
|
- 这里可以处理平台与渲染 backend 生命周期,但不应该继续写 editor 业务逻辑。
|
||||||
|
|
||||||
|
### 3.2 EditorLayer / EditorWorkspace
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
|
||||||
|
- `editor/src/Layers/EditorLayer.cpp`
|
||||||
|
- `editor/src/Core/EditorWorkspace.h`
|
||||||
|
|
||||||
|
职责:
|
||||||
|
|
||||||
|
- `EditorLayer` 只做 layer 生命周期转发。
|
||||||
|
- `EditorWorkspace` 负责组装 panel 集合、初始化 project panel、加载启动场景、挂接 dock layout controller。
|
||||||
|
- 统一调度 panel 的 attach/detach/update/render/event。
|
||||||
|
|
||||||
|
边界:
|
||||||
|
|
||||||
|
- `EditorLayer` 不能回到“自己直接维护一堆 panel 生命周期”的旧结构。
|
||||||
|
- 新增 panel 时,优先在 `EditorWorkspace` 里做装配,不回写到 `Application`。
|
||||||
|
|
||||||
|
### 3.3 UI Shared Layer
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
|
||||||
|
- `editor/src/UI/BaseTheme.h`
|
||||||
|
- `editor/src/UI/StyleTokens.h`
|
||||||
|
- `editor/src/UI/DockHostStyle.h`
|
||||||
|
- `editor/src/UI/PanelChrome.h`
|
||||||
|
- `editor/src/UI/Widgets.h`
|
||||||
|
- `editor/src/UI/PopupState.h`
|
||||||
|
- `editor/src/UI/PropertyGrid.h`
|
||||||
|
- `editor/src/UI/UI.h`
|
||||||
|
|
||||||
|
职责:
|
||||||
|
|
||||||
|
- 提供 editor 统一主题、尺寸、留白、颜色和面板 chrome。
|
||||||
|
- 提供 toolbar、tab、popup、dialog、property row、asset tile、empty state 等共享控件。
|
||||||
|
- 提供 inspector/property-grid 的公共表现层。
|
||||||
|
|
||||||
|
边界:
|
||||||
|
|
||||||
|
- 这一层可以知道 ImGui,但不应该知道 scene/project 业务对象如何变化。
|
||||||
|
- 这一层输出的是“怎样画”,不是“点了以后改什么”。
|
||||||
|
|
||||||
|
### 3.4 Actions / Routers
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
|
||||||
|
- `editor/src/Actions/EditorActions.h`
|
||||||
|
- `editor/src/Actions/ActionBinding.h`
|
||||||
|
- `editor/src/Actions/ActionRouting.h`
|
||||||
|
- `editor/src/Actions/EditActionRouter.h`
|
||||||
|
- `editor/src/Actions/MainMenuActionRouter.h`
|
||||||
|
- `editor/src/Actions/HierarchyActionRouter.h`
|
||||||
|
- `editor/src/Actions/ProjectActionRouter.h`
|
||||||
|
- `editor/src/Actions/InspectorActionRouter.h`
|
||||||
|
- `editor/src/Actions/ConsoleActionRouter.h`
|
||||||
|
|
||||||
|
职责:
|
||||||
|
|
||||||
|
- 定义 editor 内部动作项的文案、快捷键、可用状态。
|
||||||
|
- 统一菜单点击、快捷键触发、toolbar 按钮、右键菜单动作。
|
||||||
|
- 处理 active route/focused route,把 `Edit` 类动作路由到正确 panel。
|
||||||
|
- 处理 popup 请求、rename 请求、局部状态切换等“轻量交互编排”。
|
||||||
|
|
||||||
|
边界:
|
||||||
|
|
||||||
|
- `Actions` 可以依赖 `Commands`、`EventBus`、`UI`,但不负责底层数据结构修改细节。
|
||||||
|
- `Actions` 不负责持久保存复杂状态;复杂状态应留给 manager 或共享 state object。
|
||||||
|
- `Actions` 不应该包含大量 panel 私有布局代码。
|
||||||
|
|
||||||
|
判断标准:
|
||||||
|
|
||||||
|
- 一个交互如果同时被 menu、shortcut、context menu、toolbar 复用,它应先进入 router。
|
||||||
|
- 一个交互如果只是“请求 panel 开一个 popup/进入 rename”,更适合走 `Actions + EventBus/shared state`。
|
||||||
|
|
||||||
|
### 3.5 Commands
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
|
||||||
|
- `editor/src/Commands/SceneCommands.h`
|
||||||
|
- `editor/src/Commands/EntityCommands.h`
|
||||||
|
- `editor/src/Commands/ProjectCommands.h`
|
||||||
|
- `editor/src/Commands/ComponentCommands.h`
|
||||||
|
|
||||||
|
职责:
|
||||||
|
|
||||||
|
- 处理 scene/project/component 的实际编辑行为。
|
||||||
|
- 统一 undo 快照、dirty 标记、selection reset 等业务边界。
|
||||||
|
- 统一保存、加载、复制、粘贴、重命名、重挂接、资源移动等操作。
|
||||||
|
|
||||||
|
边界:
|
||||||
|
|
||||||
|
- `Commands` 不依赖 ImGui。
|
||||||
|
- `Commands` 不直接绘制 UI。
|
||||||
|
- `Commands` 返回的是业务结果,不承载 popup 绘制和菜单拼装。
|
||||||
|
|
||||||
|
经验规则:
|
||||||
|
|
||||||
|
- 只要一个操作会改 scene/project 数据,就优先考虑落到 `Commands`。
|
||||||
|
- 只要一个操作涉及 undo/redo,就不要留在 panel 本地手写。
|
||||||
|
|
||||||
|
### 3.6 Core / Managers
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
|
||||||
|
- `editor/src/Core/EditorContext.h`
|
||||||
|
- `editor/src/Core/EventBus.h`
|
||||||
|
- `editor/src/Core/SelectionManager.h`
|
||||||
|
- `editor/src/Core/UndoManager.h`
|
||||||
|
- `editor/src/Managers/SceneManager.h`
|
||||||
|
- `editor/src/Managers/ProjectManager.h`
|
||||||
|
|
||||||
|
职责:
|
||||||
|
|
||||||
|
- `EditorContext` 聚合 event bus、selection、scene、project、undo、active action route。
|
||||||
|
- `SceneManager` 持有场景对象、根节点、场景路径、dirty 状态、clipboard。
|
||||||
|
- `ProjectManager` 扫描 `Assets`、维护当前目录、选择索引、文件夹导航。
|
||||||
|
- `SelectionManager` 统一发布 selection changed 事件。
|
||||||
|
- `UndoManager` 负责快照历史、interactive change 边界、undo/redo。
|
||||||
|
|
||||||
|
所有权约定:
|
||||||
|
|
||||||
|
- selection 所有权在 `SelectionManager`。
|
||||||
|
- undo 历史所有权在 `UndoManager`。
|
||||||
|
- scene dirty 与当前场景路径所有权在 `SceneManager`。
|
||||||
|
- 当前活动编辑路由所有权在 `EditorContext`。
|
||||||
|
|
||||||
|
### 3.7 Layout
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
|
||||||
|
- `editor/src/Layout/DockLayoutController.h`
|
||||||
|
|
||||||
|
职责:
|
||||||
|
|
||||||
|
- 创建 dockspace。
|
||||||
|
- 应用默认布局。
|
||||||
|
- 响应 reset layout 请求。
|
||||||
|
- 持久化 ImGui layout 到项目目录下的 `.xceditor/imgui_layout.ini`。
|
||||||
|
|
||||||
|
边界:
|
||||||
|
|
||||||
|
- layout controller 不应该知道 hierarchy/project/scene 的编辑业务。
|
||||||
|
- panel 是否存在由 workspace 决定,不由 layout controller 反向创建。
|
||||||
|
|
||||||
|
### 3.8 Panels
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
|
||||||
|
- `editor/src/panels/MenuBar.cpp`
|
||||||
|
- `editor/src/panels/HierarchyPanel.cpp`
|
||||||
|
- `editor/src/panels/ProjectPanel.cpp`
|
||||||
|
- `editor/src/panels/InspectorPanel.cpp`
|
||||||
|
- `editor/src/panels/ConsolePanel.cpp`
|
||||||
|
|
||||||
|
当前 panel 应保留的内容:
|
||||||
|
|
||||||
|
- 窗口壳层。
|
||||||
|
- 生命周期订阅与退订。
|
||||||
|
- 非共享的极少量局部瞬时状态。
|
||||||
|
- 调用 shared UI/widget 和 action router 进行拼装。
|
||||||
|
|
||||||
|
当前 panel 不应该再承载的内容:
|
||||||
|
|
||||||
|
- undo/dirty/selection 的业务判断。
|
||||||
|
- 菜单和快捷键的重复定义。
|
||||||
|
- 资源/实体操作的底层命令执行细节。
|
||||||
|
- 大量散落的视觉常量。
|
||||||
|
|
||||||
|
### 3.9 ComponentEditors
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
|
||||||
|
- `editor/src/ComponentEditors/IComponentEditor.h`
|
||||||
|
- `editor/src/ComponentEditors/ComponentEditorRegistry.h`
|
||||||
|
- `editor/src/ComponentEditors/ComponentEditorRegistry.cpp`
|
||||||
|
- `editor/src/ComponentEditors/TransformComponentEditor.h`
|
||||||
|
- `editor/src/ComponentEditors/CameraComponentEditor.h`
|
||||||
|
- `editor/src/ComponentEditors/LightComponentEditor.h`
|
||||||
|
|
||||||
|
职责:
|
||||||
|
|
||||||
|
- 为某一类 component 提供 inspector 内容绘制。
|
||||||
|
- 定义该 component 的显示名、是否能添加、是否能删除、添加禁用原因。
|
||||||
|
- 通过 registry 统一注册到 inspector 与 component commands。
|
||||||
|
|
||||||
|
当前注册机制:
|
||||||
|
|
||||||
|
- `ComponentEditorRegistry` 是 editor 侧单例注册表。
|
||||||
|
- 注册发生在 `ComponentEditorRegistry` 构造函数内。
|
||||||
|
- `RegisterEditor(std::unique_ptr<IComponentEditor>)` 将 editor 同时放入顺序列表和按类型名索引表。
|
||||||
|
- inspector 绘制时通过 component 实例类型名查找 editor。
|
||||||
|
- Add Component 菜单通过遍历 registry 的已注册 editor 生成。
|
||||||
|
- `ComponentCommands` 通过相同 registry 复用 add/remove 规则,而不是在 inspector 单独硬编码。
|
||||||
|
|
||||||
|
这套机制的意义:
|
||||||
|
|
||||||
|
- inspector 的“显示逻辑”和“组件增删能力”在同一处收口。
|
||||||
|
- 新增一个 component editor 时,不需要去 panel 里到处补分支。
|
||||||
|
- 后续如果要做脚本组件、自定义组件 inspector,优先扩展 registry,而不是污染 panel。
|
||||||
|
|
||||||
|
新增 component editor 的推荐步骤:
|
||||||
|
|
||||||
|
1. 新建一个实现 `IComponentEditor` 的 editor。
|
||||||
|
2. 在其中定义 `GetComponentTypeName / GetDisplayName / Render / CanAddTo / CanRemove`。
|
||||||
|
3. 在 `ComponentEditorRegistry.cpp` 注册。
|
||||||
|
4. 如果涉及复杂交互,优先复用 `UI::PropertyGrid` 和 undo interactive change 机制。
|
||||||
|
|
||||||
|
## 4. EventBus 使用规则
|
||||||
|
|
||||||
|
`EventBus` 现在主要承担两类职责:
|
||||||
|
|
||||||
|
- editor 范围的状态通知,例如 selection changed、entity created/deleted、scene changed。
|
||||||
|
- UI 请求类事件,例如 rename request、exit request、reset layout request。
|
||||||
|
|
||||||
|
推荐规则:
|
||||||
|
|
||||||
|
- “请求某个 panel 进入某种 UI 状态”时,优先用事件或共享 popup state。
|
||||||
|
- “真正修改数据”时,优先走 command,不要只发 event 期待别人去改数据。
|
||||||
|
- 事件用于解耦,不用于隐藏业务路径。
|
||||||
|
|
||||||
|
一个简单判断:
|
||||||
|
|
||||||
|
- 如果动作需要 undo/dirty,通常应该先有 `Command`。
|
||||||
|
- 如果动作只是让某个 panel 弹窗或进入 rename,通常可以只发 `Event`。
|
||||||
|
|
||||||
|
## 5. Undo / Dirty / Selection 约定
|
||||||
|
|
||||||
|
这是 editor 最容易再次失控的地方,必须保持统一:
|
||||||
|
|
||||||
|
- scene 数据修改通过 `Commands` 收口。
|
||||||
|
- `UndoUtils::ExecuteSceneCommand(...)` 负责把一次编辑包成可回退命令。
|
||||||
|
- inspector 连续拖拽这类交互,使用 interactive change 边界,不在 panel 里手搓多次提交。
|
||||||
|
- 场景切换、新建场景、加载场景后,selection 与 undo 历史由 scene command 统一重置。
|
||||||
|
- scene dirty 由 `SceneManager` 维护,保存成功后归零。
|
||||||
|
|
||||||
|
禁止事项:
|
||||||
|
|
||||||
|
- 不要在 panel 里直接偷偷 `MarkSceneDirty()` 来替代 command。
|
||||||
|
- 不要让某个 panel 自己保存一份 selection。
|
||||||
|
- 不要让快捷键直接跨过 command 改 manager。
|
||||||
|
|
||||||
|
## 6. 快捷键与菜单路由规则
|
||||||
|
|
||||||
|
当前 editor 已形成两条主路由:
|
||||||
|
|
||||||
|
- `MainMenuActionRouter` 负责 `File / View / Help / global shortcut`。
|
||||||
|
- `EditActionRouter` 负责 `Edit` 菜单和 panel-focused edit shortcut。
|
||||||
|
|
||||||
|
约定:
|
||||||
|
|
||||||
|
- `Global` 类快捷键由 menu bar 统一分发。
|
||||||
|
- `Hierarchy / Project` 等焦点相关动作,先根据 `EditorActionRoute` 解析目标,再执行对应 action。
|
||||||
|
- panel 只负责声明自己何时成为 active route,不负责重复实现整套 edit 菜单。
|
||||||
|
|
||||||
|
这样做的收益:
|
||||||
|
|
||||||
|
- 同一个动作不会再出现“菜单能点、快捷键失效、右键菜单又是另一套”的问题。
|
||||||
|
- 后续增加新 panel 的编辑语义时,只需要接入 route,不必复制整套菜单逻辑。
|
||||||
|
|
||||||
|
## 7. 当前已完成与明确暂缓的部分
|
||||||
|
|
||||||
|
已完成的重点:
|
||||||
|
|
||||||
|
- 统一 UI token、panel chrome、popup/property-grid/shared widgets。
|
||||||
|
- menu/shortcut/context menu/action 的大部分共享路由。
|
||||||
|
- scene/project/entity/component 的主要编辑命令收口。
|
||||||
|
- dock layout controller 与 editor workspace 装配。
|
||||||
|
- inspector 的 component editor registry 接入。
|
||||||
|
- editor 级回归测试基础框架与关键命令测试。
|
||||||
|
|
||||||
|
当前明确暂缓:
|
||||||
|
|
||||||
|
- `Scene` panel 内容。
|
||||||
|
- `Game` panel 内容。
|
||||||
|
- `Viewport` 与 RHI 重新接入。
|
||||||
|
|
||||||
|
原因很明确:
|
||||||
|
|
||||||
|
- 这三部分依赖渲染壳层与后续 renderer/RHI 重构,暂时不适合和 editor UI 架构收尾混在一起。
|
||||||
|
|
||||||
|
## 8. 后续新增功能时的落点原则
|
||||||
|
|
||||||
|
如果以后继续扩 editor,推荐按下面的判断落点:
|
||||||
|
|
||||||
|
- 新视觉样式或共享控件:放 `UI`
|
||||||
|
- 新菜单项/快捷键/右键菜单复用:放 `Actions`
|
||||||
|
- 新的 scene/project/component 编辑行为:放 `Commands`
|
||||||
|
- 新 inspector 组件面板:放 `ComponentEditors`
|
||||||
|
- 新 dock/window 布局控制:放 `Layout`
|
||||||
|
- 新 editor 全局状态:放 `Core/Managers`
|
||||||
|
- 只是把已有能力拼进某个窗口:放 `Panels`
|
||||||
|
|
||||||
|
如果一个功能不知道放哪,一般先问自己:
|
||||||
|
|
||||||
|
- 它是不是只关乎“怎么画”?
|
||||||
|
- 它是不是多个入口共享的交互?
|
||||||
|
- 它是不是会真正改数据并进入 undo?
|
||||||
|
|
||||||
|
这三个问题基本能把落点判断清楚。
|
||||||
|
|
||||||
|
## 9. 当前收尾阶段剩余事项
|
||||||
|
|
||||||
|
从 UI 架构角度看,当前已经不是“推倒重来”阶段,而是最后的封口阶段。剩余事项主要有:
|
||||||
|
|
||||||
|
- 继续压缩少量 panel 本地瞬时状态,能下沉的继续下沉。
|
||||||
|
- 继续补命令/路由回归测试,尤其是 inspector interactive undo 边界。
|
||||||
|
- 为将来的 viewport 回归预留稳定接入口,但暂不提前接入渲染逻辑。
|
||||||
|
|
||||||
|
结论:
|
||||||
|
|
||||||
|
editor 当前已经形成稳定分层,后面再做功能迭代时,应坚持“先看边界,再落代码”,不要回到 panel 内部堆逻辑的旧路线。
|
||||||
@@ -1,346 +1,165 @@
|
|||||||
# Editor重构 3.26
|
# Editor 重构 3.26
|
||||||
|
|
||||||
## 当前判断
|
## 当前判断
|
||||||
|
|
||||||
截至 2026-03-26,Editor 的 UI 架构重构如果只计算“架构层整理”,不计算 Viewport/RHI 和 Scene/Game 真正内容开发,整体进度大约在 **92%**。
|
截至 2026-03-27,如果只看 editor 的 UI 架构与编辑器壳层整理,不把 `Viewport / RHI` 和 `Scene / Game` 的真实内容算进去,这一轮重构大约已经完成 **96%**。
|
||||||
|
|
||||||
如果把“编辑器整体可用度”也算进去,则大约在 **65%** 左右,因为 `Scene` / `Game` 面板目前仍然只是占位壳,Viewport 仍未回归,且部分编辑语义与回归验证还未补齐。
|
如果把“编辑器整体可用度”一起算进去,则仍然没有结束,因为:
|
||||||
|
|
||||||
## 已完成的架构层
|
- `Scene` panel 仍然是空壳。
|
||||||
|
- `Game` panel 仍然是空壳。
|
||||||
|
- `Viewport` 尚未跟随 RHI 重构完成回归。
|
||||||
|
|
||||||
### 1. UI 主题与样式 Token 层
|
所以现在的真实状态是:
|
||||||
|
|
||||||
已将大量直接散落在 panel 中的颜色、间距、控件尺寸、Dock 外观收口到统一 UI 基础层。
|
- **UI 架构已经基本收稳**
|
||||||
|
- **编辑器产品功能还没有做完**
|
||||||
|
|
||||||
当前主要入口:
|
## 已经完成的核心重构
|
||||||
|
|
||||||
- `editor/src/UI/BaseTheme.h`
|
### 1. 分层已经基本建立
|
||||||
- `editor/src/UI/StyleTokens.h`
|
|
||||||
- `editor/src/UI/DockHostStyle.h`
|
|
||||||
- `editor/src/UI/PanelChrome.h`
|
|
||||||
- `editor/src/UI/Core.h`
|
|
||||||
- `editor/src/UI/Widgets.h`
|
|
||||||
- `editor/src/UI/PopupState.h`
|
|
||||||
- `editor/src/UI/PropertyGrid.h`
|
|
||||||
- `editor/src/UI/UI.h`
|
|
||||||
|
|
||||||
这一层的意义是:
|
当前 editor 已经形成比较稳定的职责划分:
|
||||||
|
|
||||||
- 主题值不再零散写在各个 panel 里
|
- `UI` 负责主题、token、共享控件、popup、property-grid。
|
||||||
- panel chrome、toolbar、popup、property row、asset tile 等基础外观开始统一
|
- `Actions` 负责菜单、快捷键、右键菜单、按钮动作的共享路由。
|
||||||
- 后续继续调 UI 观感时,优先改 shared layer,而不是到处修 panel
|
- `Commands` 负责 scene/project/entity/component 的编辑行为。
|
||||||
|
- `Layout` 负责 dock host、默认布局、布局重置与持久化。
|
||||||
|
- `Panels` 逐步退化为窗口壳层。
|
||||||
|
- `Core / Managers` 负责 editor context、selection、undo、scene、project 等共享状态。
|
||||||
|
- `ComponentEditors` 负责 inspector 中各组件的编辑器与注册体系。
|
||||||
|
|
||||||
### 2. 面板共享控件层
|
这说明 editor 已经从“每个 panel 自己堆逻辑”的结构,切换到“共享层驱动”的结构。
|
||||||
|
|
||||||
已抽出并统一了:
|
### 2. 共享 UI 基础层已经落稳
|
||||||
|
|
||||||
- toolbar 搜索框
|
已经把大量原先散落在 panel 内部的视觉和控件逻辑收口到 shared UI 层,包括:
|
||||||
- toolbar 按钮 / 切换按钮
|
|
||||||
- 空状态绘制
|
- 主题和样式 token
|
||||||
- 面包屑
|
- panel chrome
|
||||||
- hierarchy tree node
|
- toolbar/search/button
|
||||||
|
- popup / modal state
|
||||||
|
- empty state
|
||||||
- asset tile
|
- asset tile
|
||||||
- deferred popup / modal state
|
- hierarchy tree node
|
||||||
- inline text edit state
|
- component section
|
||||||
- inspector component section
|
- property grid / scalar / vector 编辑控件
|
||||||
- dialog action row
|
|
||||||
- property grid / scalar / vector 控件
|
|
||||||
|
|
||||||
当前结果是 `Hierarchy / Project / Inspector / Console / MenuBar` 的基础表现逻辑已经大面积转向 shared widget,而不是每个 panel 手搓一遍。
|
这部分的意义是:后面再调 editor 外观,不应该回到 panel 内部逐个修补。
|
||||||
|
|
||||||
### 3. Command 层
|
### 3. Action 路由已经成型
|
||||||
|
|
||||||
已经从 panel 中抽出了大部分直接业务操作,形成内部命令层:
|
当前已形成两条主路由:
|
||||||
|
|
||||||
- `editor/src/Commands/SceneCommands.h`
|
- `MainMenuActionRouter`
|
||||||
- `editor/src/Commands/EntityCommands.h`
|
- `EditActionRouter`
|
||||||
- `editor/src/Commands/ProjectCommands.h`
|
|
||||||
- `editor/src/Commands/ComponentCommands.h`
|
|
||||||
|
|
||||||
当前已进入 command 的行为包括:
|
并且 `Hierarchy / Project / Inspector / Console / MenuBar` 的主要菜单和快捷键语义已经接入共享 action/router。
|
||||||
|
|
||||||
|
当前已经做到:
|
||||||
|
|
||||||
|
- `Edit` 菜单不再只是一套写死逻辑,而是跟随 active action route 切换。
|
||||||
|
- menu / shortcut / context menu / toolbar 的动作开始共用同一套 action 定义。
|
||||||
|
- rename、popup、about、exit、reset layout 等交互已不再零散写在 panel 内部。
|
||||||
|
|
||||||
|
### 4. Commands 层已经承担主要编辑语义
|
||||||
|
|
||||||
|
当前主要编辑行为已进入 command 层:
|
||||||
|
|
||||||
- 新建 / 打开 / 保存场景
|
- 新建 / 打开 / 保存场景
|
||||||
- 启动场景加载与退出时脏场景保存
|
- 脏场景 fallback 保存
|
||||||
- 创建 / 复制 / 粘贴 / Duplicate / 删除 / 重命名 / 重挂接实体
|
- 创建 / 删除 / 复制 / 粘贴 / duplicate / rename / reparent entity
|
||||||
- 创建文件夹、删除资源、打开资源、移动资源
|
- 创建文件夹 / 删除资源 / 打开资源 / 移动资源
|
||||||
- 添加 / 移除组件
|
- 添加 / 删除组件
|
||||||
|
|
||||||
这一层已经把大量 `undo / dirty / selection reset / scene switch confirm` 从 panel 中剥离出来。
|
同时,undo / dirty / selection reset 等关键边界,已经尽量不再散落在 panel。
|
||||||
|
|
||||||
### 4. Action / Shortcut 层
|
### 5. Dock / Workspace / Application 壳层已收口
|
||||||
|
|
||||||
这一层是 3.26 新补上的,用于承接:
|
当前已经完成:
|
||||||
|
|
||||||
- 文案
|
- `EditorWorkspace` 统一 panel 装配与生命周期调度
|
||||||
- shortcut 文本
|
- `DockLayoutController` 统一 dockspace 和 layout reset
|
||||||
- enabled 状态
|
- ImGui layout 持久化到项目 `.xceditor/imgui_layout.ini`
|
||||||
- 文本输入时是否允许触发
|
- `Application.cpp` 中的窗口、renderer、ImGui session、layer attach/detach 已完成明显拆分
|
||||||
- shortcut 触发上下文
|
|
||||||
- menu 与 shortcut 的统一绑定
|
|
||||||
- button / toolbar / inspector action 的统一接线
|
|
||||||
|
|
||||||
当前文件:
|
这说明 editor 顶层壳层已经不再像之前那样把 UI、layout、backend、panel 生命周期混在一起。
|
||||||
|
|
||||||
- `editor/src/Actions/ActionBinding.h`
|
### 6. Inspector 的 ComponentEditor 注册体系已稳定
|
||||||
- `editor/src/Actions/EditorActions.h`
|
|
||||||
|
|
||||||
已覆盖的区域:
|
当前 inspector 不再直接硬编码全部组件逻辑,而是通过:
|
||||||
|
|
||||||
- `MenuBar`
|
- `IComponentEditor`
|
||||||
- `Hierarchy`
|
- `ComponentEditorRegistry`
|
||||||
- `Project`
|
- 各具体 `Transform / Camera / Light` component editor
|
||||||
- `Inspector`
|
|
||||||
- `Console`
|
|
||||||
|
|
||||||
这一层已经不再只是 `MenuBar + Hierarchy` 的试点,而是开始成为 editor 范围内的统一动作定义入口。
|
来统一:
|
||||||
|
|
||||||
当前新增结果:
|
- 组件显示
|
||||||
|
- Add Component 菜单构建
|
||||||
|
- 组件可添加性 / 可删除性判断
|
||||||
|
|
||||||
- 已补 shared shortcut context
|
这部分已经是后续扩展自定义组件 inspector 的正确落点。
|
||||||
- `Global` 与 `FocusedWindow` 两种快捷键路由开始统一
|
|
||||||
- `Hierarchy` 与 `Project` 已接入同一套 shortcut 分发
|
|
||||||
- 已补 active action route
|
|
||||||
- `MenuBar Edit` 已开始跟随 `Hierarchy / Project` 切换动作目标
|
|
||||||
- `Rename` 这类依赖 panel 内联状态的动作,开始通过 `EventBus` 请求而不是直接耦合 panel 实现
|
|
||||||
- `Edit` 动作解析与菜单绘制 / shortcut 分发已开始从 `MenuBar` 抽成共享 router
|
|
||||||
- `Hierarchy / Project` 的上下文菜单与创建弹窗也开始下沉到 shared action router
|
|
||||||
- `Project` 右键菜单目标已不再依赖 panel 内裸索引字段,而是改成 targeted popup state
|
|
||||||
- `Inspector / Console` 的局部 action 组装也开始继续下沉到 shared router
|
|
||||||
- `Inspector` 的 component section header 菜单已开始改成 callback/router 驱动,而不是在 widget 层硬编码动作
|
|
||||||
- `MenuBar` 的 File / View / Help / global shortcut 也开始继续下沉到 shared main-menu router
|
|
||||||
- `Hierarchy / Project` 的 drag-drop payload、拖拽接收与部分选择语义也开始继续下沉到 shared action router
|
|
||||||
- `Hierarchy / Project / Inspector / Console` 的空白点击、popup 请求、日志绘制与 interactive-change 边界也开始继续从 panel 下沉到 shared router/state
|
|
||||||
- `MainMenu / Edit` 的菜单绘制、shortcut 触发与动作执行路径也开始继续合并成统一 router 入口
|
|
||||||
|
|
||||||
### 5. Dock / Layout 层
|
### 7. 回归测试基础已补齐
|
||||||
|
|
||||||
`Dock / Layout` 已从 `EditorLayer::setupDockspace()` 中抽出,形成独立 layout controller:
|
当前已新增 `editor_tests`,并已覆盖关键 editor 行为,包括:
|
||||||
|
|
||||||
- `editor/src/Layout/DockLayoutController.h`
|
- hierarchy edit route 的 copy / paste / duplicate / delete / rename request
|
||||||
|
- project edit route 的 open / back / delete
|
||||||
|
- scene dirty save + load 后的 selection / undo reset
|
||||||
|
- reparent 时的 parent 切换、cycle 拦截与 world transform 保持
|
||||||
|
- main menu 的 exit / reset layout / about popup request
|
||||||
|
- hierarchy rename helper
|
||||||
|
- project create-folder / move-asset / open-folder helper
|
||||||
|
|
||||||
当前已完成:
|
本轮收尾继续补充后,测试会进一步覆盖:
|
||||||
|
|
||||||
- dock host 渲染与默认布局重建从 `EditorLayer` 中移出
|
- clean scene 下的新建场景重置行为
|
||||||
- `Reset Layout` 已接入真正行为
|
- clean scene 下 fallback save 的 no-op 路径
|
||||||
- `MenuBar -> EventBus -> DockLayoutController` 的事件链已经打通
|
- project move-asset 的非法输入保护
|
||||||
- ImGui layout 已持久化到 `<project>/.xceditor/imgui_layout.ini`
|
|
||||||
|
|
||||||
当前仍待完成:
|
## 当前仍然剩下什么
|
||||||
|
|
||||||
- 默认布局参数进一步数据化
|
### 架构收尾项
|
||||||
- 更细致的 layout 状态管理
|
|
||||||
|
|
||||||
### 6. Panel 生命周期层
|
1. 继续压缩少量 panel 本地瞬时状态
|
||||||
|
目标是把还能共享的 popup / router / state 再往 shared 层收一点。
|
||||||
|
|
||||||
已开始把 `EditorLayer` 内手写的 panel 创建、context 注入、attach、detach、update、event、render 流程,收口到统一 panel collection。
|
2. 继续补 editor 回归测试
|
||||||
|
重点补 command/router 边界,而不是 UI 像素测试。
|
||||||
|
|
||||||
当前意义是:
|
3. 为 viewport 回归保留 editor shell 接口
|
||||||
|
但暂时不把 RHI/renderer 接进来。
|
||||||
|
|
||||||
- `EditorLayer` 不再保存一串分散的 panel 生命周期样板代码
|
### 暂缓项
|
||||||
- panel 的 attach / detach / render 顺序有了统一入口
|
|
||||||
- 后续继续拆 panel 或补 panel 时,不需要再改一大片壳层代码
|
|
||||||
- startup scene / dock attach / panel tree 组装已继续从 `EditorLayer` 收口到 `EditorWorkspace`
|
|
||||||
- `Inspector` 的 selection 订阅已从 `Render()` 懒接线改回生命周期接线
|
|
||||||
- `Hierarchy` 的事件订阅也已从析构清理改回显式 `OnAttach / OnDetach` 生命周期清理
|
|
||||||
|
|
||||||
### 7. Application / ImGui Session 层
|
以下内容不计入当前这一轮 UI 架构收尾:
|
||||||
|
|
||||||
`Application` 中原先混在一起的 ImGui context 创建、字体加载、ini 路径配置、layout 持久化,已经开始抽成独立 session:
|
- `Scene` panel 真正内容
|
||||||
|
- `Game` panel 真正内容
|
||||||
|
- `Viewport` 渲染接入
|
||||||
|
|
||||||
- `editor/src/UI/ImGuiSession.h`
|
原因不是不做,而是这些工作和 renderer / RHI 重构直接绑定,应该放到后续阶段。
|
||||||
- `editor/src/UI/ImGuiBackendBridge.h`
|
|
||||||
|
|
||||||
这一层的意义是:
|
## 本轮新增文档
|
||||||
|
|
||||||
- `Application` 更接近窗口 / 设备 / layer host
|
本次已补正式架构说明:
|
||||||
- ImGui 生命周期不再继续堆在 `Application.cpp`
|
|
||||||
- Win32 / DX12 backend API 不再散落在 `Application.cpp` 与 `main.cpp`
|
|
||||||
- 后续继续清理 backend 初始化边界时,有稳定落点
|
|
||||||
- Win32 window/message pump 已抽成 `Platform/Win32EditorHost.h`
|
|
||||||
- DX12 swapchain / render target / present / resize 已抽成 `Platform/D3D12WindowRenderer.h`
|
|
||||||
- scene title 拼装已抽成 `Core/EditorWindowTitle.h`
|
|
||||||
- crash filter / stderr redirect / logging sink 初始化已继续从 `Application.cpp` 抽离
|
|
||||||
- editor context 初始化、ImGui backend/session 初始化、layer attach/detach 与 frame render 壳方法也已继续从 `Application::Initialize/Shutdown/Render` 主流程中拆出
|
|
||||||
|
|
||||||
## 主要面板状态
|
- `docs/plan/Editor架构说明.md`
|
||||||
|
|
||||||
### MenuBar
|
这个文档用于明确:
|
||||||
|
|
||||||
已完成:
|
- 各层职责边界
|
||||||
|
- 允许依赖方向
|
||||||
- 文件菜单场景动作走 command
|
- panel / action / command / manager / component editor 的落点规则
|
||||||
- undo / redo / copy / paste 动作开始统一
|
- event bus、undo、dirty、selection 的统一约定
|
||||||
- shortcut 接入 action 层
|
|
||||||
- scene 状态显示统一
|
|
||||||
- `Reset Layout` 已接入事件驱动行为
|
|
||||||
- `About` 已接入真实 modal popup
|
|
||||||
- `Exit` 已通过事件驱动关闭 editor
|
|
||||||
- `Edit` 菜单已开始跟随 active action route 在 `Hierarchy / Project` 之间切换
|
|
||||||
- `Edit` 菜单与上下文快捷键开始共享同一套 edit action router
|
|
||||||
- `File / View / Help / global shortcut` 已开始继续从 panel 下沉到 shared main-menu router
|
|
||||||
- scene status 与 about dialog 已开始继续从 panel 下沉到共享 UI widget/dialog
|
|
||||||
- `MenuBar` 已进一步退化为 router 驱动的薄壳,File/View/Help/Edit 的拼装继续从 panel 移出
|
|
||||||
|
|
||||||
仍待完成:
|
|
||||||
|
|
||||||
- help / app 级动作继续向更统一的 application shell 收口
|
|
||||||
|
|
||||||
### Hierarchy
|
|
||||||
|
|
||||||
已完成:
|
|
||||||
|
|
||||||
- 搜索栏、节点样式、上下文菜单、创建菜单已共享化
|
|
||||||
- 创建 / 删除 / 重命名 / 复制 / 粘贴 / Duplicate / 重挂接走 command
|
|
||||||
- 快捷键已接 action 层
|
|
||||||
- 重命名状态已收成 `Begin / Commit / Cancel`
|
|
||||||
- 重命名交互已从 panel 局部字段收口到 shared inline edit state
|
|
||||||
- `Rename` 请求已能从 `MenuBar -> EventBus -> Hierarchy inline edit` 触发
|
|
||||||
- entity drag payload / 目标接收 / root drop / selection click 语义已开始继续从 panel 下沉到 shared hierarchy router
|
|
||||||
- hierarchy 空白点击清空选择与排序 popup 请求/菜单也已开始继续从 panel 下沉
|
|
||||||
- hierarchy 背景 / entity context popup 与 root drop target 入口也已开始继续从 panel 下沉
|
|
||||||
- rename commit 也已开始继续从 panel 直连 command 改成 shared hierarchy router 入口
|
|
||||||
|
|
||||||
仍待完成:
|
|
||||||
|
|
||||||
- 拖拽 / 空白区域目标等细节继续统一
|
|
||||||
|
|
||||||
### Project
|
|
||||||
|
|
||||||
已完成:
|
|
||||||
|
|
||||||
- toolbar、breadcrumb、search、asset grid、empty state 已共享化
|
|
||||||
- 打开资源、删除资源、创建文件夹、拖拽移动走 command
|
|
||||||
- 上下文菜单、返回按钮、创建文件夹对话框已接 action 层
|
|
||||||
- 资源图标绘制与图标配色已下沉到 shared UI token / widget
|
|
||||||
- 创建文件夹弹窗已改成 shared popup state 驱动
|
|
||||||
- `Back / Open / Delete` 已接 panel-focused keyboard action
|
|
||||||
- asset drag payload / folder drop / 拖拽预览高亮已开始继续从 panel 下沉到 shared project router
|
|
||||||
- item 选择 / item context 请求 / 空白区 context popup 请求也已开始继续从 panel 下沉
|
|
||||||
- asset open 请求也已开始继续从 panel 直连 command 改成 shared project router 入口
|
|
||||||
|
|
||||||
仍待完成:
|
|
||||||
|
|
||||||
- 更多资源对话框 / 多选语义继续统一
|
|
||||||
|
|
||||||
### Inspector
|
|
||||||
|
|
||||||
已完成:
|
|
||||||
|
|
||||||
- component section 已共享化
|
|
||||||
- Add Component 已放到底部
|
|
||||||
- 组件增删已走 command
|
|
||||||
- 组件内容编辑大部分已走 property grid
|
|
||||||
- Add Component 按钮与 popup 项已接 action 层
|
|
||||||
- Add Component popup 已接 shared popup state
|
|
||||||
- Add Component popup 菜单项组装已开始从 panel 下沉到 shared inspector action router
|
|
||||||
- 组件 section header 的移除动作已开始从 widget 层硬编码迁回 inspector action router
|
|
||||||
- selection 切换时的 interactive undo finalize、Add Component 按钮与 popup 绘制也已开始继续从 panel 下沉
|
|
||||||
|
|
||||||
仍待完成:
|
|
||||||
|
|
||||||
- section header 的动作继续统一
|
|
||||||
- 组件编辑交互与 undo 提交边界继续收口
|
|
||||||
|
|
||||||
### Console
|
|
||||||
|
|
||||||
已完成:
|
|
||||||
|
|
||||||
- 工具栏与内容区布局已共享化
|
|
||||||
- 日志行 hover 表现已统一
|
|
||||||
- `Clear / Filter` 已接 action 层
|
|
||||||
- console filter 状态已从 panel 裸布尔字段收成独立 state object
|
|
||||||
- console toolbar action 与日志文本格式化已继续从 panel 下沉到共享层
|
|
||||||
- log row 列表绘制与复制语义也已开始继续从 panel 下沉到 console router
|
|
||||||
|
|
||||||
仍待完成:
|
|
||||||
|
|
||||||
- 控制台状态与动作定义继续统一
|
|
||||||
|
|
||||||
### Scene / Game
|
|
||||||
|
|
||||||
当前状态:
|
|
||||||
|
|
||||||
- 只保留空壳 panel
|
|
||||||
- 暂时不做内容
|
|
||||||
|
|
||||||
这符合当前阶段决策,因为 Viewport 依赖的 RHI 模块仍在重构中。
|
|
||||||
|
|
||||||
## 还未完成的架构事项
|
|
||||||
|
|
||||||
### 高优先级
|
|
||||||
|
|
||||||
1. 整理 `Application.cpp` 和 `EditorLayer.cpp` 中剩余的 UI 壳逻辑
|
|
||||||
包括 backend 初始化边界、主循环中剩余 ImGui / render 壳逻辑、部分样式 push / pop 的收口。
|
|
||||||
|
|
||||||
### 中优先级
|
|
||||||
|
|
||||||
2. 继续将 panel 的本地状态机抽离
|
|
||||||
包括:
|
|
||||||
- console filter state
|
|
||||||
- 其他仍散落在 panel 内的临时交互状态
|
|
||||||
|
|
||||||
3. 将剩余少量视觉硬编码继续下沉到 token / widget 层
|
|
||||||
尤其是 `ProjectPanel` 中的 icon color 和一部分 panel 局部尺寸。
|
|
||||||
|
|
||||||
4. 统一 editor 范围内的编辑语义
|
|
||||||
例如:
|
|
||||||
- 哪些 panel 消费 Copy / Paste / Delete / Open / Back
|
|
||||||
- 哪些动作受 selection 驱动
|
|
||||||
- 哪些动作在文本输入时必须屏蔽
|
|
||||||
- 哪些动作通过 command,哪些动作通过 event 请求 panel 内联状态
|
|
||||||
|
|
||||||
### 低优先级
|
|
||||||
|
|
||||||
5. 再整理一次目录边界
|
|
||||||
当前 `UI / Commands / Actions / Layout` 方向是对的,但后续还可以再决定哪些继续 header-only,哪些拆 `.cpp` 更合适。
|
|
||||||
|
|
||||||
6. 补一份更正式的 editor 架构说明
|
|
||||||
说明 panel、actions、commands、layout、managers 的职责边界,便于后续继续迭代。
|
|
||||||
|
|
||||||
7. 根据条件补最小回归测试或最小验证脚本
|
|
||||||
重点验证 shortcut、undo、scene dirty/save switch、drag-drop/reparent。
|
|
||||||
|
|
||||||
当前新增验证:
|
|
||||||
|
|
||||||
- 已新增 `tests/editor/test_action_routing.cpp`
|
|
||||||
- 已新增 `tests/editor/CMakeLists.txt` 与 `editor_tests` target
|
|
||||||
- 当前 `editor_tests` 已覆盖 7 条 editor 回归用例
|
|
||||||
- 已覆盖 `Hierarchy Edit route` 的 copy / paste / duplicate / delete / rename request
|
|
||||||
- 已覆盖 `Project Edit route` 的 open / back / delete
|
|
||||||
- 已覆盖 `scene dirty save + load` 后的 selection / undo reset
|
|
||||||
- 已覆盖 `reparent` 的 parent 切换、cycle 拦截与 world position / scale 保持
|
|
||||||
- 已覆盖 `MainMenu` 的 exit / reset-layout 事件请求与 about popup 请求
|
|
||||||
- 已覆盖 `Hierarchy` rename helper 的 request / commit 语义
|
|
||||||
- 已覆盖 `Project` 的 create-folder / move-asset / open-folder helper
|
|
||||||
|
|
||||||
## 下一阶段建议执行顺序
|
|
||||||
|
|
||||||
### 第一阶段
|
|
||||||
|
|
||||||
1. 清理 `Application + EditorLayer` UI 壳层
|
|
||||||
2. 清理 panel 内剩余局部状态逻辑
|
|
||||||
|
|
||||||
### 第二阶段
|
|
||||||
|
|
||||||
3. 收口剩余视觉硬编码
|
|
||||||
4. 统一 editor 范围内的编辑语义
|
|
||||||
|
|
||||||
### 第三阶段
|
|
||||||
|
|
||||||
5. 根据需要继续细分 `Actions / Commands / Layout` 的目录边界
|
|
||||||
6. 补最小回归验证
|
|
||||||
7. 为 Viewport 回归预留 editor shell 接口
|
|
||||||
|
|
||||||
## 阶段性结论
|
## 阶段性结论
|
||||||
|
|
||||||
当前 editor 重构已经从“到处修视觉 bug”切换到了“逐层收口架构”的状态,方向是正确的。
|
当前 editor 可以明确地说:
|
||||||
|
|
||||||
最关键的变化不是某个 panel 看起来更像 Unity,而是已经开始形成稳定分层:
|
- **UI 架构层面已经基本重构完成**
|
||||||
|
- **剩余主要是封口、验证和后续 viewport 接入准备**
|
||||||
|
|
||||||
- `UI` 负责外观与共享控件
|
也就是说,后面不应该再回到“看到一个 panel 问题就地补一段特殊逻辑”的方式,而应该继续沿着现有分层做增量完善。
|
||||||
- `Actions` 负责动作定义与 shortcut / button 绑定
|
|
||||||
- `Commands` 负责编辑器业务行为
|
|
||||||
- `Layout` 负责 dock host 与默认布局
|
|
||||||
- `Panels` 逐步退化为纯 UI 壳
|
|
||||||
|
|
||||||
只要继续沿这个方向收口,后面再做 Scene/Game 接入、Viewport 回归时,成本会明显低于继续在 panel 内部堆逻辑。
|
|
||||||
|
|||||||
@@ -196,6 +196,38 @@ TEST_F(EditorActionRoutingTest, LoadSceneResetsSelectionAndUndoAfterFallbackSave
|
|||||||
EXPECT_EQ(m_context.GetSceneManager().GetRootEntities()[0]->GetName(), "SavedEntity");
|
EXPECT_EQ(m_context.GetSceneManager().GetRootEntities()[0]->GetName(), "SavedEntity");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(EditorActionRoutingTest, NewSceneResetsSelectionAndUndoForCleanScene) {
|
||||||
|
auto* entity = Commands::CreateEmptyEntity(m_context, nullptr, "Create Entity", "BeforeNewScene");
|
||||||
|
ASSERT_NE(entity, nullptr);
|
||||||
|
ASSERT_TRUE(m_context.GetSelectionManager().HasSelection());
|
||||||
|
ASSERT_TRUE(m_context.GetUndoManager().CanUndo());
|
||||||
|
|
||||||
|
const fs::path savedScenePath = m_projectRoot / "Assets" / "Scenes" / "BeforeNewScene.xc";
|
||||||
|
ASSERT_TRUE(m_context.GetSceneManager().SaveSceneAs(savedScenePath.string()));
|
||||||
|
ASSERT_FALSE(m_context.GetSceneManager().IsSceneDirty());
|
||||||
|
|
||||||
|
EXPECT_TRUE(Commands::NewScene(m_context, "Fresh Scene"));
|
||||||
|
EXPECT_TRUE(m_context.GetSceneManager().HasActiveScene());
|
||||||
|
EXPECT_TRUE(m_context.GetSceneManager().IsSceneDirty());
|
||||||
|
EXPECT_EQ(m_context.GetSceneManager().GetCurrentSceneName(), "Fresh Scene");
|
||||||
|
EXPECT_TRUE(m_context.GetSceneManager().GetCurrentScenePath().empty());
|
||||||
|
EXPECT_TRUE(m_context.GetSceneManager().GetRootEntities().empty());
|
||||||
|
EXPECT_FALSE(m_context.GetSelectionManager().HasSelection());
|
||||||
|
EXPECT_FALSE(m_context.GetUndoManager().CanUndo());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(EditorActionRoutingTest, SaveDirtySceneWithFallbackDoesNothingWhenSceneIsAlreadyClean) {
|
||||||
|
const fs::path savedScenePath = m_projectRoot / "Assets" / "Scenes" / "CleanScene.xc";
|
||||||
|
ASSERT_TRUE(m_context.GetSceneManager().SaveSceneAs(savedScenePath.string()));
|
||||||
|
ASSERT_FALSE(m_context.GetSceneManager().IsSceneDirty());
|
||||||
|
|
||||||
|
const fs::path unusedFallbackPath = m_projectRoot / "Assets" / "Scenes" / "UnusedFallback.xc";
|
||||||
|
EXPECT_TRUE(Commands::SaveDirtySceneWithFallback(m_context, unusedFallbackPath.string()));
|
||||||
|
EXPECT_TRUE(fs::exists(savedScenePath));
|
||||||
|
EXPECT_FALSE(fs::exists(unusedFallbackPath));
|
||||||
|
EXPECT_EQ(fs::path(m_context.GetSceneManager().GetCurrentScenePath()), savedScenePath);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(EditorActionRoutingTest, ReparentPreserveWorldTransformKeepsWorldPose) {
|
TEST_F(EditorActionRoutingTest, ReparentPreserveWorldTransformKeepsWorldPose) {
|
||||||
auto* parentA = Commands::CreateEmptyEntity(m_context, nullptr, "Create Parent A", "ParentA");
|
auto* parentA = Commands::CreateEmptyEntity(m_context, nullptr, "Create Parent A", "ParentA");
|
||||||
auto* child = Commands::CreateEmptyEntity(m_context, parentA, "Create Child", "Child");
|
auto* child = Commands::CreateEmptyEntity(m_context, parentA, "Create Child", "Child");
|
||||||
@@ -296,5 +328,30 @@ TEST_F(EditorActionRoutingTest, ProjectCommandsCreateFolderMoveAssetAndOpenFolde
|
|||||||
EXPECT_EQ(m_context.GetProjectManager().GetCurrentPath(), "Assets/MovedFolder");
|
EXPECT_EQ(m_context.GetProjectManager().GetCurrentPath(), "Assets/MovedFolder");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(EditorActionRoutingTest, ProjectCommandsRejectInvalidMoveTargets) {
|
||||||
|
const fs::path assetsDir = m_projectRoot / "Assets";
|
||||||
|
const fs::path sourceFilePath = assetsDir / "MoveSource.txt";
|
||||||
|
const fs::path targetFolderPath = assetsDir / "TargetFolder";
|
||||||
|
const fs::path plainFilePath = assetsDir / "PlainFile.txt";
|
||||||
|
|
||||||
|
std::ofstream(sourceFilePath.string()) << "move source";
|
||||||
|
std::ofstream(plainFilePath.string()) << "not a folder";
|
||||||
|
fs::create_directories(targetFolderPath);
|
||||||
|
m_context.GetProjectManager().RefreshCurrentFolder();
|
||||||
|
|
||||||
|
const AssetItemPtr targetFolder = FindCurrentItemByName("TargetFolder");
|
||||||
|
const AssetItemPtr plainFile = FindCurrentItemByName("PlainFile.txt");
|
||||||
|
ASSERT_NE(targetFolder, nullptr);
|
||||||
|
ASSERT_TRUE(targetFolder->isFolder);
|
||||||
|
ASSERT_NE(plainFile, nullptr);
|
||||||
|
ASSERT_FALSE(plainFile->isFolder);
|
||||||
|
|
||||||
|
EXPECT_FALSE(Commands::MoveAssetToFolder(m_context.GetProjectManager(), "", targetFolder));
|
||||||
|
EXPECT_FALSE(Commands::MoveAssetToFolder(m_context.GetProjectManager(), sourceFilePath.string(), nullptr));
|
||||||
|
EXPECT_FALSE(Commands::MoveAssetToFolder(m_context.GetProjectManager(), sourceFilePath.string(), plainFile));
|
||||||
|
EXPECT_FALSE(Commands::MoveAssetToFolder(m_context.GetProjectManager(), targetFolder->fullPath, targetFolder));
|
||||||
|
EXPECT_TRUE(fs::exists(sourceFilePath));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace XCEngine::Editor
|
} // namespace XCEngine::Editor
|
||||||
|
|||||||
Reference in New Issue
Block a user