From 9bad86eccf8debe1e2a385e75db1aa17e4f781a9 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sun, 5 Apr 2026 16:26:29 +0800 Subject: [PATCH] Hide ImGui compat factories behind legacy XCUI interop --- docs/plan/XCUI_Phase_Status_2026-04-05.md | 37 +++++---- .../src/XCUIBackend/IEditorHostCompositor.h | 2 - .../src/XCUIBackend/IWindowUICompositor.h | 1 - .../src/XCUIBackend/ImGuiWindowUICompositor.h | 6 +- .../XCUIBackend/LegacyImGuiHostInterop.cpp | 11 +-- .../src/XCUIBackend/LegacyImGuiHostInterop.h | 5 ++ .../src/XCUIBackend/XCUIEditorFontSetup.cpp | 15 +++- .../src/XCUIBackend/XCUIEditorFontSetup.h | 5 +- .../XCUIStandaloneTextAtlasProvider.h | 1 - .../test_imgui_window_ui_compositor.cpp | 1 + .../test_legacy_imgui_host_interop.cpp | 78 +++++++++++++++++++ 11 files changed, 127 insertions(+), 35 deletions(-) create mode 100644 tests/NewEditor/test_legacy_imgui_host_interop.cpp diff --git a/docs/plan/XCUI_Phase_Status_2026-04-05.md b/docs/plan/XCUI_Phase_Status_2026-04-05.md index 61355eed..34006698 100644 --- a/docs/plan/XCUI_Phase_Status_2026-04-05.md +++ b/docs/plan/XCUI_Phase_Status_2026-04-05.md @@ -32,11 +32,16 @@ Old `editor` replacement is explicitly out of scope for this phase. - `Application.cpp` no longer directly includes ``, even though the compatibility host path is still compiled into `new_editor` - The `new_editor` build now also has an explicit compatibility-source slice: - legacy ImGui shell sources and vendored ImGui backend sources are now grouped into a dedicated compatibility static library instead of being compiled directly as part of the main `XCNewEditor` source list - - the main `XCNewEditor` target no longer compiles those legacy sources directly, but it still carries `${IMGUI_SOURCE_DIR}` / `${IMGUI_SOURCE_DIR}/backends` on its private include surface and still reaches `` through older editor bridge headers such as `editor/src/UI/ImGuiBackendBridge.h` -- The next checkpoint to append after this file should be the main-target header-isolation milestone: +- The main-target header-isolation milestone is now also landed in `new_editor`: - `XCNewEditor` drops direct `${IMGUI_SOURCE_DIR}` / `${IMGUI_SOURCE_DIR}/backends` include-directory requirements + - the generated `XCNewEditor.vcxproj` no longer advertises `imgui-src` or `editor/src` on the default include surface - the default native compile path no longer reaches `` through `editor/src/UI/ImGuiBackendBridge.h` or equivalent bridge headers - `XCNewEditorImGuiCompat` remains the explicit compatibility-only consumer of legacy ImGui headers and backend sources +- The compositor/font compat boundary is now tighter as well: + - `IWindowUICompositor.h` and `IEditorHostCompositor.h` no longer declare `CreateImGui*` factories on the generic XCUI seam + - ImGui factory entry points now live only in `LegacyImGuiHostInterop.h` + - `XCUIEditorFontSetup.h` no longer exposes `ImFont` / `ImFontAtlas`; the public API is now a current-context font bootstrap helper + - `XCUIStandaloneTextAtlasProvider.h` no longer depends on the ImGui-oriented font bootstrap header - Old `editor` replacement remains deferred; all active execution still stays inside XCUI shared code and `new_editor`. ## Three-Layer Status @@ -131,7 +136,7 @@ Current gap: Current gap: - The default shell host is now native, but the legacy ImGui shell and panel path still exists as a compatibility host and is still compiled into `new_editor`. -- The default native shell path is now split away from direct `ImGui::*` calls at the translation-unit/source-list level, but the main `XCNewEditor` target still keeps ImGui on its header/include surface through `target_include_directories(...)` and older editor bridge headers. +- The default native shell path is now split away from direct `ImGui::*` calls at the main-target header/include level, but the legacy ImGui shell and panel implementations still remain compiled behind `XCNewEditorImGuiCompat`. - The native shell currently proves direct runtime composition, but its shell chrome is still a bespoke `Application`-side layout rather than a fully shared XCUI-authored editor shell document. - Editor-specialized widgets are still incomplete at the shared-module level: the authored prototypes exist, but virtualization, multi-selection/focus traversal, toolbar/menu chrome, menu interaction widgets, and icon-atlas widgets are not yet extracted into reusable XCUI modules. - The default native text path now uses a standalone Windows/GDI atlas through `XCUIStandaloneTextAtlasProvider`, but that provider still lives inside `new_editor` and is not yet promoted into a shared/cross-platform text subsystem. @@ -147,6 +152,7 @@ Current gap: - `new_editor_xcui_rhi_render_backend_tests`: `5/5` - `new_editor_xcui_standalone_text_atlas_provider_tests`: `6/6` - `new_editor_xcui_hosted_preview_presenter_tests`: `20/20` +- `new_editor_legacy_imgui_host_interop_tests`: `4/4` - `new_editor_imgui_window_ui_compositor_tests`: `7/7` - `new_editor_native_window_ui_compositor_tests`: `8/8` - `new_editor_xcui_editor_command_router_tests`: `5/5` @@ -237,6 +243,7 @@ Current gap: - `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 legacy compatibility seam now also has dedicated regression coverage around `LegacyImGuiHostInterop`, covering compat factory wiring, ImGui input-capture forwarding, legacy font bootstrap, and the disabled demo-window path. - 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: @@ -292,14 +299,18 @@ Current gap: - `new_editor` build composition is now split into main/native and compatibility slices: - the main `XCNewEditor` target no longer compiles legacy ImGui shell/panel/backend source files directly - legacy ImGui shell/panel/backend sources plus vendored ImGui sources now build behind a dedicated compatibility static library that the main executable links - - the remaining build-side cleanup is narrower now: `XCNewEditor` still carries ImGui include directories and older editor bridge headers on its direct compile surface, so the next checkpoint is header/include isolation rather than another source-list split + - the main target no longer carries ImGui include directories or older editor bridge headers on its direct compile surface; those remain confined to the compatibility slice +- The default XCUI compile seam is now also cleaner: + - generic compositor headers no longer advertise compat-only `CreateImGui*` factories + - compat-only host/window compositor factories now live behind `LegacyImGuiHostInterop.*` + - public font bootstrap headers no longer leak `ImFont` / `ImFontAtlas` types into generic XCUI consumers ## 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. -- `Application.cpp` is now split away from direct `ImGui::*` calls, but `XCNewEditor` still depends on ImGui at the header/include level because `new_editor/CMakeLists.txt` still adds `${IMGUI_SOURCE_DIR}` / `${IMGUI_SOURCE_DIR}/backends` to the main target and the editor bridge chain still reaches `editor/src/UI/ImGuiBackendBridge.h` -> ``. -- `XCNewEditorImGuiCompat` is now the compatibility-source slice, but the main executable still links it unconditionally and has not yet reduced its direct include surface to a compat-free default-path baseline. +- `XCNewEditor` no longer depends on ImGui at the default-path header/include level, but `XCNewEditorImGuiCompat` is still linked into the executable and still houses the legacy shell/panel path. +- Legacy panel implementations such as `XCUIDemoPanel` / `XCUILayoutLabPanel` still render as ImGui windows inside the compatibility slice, so editor-layer behavior is not yet fully carried by XCUI-native shell composition. - The default native text path now owns its atlas without ImGui, but the provider is still Windows-only and remains trapped inside `new_editor` instead of a shared/cross-platform text layer. - 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. @@ -307,12 +318,12 @@ Current gap: ## Execution-Plan Alignment - Against `XCUI完整架构设计与执行计划.md`, current `new_editor` progress should be treated as an early `Phase 8` foothold rather than full `Milestone E` completion: - - landed: `NativeWindowUICompositor`, native shell packet composition, native hosted-preview publication, XCUI-owned texture registrations, native panel surface-image presentation, standalone native text-atlas ownership inside `new_editor`, legacy `Application` TU split, and `XCNewEditorImGuiCompat` - - not yet landed: main-target header/include isolation from ImGui, promotion of the native text-atlas path into a shared/cross-platform text subsystem, shared XCUI-authored editor shell chrome + - landed: `NativeWindowUICompositor`, native shell packet composition, native hosted-preview publication, XCUI-owned texture registrations, native panel surface-image presentation, standalone native text-atlas ownership inside `new_editor`, legacy `Application` TU split, `XCNewEditorImGuiCompat`, main-target header/include isolation from ImGui, and compat-only factory/font seam tightening + - not yet landed: promotion of the native text-atlas path into a shared/cross-platform text subsystem, shared XCUI-authored editor shell chrome, and retirement of legacy ImGui-window panel implementations - That means the next de-ImGui push should not keep centering on hosted-preview publication; that milestone is now effectively closed for the default native shell path. - The real remaining default-path blockers are: - - remove the main `XCNewEditor` target's direct ImGui header dependency by cutting the `D3D12WindowRenderer` / `ImGuiBackendBridge` include chain off the default compile path - - stop treating ImGui include paths as part of the default `new_editor` compilation surface, leaving them only on the compatibility slice + - move editor-layer behavior off the legacy ImGui-window panel implementations and into native/XCUI shell composition + - keep compat-only factories/types from drifting back into generic XCUI seams while the compatibility slice remains linked - harden and promote `XCUIStandaloneTextAtlasProvider` / editor font bootstrap into a shared native text subsystem - move native shell chrome out of bespoke `Application` layout code and into a shared XCUI shell model or authored shell document @@ -320,7 +331,7 @@ Current gap: 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. Finish the remaining main-target de-ImGui cleanup first: remove direct ImGui header/include requirements from `XCNewEditor`, keep ImGui confined to `XCNewEditorImGuiCompat`, and append the next checkpoint once the default native compile path no longer reaches ``. -4. Promote the current standalone native text/font path out of `new_editor`, harden its atlas invalidation/caching contract, and remove the remaining default-path ImGui text/bootstrap ownership around compatibility-only code. -5. Promote the native shell chrome and card layout out of bespoke `Application` code into a shared XCUI/editor-layer shell model or authored shell document. +3. Push the remaining de-ImGui cleanup from build/header isolation into behavior isolation: keep ImGui confined to `XCNewEditorImGuiCompat`, keep generic XCUI seams free of compat-only factories/types, and keep shrinking the legacy shell boundary. +4. Promote the current standalone native text/font path out of `new_editor`, harden its atlas invalidation/caching contract, and keep the remaining font/bootstrap ownership compatibility-only. +5. Promote the native shell chrome and card layout out of bespoke `Application` code into a shared XCUI/editor-layer shell model or authored shell document, then retire the remaining ImGui-window panel path. 6. Continue phased validation, commit, push, and plan refresh after each stable batch. diff --git a/new_editor/src/XCUIBackend/IEditorHostCompositor.h b/new_editor/src/XCUIBackend/IEditorHostCompositor.h index 4b933fa5..ff4b8ded 100644 --- a/new_editor/src/XCUIBackend/IEditorHostCompositor.h +++ b/new_editor/src/XCUIBackend/IEditorHostCompositor.h @@ -61,8 +61,6 @@ public: virtual void FreeTextureDescriptor(const UITextureRegistration& registration) = 0; }; -std::unique_ptr CreateImGuiHostCompositor(); - } // namespace XCUIBackend } // namespace Editor } // namespace XCEngine diff --git a/new_editor/src/XCUIBackend/IWindowUICompositor.h b/new_editor/src/XCUIBackend/IWindowUICompositor.h index 92c7474b..0a5a9a5f 100644 --- a/new_editor/src/XCUIBackend/IWindowUICompositor.h +++ b/new_editor/src/XCUIBackend/IWindowUICompositor.h @@ -61,7 +61,6 @@ public: virtual void FreeTextureDescriptor(const UITextureRegistration& registration) = 0; }; -std::unique_ptr CreateImGuiWindowUICompositor(); std::unique_ptr CreateNativeWindowUICompositor(); } // namespace XCUIBackend diff --git a/new_editor/src/XCUIBackend/ImGuiWindowUICompositor.h b/new_editor/src/XCUIBackend/ImGuiWindowUICompositor.h index 47f66f37..148cc8c5 100644 --- a/new_editor/src/XCUIBackend/ImGuiWindowUICompositor.h +++ b/new_editor/src/XCUIBackend/ImGuiWindowUICompositor.h @@ -13,7 +13,7 @@ namespace XCUIBackend { class ImGuiWindowUICompositor final : public IWindowUICompositor { public: explicit ImGuiWindowUICompositor( - std::unique_ptr hostCompositor = CreateImGuiHostCompositor()) + std::unique_ptr hostCompositor) : m_hostCompositor(std::move(hostCompositor)) { } @@ -79,10 +79,6 @@ private: std::unique_ptr m_hostCompositor; }; -inline std::unique_ptr CreateImGuiWindowUICompositor() { - return std::make_unique(); -} - } // namespace XCUIBackend } // namespace Editor } // namespace XCEngine diff --git a/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.cpp b/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.cpp index b9931041..7c179801 100644 --- a/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.cpp +++ b/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.cpp @@ -12,14 +12,11 @@ namespace Editor { namespace XCUIBackend { bool ConfigureLegacyImGuiHostFonts() { - ImGuiIO& io = ImGui::GetIO(); - ImFont* uiFont = nullptr; - if (!BuildDefaultXCUIEditorFontAtlas(*io.Fonts, uiFont)) { - return false; - } + return ConfigureDefaultXCUIEditorFontsForCurrentContext(); +} - io.FontDefault = uiFont; - return io.FontDefault != nullptr; +std::unique_ptr CreateImGuiWindowUICompositor() { + return std::make_unique(CreateImGuiHostCompositor()); } std::unique_ptr CreateLegacyImGuiWindowUICompositor() { diff --git a/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.h b/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.h index 0a2aa36f..7a828feb 100644 --- a/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.h +++ b/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.h @@ -1,5 +1,6 @@ #pragma once +#include "XCUIBackend/IEditorHostCompositor.h" #include "XCUIBackend/XCUIInputBridge.h" #include "XCUIBackend/IWindowUICompositor.h" #include "XCUIBackend/XCUIHostedPreviewPresenter.h" @@ -13,6 +14,10 @@ namespace XCUIBackend { bool ConfigureLegacyImGuiHostFonts(); +std::unique_ptr CreateImGuiHostCompositor(); + +std::unique_ptr CreateImGuiWindowUICompositor(); + std::unique_ptr CreateLegacyImGuiWindowUICompositor(); std::unique_ptr CreateLegacyImGuiHostedPreviewPresenter(); diff --git a/new_editor/src/XCUIBackend/XCUIEditorFontSetup.cpp b/new_editor/src/XCUIBackend/XCUIEditorFontSetup.cpp index 088f82d0..7a02d46d 100644 --- a/new_editor/src/XCUIBackend/XCUIEditorFontSetup.cpp +++ b/new_editor/src/XCUIBackend/XCUIEditorFontSetup.cpp @@ -12,8 +12,6 @@ constexpr float kUiFontSize = 18.0f; constexpr const char* kPrimaryUiFontPath = "C:/Windows/Fonts/segoeui.ttf"; constexpr const char* kChineseFallbackFontPath = "C:/Windows/Fonts/msyh.ttc"; -} // namespace - bool BuildDefaultXCUIEditorFontAtlas(::ImFontAtlas& atlas, ::ImFont*& outDefaultFont) { outDefaultFont = nullptr; atlas.Clear(); @@ -55,6 +53,19 @@ bool BuildDefaultXCUIEditorFontAtlas(::ImFontAtlas& atlas, ::ImFont*& outDefault return outDefaultFont != nullptr; } +} // namespace + +bool ConfigureDefaultXCUIEditorFontsForCurrentContext() { + ImGuiIO& io = ImGui::GetIO(); + ImFont* uiFont = nullptr; + if (!BuildDefaultXCUIEditorFontAtlas(*io.Fonts, uiFont)) { + return false; + } + + io.FontDefault = uiFont; + return io.FontDefault != nullptr; +} + } // namespace XCUIBackend } // namespace Editor } // namespace XCEngine diff --git a/new_editor/src/XCUIBackend/XCUIEditorFontSetup.h b/new_editor/src/XCUIBackend/XCUIEditorFontSetup.h index 33725a16..290c0a10 100644 --- a/new_editor/src/XCUIBackend/XCUIEditorFontSetup.h +++ b/new_editor/src/XCUIBackend/XCUIEditorFontSetup.h @@ -1,13 +1,10 @@ #pragma once -struct ImFont; -struct ImFontAtlas; - namespace XCEngine { namespace Editor { namespace XCUIBackend { -bool BuildDefaultXCUIEditorFontAtlas(::ImFontAtlas& atlas, ::ImFont*& outDefaultFont); +bool ConfigureDefaultXCUIEditorFontsForCurrentContext(); } // namespace XCUIBackend } // namespace Editor diff --git a/new_editor/src/XCUIBackend/XCUIStandaloneTextAtlasProvider.h b/new_editor/src/XCUIBackend/XCUIStandaloneTextAtlasProvider.h index acf8c19f..fbc997bd 100644 --- a/new_editor/src/XCUIBackend/XCUIStandaloneTextAtlasProvider.h +++ b/new_editor/src/XCUIBackend/XCUIStandaloneTextAtlasProvider.h @@ -1,7 +1,6 @@ #pragma once #include "IXCUITextAtlasProvider.h" -#include "XCUIEditorFontSetup.h" #include diff --git a/tests/NewEditor/test_imgui_window_ui_compositor.cpp b/tests/NewEditor/test_imgui_window_ui_compositor.cpp index 6ee5acf3..592df64b 100644 --- a/tests/NewEditor/test_imgui_window_ui_compositor.cpp +++ b/tests/NewEditor/test_imgui_window_ui_compositor.cpp @@ -1,5 +1,6 @@ #include +#include "Platform/D3D12WindowRenderer.h" #include "XCUIBackend/IEditorHostCompositor.h" #include "XCUIBackend/ImGuiWindowUICompositor.h" #include "XCUIBackend/UITextureRegistration.h" diff --git a/tests/NewEditor/test_legacy_imgui_host_interop.cpp b/tests/NewEditor/test_legacy_imgui_host_interop.cpp new file mode 100644 index 00000000..2e742b45 --- /dev/null +++ b/tests/NewEditor/test_legacy_imgui_host_interop.cpp @@ -0,0 +1,78 @@ +#include + +#include "XCUIBackend/LegacyImGuiHostInterop.h" + +#include + +namespace { + +using XCEngine::Editor::XCUIBackend::ApplyLegacyImGuiHostInputCapture; +using XCEngine::Editor::XCUIBackend::ConfigureLegacyImGuiHostFonts; +using XCEngine::Editor::XCUIBackend::CreateLegacyImGuiHostedPreviewPresenter; +using XCEngine::Editor::XCUIBackend::CreateLegacyImGuiPanelCanvasHost; +using XCEngine::Editor::XCUIBackend::CreateLegacyImGuiWindowUICompositor; +using XCEngine::Editor::XCUIBackend::RenderLegacyImGuiDemoWindow; +using XCEngine::Editor::XCUIBackend::XCUIPanelCanvasHostBackend; +using XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot; + +class ScopedImGuiContext final { +public: + ScopedImGuiContext() { + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + } + + ~ScopedImGuiContext() { + ImGui::DestroyContext(); + } + + ScopedImGuiContext(const ScopedImGuiContext&) = delete; + ScopedImGuiContext& operator=(const ScopedImGuiContext&) = delete; +}; + +TEST(LegacyImGuiHostInteropTest, CreatesLegacyHostAdaptersWithImGuiBackends) { + auto compositor = CreateLegacyImGuiWindowUICompositor(); + auto presenter = CreateLegacyImGuiHostedPreviewPresenter(); + auto canvasHost = CreateLegacyImGuiPanelCanvasHost(); + + ASSERT_NE(compositor, nullptr); + ASSERT_NE(presenter, nullptr); + ASSERT_NE(canvasHost, nullptr); + EXPECT_EQ(canvasHost->GetBackend(), XCUIPanelCanvasHostBackend::ImGui); + + const auto capabilities = canvasHost->GetCapabilities(); + EXPECT_TRUE(capabilities.supportsPointerHitTesting); + EXPECT_TRUE(capabilities.supportsHostedSurfaceImages); + EXPECT_TRUE(capabilities.supportsPrimitiveOverlays); +} + +TEST(LegacyImGuiHostInteropTest, ApplyInputCaptureCopiesImGuiIoFlagsIntoSnapshot) { + ScopedImGuiContext context = {}; + ImGuiIO& io = ImGui::GetIO(); + io.WantCaptureKeyboard = true; + io.WantTextInput = true; + + XCUIInputBridgeFrameSnapshot snapshot = {}; + ApplyLegacyImGuiHostInputCapture(snapshot); + + EXPECT_TRUE(snapshot.wantCaptureKeyboard); + EXPECT_TRUE(snapshot.wantTextInput); +} + +TEST(LegacyImGuiHostInteropTest, ConfigureFontsPopulatesDefaultFont) { + ScopedImGuiContext context = {}; + + ASSERT_TRUE(ConfigureLegacyImGuiHostFonts()); + EXPECT_NE(ImGui::GetIO().FontDefault, nullptr); + EXPECT_GT(ImGui::GetIO().Fonts->Fonts.Size, 0); +} + +TEST(LegacyImGuiHostInteropTest, RenderDemoWindowSkipsWorkWhenVisibilityIsDisabled) { + ScopedImGuiContext context = {}; + bool visible = false; + + EXPECT_FALSE(RenderLegacyImGuiDemoWindow(visible)); + EXPECT_FALSE(visible); +} + +} // namespace