Files
XCEngine/docs/plan/NewEditor_EditorLayerAppBoundaryClosurePlan_2026-04-22.md

562 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# NewEditor Editor Layer / App Boundary Closure Plan
Date: 2026-04-22
## 1. Objective
这份计划的目标不是泛泛讨论“分层好不好看”,而是把 `new_editor` 当前最明显的边界泄漏收口掉:
1. `engine` 中的 UI 基础层 / 运行时层继续只负责通用 UI 能力
2. `new_editor/include` + `new_editor/src` 中的 `XCUIEditorLib` 明确成为“编辑器 UI 模块层”
3. `new_editor/app` 只保留编辑器应用层应当持有的业务、平台、渲染宿主和应用装配
4. 把当前明显散落在 `app`、但本质属于“通用 editor UI 机制”的内容,抽回 `XCUIEditorLib`
本轮计划只聚焦已经确认影响较大的三类问题:
1. hosted panel 运行时机制散落在 `app`
2. detached/tool-window 策略散落在多个 `app` 模块
3. panel lifecycle / focus / input gate 已经在 editor 层有雏形,但 `app` 没有接上,反而重复实现
## 2. Confirmed Findings
### 2.1 Dependency direction is mostly correct
当前 `XCUIEditorLib` 没有实际反向 include `app`,说明“目录依赖方向”本身没有彻底坏掉。
这点必须保留:
1. `engine` 不依赖 `new_editor`
2. `XCUIEditorLib` 不依赖 `new_editor/app`
3. `app` 依赖 `XCUIEditorLib` 和 host/platform/rendering ports
所以本次工作不是纠正反向依赖,而是纠正“职责泄漏到 `app`”。
### 2.2 Hosted panel runtime is still implemented in app
以下职责目前散落在 `app`
1. 具体 panel 实例持有与总调度:
- `new_editor/app/Composition/EditorShellRuntime.h`
2. 面板更新分发、输入过滤、焦点派发:
- `new_editor/app/Composition/EditorShellHostedPanelCoordinator.cpp`
3. 面板绘制拼装:
- `new_editor/app/Composition/EditorShellDrawComposer.cpp`
4. 面板事件回流到 session/status
- `new_editor/app/Composition/WorkspaceEventSync.cpp`
问题不在于这些代码位于 `app`,而在于它们实现的内容已经不是“应用业务”,而是“通用 editor-hosted-panel 运行时机制”。
### 2.3 Detached window policy is duplicated in multiple app modules
当前至少有三处各自推导 detached/tool-window 规则:
1. shell 交互期的 metrics 调整:
- `new_editor/app/Composition/EditorShellInteractionEngine.cpp`
2. 窗口 chrome 的 tool-window 判定和最小尺寸:
- `new_editor/app/Platform/Win32/EditorWindowChromeController.cpp`
3. 打开 detached panel 时的 preferred size 解析:
- `new_editor/app/Platform/Win32/EditorWindowFrameOrchestrator.cpp`
但这些规则依赖的源数据其实都在:
1. `UIEditorPanelRegistry`
2. `UIEditorWorkspaceController`
3. `UIEditorWindowWorkspaceController`
也就是说,策略消费被拆散了,策略数据却本来就在 editor 层。
### 2.4 Panel lifecycle abstraction exists in editor layer but is not used
`XCUIEditorLib` 已经有:
1. `new_editor/include/XCEditor/Panels/UIEditorPanelHostLifecycle.h`
2. `new_editor/src/Panels/UIEditorPanelHostLifecycle.cpp`
`app` 里仍然自己维护:
1. `PanelInputContext`
2. `allowInteraction`
3. `focusGained`
4. `focusLost`
5. mounted panel 扫描
对应文件包括:
1. `new_editor/app/Composition/EditorShellHostedPanelCoordinator.cpp`
2. `new_editor/app/Features/PanelInputContext.h`
3. `new_editor/app/Features/Hierarchy/HierarchyPanel.cpp`
4. `new_editor/app/Features/Project/ProjectPanel.cpp`
5. `new_editor/app/Features/Inspector/InspectorPanel.cpp`
6. `new_editor/app/Features/ColorPicker/ColorPickerPanel.cpp`
7. `new_editor/app/Features/Console/ConsolePanel.cpp`
这会导致同一套 host lifecycle 语义出现两份实现,并最终行为漂移。
### 2.5 Resulting symptom: EditorShellRuntime is carrying too much generic mechanism
`EditorShellRuntime` 现在同时承担:
1. shell interaction orchestration
2. hosted panel runtime orchestration
3. viewport host service orchestration
4. app feature update routing
5. draw packet assembly
6. workspace event tracing and status backflow
其中前半部分有相当一块本应属于 editor 模块层,而不是 app 层。
## 3. Boundary Rules
## 3.1 What must stay in engine UI base/runtime layers
`engine` 中的 UI 层继续只负责:
1. `UIInputEvent``UIRect``UIDrawData` 等基础类型
2. 通用 UI 输入、焦点、样式、文字、widget model
3. 与编辑器无关的 runtime UI 能力
`engine` 不负责:
1. editor panel host lifecycle
2. docked editor shell 策略
3. detached editor window 策略
4. editor hosted panel 调度
## 3.2 What must belong to XCUIEditorLib
`XCUIEditorLib` 应当负责所有“与具体业务面板无关、但与 editor shell / workspace / panel host 强相关”的通用机制,包括:
1. panel registry 元数据及校验
2. workspace / dock host / viewport shell 的通用交互
3. hosted panel mount/layout/lifecycle 的统一推导
4. hosted panel 输入过滤与 input-owner 对接
5. detached/tool-window 策略与尺寸解析
6. 供应用层消费的 panel dispatch frame / request / result
这里的核心判断标准是:
只要某段逻辑不关心“这是 Hierarchy 还是 Project”而只关心“这是一个 externally hosted panel”它就应该在 `XCUIEditorLib`
## 3.3 What must stay in app
`new_editor/app` 只保留以下内容:
1. 具体业务 panel 本身:
- `HierarchyPanel`
- `ProjectPanel`
- `InspectorPanel`
- `ConsolePanel`
- `ColorPickerPanel`
- `SceneViewportFeature`
2. 业务 runtime
- `EditorSceneRuntime`
- `EditorProjectRuntime`
- `EditorSession`
3. 应用装配:
- 菜单/命令/toolbar 的应用级定义
- panel registry 的应用级实例构建
4. 平台宿主:
- Win32 window lifecycle
- D3D12 host renderer
- capture/system interaction
## 3.4 What must not be moved into XCUIEditorLib
本次收口不是把所有东西都塞进 editor 库。
以下内容明确不下沉:
1. Hierarchy / Project / Inspector 的业务数据模型
2. Scene runtime / project runtime / selection service
3. Win32 chrome 和窗口消息循环
4. D3D12 host 层资源与 swapchain
5. app 级菜单、命令、panel 组合方案
## 3.5 Placement rule for new modules
当前 `new_editor/src/App` 实际没有形成可用公共层,`include/XCEditor/App` 也不存在。
因此新增 editor-layer 模块不应新建一个伪 `XCEditor/App`
新增通用能力应优先落到:
1. `XCEditor/Panels`
2. `XCEditor/Shell`
3. `XCEditor/Workspace`
只在确实与上述三个域都不匹配时,才考虑新增域。
## 4. Target End State
## 4.1 Target runtime flow
目标运行流应当变成:
1. `app` 构造应用级 `EditorShellAsset`、业务 panel、业务 runtime
2. `XCUIEditorLib` 负责:
- shell/workspace 交互
- content host mount frame
- panel host lifecycle frame
- hosted panel dispatch frame
- detached window policy resolution
3. `app` 只做两件事:
- 把 dispatch frame 按 `panelId` 路由给具体 panel
- 把具体 panel 的业务事件回流到 app session/runtime
这样 `app` 保留“内容”,`XCUIEditorLib` 收回“机制”。
## 4.2 Target module responsibilities
### Editor layer should own
建议新增或重组出以下 editor-layer 模块:
1. `UIEditorHostedPanelDispatch`
- 统一生成每个 hosted panel 的 update request / input slice / lifecycle view
2. `UIEditorDetachedWindowPolicy`
- 统一解析 tool-window、preferred size、minimum size、detached title behavior
3. `UIEditorPanelHostLifecycle`
- 从“已有但未接入”变为“活跃主路径能力”
### App layer should own
`app` 最终只保留一个轻量路由层,例如:
1. `EditorHostedPanelRouter`
- 根据 `panelId` 找到具体 panel 对象
- 调用 `Update(...)`
- 调用 `Append(...)`
- 收集业务事件
如果不需要单独新建这个类,也至少要把现有 `EditorShellHostedPanelCoordinator` 收缩到这个角色。
## 4.3 Target data model direction
本次收口后,`app` 不应再自己定义一套 panel host 语义。
目标是:
1. 删除或废弃 `PanelInputContext`
2. 用 editor-layer 的统一 dispatch entry / lifecycle frame 取代
3. hosted panel 的 mounted / visible / active / focused / focus gained / focus lost 只存在一套来源
## 5. Refactor Strategy
### Phase A. Freeze boundary and add structural guardrails
目标:
在开始搬迁逻辑前,先把边界约束写清楚,避免中途再次把通用 editor 机制塞回 `app`
要做的事:
1. 在文档中明确本计划的边界规则
2. 审查 `new_editor/CMakeLists.txt``XCUIEditorLib``app` 的 include 使用现状
3. 增加最小结构验证规则:
- `new_editor/src` / `new_editor/include` 不反向 include `app`
- `app` 不再新增 editor-generic duplicated helper
4. 为后续 grep 验证准备检查项
验收标准:
1. 本计划作为边界基线落地
2. 后续每一阶段都能用 grep 验证“重复逻辑是否还活着”
### Phase B. Make panel host lifecycle a live path
目标:
先把已有的 `UIEditorPanelHostLifecycle` 从死代码变成活代码。
要做的事:
1. 在 editor-layer 主路径中接入 `UpdateUIEditorPanelHostLifecycle(...)`
2. 明确 `focusedPanelId` 的统一来源
3. 让 lifecycle frame 成为 hosted panel dispatch 的基础输入
4. 禁止 `app` 再自行推导:
- `focusGained`
- `focusLost`
- `active`
- `visible`
- `attached`
重点文件范围:
1. `new_editor/include/XCEditor/Panels/UIEditorPanelHostLifecycle.h`
2. `new_editor/src/Panels/UIEditorPanelHostLifecycle.cpp`
3. `new_editor/include/XCEditor/Shell/UIEditorShellInteraction.h`
4. `new_editor/include/XCEditor/Workspace/UIEditorWorkspaceInteraction.h`
5. `new_editor/app/Composition/EditorShellHostedPanelCoordinator.cpp`
验收标准:
1. `UpdateUIEditorPanelHostLifecycle(...)` 进入实际 frame path
2. `app` 不再自己计算 focus enter/leave 语义
### Phase C. Introduce hosted panel dispatch into XCUIEditorLib
目标:
把 hosted panel 的通用运行时机制从 `app` 抽回 editor 层,但不把具体业务 panel 也硬搬进库里。
建议新增能力:
1. `UIEditorHostedPanelDispatchEntry`
2. `UIEditorHostedPanelDispatchFrame`
3. `UIEditorHostedPanelDispatchRequest`
每个 entry 至少应包含:
1. `panelId`
2. mounted/bounds 信息
3. lifecycle state 或 lifecycle frame 视图
4. 该 panel 可见时应收到的过滤后输入
5. 交互是否允许
6. 当前 input owner / capture owner 相关视图
要做的事:
1.`EditorShellHostedPanelCoordinator.cpp` 中的通用逻辑下沉到 `XCUIEditorLib`
2. 统一处理 hosted-content 与 shell 对 pointer stream 的让渡规则
3. 统一处理 panel 的 mounted/bounds 查找
4. 统一处理 focus/lifecycle/input gate 计算
5. app 只保留:
- `panelId -> concrete panel` 路由
- 业务对象依赖注入
- 业务事件回流
重点文件范围:
1. 新增:
- `new_editor/include/XCEditor/Panels/UIEditorHostedPanelDispatch.h`
- `new_editor/src/Panels/UIEditorHostedPanelDispatch.cpp`
2. 调整:
- `new_editor/app/Composition/EditorShellHostedPanelCoordinator.h`
- `new_editor/app/Composition/EditorShellHostedPanelCoordinator.cpp`
- `new_editor/app/Features/PanelInputContext.h`
- `new_editor/app/Features/Hierarchy/HierarchyPanel.*`
- `new_editor/app/Features/Project/ProjectPanel.*`
- `new_editor/app/Features/Inspector/InspectorPanel.*`
- `new_editor/app/Features/ColorPicker/ColorPickerPanel.*`
- `new_editor/app/Features/Console/ConsolePanel.*`
验收标准:
1. `PanelInputContext` 被删除或降级为纯 app-specific 数据,不再承载通用 host 语义
2. `FindMountedHierarchyPanel``FindMountedProjectPanel` 等重复 mounted 扫描逻辑被消除或大幅收缩
3. `EditorShellHostedPanelCoordinator` 只剩业务路由和依赖注入
### Phase D. Centralize detached window / tool-window policy
目标:
把 detached/tool-window 相关策略从多个 `app` 模块收成一套 editor-layer 能力。
建议新增能力:
1. `UIEditorDetachedWindowPolicy`
2. `UIEditorDetachedWindowPolicyResult`
3. `ResolveUIEditorDetachedWindowPolicy(...)`
这套策略至少统一回答:
1. 当前 workspace 是否表现为 tool-window
2. 当前 detached window 的 preferred/minimum 尺寸是多少
3. 是否应使用 detached title-bar tab strip
4. 单面板 root tab stack 的判定逻辑
要做的事:
1.`ResolveSingleVisibleToolWindowPanelDescriptor(...)` 和同类逻辑下沉
2.`ResolveMinimumOuterSize(...)``ResolveDetachedPanelPreferredSize(...)` 统一消费同一套 API
3. 让 shell interaction metrics、Win32 chrome、detached-panel open request 共用同一套策略结果
重点文件范围:
1. 新增:
- `new_editor/include/XCEditor/Shell/UIEditorDetachedWindowPolicy.h`
- `new_editor/src/Shell/UIEditorDetachedWindowPolicy.cpp`
2. 调整:
- `new_editor/app/Composition/EditorShellInteractionEngine.cpp`
- `new_editor/app/Platform/Win32/EditorWindowChromeController.cpp`
- `new_editor/app/Platform/Win32/EditorWindowFrameOrchestrator.cpp`
验收标准:
1. `toolWindow`、preferred size、minimum size 的判定只有一套主实现
2. 上述三个 app 文件只消费统一 helper不再保留平行推导函数
### Phase E. Reduce EditorShellRuntime to app orchestration
目标:
在完成通用机制下沉之后,让 `EditorShellRuntime` 从“半个 editor framework”收缩为“应用层编排器”。
要做的事:
1. 移除 `EditorShellRuntime` 中与 hosted panel lifecycle / dispatch / policy 直接相关的通用逻辑
2. 保留:
- app feature 依赖注入
- scene/project runtime 同步
- status / selection / command route 回流
- viewport host 服务调用
3. 视情况把 `EditorShellDrawComposer` 收缩为纯 draw packet 组织器
4. 视情况把 `WorkspaceEventSync` 收缩为纯 app 业务事件同步器
重点文件范围:
1. `new_editor/app/Composition/EditorShellRuntime.h`
2. `new_editor/app/Composition/EditorShellRuntime.cpp`
3. `new_editor/app/Composition/EditorShellDrawComposer.h`
4. `new_editor/app/Composition/EditorShellDrawComposer.cpp`
5. `new_editor/app/Composition/WorkspaceEventSync.h`
6. `new_editor/app/Composition/WorkspaceEventSync.cpp`
验收标准:
1. `EditorShellRuntime` 不再承载 editor-generic host policy
2. 它只负责应用装配和业务流程编排
### Phase F. Verification and regression hardening
目标:
确保这次收口不是“结构更好看了,但行为退化了”。
要做的事:
1. 编译 `XCUIEditorLib``XCUIEditorAppCore``XCUIEditorApp`
2. 验证 primary window、detached window、tool-window 三种路径
3. 验证以下交互没有回归:
- hierarchy 选择/拖拽/rename
- project tree/grid 交互
- inspector 输入
- color picker detached open
- scene viewport capture
4. 增加 grep 结构检查:
- `PanelInputContext` 不再作为通用 host 语义入口
- `ResolveSingleVisibleToolWindowPanelDescriptor` 等重复 helper 已移除
- `UpdateUIEditorPanelHostLifecycle` 已有活跃调用
## 6. File-Level Scope
### 6.1 Existing editor-layer files to extend
1. `new_editor/include/XCEditor/Panels/UIEditorPanelHostLifecycle.h`
2. `new_editor/src/Panels/UIEditorPanelHostLifecycle.cpp`
3. `new_editor/include/XCEditor/Panels/UIEditorPanelContentHost.h`
4. `new_editor/src/Panels/UIEditorPanelContentHost.cpp`
5. `new_editor/include/XCEditor/Workspace/UIEditorWorkspaceInteraction.h`
6. `new_editor/src/Workspace/UIEditorWorkspaceInteraction.cpp`
7. `new_editor/include/XCEditor/Shell/UIEditorShellInteraction.h`
### 6.2 New editor-layer files expected
1. `new_editor/include/XCEditor/Panels/UIEditorHostedPanelDispatch.h`
2. `new_editor/src/Panels/UIEditorHostedPanelDispatch.cpp`
3. `new_editor/include/XCEditor/Shell/UIEditorDetachedWindowPolicy.h`
4. `new_editor/src/Shell/UIEditorDetachedWindowPolicy.cpp`
如果实际实现中发现这些文件更适合放到 `Workspace/`,可以调整,但不能回退到 `app`
### 6.3 App files expected to shrink
1. `new_editor/app/Composition/EditorShellHostedPanelCoordinator.h`
2. `new_editor/app/Composition/EditorShellHostedPanelCoordinator.cpp`
3. `new_editor/app/Composition/EditorShellInteractionEngine.cpp`
4. `new_editor/app/Composition/EditorShellRuntime.h`
5. `new_editor/app/Composition/EditorShellRuntime.cpp`
6. `new_editor/app/Platform/Win32/EditorWindowChromeController.cpp`
7. `new_editor/app/Platform/Win32/EditorWindowFrameOrchestrator.cpp`
8. `new_editor/app/Features/PanelInputContext.h`
### 6.4 App business files expected to adapt, not be generalized
1. `new_editor/app/Features/Hierarchy/HierarchyPanel.*`
2. `new_editor/app/Features/Project/ProjectPanel.*`
3. `new_editor/app/Features/Inspector/InspectorPanel.*`
4. `new_editor/app/Features/ColorPicker/ColorPickerPanel.*`
5. `new_editor/app/Features/Console/ConsolePanel.*`
6. `new_editor/app/Features/Scene/SceneViewportFeature.*`
这些文件应改为消费 editor-layer 的统一 dispatch/lifecycle 数据,而不是继续维护自己的 host 规则。
## 7. Validation
## 7.1 Structural validation
完成后必须满足:
1. `XCUIEditorLib` 仍然不 include `app`
2. `UpdateUIEditorPanelHostLifecycle(...)` 已进入活跃路径
3. `app` 中不再保留三套 detached/tool-window 策略推导
4. `app` 中不再保留多套 mounted/focus/allowInteraction 推导
## 7.2 Behavior validation
至少验证以下行为:
1. 主窗口正常更新/绘制
2. detached panel 正常打开
3. tool-window 最小尺寸和标题栏行为正确
4. hierarchy / project / inspector / color picker 交互不回归
5. scene viewport capture / pointer ownership 不回归
6. 跨窗口面板移动后生命周期和交互状态仍然正确
## 7.3 Regression grep checklist
收口完成后,以下 grep 结果应显著收缩或归零:
1. `FindMountedHierarchyPanel`
2. `FindMountedProjectPanel`
3. `FindMountedInspectorPanel`
4. `FindMountedColorPickerPanel`
5. `PanelInputContext`
6. `ResolveSingleVisibleToolWindowPanelDescriptor`
7. `ResolveSingleVisibleRootPanelDescriptor`
8. `ResolveMinimumOuterSize`
9. `ResolveDetachedPanelPreferredSize`
## 8. Execution Order
建议按以下顺序执行,降低回归面:
1. 先接通 `UIEditorPanelHostLifecycle` 活路径
2. 再引入 `UIEditorHostedPanelDispatch`
3. 然后让各个 panel 改为消费统一 dispatch entry
4. 再统一 detached/tool-window policy
5. 最后收缩 `EditorShellRuntime``EditorShellHostedPanelCoordinator``EditorShellDrawComposer`
不要反过来先大拆 `EditorShellRuntime`,否则会在旧重复逻辑还没收掉时放大回归风险。
## 9. Out Of Scope
本计划明确不包含:
1. 把业务 panel 本身迁入 `XCUIEditorLib`
2. 重做 Win32 window manager
3. 重做 D3D12 host renderer
4. 重做 viewport render pipeline
5. 全面重命名 `new_editor` 目录结构
## 10. Completion Criteria
只有满足以下条件,这次分层收口才算真正完成:
1. `app` 中不再保存 editor-generic hosted-panel 机制实现
2. detached/tool-window 策略收成 editor-layer 单一入口
3. `UIEditorPanelHostLifecycle` 从闲置工具变为真实主路径能力
4. 具体业务 panel 继续留在 `app`,但只消费统一的 editor-layer dispatch/lifecycle 数据
5. `EditorShellRuntime` 收缩为应用编排器,而不是第二套 editor framework
6. `XCUIEditorLib``app` 的单向依赖方向保持不变
## 11. Final Statement
这次收口的目标不是“把更多代码搬到 `new_editor/src` 就算成功”,而是:
1.`XCUIEditorLib` 真的承担 editor 模块层职责
2.`app` 退回应用层职责
3. 让同一套 panel host / detached-window 语义只保留一份实现
做到这三点之后,`new_editor` 的四层结构才算从“目录上分层”走到“职责上分层”。