318 lines
13 KiB
Markdown
318 lines
13 KiB
Markdown
# XCUI Editor Agent Guide
|
||
## Build Shape
|
||
|
||
The editor is a two-layer system.
|
||
|
||
- `XCUIEditorLib` is the reusable framework library.
|
||
- `XCUIEditorApp` is the concrete editor application executable.
|
||
- The build shape of `editor/` is `XCUIEditorLib <- XCUIEditorApp`.
|
||
- `editor/src/**` and `editor/include/XCEditor/**` belong to the framework layer.
|
||
- `editor/app/**` belongs to the app layer and is compiled directly into `XCUIEditorApp`.
|
||
- `app/Windowing`, `app/Platform/Win32`, `app/Rendering`, `app/Composition`, and similar directories are internal partitions inside the app layer.
|
||
- Types for multi-window editor authority, Win32 hosting, utility windows, and editor-only rendering stay in `editor/app/**`.
|
||
- `editor/include/XCEditor/**` exposes framework-level APIs only.
|
||
|
||
Architecture shorthand for this repo:
|
||
|
||
```text
|
||
XCUIEditorLib
|
||
<- XCUIEditorApp
|
||
```
|
||
|
||
When describing the current architecture, refer to `windowing`, `platform`, `rendering`, and `host` as app-internal partitions unless the build graph itself changes.
|
||
|
||
这份文档面向在 `editor/` 下工作的 coding agent / 开发者。它描述当前 editor 的真实工程边界、窗口架构和修改约束,不是产品说明。
|
||
|
||
如果本文和当前代码、`editor/CMakeLists.txt`、实际目录结构冲突,以当前 checkout 为准,并在本次工作里同步修正文档。
|
||
|
||
## 1. 当前定位
|
||
|
||
`editor/` 是当前 editor 应用主线。
|
||
|
||
当前工程事实:
|
||
|
||
- 可复用 UI framework 的真实构建目标是 `XCUIEditorLib`。
|
||
- editor 应用的真实可执行目标是 `XCUIEditorApp`。
|
||
- `XCUIEditorApp` 的输出名是 `XCEngine`,产物为 `XCEngine.exe`。
|
||
- `editor/src` + `editor/include/XCEditor` 是 XCEditor framework。
|
||
- `editor/app` 是具体 XCEngine editor 应用层。
|
||
- `editor/resources` 存放 editor 自有图标和 scene viewport shader。
|
||
|
||
当前 `editor/CMakeLists.txt` 并没有把 app core、windowing seam、Win32 host、rendering host 拆成独立生产 target。除了 `XCUIEditorLib` 之外,其余 app / windowing / platform / rendering 代码仍直接编进 `XCUIEditorApp`。
|
||
|
||
`XCUIEditorAppLib`、`XCUIEditorAppCore`、`XCUIEditorHost` 不是当前 `editor/CMakeLists.txt` 里真实定义的生产 target;它们只在部分测试脚本或清理逻辑里作为条件名称出现。不要把这些名字当成当前已经存在的模块边界。
|
||
|
||
## 2. 当前分层
|
||
|
||
推荐按下面五层理解和修改,但要记住这主要是目录和语义边界,不是已经被 CMake 强制落实的编译边界:
|
||
|
||
1. `editor/src` / `editor/include/XCEditor`
|
||
XCEditor framework。这里应保持通用,不依赖具体 editor app、Win32 宿主或 D3D12 实现。
|
||
|
||
2. `editor/app/Composition`、`Commands`、`Features`、`Project`、`Scene`、`State`、`Support`、`System`、`UtilityWindows`
|
||
editor 应用语义层。这里放 panel 组合、项目/场景运行时、选择状态、命令桥接、utility window 描述和 app 侧服务。
|
||
|
||
3. `editor/app/Windowing`
|
||
平台无关的窗口主线。这里放 content controller contract、frame transfer request、workspace synchronization planner / presentation policy / authoritative window set。
|
||
|
||
4. `editor/app/Platform/Win32`
|
||
Win32 host。这里负责 native window、消息分发、生命周期、输入翻译、窗口 chrome、host runtime 和 coordinator。
|
||
|
||
5. `editor/app/Rendering`
|
||
editor 渲染宿主。这里负责 D3D12 UI 渲染、窗口 render loop、swapchain、viewport 资源和 scene viewport pass。
|
||
|
||
依赖方向尽量保持为:
|
||
|
||
```text
|
||
XCEditor framework
|
||
<- app semantics
|
||
<- app/windowing
|
||
<- Platform/Win32
|
||
<- app/Rendering
|
||
```
|
||
|
||
现实约束:
|
||
|
||
- 当前只有 `XCUIEditorLib` 是真实独立 target。
|
||
- 其余层级只是目录和 ownership 约定。
|
||
- 修改时仍要主动控制 include 方向,避免把“语义上应该分层”写回成硬耦合。
|
||
|
||
## 3. 顶层启动流程
|
||
|
||
入口:
|
||
|
||
```text
|
||
app/main.cpp
|
||
-> RunXCUIEditorApp
|
||
-> Application::Run
|
||
```
|
||
|
||
当前启动主线:
|
||
|
||
```text
|
||
Application
|
||
-> EditorContext
|
||
-> Win32SystemInteractionHost
|
||
-> EditorWindowSystem
|
||
-> EditorWindowManager
|
||
-> EditorWindowHostRuntime
|
||
-> EditorWindowWorkspaceCoordinator
|
||
-> EditorUtilityWindowCoordinator
|
||
-> EditorWindowLifecycleCoordinator
|
||
```
|
||
|
||
primary workspace window 的 bootstrap 顺序是:
|
||
|
||
```text
|
||
EditorContext::BuildWorkspaceController()
|
||
-> EditorWindowSystem::BootstrapPrimaryWindow(...)
|
||
-> EditorWindowManager::CreateWorkspaceWindow(...)
|
||
```
|
||
|
||
不要把 primary window authority 初始化放回 Win32 host。当前 authoritative bootstrap 已经在 `EditorWindowSystem`。
|
||
|
||
## 4. 当前窗口系统事实
|
||
|
||
### 4.1 Authority / Planner / Host Execution
|
||
|
||
当前多窗口 workspace 主线里,几个核心对象的职责是:
|
||
|
||
- `EditorWindowSystem`
|
||
- 持有 authoritative `UIEditorWindowWorkspaceSet`
|
||
- 通过 `EditorWindowWorkspaceStore` 做 validation / commit
|
||
- 构建 synchronization plan
|
||
- 处理显式 `workspaceMutation` request
|
||
- 处理 destroyed window reconciliation
|
||
|
||
- `EditorWindowWorkspaceCoordinator`
|
||
- 采集 host snapshot
|
||
- 调用 `EditorWindowSystem` 生成 plan
|
||
- 执行 host-side create / update / close
|
||
- 处理 global tab drag、detach、cross-window dock/drop
|
||
- 处理 window presentation refresh
|
||
- 在 native destroy 时回到 `EditorWindowSystem` 做 reconcile
|
||
|
||
重要事实:
|
||
|
||
- `EditorWindowWorkspaceCoordinator` 已不再直接写 authority store。
|
||
- 但它仍然很厚,仍同时承担 host 执行、presentation refresh、drag/detach 流程和部分回滚时序。
|
||
- 当前不要把它描述成“纯粹的薄 host adapter”。
|
||
|
||
### 4.2 Live Workspace Projection 还没有完全去状态化
|
||
|
||
`EditorWorkspaceWindowContentController` 当前内部仍持有:
|
||
|
||
- `UIEditorWorkspaceController`
|
||
- `EditorShellRuntime`
|
||
- `EditorWindowFrameOrchestrator`
|
||
|
||
这意味着当前 workspace window 仍然有一个 live mutable controller。它已经不是 authority,但也还不是完全只读 projection。
|
||
|
||
当前真实行为是:
|
||
|
||
- `EditorWorkspaceWindowContentController::UpdateAndAppend(...)` 会在 layout snapshot 发生变化时发出显式 `workspaceMutation` request。
|
||
- `EditorWorkspaceWindowContentController` 现在持有显式 `EditorWorkspaceWindowProjection`,host 常规同步 / presentation 路径优先消费 projection refresh,而不是整体替换 live controller。
|
||
- `EditorWindow::TryGetWorkspaceController()` 仍保留给局部交互 / 过渡路径使用,但它不再是常规 host 同步主接口。
|
||
- 每个 workspace window 依然持有自己的一份 live `UIEditorWorkspaceController`。
|
||
|
||
因此:
|
||
|
||
- 不要把当前架构写成“projection-only content controller 已完成”。
|
||
|
||
## 5. 窗口类别与内容控制器
|
||
|
||
当前窗口系统显式区分两类窗口:
|
||
|
||
- `EditorWindowCategory::Workspace`
|
||
- `EditorWindowCategory::Utility`
|
||
|
||
两类窗口共享同一个 native host:
|
||
|
||
```text
|
||
EditorWindow
|
||
-> EditorWindowRuntimeController
|
||
-> D3D12WindowRenderLoop
|
||
-> EditorWindowContentController
|
||
```
|
||
|
||
### 5.1 Workspace Window
|
||
|
||
workspace window 的内容控制器是 `EditorWorkspaceWindowContentController`。
|
||
|
||
它是完整 shell/workspace 容器,用于:
|
||
|
||
- 主窗口
|
||
- detached workspace window
|
||
|
||
### 5.2 Utility Window
|
||
|
||
utility window 的内容控制器是 `EditorUtilityWindowContentController`,内部承载 `EditorUtilityWindowPanel`。
|
||
|
||
当前 registry / factory 主线是显式 descriptor 驱动:
|
||
|
||
- `EditorUtilityWindowDescriptor`
|
||
- `EditorUtilityWindowReusePolicy`
|
||
- `EditorWindowChromePolicy`
|
||
- `EditorWindowNativeStylePolicy`
|
||
|
||
当前已注册的 utility window:
|
||
|
||
- `ColorPicker`
|
||
- `AddComponent`
|
||
|
||
两者当前都使用 `SingleInstance` 复用策略。
|
||
|
||
### 5.3 Capability 校验
|
||
|
||
窗口内容能力由 `EditorWindowContentCapabilities` 描述,`EditorWindowHostRuntime::CreateEditorWindow(...)` 在创建时做硬校验:
|
||
|
||
- `Workspace` 分类要求 `capabilities.workspace == true`
|
||
- `Utility` 分类要求 `capabilities.utilityPanel == true`
|
||
|
||
不要依赖“后续 coordinator 看到空 binding 再兜底”这种旧思路。
|
||
|
||
## 6. Frame Request 与每帧主线
|
||
|
||
`EditorWindowFrameTransferRequests` 当前按两个子域组织:
|
||
|
||
```text
|
||
workspace:
|
||
workspaceMutation
|
||
beginGlobalTabDrag
|
||
detachPanel
|
||
|
||
utility:
|
||
openUtilityWindow
|
||
```
|
||
|
||
当前事实:
|
||
|
||
- `workspaceMutation` 已经是 frame 内 workspace 变化的显式回传入口。
|
||
- `openUtilityWindow` 来自 `EditorContext::RequestOpenUtilityWindow(...)`,由 `EditorWindowFrameOrchestrator` 消费 request state 后写入 frame transfer request。
|
||
- queued immediate frame 也按这两个子域分别合并。
|
||
|
||
steady-state frame 主路径是:
|
||
|
||
```text
|
||
Application::Run
|
||
-> EditorWindowManager::RenderAllWindows
|
||
-> EditorWindowHostRuntime::RenderAllWindows
|
||
-> EditorWindowFrameDriver::DriveFrame
|
||
-> EditorWindowRuntimeController::UpdateAndAppend
|
||
-> EditorWindowContentController::UpdateAndAppend
|
||
-> EditorWindowWorkspaceCoordinator::RefreshWindowPresentation
|
||
-> EditorWindowWorkspaceCoordinator::HandleWindowFrameTransferRequests
|
||
-> EditorUtilityWindowCoordinator::HandleWindowFrameTransferRequests
|
||
```
|
||
|
||
另外,`WM_PAINT`、`WM_SIZE`、`WM_DPICHANGED`、`WM_EXITSIZEMOVE` 等消息也会触发 immediate frame,并在 message dispatcher 中 flush queued transfer requests。
|
||
|
||
## 7. 修改规范
|
||
|
||
- 先判断改动属于 framework、app semantics、windowing、Win32 host 还是 rendering host。
|
||
- 小改动优先贴近现有模式,不顺手做跨层重构。
|
||
- 新增通用 widget 放 `editor/src` / `editor/include/XCEditor`。
|
||
- 新增具体 editor 功能放 `editor/app/Features/<FeatureName>`,再由 composition 装配。
|
||
- 新增 utility window 先补 `EditorUtilityWindowKind`、`EditorUtilityWindowDescriptor`、`CreateEditorUtilityWindowPanel(...)`,不要直接在 Win32 coordinator 里硬编码窗口类型。
|
||
- 新增跨窗口语义时,优先增加显式 frame request 或 app/windowing plan builder,不要回退到 Win32 host 直接读写 authority。
|
||
- 修改窗口同步逻辑时,区分“当前代码事实”和“长期目标”。当前还存在 live mutable projection,不要按尚未落地的终态直接删接口。
|
||
- 不要让 `editor/include/XCEditor/**` 暴露 `App::*` 类型。
|
||
- draw append 只负责绘制,不顺手修改 scene / project / authority 状态。
|
||
|
||
## 8. 当前架构债务
|
||
|
||
当前仍然存在的主要债务:
|
||
|
||
- 除 `XCUIEditorLib` 外,app core、windowing、Win32 host、rendering host 还没有真实拆成独立 target。
|
||
- `EditorContext` 仍然很重,混合了 shell asset、项目/场景运行时、selection、command focus、utility window request 和状态输出。
|
||
- `EditorShellRuntime` 仍然很重,既负责 shell compose / interaction,也负责 hosted panel、viewport request 和 draw append。
|
||
- `EditorWorkspaceWindowContentController` 仍持有 live mutable `UIEditorWorkspaceController`,只是 host 常规同步 / presentation 已经切到 projection refresh;projection 和 authority 的边界仍未完全硬化。
|
||
- `EditorWindowWorkspaceCoordinator` 仍然过厚,还没有收缩成纯粹的 host executor + native event bridge。
|
||
- `EditorWindow::TryGetWorkspaceController()` 仍存在于交互 / 过渡路径,说明 live projection 去状态化还没完成;但常规同步路径已经不再依赖 `ReplaceWorkspaceController()`。
|
||
|
||
这些都是当前事实,不要在新的规范文档或代码注释里把它们写成“已经解决”。
|
||
|
||
## 9. 验证入口
|
||
|
||
当前 editor 相关验证入口统一在 `tests/UI/Editor`:
|
||
|
||
- `editor_ui_tests`
|
||
framework 和基础 UI 单元测试。
|
||
- `editor_windowing_phase1_tests`
|
||
当前窗口 authority / planner / reconcile 规则的 focused unit tests。
|
||
- `xcui_editor_app_smoke`
|
||
editor 启动烟测。
|
||
- `tests/UI/Editor/manual_validation`
|
||
复杂交互人工验证场景。
|
||
|
||
条件事实:
|
||
|
||
- `editor_app_feature_tests` 只会在 `XCUIEditorAppLib` 等可选 target 真实存在时创建。
|
||
- 不要把它当成当前 checkout 一定存在的测试目标。
|
||
|
||
涉及窗口系统的改动,优先保持:
|
||
|
||
- `XCUIEditorApp` 可构建
|
||
- `editor_windowing_phase1_tests` 不回退
|
||
- `xcui_editor_app_smoke` 不回退
|
||
|
||
## 10. 推荐阅读入口
|
||
|
||
开始 editor 相关任务时,优先阅读:
|
||
|
||
- `editor/CMakeLists.txt`
|
||
- `editor/app/Bootstrap/Application.*`
|
||
- `editor/app/Composition/EditorContext.*`
|
||
- `editor/app/Composition/EditorShellRuntime.*`
|
||
- `editor/app/Windowing/Content/EditorWindowContentController.h`
|
||
- `editor/app/Windowing/Content/EditorWorkspaceWindowContentController.*`
|
||
- `editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.*`
|
||
- `editor/app/Windowing/System/EditorWindowSystem.*`
|
||
- `editor/app/Platform/Win32/Windowing/EditorWindow.*`
|
||
- `editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.*`
|
||
- `editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.*`
|
||
- `editor/app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.*`
|
||
- `editor/app/UtilityWindows/EditorUtilityWindowRegistry.*`
|
||
- `tests/UI/Editor/unit/CMakeLists.txt`
|
||
- `tests/UI/Editor/smoke/CMakeLists.txt`
|