Files
XCEngine/docs/plan/Editor架构说明.md

14 KiB
Raw Blame History

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 routeEdit 类动作路由到正确 panel。
  • 处理 popup 请求、rename 请求、局部状态切换等“轻量交互编排”。

边界:

  • Actions 可以依赖 CommandsEventBusUI,但不负责底层数据结构修改细节。
  • 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 内部堆逻辑的旧路线。