Files
XCEngine/docs/used/NewEditor_WorkspaceUtilityWindowSemanticSplitPlan_2026-04-22.md

530 lines
18 KiB
Markdown
Raw Normal View History

# NewEditor Workspace Utility Window Semantic Split Plan
Date: 2026-04-22
Status: Complete and Archived
## 0. Execution Progress
### Completed on 2026-04-22
Phase A shell/content split foundation is now in place.
Completed changes:
1. `EditorWindow` no longer constructs itself from `UIEditorWorkspaceController`
2. `EditorWindowRuntimeController` no longer directly owns `m_workspaceController`
3. `EditorWindowHostRuntime::CreateEditorWindow(...)` no longer accepts raw workspace controller input
4. a new content abstraction now exists:
- `EditorWindowContentController`
- `EditorWorkspaceWindowContentController`
5. current workspace-backed behavior has been moved behind the new content layer without changing workspace semantics
6. detached workspace chrome policy now reads window-content capabilities instead of shell-owned workspace state
7. `XCUIEditorApp` Debug build passes after this phase
What is intentionally not done yet:
1. utility window domain does not exist yet
2. color picker still opens through detached panel workflow
3. `toolWindow` still exists in workspace panel descriptors as legacy leakage to remove in later phases
This means the root shell/content ownership boundary has been extracted first, while feature semantics remain unchanged for safety.
### Completed on 2026-04-22
Phase C, Phase D, and Phase E implementation are now in place.
Completed changes:
1. a first-class utility window domain now exists:
- `EditorUtilityWindowKind`
- `EditorWindowOpenUtilityWindowRequest`
- `EditorUtilityWindowCoordinator`
- `EditorUtilityWindowRegistry`
2. color picker open intent no longer routes through detached workspace mutation
3. the color picker now opens through utility-window coordination and utility content
4. color picker has been removed from:
- `UIEditorPanelRegistry`
- shell hosted-panel composition
- workspace detached-panel request plumbing
5. `openDetachedPanel` has been deleted from frame transfer routing
6. `UIEditorWindowWorkspaceController::OpenPanelInNewWindow(...)` has been deleted
7. `toolWindow` and related workspace utility inference have been deleted
8. detached floating-window placement logic is now shared instead of duplicated
9. `XCUIEditorApp` Debug build passes after this phase
### Verified and Archived on 2026-04-22
Section 10 manual verification has now passed.
Verified outcomes:
1. workspace regression checks passed
2. utility window checks passed
3. cross-boundary checks passed
4. `XCUIEditorApp` Debug build passes
5. `XCUIEditorApp` Release build passes
## 1. Objective
This plan solves the problem from the architectural root, not by adding another special case.
The goal is to completely separate:
1. native window shell
2. workspace-backed dockable windows
3. utility windows that are inherently standalone
The immediate trigger is the color picker window, but the real issue is broader:
1. `EditorWindow` is still specialized around `UIEditorWorkspaceController`
2. utility windows are currently modeled as panels with a `toolWindow` flag
3. the color picker is opened through `openDetachedPanel`
4. utility window policy is inferred indirectly from workspace root content
This architecture is semantically wrong.
If a window is inherently standalone and can never merge back into the main workspace, it must not live inside the workspace panel model.
## 2. Architectural Judgment
The current implementation shares too much.
Sharing the native host is correct:
1. HWND lifetime
2. D3D render loop
3. DPI handling
4. input capture
5. title bar / chrome
6. screenshot and frame pacing
Sharing the workspace panel abstraction is not correct for the color picker.
The color picker is currently treated as:
1. a panel descriptor in `UIEditorPanelRegistry`
2. a hosted panel inside shell composition
3. a detached panel opened via `OpenPanelInNewWindow(...)`
4. a single-root detached workspace that is merely styled as a tool window
That means the color picker still belongs to the dock / detach / re-dock / transfer universe.
This is the wrong owner domain.
## 3. Confirmed Root Cause
The root cause is not one bad function.
The root cause is that the codebase currently conflates two different semantics:
1. workspace window
- owns a `UIEditorWorkspaceController`
- participates in layout, docking, tab stacks, cross-window transfer
- can detach and re-merge
2. utility window
- owns standalone tool content
- does not belong to workspace layout
- does not expose `nodeId` / `panelId` docking semantics
- cannot merge into the main window
Today those two semantics are both forced through `EditorWindow` + `UIEditorWorkspaceController`.
That is why the color picker can only exist as a fake panel.
## 4. Current Coupling Points
These are the concrete places where the semantic mistake is encoded today:
1. `new_editor/app/Platform/Win32/EditorWindow.h`
- `EditorWindow` constructor directly requires `UIEditorWorkspaceController`
2. `new_editor/app/Platform/Win32/EditorWindowRuntimeController.h`
- runtime directly stores `m_workspaceController`
3. `new_editor/app/Platform/Win32/EditorWindowTransferRequests.h`
- utility-open intent is modeled as `EditorWindowOpenDetachedPanelRequest`
4. `new_editor/app/Platform/Win32/EditorWindowFrameOrchestrator.cpp`
- color picker open is converted into `openDetachedPanel`
5. `new_editor/app/Platform/Win32/WindowManager/EditorWindowWorkspaceCoordinator.cpp`
- color picker window creation is routed through workspace mutation
6. `new_editor/src/Workspace/UIEditorWindowWorkspaceController.cpp`
- `OpenPanelInNewWindow(...)` is used to create the color picker window
7. `new_editor/include/XCEditor/Panels/UIEditorPanelRegistry.h`
- `toolWindow` is attached to panel descriptors
8. `new_editor/src/Workspace/UIEditorDetachedWindowPolicy.cpp`
- utility semantics are inferred from detached workspace root content
9. `new_editor/app/Composition/EditorShellAssetBuilder.cpp`
- color picker is registered as a panel
10. `new_editor/app/Composition/EditorShellHostedPanelCoordinator.cpp`
- color picker lives inside hosted panel update / dispatch infrastructure
11. `new_editor/app/State/EditorColorPickerToolState.h`
- color picker open intent is expressed as `requestOpenDetachedPanel`
This is not one bug. This is an ownership boundary failure.
## 5. Refactor Red Lines
The refactor must not degrade into any of the following:
1. keeping the color picker in `UIEditorPanelRegistry` and merely adding more `if (panelId == "color-picker")` branches
2. keeping `toolWindow` as the long-term mechanism for non-workspace windows
3. keeping `openDetachedPanel` as the generic request type for utility windows
4. allowing utility windows to remain inside `UIEditorWindowWorkspaceSet`
5. preserving `nodeId` / `panelId` transfer semantics for utility windows
6. building a second special-case bypass inside `EditorWindowWorkspaceCoordinator`
7. renaming the current path without changing ownership
If any of those remain in the end state, the architecture is still wrong.
## 6. Target End State
The correct end state has three layers.
### 6.1 Native Shell Layer
A shared native shell owns only platform and rendering responsibilities:
1. HWND creation and destruction
2. D3D renderer / swap chain / present loop
3. window chrome and title
4. DPI, sizing, screenshot, pointer capture plumbing
5. input event collection and dispatch to content
This layer must not know whether the content is workspace-backed or utility-backed.
Suggested shape:
1. `EditorNativeWindowShell`
2. `EditorNativeWindowRuntime`
3. `IEditorWindowContentController`
The names can change, but the boundary cannot.
### 6.2 Workspace Window Layer
Workspace windows are the only windows allowed to own:
1. `UIEditorWorkspaceController`
2. `EditorShellRuntime`
3. dock host interaction state
4. detached panel transfer requests
5. cross-window tab drag and dock / re-dock semantics
A workspace window can:
1. represent the main window
2. represent a detached panel window
3. merge back into another workspace window
Suggested shape:
1. `EditorWorkspaceWindowContent`
2. `EditorWorkspaceWindowCoordinator`
3. `EditorWorkspaceWindowTransferRequests`
### 6.3 Utility Window Layer
Utility windows are standalone tool surfaces.
They must not own:
1. `UIEditorWorkspaceController`
2. dock layout
3. tab stack state
4. `nodeId`
5. `panelId` workspace transfer semantics
They may own:
1. dedicated tool state
2. dedicated rendering and input logic
3. explicit request / response channels to editor features
4. separate focus / reuse / close rules
Suggested shape:
1. `EditorUtilityWindowContent`
2. `EditorUtilityWindowCoordinator`
3. `EditorOpenUtilityWindowRequest`
4. `EditorUtilityWindowKind`
5. `EditorColorPickerUtilityWindowContent`
## 7. Required Ownership Changes
The refactor must move ownership, not just code location.
### 7.1 Window Identity
Today:
1. detached color picker window identity is derived from `panelId`
2. detached utility behavior is inferred from the root workspace panel descriptor
After refactor:
1. workspace windows are identified inside workspace domain
2. utility windows are identified inside utility domain
3. utility window identity is no longer a panel identity
### 7.2 Open Intent
Today:
1. color picker open intent means "open this panel in a detached window"
After refactor:
1. color picker open intent means "open or focus utility window of kind color picker"
2. the request carries explicit tool payload:
- initial color
- alpha mode
- inspector target binding
- focus / reuse policy
### 7.3 Persistence
Today:
1. utility-like windows still pass through workspace layout concepts
After refactor:
1. workspace window state remains in workspace layout persistence
2. utility window state uses separate persistence or no persistence
3. utility windows never enter `UIEditorWindowWorkspaceSet`
### 7.4 Visual Policy
Today:
1. tool-window policy is inferred from detached workspace content
After refactor:
1. workspace detached-window policy is only about workspace windows
2. utility-window policy belongs to utility window descriptors or utility content
## 8. Migration Strategy
The migration must happen in strict phases.
### Phase A. Extract a Content-Neutral Native Window Shell
Goal:
1. stop making the native window host depend directly on `UIEditorWorkspaceController`
Required changes:
1. split the current `EditorWindow` responsibilities into:
- shell responsibilities
- content responsibilities
2. extract a content interface for:
- update
- draw
- input dispatch
- external preview / title contributions if needed
3. make `EditorWindowHostRuntime::CreateEditorWindow(...)` create a shell plus content, not a workspace-bound window object
Success criteria:
1. a shell instance can host workspace content
2. a shell instance can host non-workspace content
3. no shell constructor directly requires `UIEditorWorkspaceController`
Red line:
1. do not keep a hidden `m_workspaceController` in shell-level runtime
### Phase B. Move Existing Workspace Windows onto the New Content Layer
Goal:
1. preserve all existing main-window and detached-panel behavior while isolating it into workspace-specific content
Required changes:
1. create `EditorWorkspaceWindowContent`
2. move current workspace-only responsibilities into it:
- `UIEditorWorkspaceController`
- `EditorShellRuntime`
- workspace frame orchestration
- dock transfer request generation
3. keep `EditorWindowWorkspaceCoordinator` but narrow its responsibility to workspace windows only
Success criteria:
1. main window still works
2. detached panel windows still work
3. cross-window tab drag and re-dock still work
4. utility windows are still not introduced yet, but the shell is already ready for them
### Phase C. Introduce a Real Utility Window Domain
Goal:
1. create a first-class utility window pipeline independent from workspace mutation
Required changes:
1. add utility window request types
2. add `EditorUtilityWindowCoordinator`
3. add utility window registry / descriptor model
4. define utility window lifecycle:
- open
- reuse
- focus
- close
- app-shutdown behavior
5. define utility window payload passing
Success criteria:
1. utility windows can be created without touching `UIEditorWindowWorkspaceController`
2. utility windows can be reused without entering workspace state
3. utility windows do not generate dock transfer requests
### Phase D. Migrate Color Picker Out of the Panel System
Goal:
1. make the color picker the first true utility window
Required changes:
1. remove color picker from `UIEditorPanelRegistry`
2. remove color picker from shell hosted-panel composition
3. replace `requestOpenDetachedPanel` with utility-window open intent
4. implement `EditorColorPickerUtilityWindowContent`
5. wire inspector -> color picker communication through explicit tool contracts instead of workspace panel visibility
Success criteria:
1. opening the color picker never calls `OpenPanelInNewWindow(...)`
2. color picker never appears in workspace visible panels
3. color picker never participates in detach / re-dock / cross-window panel transfer
4. color picker still updates inspector-bound color correctly
### Phase E. Delete Transitional Workspace Utility Leakage
Goal:
1. remove all leftover semantic leakage after color picker migration
Required deletions:
1. remove `toolWindow` from `UIEditorPanelDescriptor`
2. remove color-picker-specific detached-panel request plumbing
3. remove utility inference from `UIEditorDetachedWindowPolicy`
4. remove any remaining workspace special cases that exist only for the old color picker path
Success criteria:
1. workspace code no longer knows the color picker exists
2. utility code no longer depends on panel registry membership
3. no root-content-based inference is needed to determine window kind
## 9. Files Expected to Change
This refactor is architecture-level and will necessarily touch a broad slice.
### Shell / Host Layer
1. `new_editor/app/Platform/Win32/EditorWindow.h`
2. `new_editor/app/Platform/Win32/EditorWindow.cpp`
3. `new_editor/app/Platform/Win32/EditorWindowRuntimeController.h`
4. `new_editor/app/Platform/Win32/EditorWindowRuntimeController.cpp`
5. `new_editor/app/Platform/Win32/WindowManager/EditorWindowHostRuntime.h`
6. `new_editor/app/Platform/Win32/WindowManager/EditorWindowHostRuntime.cpp`
7. `new_editor/app/Platform/Win32/WindowManager/EditorWindowManager.cpp`
8. `new_editor/app/Platform/Win32/WindowManager/EditorWindowMessageDispatcher.cpp`
### Workspace Layer
1. `new_editor/app/Platform/Win32/WindowManager/EditorWindowWorkspaceCoordinator.h`
2. `new_editor/app/Platform/Win32/WindowManager/EditorWindowWorkspaceCoordinator.cpp`
3. `new_editor/app/Platform/Win32/EditorWindowFrameOrchestrator.cpp`
4. `new_editor/app/Platform/Win32/EditorWindowTransferRequests.h`
5. `new_editor/src/Workspace/UIEditorWindowWorkspaceController.cpp`
6. `new_editor/src/Workspace/UIEditorDetachedWindowPolicy.cpp`
### Utility Layer
1. new utility window coordinator files
2. new utility window descriptor / request files
3. new color picker utility content files
4. possibly a host-level coordinator that routes shell instances to workspace or utility content
### Color Picker / Feature Layer
1. `new_editor/app/State/EditorColorPickerToolState.h`
2. `new_editor/app/State/EditorColorPickerToolState.cpp`
3. `new_editor/app/Features/ColorPicker/ColorPickerPanel.h`
4. `new_editor/app/Features/ColorPicker/ColorPickerPanel.cpp`
5. `new_editor/app/Composition/EditorShellAssetBuilder.cpp`
6. `new_editor/app/Composition/EditorShellHostedPanelCoordinator.cpp`
7. `new_editor/app/Composition/EditorShellRuntime.h`
8. `new_editor/app/Composition/EditorShellRuntime.cpp`
9. `new_editor/app/Composition/EditorShellDrawComposer.cpp`
## 10. Verification Matrix
This refactor is not done unless all of the following are checked.
### Workspace Regression Checks
1. main window render / input / title behavior is intact
2. panel detach still creates detached workspace windows
3. detached workspace windows can re-dock
4. cross-window tab drag-drop still works
5. workspace close path still closes detached workspace windows correctly
6. workspace layout persistence still excludes utility windows
### Utility Window Checks
1. opening color picker creates or focuses a utility window, not a detached panel window
2. color picker cannot be docked into the main window
3. color picker cannot be merged by tab drag
4. color picker does not appear in workspace visible-panel lists
5. color picker close does not mutate workspace layout
6. color picker reuse policy is correct:
- single-instance reuse or explicit multi-instance rule, but not accidental workspace reuse
### Cross-Boundary Checks
1. inspector -> color picker color synchronization still works
2. closing the main app tears down utility windows correctly
3. focus changes between workspace and utility windows do not break input capture
4. utility windows do not accidentally receive workspace drop preview / transfer state
### Build Checks
1. `XCUIEditorApp` Debug build passes
2. `XCUIEditorApp` Release build passes if used by the team
## 11. Completion Criteria
This refactor is complete only when all of these are true:
1. no utility window is represented as a workspace panel
2. `EditorWindow` shell no longer requires `UIEditorWorkspaceController`
3. workspace and utility windows share only native shell infrastructure
4. `toolWindow` is not the long-term carrier of utility-window semantics
5. the color picker opens through utility window coordination, not workspace mutation
6. workspace code can be reasoned about without referencing utility windows
7. utility windows can be reasoned about without referencing dock / transfer semantics
## 12. Final Statement
The correct fix is not:
1. a new bypass for the color picker
2. another flag on panel descriptors
3. another branch inside workspace mutation
The correct fix is to restore the missing architectural boundary:
1. native shell is shared
2. workspace semantics are isolated
3. utility semantics are isolated
Only after this split is in place will the color picker stop being a fake detached panel and become a real standalone editor window type.