10 KiB
10 KiB
XCUIEditor Windowing Architecture Plan
更新日期: 2026-04-26
状态: Phase 1 completed, Phase 2 completed, Subplan 3A planned
1. Plan 管理规则
docs/plan现在只保留总 plan。- 已完成 subplan 的结论直接并回总 plan,不再保留阶段性执行文档。
- 新的 subplan 必须保持小切口;一次只收口一条主链,不把 projection、destroy、drag/drop 等多条链路捆在同一阶段。
2. 顶层架构问题
当前 XCUIEditorApp 最严重的架构问题仍然是: 多窗口工作区状态存在双真相源。
这两个真相源目前分别落在:
editor/app/Windowing/System/EditorWindowSystem.*editor/app/Composition/EditorWindowWorkspaceStore.*- live
EditorWindow editor/app/Windowing/Content/EditorWorkspaceWindowContentController.*editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.*
虽然已经把“同步 diff 规划”和“标题策略”从 Win32 层切回了 app 层,但当前仍然存在以下事实:
- live
EditorWindow/EditorWorkspaceWindowContentController里的UIEditorWorkspaceController仍然是可变状态容器。 EditorWorkspaceWindowContentController::UpdateAndAppend(...)已经产出显式workspaceMutationrequest,但 request 仍然来自 live controller 的本地副本。EditorWindowWorkspaceCoordinator::HandleWindowFrameTransferRequests(...)收到workspaceMutation后,仍通过CommitLiveWindowMutation(sourceWindow)回读window.GetWorkspaceController(),而不是直接消费 request payload。EditorWindowWorkspaceCoordinator::ApplySynchronizationPlan(...)仍同时承担 host 执行与 authoritative commit 的时序编排。
这会带来四个直接后果:
- app 层和 host 层都能改
UIEditorWindowWorkspaceSet,单一真相源名义存在、实际不存在。 - frame 内部交互修改依赖平台回写兜底,边界方向不稳定。
- 关闭、拖拽、分离、primary 切换这些跨窗口时序很难纯粹按领域规则推导。
editor/app/Platform/Win32继续承担了不属于 host adapter 的领域编排责任。
3. 已完成收口
当前已完成两阶段收口,结果如下:
- 新增 app 层同步模型:
EditorWindowSynchronizationPlanEditorWindowSynchronizationPlannerEditorWindowPresentationPolicy
EditorWindowSystem新增BuildSynchronizationPlan(...),开始由 app 层产出 authoritative sync plan。EditorWindowWorkspaceCoordinator不再持有窗口集 diff 算法和标题策略定义,只保留 host snapshot 采集与 plan 执行。EditorWindowTransferRequests新增显式 workspace mutation request,frame 内工作区变化不再依赖平台隐式回写 authority。EditorWindowSystem收口 live window mutation / native destroy observation / authoritative commit 语义入口。EditorWindowLifecycleCoordinator::HandleNativeWindowDestroyed(...)不再直接删除 authority store entry,而是回到 app 层做 destroy reconciliation。editor/app/Platform/Win32/**已移除对 authority raw write 接口的直接依赖。- 新增聚焦验证:
tests/UI/Editor/unit/test_editor_window_synchronization_planner.cppeditor_windowing_phase1_tests
- editor 本体已完成构建与
12s启动冒烟验证。 - editor 测试入口回到
tests/UI/Editor,legacytests/editor已从当前活动构建图移除。
这两阶段的目标分别是:
- Phase 1: 先把“领域决策”和“Win32 执行”切开。
- Phase 2: 删除“平台回写 authority”链路,把 authority mutation 收口回
editor/app/Windowing。
这两个目标都已经达成。
4. 当前剩余缺口
原计划中针对 authority writeback 的核心缺口已经完成收口,但还有一段“半收口”链路仍然存在:
EditorWindowTransferRequests已有EditorWindowWorkspaceMutationRequest,但EditorWorkspaceWindowContentController::UpdateAndAppend(...)目前只填workspace/session,没有把稳定的windowId一并带出。EditorWindowWorkspaceCoordinator::HandleWindowFrameTransferRequests(...)目前拿到 request 后并不直接消费它,而是再次回读sourceWindow上的 live projection。- 这意味着 frame 内交互虽然已经显式化为 request,但 request 还没有真正成为这条链路的唯一语义输入。
这一段正好适合作为下一个小 subplan;在它完成之前,不适合继续展开更大的 live projection 去状态化。
5. 目标架构
目标状态下,窗口系统的数据流应当是单向的:
authoritative window set (app/windowing)
-> mutation validation
-> synchronization plan
-> Win32 execution
-> host observation / lifecycle event
-> app-layer semantic callback
这里的关键约束是:
- authority 只在
editor/app/Windowing侧维护。 Win32层只能消费 plan、上报 observation/event,不能直接写 authority store。- live content controller 只能是 projection 或受控 mutation producer,不能再充当隐式 authority 副本。
6. 后续路线
后续按“小 subplan”推进,每个 subplan 只解决一条主链。下一个 subplan 只收口 frame 内显式 workspaceMutation request,不同时处理 live projection 去状态化、native destroy reconciliation 或 global tab drag。
6.1 Subplan 3A: 显式 workspace mutation request 收口
6.1.1 阶段目标
- 让 frame 内 workspace 变更通过
EditorWindowWorkspaceMutationRequest进入 app 层,而不是由 coordinator 再回读 liveUIEditorWorkspaceController。 - 把“显式 request 已存在但未真正成为主输入”的半收口状态补完整。
- 保持本阶段足够小,只处理 live workspace mutation commit 这条链。
6.1.2 建议改动范围
editor/app/Windowing/Frame/EditorWindowTransferRequests.heditor/app/Windowing/Content/EditorWorkspaceWindowContentController.*editor/app/Windowing/System/EditorWindowSystem.*editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.*tests/UI/Editor/unit/test_editor_window_synchronization_planner.cpp- 必要时补
tests/UI/Editor/unit下新的 focused test;若必须经过 host 协调器,再补editor_app_feature_tests
6.1.3 详细执行步骤
- 先把
workspaceMutationrequest payload 补完整。EditorWorkspaceWindowContentController::UpdateAndAppend(...)在生成EditorWindowWorkspaceMutationRequest时补齐windowState.windowId。- request 的有效性以“
windowId非空且 workspace state 可用”为准,避免继续依赖 source window 隐式补信息。
- 再把 app 层计划入口改成显式 request 驱动。
- 在
EditorWindowSystem中新增或收口一个直接消费EditorWindowWorkspaceMutationRequest的 plan builder。 - 该入口只做三件事: 校验
windowId、把 request 中的workspace/session合并进当前 authoritative window set、复用BuildPlanForWindowSet(...)。 BuildPlanForLiveWindowMutation(...)如果还有旧调用方,可暂时降级成薄封装;本阶段不再新增新的调用点依赖它。
- 在
- 最后切换
EditorWindowWorkspaceCoordinator的 live commit 路径。HandleWindowFrameTransferRequests(...)直接把transferRequests.workspace.workspaceMutation传给 commit 路径。CommitLiveWindowMutation(...)改成基于 request 的实现,或者删除旧的EditorWindow&回读版本。- 本阶段完成后,live workspace mutation commit 不再依赖
window.GetWorkspaceController();该接口只允许继续服务 projection / title / UI 查询。
- 补 focused 验证,保证这是小收口而不是行为漂移。
- planner/system 侧至少覆盖“显式 request 改 active panel 后可生成并提交有效 plan”。
- 增加“unknown windowId request 被拒绝”的负向验证。
- 如果 unit 无法覆盖 coordinator 消费 request 的行为,再补一条最小 host-side focused test,而不是直接上重型运行时验证。
6.1.4 本阶段验收标准
workspaceMutationrequest 自身携带稳定windowId,不再需要 source window 兜底补齐。EditorWindowWorkspaceCoordinator::HandleWindowFrameTransferRequests(...)不再忽略 request payload。- live frame mutation 的 authoritative commit 路径不再回读
sourceWindow.GetWorkspaceController()。 - 现有 detach / dock / close / primary switch 行为不因本阶段发生语义漂移。
editor_windowing_phase1_tests继续通过;若新增 host-side focused test,对应测试目标也应通过。
6.1.5 本阶段非目标
- 不在本阶段删除
EditorWindow/ content controller 上的TryGetWorkspaceController()或ReplaceWorkspaceController()。 - 不在本阶段处理 native destroy reconciliation。
- 不在本阶段重做 global tab drag / detach 相关编排。
- 不在本阶段把 live content controller 改成完全只读 projection。
7. 验收标准
整个计划完成后,应至少满足:
editor/app/Platform/Win32/**不再直接调用 authority store 原始写接口。EditorWindowSystem成为窗口工作区 authoritative mutation 的唯一 app 层入口。- 任意多窗口变化都先形成 app 层语义上的 mutation / transition,再形成 sync plan,再由 host 执行。
- native close / destroy 不再通过平台层“直接删 store entry”完成状态收口。
EditorWorkspaceWindowContentController不再被当作 authority 副本,而只是 projection 或受控 mutation source。- detach / dock / close / primary switch 至少具备稳定 unit coverage,并对关键 host 时序具备 focused integration coverage。
8. 验证策略
验证入口仍然以 tests/UI/Editor 为主,不重新启用旧 tests/editor。
优先级如下:
tests/UI/Editor/unit- 必要时补
tests/UI/Editor/smoke - 只有在 unit / focused integration 无法覆盖的 host 时序下,才考虑更重的运行时验证
当前已建立的验证基线:
cmake --build build --config Debug --target editor_windowing_phase1_testsbuild/tests/UI/Editor/unit/Debug/editor_windowing_phase1_tests.exe
9. 非目标
本计划当前不直接解决以下事项:
- 把
XCUIEditorApp立即改造成跨平台窗口系统 - 全量重写 shell / viewport / message dispatcher
- 顺手重构所有 inspector、panel、runtime 业务逻辑
- 在 authority 收口前提前合并全部 Win32 coordinator
这些都必须排在“窗口领域 authority 真正收口”之后。