# XCUI Phase Status 2026-04-05 ## Scope Current execution stays inside the XCUI module and `new_editor`. Old `editor` replacement is explicitly out of scope for this phase. ## Latest Checkpoint - Phase 1 sandbox batch committed and pushed as `67a28bd` (`Add XCUI new editor sandbox phase 1`). - Phase 2 common/runtime batch committed and pushed as `ade5be3` (`Add XCUI runtime screen layer and demo textarea`). - Phase 3 has now produced a stable mixed batch across common/runtime/editor: - schema document definition data is now retained on `UIDocumentModel` and round-trips through the UI artifact path - schema self-definition validation is now stricter around enum/document-only metadata combinations - engine runtime coverage was tightened again around `UISystem` and concrete document-host rendering - `LayoutLab` continues as the editor widget proving ground for tree/list/property-section style controls - the demo sandbox and editor bridge APIs were tightened again without touching the old editor replacement scope - `new_editor` now has an explicit two-step window compositor seam through `IWindowUICompositor` / `ImGuiWindowUICompositor`, layered on top of `IEditorHostCompositor` / `ImGuiHostCompositor` - Old `editor` replacement remains deferred; all active execution still stays inside XCUI shared code and `new_editor`. ## Three-Layer Status ### 1. Common Core - `UI::DrawData`, input event types, focus routing, style/theme resolution are in active use. - `UIDocumentCompiler` is buildable again after repairing the duplicated schema helper regression introduced by overlapping schema work. - `UIDocumentModel` / `UIDocumentResource` now retain schema definition metadata explicitly, including memory accounting and `UISchema` accessors. - `.xcschema` round-trip coverage is now present through compile, loader, artifact write, and artifact read paths. - Build-system hardening for MSVC/PDB output paths has started in root CMake, `engine/CMakeLists.txt`, `new_editor/CMakeLists.txt`, and `tests/NewEditor/CMakeLists.txt`. - Shared engine-side XCUI runtime scaffolding is now present under `engine/include/XCEngine/UI/Runtime` and `engine/src/UI/Runtime`. - Shared engine-side `UIDocumentScreenHost` now compiles `.xcui` / `.xctheme` screen documents into a runtime-facing document host path instead of leaving all document ownership in `new_editor`. - Shared text-editing primitives now live under `engine/include/XCEngine/UI/Text` and `engine/src/UI/Text`, so UTF-8 caret movement, line splitting, and multiline navigation are no longer trapped inside `XCUI Demo`. - Shared text-input controller/state now also lives under `engine/include/XCEngine/UI/Text` and `engine/src/UI/Text`, so character insertion, backspace/delete, submit, and multiline key handling no longer need to be reimplemented per host. - Shared editor collection primitive classification and metric helpers now also live under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets`, covering the current `ScrollView` / `TreeView` / `ListView` / `PropertySection` / `FieldRow` prototype taxonomy. - Shared single-selection state now also lives under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets` as `UISelectionModel`, so collection-style widget selection no longer has to stay private to `LayoutLab`. - Shared expansion state now also lives under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets` as `UIExpansionModel`, so collapsible tree/property-style widget state no longer has to stay private to `LayoutLab`. - Shared keyboard-navigation state now also lives under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets` as `UIKeyboardNavigationModel`, so list/tree/property-style widgets can share current-index, anchor, and home/end/step navigation rules instead of re-rolling them per sandbox. - Shared property-edit session state now also lives under `engine/include/XCEngine/UI/Widgets` and `engine/src/UI/Widgets` as `UIPropertyEditModel`, so editor-facing field rows can reuse begin/edit/commit/cancel transaction state instead of baking that directly into sandbox runtimes. - Core regression coverage now includes `UIContext`, layout, style, runtime screen player/system, and real document-host tests through `core_ui_tests`. Current gap: - Minimal schema self-definition support is landed, including consistency checks for enum/document-only schema metadata, but schema-driven validation for `.xcui` / `.xctheme` instances is still not implemented. - Shared widget/runtime instantiation is still thin and mostly editor-side. - Common widget primitives are still incomplete: shared text-input presentation/composition on top of the new text controller, multi-selection/focus-traversal/virtualized collection state on top of the new editor-primitive helpers, and native image/source-rect level APIs. ### 2. Runtime/Game Layer - The main concrete progress here is that the retained-mode demo runtime now supports a real `TextField` input path with UTF-8 text entry and backspace handling. - The demo runtime has moved past single-line input: multiline `TextArea` behavior is now covered in the sandbox testbed. - Engine-side runtime ownership is no longer zero: `UIScreenPlayer`, `UIDocumentScreenHost`, and `UISystem` now define a shared runtime contract for loading a screen document, ticking it with input, and collecting `UI::UIDrawData`. - `UISystem` now supports layered screen composition semantics: stacked screen players, top-interactive input routing, and modal layers that block lower screens. - `UIScreenStackController::ReplaceTop` now preserves the previous top screen if the replacement screen fails to load, so runtime menu flows do not silently drop their active layer on bad assets. - `SceneRuntime` now owns a dedicated `UISceneRuntimeContext`, so game/runtime code has a first-class place to configure viewport/focus, queue `UIInputEvent`s, drive `UISystem` each `Update`, and inspect the latest UI frame result. - Runtime screen emission now also carries concrete button text in the shared document host path instead of silently dropping button labels. - `UISystemFrameResult` now also preserves viewport rect, submitted input count, frame delta, and focus state, and both `UISystem` and `UISceneRuntimeContext` now expose `ConsumeLastFrame()` so runtime/game hosts can drain the last retained frame packet without copying editor-side concepts into the shared layer. - `UIScreenPlayer` now also exposes `ConsumeLastFrame()`, so player/system/context all share the same consume-vs-borrow frame ownership semantics in the runtime layer. Current gap: - Runtime UI is now wired into `SceneRuntime`, but render submission is still limited to producing `UIDrawData`; there is no game-view/runtime presenter path that automatically draws those frames yet. - The runtime widget library is still shallow and missing the editor-grade controls that will later be shared downward. ### 3. Editor Layer - `new_editor` remains the isolated XCUI sandbox. - Native hosted preview is working as `RHI offscreen surface -> ImGui shell texture embed`. - Hosted preview surface descriptors now stay on XCUI-owned value types (`UITextureHandle`, `UIPoint`, `UIRect`) instead of exposing ImGui texture/UV types through the generic preview contract. - `XCUI Demo` remains the long-lived effect and behavior testbed. - `XCUI Demo` now covers both single-line and multiline text authoring behavior, including click caret placement, delete/backspace, tab indentation, and optional text-area line numbers. - `XCUI Demo` now consumes the shared `UITextInputController` path for text editing instead of carrying a private key-handling state machine. - `LayoutLab` now includes a `ScrollView` prototype and a more editor-like three-column authored layout. - `LayoutLab` now also covers editor-facing widget prototypes: `TreeView`, `TreeItem`, `ListView`, `ListItem`, `PropertySection`, and `FieldRow`. - `LayoutLab` now consumes the shared `UIEditorCollectionPrimitives` helper layer for collection-widget tag classification, clipping flags, and default metric resolution instead of keeping that taxonomy private to the sandbox runtime. - `LayoutLab` now also consumes the shared `UISelectionModel` for click-selection persistence across collection-style widgets, and the diagnostics panel now exposes both hovered and selected element ids. - `LayoutLab` now also consumes the shared `UIExpansionModel` for tree expansion and property-section collapse, with reserved property headers, disclosure glyphs, and persisted click-toggle behavior in the sandbox runtime. - `new_editor` now also has an isolated `XCUIEditorCommandRouter` model with shortcut matching, enable predicates, and direct command invocation semantics covered by dedicated tests, ready for shell-frame integration. - `XCUI Demo` now exports pending per-frame command ids through `DrainPendingCommandIds()`, so editor-side hosts have a clean seam for observing demo/runtime command traffic without parsing draw data. - `XCUI Demo` and `LayoutLab` panel canvases are now being pulled behind a dedicated `IXCUIPanelCanvasHost` seam, so canvas surface presentation, hover/focus fallback state, and overlay draw hooks no longer have to stay hard-coded inside each ImGui panel implementation. - The panel-canvas seam now also exposes explicit backend/capability metadata and a minimal `NullXCUIPanelCanvasHost`, so non-ImGui host paths have a concrete placeholder backend instead of relying on an implicit ImGui default. - Panel diagnostics were expanded to clearly separate preview/runtime/input state and native vs legacy paths. - The editor bridge layer now has smoke coverage for swapchain after-UI rendering hooks and SRV-backed ImGui texture descriptor registration. - `Application` no longer owns the ImGui backend directly; window presentation now routes through `IWindowUICompositor` with an `ImGuiWindowUICompositor` implementation, which currently delegates to `IEditorHostCompositor` / `ImGuiHostCompositor`. - Hosted preview offscreen surfaces now keep compositor-returned `UITextureRegistration` / `UITextureHandle` data inside `Application` instead of storing `ImTextureID` directly. - The generic hosted-preview presenter contract no longer owns `ImGuiTransitionBackend`; the ImGui presenter now sits in a separate `ImGuiXCUIHostedPreviewPresenter` header while the native queue/surface registry remains XCUI-generic. - The generic hosted-preview frame contract no longer carries an ImGui draw-list pointer; the legacy ImGui presenter resolves its inline draw target from the active ImGui window context instead of pushing that type through the XCUI contract. - The legacy ImGui hosted-preview presenter now also accepts an explicit draw-target binding object, so presenter-side `ImGui::GetWindowDrawList()` lookup is no longer hard-coded inside the generic presenter path and can stay isolated behind the ImGui adapter layer. - `Application` shell menu toggles and global shortcuts now route through `XCUIEditorCommandRouter` instead of directly mutating shell booleans from menu callbacks, giving the editor layer a real command-routing seam. - `LayoutLab` runtime now consumes the shared `UIKeyboardNavigationModel` for abstract list/tree/property navigation actions (`previous/next/home/end/collapse/expand`), so keyboard collection traversal rules are no longer trapped in sandbox-local state. - `LayoutLab` panel input now also maps concrete arrow/home/end keys into those shared navigation actions, so keyboard traversal is reachable from the sandbox UI instead of staying runtime-only. - `XCUIDemoRuntime` now bridges pointer activation, text-edit commands, and shortcut-triggered commands through a unified command path, and `DrainPendingCommandIds()` now preserves mixed pointer/text/shortcut ordering. - `new_editor` now also has a pure `XCUIShellChromeState` model covering panel visibility, hosted-preview mode, and shell-level view toggles without depending on ImGui, `Application`, or the old editor. - `XCNewEditor` builds successfully to `build/new_editor/bin/Debug/XCNewEditor.exe`. Current gap: - The shell is still ImGui-hosted. - Legacy hosted preview still depends on an ImGui-specific inline draw target binding for presentation. - The new panel-canvas seam still only has an ImGui adapter today; a native panel/shell host still needs to replace it before ImGui can stop being the default editor host path. - Editor-specialized widgets are still incomplete at the shared-module level: the authored prototypes exist, but virtualization, multi-selection/focus traversal, toolbar/menu chrome, and icon-atlas widgets are not yet extracted into reusable XCUI modules, and broader editor-host keybinding plus full shell-state adoption are still only partially integrated. ## Validated This Phase - `new_editor_xcui_demo_runtime_tests`: `12/12` - `new_editor_xcui_layout_lab_runtime_tests`: `12/12` - `new_editor_xcui_rhi_command_compiler_tests`: `6/6` - `new_editor_xcui_rhi_render_backend_tests`: `5/5` - `new_editor_xcui_hosted_preview_presenter_tests`: `17/17` - `new_editor_imgui_window_ui_compositor_tests`: `7/7` - `new_editor_xcui_editor_command_router_tests`: `5/5` - `new_editor_application_shell_command_bindings_tests`: `6/6` - `new_editor_xcui_shell_chrome_state_tests`: `8/8` - `new_editor_xcui_panel_canvas_host_tests`: `2/2` - `new_editor_imgui_xcui_panel_canvas_host_tests`: `1/1` - `new_editor_xcui_layout_lab_panel_tests`: `2/2` - `XCNewEditor` Debug target builds successfully - `core_ui_tests`: `52 total` (`50` passed, `2` skipped because `KeyCode::Delete` currently aliases `Backspace`) - `scene_tests`: `68/68` - `core_ui_style_tests`: `5/5` - `ui_resource_tests`: `11/11` - `editor_tests` targeted bridge smoke: `3/3` ## Landed This Phase - Demo runtime `TextField` with UTF-8 text insertion, caret state, and backspace. - Demo runtime multiline `TextArea` path in the sandbox and test coverage for caret movement / multiline input. - Common-core `UITextEditing` extraction now owns UTF-8 offset stepping, codepoint counting, line splitting, and vertical caret motion with dedicated `core_ui_tests` coverage. - Common-core `UITextInputController` extraction now owns per-field text state, character insertion, enter-submit, and multiline keyboard editing behavior with dedicated `core_ui_tests` coverage. - Common-core `UIEditorCollectionPrimitives` extraction now owns the editor collection tag taxonomy and default metric resolution used by current `LayoutLab` widget prototypes, with dedicated `core_ui_tests` coverage. - Common-core `UISelectionModel` extraction now owns reusable single-selection state for collection-style widgets, with dedicated `core_ui_tests` coverage. - Common-core `UIExpansionModel` extraction now owns reusable expansion/collapse state for tree/property-style widgets, with dedicated `core_ui_tests` coverage. - Common-core `UIKeyboardNavigationModel` extraction now owns reusable current-index/anchor navigation state for collection-style widgets, with dedicated `core_ui_tests` coverage. - Common-core `UIPropertyEditModel` extraction now owns reusable property-field edit session state, including staged values and commit/cancel behavior, with dedicated `core_ui_tests` coverage. - Runtime frame ownership was tightened again: - `UIScreenPlayer::ConsumeLastFrame()` now exposes consume-style packet ownership at the player layer - `UISystemFrameResult` now carries viewport rect, submitted input event count, frame delta, and focus state - `UISystem::ConsumeLastFrame()` moves the retained packet out of the runtime layer - `UISceneRuntimeContext::ConsumeLastFrame()` forwards the same shared runtime seam upward - Demo runtime text editing was extended with: - click-to-place caret - `Delete` support - `Tab` / `Shift+Tab` indentation for text areas - optional text-area line-number gutter rendering - Demo authored resources updated to exercise the input field. - LayoutLab `ScrollView` prototype with clipping and hover rejection outside clipped content. - LayoutLab editor-widget prototypes for tree/list/property-style sections with dedicated runtime coverage. - LayoutLab click-selection now persists through the shared `UISelectionModel`, including selected-state diagnostics and reusable visual selection feedback on cards, collection rows, and field rows. - LayoutLab tree expansion and property-section collapse now persist through the shared `UIExpansionModel`, including reserved property headers, disclosure glyphs, and runtime coverage for collapsed/expanded visibility. - `XCUIDemoRuntime` now exposes `DrainPendingCommandIds()` so hosts can observe emitted runtime commands in order across pointer/text interactions without scraping UI text or draw-command payloads. - `XCUIDemoRuntime` command recording was tightened so pointer activation, text editing, and shortcut-triggered commands now share one bridge path and preserve mixed ordering in `DrainPendingCommandIds()`. - Schema document support extended with: - retained `UISchemaDefinition` data on `UIDocumentModel` - artifact schema version bump for UI documents - loader/resource accessors and memory accounting - schema compile/load/artifact regression coverage - schema consistency rules for: - `allowedValues` only on `enum` - `documentKind` / `restrictDocumentKind` only on `document` - explicit `documentKind` required when `restrictDocumentKind=true` - Engine runtime layer added: - `UIScreenPlayer` - `UIDocumentScreenHost` - `UISceneRuntimeContext` - `UIScreenStackController` - `UISystem` - layered screen composition and modal blocking semantics - Runtime/game integration scaffolding now includes reusable `HUD/menu/modal` stack helpers on top of `UISystem`. - `UIScreenStackController` replacement now rolls back safely on failure instead of popping the active top layer first. - `SceneRuntime` now exposes XCUI runtime ownership directly: - `GetUISystem()` - `GetUIScreenStackController()` - `GetLastUIFrame()` - `SetUIViewportRect(...)` - `SetUIFocused(...)` - `QueueUIInputEvent(...)` - `ClearQueuedUIInputEvents()` - automatic `UISystem` ticking during `SceneRuntime::Update(...)` - Runtime document-host draw emission now preserves button labels for shared screen rendering. - RHI image path improvements: - clipped image UV adjustment - mirrored image UV preservation - external texture binding reuse - per-batch scissor application - Editor bridge helpers now expose: - an `afterUiRender` swapchain callback hook in `D3D12WindowRenderer` - SRV-view based texture descriptor registration in `ImGuiBackendBridge` - smoke tests for window renderer, ImGui backend bridge, and console sink registration - `new_editor` host presentation now has a first-class compositor seam: - `IWindowUICompositor` - `ImGuiWindowUICompositor` - `IEditorHostCompositor` - `ImGuiHostCompositor` - `Application` frame/present flow routed through the compositor instead of direct `m_imguiBackend` ownership - The window-level XCUI compositor seam now also has a dedicated regression target around `ImGuiWindowUICompositor`, covering initialization, render-frame ordering, Win32 message forwarding, texture registration forwarding, and shutdown safety. - The window compositor and hosted-preview seams gained more edge-case coverage around no-UI render passes, compositor re-initialization/rebinding, partial logical-size fallback, and descriptor reuse for repeated queued-frame keys. - `new_editor` now has a dedicated `XCUIEditorCommandRouter` test target covering command registration, replacement, enable predicates, accelerator matching, and policy gates around focus/keyboard capture/text input. - `Application` now integrates `XCUIEditorCommandRouter` into the shell itself: - `View` menu items invoke routed commands instead of directly mutating shell state - shell-level shortcuts now flow from `XCUIWin32InputSource` through `XCUIInputBridge` into command matching - hosted-preview mode toggles still trigger presenter reconfiguration through the routed command bindings - `new_editor` panel canvas ownership is now being split behind `IXCUIPanelCanvasHost`, with an `ImGuiXCUIPanelCanvasHost` adapter carrying the legacy path so panel code stops directly owning `ImGui::Image` / `ImGui::InvisibleButton` / draw-list preview plumbing. - `new_editor` now also has a pure `XCUIShellChromeState` model with dedicated tests, covering shell panel visibility, hosted-preview mode, and shell view toggles without depending on ImGui or `Application`. - `XCUIShellChromeState` now also exposes effective hosted-preview state helpers and shell view-toggle command-id helpers, so shell routing code no longer has to manually combine enablement and requested preview mode. - The panel-canvas seam now has dedicated null/imgui backend coverage, including explicit backend/capability reporting and a non-ImGui placeholder host path for future native shell adoption. - `SceneRuntime` layered XCUI routing now has dedicated regression coverage for: - top-interactive layer input ownership - blocking/modal layer suppression of lower layers - hidden top-layer pass-through back to visible underlying layers - Shared `UITextInputController` coverage now includes more caret-boundary / modifier branches; the remaining `Delete` distinction stays blocked on `KeyCode::Delete` and `KeyCode::Backspace` still sharing the same enum value. - Window compositor texture registration now also flows back into `Application` as XCUI-owned `UITextureRegistration` / `UITextureHandle` data instead of exposing raw `ImTextureID` there. - Hosted preview contracts were tightened again: - generic preview surface metadata stays on XCUI-owned value types - `ImGuiTransitionBackend` moved behind `ImGuiXCUIHostedPreviewPresenter` - generic preview frame submission no longer carries an ImGui draw-list pointer - the ImGui presenter now resolves inline draw targets through an explicit ImGui-only binding seam - panel/runtime callers still preserve the same legacy and native-preview behavior - `LayoutLab` now resolves editor collection widget taxonomy and metrics through shared `UIEditorCollectionPrimitives` helpers instead of duplicating the same tag and metric rules inside the sandbox runtime. - `LayoutLab` runtime now consumes shared keyboard-navigation semantics for list/tree/property traversal, while the remaining panel-level key mapping is tracked as an editor-host integration gap rather than a runtime gap. - `LayoutLab` panel now maps concrete arrow/home/end keys into the shared navigation model, with dedicated panel-level coverage proving that the sandbox UI can actually drive the runtime navigation seam end-to-end. - `new_editor` panel/shell diagnostics improvements for hosted preview state. - XCUI asset document loading changed to prefer direct source compilation before `ResourceManager` fallback for the sandbox path, fixing the LayoutLab crash. - `UIDocumentCompiler.cpp` repaired enough to restore full local builds after the duplicated schema-helper regression. - MSVC debug build hardening was tightened again so large parallel `engine` rebuilds stop tripping over compile-PDB contention. ## Phase Risks Still Open - Schema instance validation is still open beyond `.xcschema` self-definition and artifact round-trip coverage. - `ScrollView` is still authored/static; no wheel-driven scrolling or virtualization yet. - `Image` widgets still do not have source-rect/atlas-subregion level API in the high-level draw command model. - Editor shell still depends on ImGui as host chrome. - Legacy hosted preview still depends on an ImGui-only inline presenter path when not using the queued native surface path. - Editor widget coverage is still prototype-driven inside `LayoutLab`; it has not yet been promoted into a full reusable shared widget/runtime layer with command routing, virtualization, and property-edit transactions. ## Next Phase 1. Expand runtime/game-layer ownership from the current `SceneRuntime` UI context into scene-declared HUD/menu bootstrapping, draw submission, and higher-level runtime UI policies. 2. Promote the current editor-facing widget prototypes out of authored `LayoutLab` content and into reusable XCUI widget/runtime modules, then continue with toolbar/menu chrome, shell-state adoption, and panel-level keyboard/navigation input plumbing. 3. Add a native XCUI host compositor on the existing window-level compositor seam so `new_editor` can present without going through ImGui-owned draw data. 4. Replace the remaining ImGui-only fallback seams in hosted preview and panel canvas hosting with native host implementations so ImGui can become compatibility-only instead of the default shell path. 5. Continue phased validation, commit, push, and plan refresh after each stable batch.