Files
XCEngine/docs/plan/XCUI_Phase_Status_2026-04-05.md

26 KiB

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 UIInputEvents, 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 -> hosted surface image present through the current shell adapter path.
  • 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.
  • XCUI Demo and LayoutLab panel input now also flows through an explicit IXCUIInputSnapshotSource seam, so panel/runtime code no longer reads ImGuiIO / ImGui::IsKeyPressed / ImGui::IsMouseClicked directly when the shell wants to use an ImGui adapter.
  • new_editor now also has an explicit ImGuiXCUIInputSnapshotSource adapter, keeping ImGui-specific input capture in the host adapter layer instead of inside panel/runtime update code.
  • 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.
  • XCUIShellChromeState hosted-preview mode naming is now backend-neutral (HostedPresenter / NativeOffscreen) instead of encoding ImGui into the XCUI shell model.
  • XCNewEditor builds successfully to build/new_editor/bin/Debug/XCNewEditor.exe.

Current gap:

  • The shell is still ImGui-hosted.
  • Hosted-preview compatibility presentation still depends on an ImGui-specific inline draw target binding when the native queued surface path is disabled.
  • The panel-canvas seam still only has an ImGui adapter today; a native panel/shell host still needs to replace it before the editor shell can stop depending on ImGui host chrome.
  • 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_panel_tests: 3/3
  • new_editor_xcui_demo_runtime_tests: 12/12
  • new_editor_xcui_input_bridge_tests: 4/4
  • new_editor_imgui_xcui_input_adapter_tests: 2/2
  • 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: 20/20
  • 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: 11/11
  • new_editor_xcui_panel_canvas_host_tests: 4/4
  • new_editor_imgui_xcui_panel_canvas_host_tests: 1/1
  • new_editor_xcui_layout_lab_panel_tests: 3/3
  • 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.
  • XCUIDemoPanel and XCUILayoutLabPanel no longer create an ImGui hosted-preview presenter or ImGui panel canvas host implicitly; default construction now stays on null/explicitly injected backends until the outer shell binds a concrete host adapter.
  • Application now binds ImGuiXCUIPanelCanvasHost explicitly at the shell composition root, so the current ImGui panel host path is visible as a host-layer decision instead of a panel-layer fallback.
  • XCUIDemoPanel and XCUILayoutLabPanel no longer read ImGui input directly; both now consume an injected IXCUIInputSnapshotSource, and the new ImGuiXCUIInputSnapshotSource keeps the current ImGui-backed input path isolated behind an explicit adapter.
  • 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.
  • XCUIShellChromeState hosted-preview modes were renamed away from LegacyImGui, so XCUI shell state no longer treats ImGui as the generic fallback concept.
  • 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.
  • Hosted-preview compatibility presentation 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, virtualization, and broader focus/multi-selection behavior.
  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.