11 KiB
XCUI Editor Agent Guide
这份文档面向在 editor/ 下工作的 coding agent / 开发者。它描述当前 editor 的真实工程边界、窗口架构和修改约束,不是产品说明。
如果本文和当前代码、editor/CMakeLists.txt、实际目录结构冲突,以当前 checkout 为准,并在本次工作里同步修正文档。
1. 当前定位
editor/ 是当前 editor 应用主线。
当前工程事实:
- 构建目标是
XCUIEditorApp。 - 输出名是
XCEngine,构建产物为XCEngine.exe。 editor/src是可复用的 XCEditor UI framework。editor/include/XCEditor是editor/src的公开接口。editor/app是具体 XCEngine editor 应用层。editor/resources存放 editor 自有图标和 scene viewport shader。
虽然 editor/CMakeLists.txt 仍把这些代码编进一个可执行目标,但语义上已经分成 framework、app core、Win32 host 和 D3D12 host 四层。
2. 分层边界
推荐按下面五层理解和修改:
-
editor/src/editor/include/XCEditorXCEditor UI framework。这里应保持通用,不依赖具体项目、场景、Win32 宿主或 D3D12 实现。 -
editor/app/Composition、Features、Project、Scene、State、CommandsEditor app core。这里装配 panel、项目运行时、场景运行时、选择状态、命令路由和 shell frame。 -
editor/app/WindowingEditor host seam。这里放平台无关的窗口内容 contract、workspace/utility content assembly、frame orchestration 和 host-facing transfer request。 -
editor/app/Platform/Win32Win32 host。这里负责原生窗口、消息分发、窗口生命周期、输入收集、title bar / chrome 和 surface/presenter。 -
editor/app/RenderingEditor rendering host。这里负责 D3D12 UI 渲染、窗口 swapchain、viewport 离屏资源、editor 图标纹理和 scene viewport pass。
依赖方向尽量保持:
app/platform + app/rendering -> app/windowing -> app/core -> XCEditor framework -> engine UI primitives
不要让 editor/src 反向依赖 editor/app。
2.1 长期目标:把语义分层落成真实模块边界
当前 editor/ 已经有语义上的四层划分,但还没有真正的编译/依赖边界。窗口相关代码最重要的长期目标,不是继续细拆 Win32 目录里的类,而是把下面这条关系变成代码和构建系统都能强制执行的事实:
XCEditor framework
<- editor app core
<- editor host seam / app/Windowing
<- Win32 host + D3D12 host
收敛要求:
editor/app/Platform/Win32只负责 native window、message pump、input translation、chrome、lifecycle 和 surface/presenter 等宿主职责。- workspace mutation、utility window routing、shell composition、window content assembly 这类产品语义应逐步收敛到平台无关层,而不是继续堆进 Win32 host。
editor/src/editor/include/XCEditor不允许反向依赖editor/app;公开头文件不应暴露App::*类型。- 最终要通过独立 CMake target 和受限 include dirs 落实,而不是只靠目录约定。
后续凡是修改窗口系统,优先朝“缩小 Platform/Win32 对 Composition、Features、UtilityWindows、Rendering 的直接依赖面”这个方向收敛。如果一个新需求只能通过继续把业务语义塞进 Win32 host 才能完成,应先停下来评估是否缺少平台无关的 host seam。
3. 顶层启动流程
入口:
app/main.cpp
-> RunXCUIEditorApp
-> Application::Run
核心对象关系:
Application
-> EditorContext
-> EditorWindowManager
-> EditorWindow
-> EditorWindowRuntimeController
-> D3D12WindowRenderer
-> D3D12UiRenderer
-> D3D12WindowRenderLoop
-> EditorWindowContentController
-> EditorWorkspaceWindowContentController
-> EditorUtilityWindowContentController
EditorContext 是全局共享应用状态。每个 workspace window 持有自己的 UIEditorWorkspaceController 和 EditorShellRuntime。utility window 不持有 workspace controller。
4. 显式窗口架构
当前窗口系统已经显式区分两类窗口:
EditorWindowCategory::Workspace主窗口和由 dock tab / panel 拖出的独立窗口。EditorWindowCategory::Utility颜色选择器、Add Component 这类天然工具窗。
两类窗口共享同一个 native host:
EditorWindow
-> EditorWindowRuntimeController
-> D3D12WindowRenderLoop
-> EditorWindowContentController
差异放在 content controller 和 policy 上,而不是靠“有没有 workspace binding”隐式推断。
4.1 Workspace Window
workspace window 的内容控制器是 EditorWorkspaceWindowContentController,内部持有:
UIEditorWorkspaceControllerEditorShellRuntimeEditorWindowFrameOrchestrator
这类窗口是完整 shell/workspace 容器。主窗口和 detached workspace window 都属于这一类。
4.2 Utility Window
utility window 的内容控制器是 EditorUtilityWindowContentController,内部持有:
EditorUtilityWindowPanel
当前 utility 窗口通过 registry/factory 创建,已实现显式 descriptor:
EditorUtilityWindowDescriptorEditorUtilityWindowReusePolicyEditorWindowChromePolicyEditorWindowNativeStylePolicy
当前 ColorPicker 和 AddComponent 都是单例复用窗口。
5. 窗口能力与策略
窗口内容能力由 EditorWindowContentCapabilities 描述。创建窗口时,host 会校验:
Workspace分类必须对应具备 workspace capability 的 content controller。Utility分类必须对应具备 utility capability 的 content controller。
这一步是硬校验,不再依赖后续 coordinator 通过空 binding 兜底。
窗口外观/宿主策略当前最少包括:
EditorWindowChromePolicy当前用于控制 detached title bar tab strip、frame stats,以及 utility window 的 topmost pin 按钮/默认置顶策略。EditorWindowNativeStylePolicy当前用于控制扩展窗口样式和是否复用 host 默认 style。
当前默认规则:
- workspace window 允许 detached title bar tab strip。
- utility window 不允许 detached title bar tab strip。
- utility window 不显示 frame stats。
- utility window 顶栏显示 topmost pin 按钮,且默认置顶。
- utility window 使用
WS_EX_TOOLWINDOW。
6. Workspace / Utility 协调器边界
6.1 Workspace Coordinator
EditorWindowWorkspaceCoordinator 只处理 Workspace 窗口:
- 维护 window workspace set
- 处理 detach panel
- 处理 global tab drag
- 处理 cross-window dock/drop
- 更新 detached workspace title
utility window 不参与:
- workspace set
- dock target hit test
- cross-window drop target
6.2 Utility Coordinator
EditorUtilityWindowCoordinator 只处理 utility window 请求:
- 根据
EditorUtilityWindowDescriptor创建工具窗 - 按 reuse policy 决定复用还是新建
- 对现有 utility window 做 focus / restore
不要把 utility window 塞进 workspace mutation 流程。
7. Frame Request 结构
EditorWindowFrameTransferRequests 已拆成两个子域:
workspace:
beginGlobalTabDrag
detachPanel
utility:
openUtilityWindow
规则:
EditorWindowWorkspaceCoordinator只消费transferRequests.workspaceEditorUtilityWindowCoordinator只消费transferRequests.utility- queued immediate frame 也按两个子域分别合并
不要再把 workspace 和 utility 的窗口请求混进同一个平铺字段集合。
8. 每帧流程
当前每帧主路径:
Application::Run
-> EditorWindowManager::RenderAllWindows
-> EditorWindowRuntimeController::BeginFrame
-> EditorWindowRuntimeController::UpdateAndAppend
-> EditorWindowFrameOrchestrator::UpdateAndAppend
-> EditorShellRuntime::Update
-> EditorShellSessionCoordinator::PrepareShellDefinition
-> EditorShellInteractionEngine::Update
-> UpdateUIEditorShellInteraction
-> ViewportHostService::RequestViewport
-> EditorShellSessionCoordinator::FinalizeFrame
-> EditorShellHostedPanelCoordinator::Update
-> EditorShellRuntime::Append
-> EditorWindowRuntimeController::RenderRequestedViewports
-> EditorWindowRuntimeController::Present
重要约束:
- viewport 先在 shell layout 阶段通过
RequestViewport()申明尺寸和纹理需求。 - 再在
RenderRequestedViewports()阶段真正渲染。 - 不要打乱这两个阶段的顺序。
9. 修改规范
- 先判断改动属于 framework、app core、windowing seam、Win32 host 还是 rendering host。
- 小改动优先贴近现有模式,不顺手做跨层重构。
- 新增通用 widget 放
editor/src/editor/include/XCEditor。 - 新增具体 editor 功能放
editor/app/Features/<FeatureName>,再由 composition 装配。 - 新增 host-facing window content、frame request、content assembly 放
editor/app/Windowing。 - 新增 Win32 宿主行为放
editor/app/Platform/Win32,通过 seam/controller/policy 与 app core 通信。 - 新增 viewport 渲染资源或 pass 放
editor/app/Rendering/Viewport或editor/resources/shaders/scene-viewport。 - draw append 只负责绘制,不修改 editor runtime 或 scene/project state。
- D3D12 host、Win32 host、panel 之间避免双向强耦合。
10. 当前架构债务
这轮重构完成后,仍有以下债务:
editor/CMakeLists.txt还没有把 framework、app core、windowing seam、Win32 host、D3D12 host 拆成独立 target。EditorContext仍然过重,混合了全局状态、service locator、command bridge、session sync 和状态输出。EditorShellRuntime仍然过重,既持有 panel,又持有 viewport host、shell frame、draw composer 和 interaction coordinator。- panel 仍缺少统一 feature 接口,
HierarchyPanel、ProjectPanel、InspectorPanel只是模式相似。 app/Windowing目前只是首刀 ownership 调整,workspace / utility window orchestration 仍主要停留在 Win32 coordinator。
这些债务后续要分步处理,不要一次性重写整个窗口系统。
11. 验证基线
当前这轮窗口架构重构的验证状态:
- 用户已完成 GUI 行为验证。
cmake --build build --config Debug --target XCUIEditorApp已通过。build/editor/Debug/XCEngine.exe的 12 秒启动冒烟测试已通过,进程可正常响应关闭请求。
后续涉及窗口架构的改动,至少应维持这条验证基线。
12. 推荐阅读入口
开始 editor 相关任务时,优先阅读:
editor/CMakeLists.txteditor/app/Bootstrap/Application.*editor/app/Composition/EditorContext.*editor/app/Composition/EditorShellRuntime.*editor/app/Windowing/Content/EditorWindowContentController.heditor/app/Windowing/Frame/EditorWindowFrameOrchestrator.*editor/app/Platform/Win32/Windowing/EditorWindow.*editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.*editor/app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.*editor/app/UtilityWindows/EditorUtilityWindowRegistry.*editor/include/XCEditor/Workspace/UIEditorWorkspaceController.heditor/include/XCEditor/Shell/UIEditorShellInteraction.h