diff --git a/docs/plan/XCUI_Phase_Status_2026-04-05.md b/docs/plan/XCUI_Phase_Status_2026-04-05.md index 37622565..83061da2 100644 --- a/docs/plan/XCUI_Phase_Status_2026-04-05.md +++ b/docs/plan/XCUI_Phase_Status_2026-04-05.md @@ -27,6 +27,9 @@ Old `editor` replacement is explicitly out of scope for this phase. - The default native text path is now also de-ImGuiized inside `new_editor`: - `XCUIStandaloneTextAtlasProvider` now builds and owns its atlas through a Windows/GDI raster path instead of using ImGui font-atlas internals - the standalone atlas provider now exposes both `RGBA32` and `Alpha8` views, supports non-nominal size resolution, lazily adds non-prebaked BMP glyphs, and falls back to `?` when a glyph cannot be rasterized +- The default native shell path now also has a cleaner translation-unit seam: + - legacy ImGui shell chrome / HUD rendering now lives in a dedicated legacy-only `Application` translation unit instead of keeping direct `ImGui::*` calls inside the main native host TU + - `Application.cpp` no longer directly includes ``, even though the compatibility host path is still compiled into `new_editor` - Old `editor` replacement remains deferred; all active execution still stays inside XCUI shared code and `new_editor`. ## Three-Layer Status @@ -276,6 +279,9 @@ Current gap: - atlas ownership now stays inside a standalone provider implementation built on Windows/GDI glyph rasterization - default editor atlas prewarms the current supported nominal sizes, exposes both `RGBA32` and `Alpha8` atlas views, lazily inserts non-prebaked BMP glyphs, and falls back to `?` for unrasterizable codepoints - standalone atlas coverage now includes reset/rebuild, non-nominal size resolution, lazy glyph insertion/fallback behavior, and smoke use without any ImGui context +- Legacy shell chrome / HUD rendering is now split out of the main `Application.cpp` translation unit: + - the direct `ImGui::*` shell rendering path now lives in a dedicated legacy-only `Application` implementation file + - the main `Application.cpp` native host path no longer directly includes ``, reducing default-path compile-time coupling before the larger compat-target split ## Phase Risks Still Open diff --git a/new_editor/CMakeLists.txt b/new_editor/CMakeLists.txt index b71b0eaa..1d0e5355 100644 --- a/new_editor/CMakeLists.txt +++ b/new_editor/CMakeLists.txt @@ -44,13 +44,16 @@ endif() set(NEW_EDITOR_SOURCES src/main.cpp src/Application.cpp + src/ApplicationLegacyImGui.cpp src/panels/Panel.cpp src/panels/XCUIDemoPanel.cpp src/panels/XCUILayoutLabPanel.cpp src/Rendering/MainWindowBackdropPass.cpp src/Rendering/MainWindowNativeBackdropRenderer.cpp src/XCUIBackend/ImGuiXCUIInputAdapter.cpp + src/XCUIBackend/LegacyImGuiHostInterop.cpp src/XCUIBackend/ImGuiHostCompositor.cpp + src/XCUIBackend/NativeWindowUICompositor.cpp src/XCUIBackend/XCUIEditorFontSetup.cpp src/XCUIBackend/XCUIAssetDocumentSource.cpp src/XCUIBackend/XCUIEditorCommandRouter.cpp diff --git a/new_editor/src/Application.cpp b/new_editor/src/Application.cpp index 2775487d..a3d18648 100644 --- a/new_editor/src/Application.cpp +++ b/new_editor/src/Application.cpp @@ -1,14 +1,10 @@ #include "Application.h" -#include "XCUIBackend/ImGuiXCUIPanelCanvasHost.h" -#include "XCUIBackend/ImGuiXCUIHostedPreviewPresenter.h" -#include "XCUIBackend/ImGuiWindowUICompositor.h" +#include "XCUIBackend/LegacyImGuiHostInterop.h" #include "XCUIBackend/NativeWindowUICompositor.h" #include #include -#include - #include #include #include @@ -41,13 +37,6 @@ void ShutdownAndDelete(ResourceType*& resource) { resource = nullptr; } -void ConfigureFonts() { - ImGuiIO& io = ImGui::GetIO(); - ImFont* uiFont = nullptr; - ::XCEngine::Editor::XCUIBackend::BuildDefaultXCUIEditorFontAtlas(*io.Fonts, uiFont); - io.FontDefault = uiFont; -} - const char* GetHostedPreviewPathLabel(bool nativeRequested, bool nativePresenterBound) { if (nativeRequested && nativePresenterBound) { return "native queued offscreen surface"; @@ -193,13 +182,34 @@ Application::CreateHostedPreviewPresenter(bool nativePreview) { m_hostedPreviewSurfaceRegistry); } - return ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIHostedPreviewPresenter(); + return ::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiHostedPreviewPresenter(); } bool Application::IsNativeWindowHostEnabled() const { return m_windowHostMode == WindowHostMode::NativeXCUI; } +void Application::InitializePanelsForActiveWindowHost() { + if (IsNativeWindowHostEnabled()) { + InitializeNativeShell(); + return; + } + + InitializeLegacyImGuiPanels(); + ConfigureHostedPreviewPresenters(); +} + +void Application::InitializeLegacyImGuiPanels() { + m_demoPanel = std::make_unique( + &m_xcuiInputSource, + CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)), + ::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiPanelCanvasHost()); + m_layoutLabPanel = std::make_unique( + &m_xcuiInputSource, + CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab)), + ::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiPanelCanvasHost()); +} + void Application::InitializeNativeShell() { m_nativeActivePanel = m_shellChromeState.IsPanelVisible(ShellPanelId::XCUIDemo) ? ShellPanelId::XCUIDemo @@ -323,9 +333,7 @@ Application::DispatchShellShortcuts( const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot& snapshot) { ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot shellSnapshot = snapshot; if (!IsNativeWindowHostEnabled()) { - ImGuiIO& io = ImGui::GetIO(); - shellSnapshot.wantCaptureKeyboard = io.WantCaptureKeyboard; - shellSnapshot.wantTextInput = io.WantTextInput; + ::XCEngine::Editor::XCUIBackend::ApplyLegacyImGuiHostInputCapture(shellSnapshot); } if (!m_shellInputBridge.HasBaseline()) { @@ -419,19 +427,7 @@ int Application::Run(HINSTANCE instance, int nCmdShow) { } InitializeWindowCompositor(); - if (IsNativeWindowHostEnabled()) { - InitializeNativeShell(); - } else { - m_demoPanel = std::make_unique( - &m_xcuiInputSource, - CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)), - ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost()); - m_layoutLabPanel = std::make_unique( - &m_xcuiInputSource, - CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab)), - ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost()); - ConfigureHostedPreviewPresenters(); - } + InitializePanelsForActiveWindowHost(); m_shellInputBridge.Reset(); ConfigureShellCommandRouter(); m_running = true; @@ -565,12 +561,12 @@ bool Application::InitializeRenderer() { void Application::InitializeWindowCompositor() { m_windowCompositor = IsNativeWindowHostEnabled() ? ::XCEngine::Editor::XCUIBackend::CreateNativeWindowUICompositor() - : ::XCEngine::Editor::XCUIBackend::CreateImGuiWindowUICompositor(); + : ::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiWindowUICompositor(); if (m_windowCompositor != nullptr) { m_windowCompositor->Initialize( m_hwnd, m_windowRenderer, - []() { ConfigureFonts(); }); + []() { (void)::XCEngine::Editor::XCUIBackend::ConfigureLegacyImGuiHostFonts(); }); } } @@ -1001,7 +997,7 @@ bool Application::RenderHostedPreviewOffscreenSurface( m_hostedPreviewSurfaceRegistry.TryGetSurfaceImage( panelState->previewDebugName.data(), hostedSurfaceImage); - const NativeHostedPreviewConsumption previewConsumption = + const Application::NativeHostedPreviewConsumption previewConsumption = Application::ResolveNativeHostedPreviewConsumption( nativeHostedPreview, hasHostedSurfaceDescriptor, @@ -1168,7 +1164,7 @@ bool Application::RenderHostedPreviewOffscreenSurface( m_hostedPreviewSurfaceRegistry.TryGetSurfaceImage( panelState->previewDebugName.data(), hostedSurfaceImage); - const NativeHostedPreviewConsumption previewConsumption = + const Application::NativeHostedPreviewConsumption previewConsumption = Application::ResolveNativeHostedPreviewConsumption( nativeHostedPreview, hasHostedSurfaceDescriptor, @@ -1309,83 +1305,6 @@ bool Application::RenderHostedPreviewOffscreenSurface( return composedDrawData; } -void Application::FrameLegacyImGuiHost() { - Application::BeginHostedPreviewFrameLifecycle( - m_hostedPreviewQueue, - m_hostedPreviewSurfaceRegistry); - SyncHostedPreviewSurfaces(); - if (m_windowCompositor == nullptr) { - m_xcuiInputSource.ClearFrameTransients(); - return; - } - - m_windowCompositor->RenderFrame( - kClearColor, - [this]() { - ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeCaptureOptions options = {}; - options.timestampNanoseconds = MakeFrameTimestampNanoseconds(); - options.windowFocused = m_xcuiInputSource.IsWindowFocused(); - const auto shellSnapshot = m_xcuiInputSource.CaptureSnapshot(options); - DispatchShellShortcuts(shellSnapshot); - RenderShellChrome(); - if (m_demoPanel) { - m_demoPanel->RenderIfVisible(); - } - if (m_layoutLabPanel) { - m_layoutLabPanel->RenderIfVisible(); - } - bool showImGuiDemoWindow = IsShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow); - if (showImGuiDemoWindow) { - ImGui::ShowDemoWindow(&showImGuiDemoWindow); - SetShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow, showImGuiDemoWindow); - } - - SyncShellChromePanelStateFromPanels(); - SyncHostedPreviewSurfaces(); - }, - [this]( - const ::XCEngine::Rendering::RenderContext& renderContext, - const ::XCEngine::Rendering::RenderSurface& surface) { - RenderQueuedHostedPreviews(renderContext, surface); - - if (!IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop) && - !IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) { - return; - } - - MainWindowNativeBackdropRenderer::FrameState frameState = {}; - frameState.elapsedSeconds = static_cast( - std::chrono::duration(std::chrono::steady_clock::now() - m_startTime).count()); - frameState.pulseAccent = IsShellViewToggleEnabled(ShellViewToggleId::PulseAccent); - frameState.drawBackdrop = IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop); - if (IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) { - const float width = static_cast(surface.GetWidth()); - const float height = static_cast(surface.GetHeight()); - const float horizontalMargin = (std::min)(width * 0.14f, 128.0f); - const float topMargin = (std::min)(height * 0.15f, 132.0f); - const float bottomMargin = (std::min)(height * 0.12f, 96.0f); - - ::XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState overlayInput = {}; - overlayInput.canvasRect = ::XCEngine::UI::UIRect( - horizontalMargin, - topMargin, - (std::max)(0.0f, width - horizontalMargin * 2.0f), - (std::max)(0.0f, height - topMargin - bottomMargin)); - overlayInput.pointerPosition = m_xcuiInputSource.GetPointerPosition(); - overlayInput.pointerInside = - overlayInput.pointerPosition.x >= overlayInput.canvasRect.x && - overlayInput.pointerPosition.y >= overlayInput.canvasRect.y && - overlayInput.pointerPosition.x <= overlayInput.canvasRect.x + overlayInput.canvasRect.width && - overlayInput.pointerPosition.y <= overlayInput.canvasRect.y + overlayInput.canvasRect.height; - const auto& overlayFrame = m_nativeOverlayRuntime.Update(overlayInput); - frameState.overlayDrawData = &overlayFrame.drawData; - } - m_nativeBackdropRenderer.Render(renderContext, surface, frameState); - }); - - m_xcuiInputSource.ClearFrameTransients(); -} - void Application::FrameNativeXCUIHost() { Application::BeginHostedPreviewFrameLifecycle( m_hostedPreviewQueue, @@ -1427,303 +1346,6 @@ void Application::FrameNativeXCUIHost() { m_xcuiInputSource.ClearFrameTransients(); } -void Application::RenderShellChrome() { - SyncShellChromePanelStateFromPanels(); - - ImGuiViewport* viewport = ImGui::GetMainViewport(); - if (viewport == nullptr) { - return; - } - - ImGuiWindowFlags windowFlags = - ImGuiWindowFlags_NoDocking | - ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoCollapse | - ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoBringToFrontOnFocus | - ImGuiWindowFlags_NoNavFocus | - ImGuiWindowFlags_MenuBar; - - ImGui::SetNextWindowPos(viewport->WorkPos); - ImGui::SetNextWindowSize(viewport->WorkSize); - ImGui::SetNextWindowViewport(viewport->ID); - ImGui::SetNextWindowBgAlpha(0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - const bool opened = ImGui::Begin("XCNewEditorShell", nullptr, windowFlags); - ImGui::PopStyleVar(3); - - if (opened) { - if (ImGui::BeginMenuBar()) { - if (ImGui::BeginMenu("View")) { - const auto drawCommandMenuItem = - [this](const char* label, const char* shortcut, bool selected, const char* commandId) { - const bool enabled = m_shellCommandRouter.IsCommandEnabled(commandId); - if (ImGui::MenuItem(label, shortcut, selected, enabled)) { - m_shellCommandRouter.InvokeCommand(commandId); - } - }; - - drawCommandMenuItem( - "XCUI Demo", - "Ctrl+1", - TryGetShellPanelState(ShellPanelId::XCUIDemo) != nullptr && - TryGetShellPanelState(ShellPanelId::XCUIDemo)->visible, - ShellCommandIds::ToggleXCUIDemoPanel); - drawCommandMenuItem( - "XCUI Layout Lab", - "Ctrl+2", - TryGetShellPanelState(ShellPanelId::XCUILayoutLab) != nullptr && - TryGetShellPanelState(ShellPanelId::XCUILayoutLab)->visible, - ShellCommandIds::ToggleXCUILayoutLabPanel); - drawCommandMenuItem( - "ImGui Demo", - "Ctrl+3", - IsShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow), - ShellCommandIds::ToggleImGuiDemoWindow); - ImGui::Separator(); - drawCommandMenuItem( - "Native Backdrop", - "Ctrl+Shift+B", - IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop), - ShellCommandIds::ToggleNativeBackdrop); - drawCommandMenuItem( - "Pulse Accent", - "Ctrl+Shift+P", - IsShellViewToggleEnabled(ShellViewToggleId::PulseAccent), - ShellCommandIds::TogglePulseAccent); - drawCommandMenuItem( - "Native XCUI Overlay", - "Ctrl+Shift+O", - IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay), - ShellCommandIds::ToggleNativeXCUIOverlay); - drawCommandMenuItem( - "Hosted Preview HUD", - "Ctrl+Shift+H", - IsShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud), - ShellCommandIds::ToggleHostedPreviewHud); - drawCommandMenuItem( - "Native Demo Panel Preview", - "Ctrl+Alt+1", - IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo), - ShellCommandIds::ToggleNativeDemoPanelPreview); - drawCommandMenuItem( - "Native Layout Lab Preview", - "Ctrl+Alt+2", - IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab), - ShellCommandIds::ToggleNativeLayoutLabPreview); - ImGui::EndMenu(); - } - - ImGui::SeparatorText("XCUI Sandbox"); - const MainWindowNativeBackdropRenderer::OverlayStats& nativeOverlayStats = - m_nativeBackdropRenderer.GetLastOverlayStats(); - const ::XCEngine::Editor::XCUIBackend::XCUILayoutLabFrameStats& overlayFrameStats = - m_nativeOverlayRuntime.GetFrameResult().stats; - const ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewDrainStats& hostedPreviewStats = - m_hostedPreviewQueue.GetLastDrainStats(); - if (IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) { - ImGui::TextDisabled( - "Native XCUI overlay: %s | runtime %zu cmds (%zu fill, %zu outline, %zu text, %zu image, clips %zu/%zu)", - overlayFrameStats.nativeOverlayReady ? "preflight OK" : "preflight issues", - overlayFrameStats.commandCount, - overlayFrameStats.filledRectCommandCount, - overlayFrameStats.rectOutlineCommandCount, - overlayFrameStats.textCommandCount, - overlayFrameStats.imageCommandCount, - overlayFrameStats.clipPushCommandCount, - overlayFrameStats.clipPopCommandCount); - ImGui::TextDisabled( - "%s | supported %zu | unsupported %zu | prev native pass %zu cmds, %zu rendered, %zu skipped", - overlayFrameStats.nativeOverlayStatusMessage.empty() - ? "Overlay diagnostics unavailable" - : overlayFrameStats.nativeOverlayStatusMessage.c_str(), - overlayFrameStats.nativeSupportedCommandCount, - overlayFrameStats.nativeUnsupportedCommandCount, - nativeOverlayStats.commandCount, - nativeOverlayStats.renderedCommandCount, - nativeOverlayStats.skippedCommandCount); - } else { - ImGui::TextDisabled( - IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop) - ? "Transition backend + runtime diagnostics + native backbuffer pass" - : "Transition backend + runtime diagnostics"); - } - ImGui::TextDisabled( - "Hosted preview queue: %zu frames | queued %zu cmds | rendered %zu cmds | skipped %zu cmds", - hostedPreviewStats.queuedFrameCount, - hostedPreviewStats.queuedCommandCount, - hostedPreviewStats.renderedCommandCount, - hostedPreviewStats.skippedCommandCount); - std::size_t allocatedSurfaceCount = 0u; - std::size_t readySurfaceCount = 0u; - for (const HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) { - if (previewSurface.colorTexture != nullptr || previewSurface.colorView != nullptr) { - ++allocatedSurfaceCount; - } - if (previewSurface.IsReady()) { - ++readySurfaceCount; - } - } - ImGui::TextDisabled( - "Hosted surfaces: %zu registry entries | %zu allocated | %zu ready", - m_hostedPreviewSurfaceRegistry.GetDescriptors().size(), - allocatedSurfaceCount, - readySurfaceCount); - if (m_demoPanel != nullptr) { - ImGui::TextDisabled( - "XCUI Demo preview: %s", - IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo) - ? "native offscreen preview surface" - : "hosted presenter"); - } - if (m_layoutLabPanel != nullptr) { - ImGui::TextDisabled( - "Layout Lab preview: %s", - IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab) - ? "native offscreen preview surface" - : "hosted presenter"); - } - ImGui::EndMenuBar(); - } - - ImGui::DockSpace( - ImGui::GetID("XCNewEditorDockSpace"), - ImVec2(0.0f, 0.0f), - ImGuiDockNodeFlags_PassthruCentralNode); - } - - ImGui::End(); - - if (IsShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud)) { - RenderHostedPreviewHud(); - } -} - -void Application::RenderHostedPreviewHud() { - ImGuiViewport* viewport = ImGui::GetMainViewport(); - if (viewport == nullptr) { - return; - } - - const ShellPanelChromeState* demoState = TryGetShellPanelState(ShellPanelId::XCUIDemo); - const ShellPanelChromeState* layoutLabState = TryGetShellPanelState(ShellPanelId::XCUILayoutLab); - const HostedPreviewPanelDiagnostics demoDiagnostics = BuildHostedPreviewPanelDiagnostics( - demoState != nullptr ? demoState->previewDebugName.data() : "XCUI Demo", - demoState != nullptr ? demoState->previewDebugSource.data() : "new_editor.panels.xcui_demo", - demoState != nullptr && demoState->visible, - demoState != nullptr && demoState->hostedPreviewEnabled, - IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo), - m_demoPanel != nullptr && m_demoPanel->IsUsingNativeHostedPreview(), - m_demoPanel != nullptr - ? m_demoPanel->GetLastPreviewStats() - : ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats{}); - const HostedPreviewPanelDiagnostics layoutLabDiagnostics = BuildHostedPreviewPanelDiagnostics( - layoutLabState != nullptr ? layoutLabState->previewDebugName.data() : "XCUI Layout Lab", - layoutLabState != nullptr ? layoutLabState->previewDebugSource.data() : "new_editor.panels.xcui_layout_lab", - layoutLabState != nullptr && layoutLabState->visible, - layoutLabState != nullptr && layoutLabState->hostedPreviewEnabled, - IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab), - m_layoutLabPanel != nullptr && m_layoutLabPanel->IsUsingNativeHostedPreview(), - m_layoutLabPanel != nullptr - ? m_layoutLabPanel->GetLastPreviewStats() - : ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats{}); - - std::size_t allocatedSurfaceCount = 0u; - std::size_t readySurfaceCount = 0u; - for (const HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) { - if (previewSurface.colorTexture != nullptr || previewSurface.colorView != nullptr) { - ++allocatedSurfaceCount; - } - if (previewSurface.IsReady()) { - ++readySurfaceCount; - } - } - - const ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewDrainStats& drainStats = - m_hostedPreviewQueue.GetLastDrainStats(); - - ImGuiWindowFlags windowFlags = - ImGuiWindowFlags_NoDocking | - ImGuiWindowFlags_NoSavedSettings | - ImGuiWindowFlags_AlwaysAutoResize | - ImGuiWindowFlags_NoFocusOnAppearing | - ImGuiWindowFlags_NoNav; - - ImGui::SetNextWindowViewport(viewport->ID); - ImGui::SetNextWindowPos( - ImVec2(viewport->WorkPos.x + viewport->WorkSize.x - 18.0f, viewport->WorkPos.y + 42.0f), - ImGuiCond_Always, - ImVec2(1.0f, 0.0f)); - ImGui::SetNextWindowBgAlpha(0.9f); - if (!ImGui::Begin("XCUI Hosted Preview HUD", nullptr, windowFlags)) { - ImGui::End(); - return; - } - - ImGui::TextUnformatted("XCUI Hosted Preview"); - ImGui::Text( - "Registry %zu | surfaces %zu/%zu ready | last native drain %zu rendered, %zu skipped", - m_hostedPreviewSurfaceRegistry.GetDescriptors().size(), - readySurfaceCount, - allocatedSurfaceCount, - drainStats.renderedFrameCount, - drainStats.skippedFrameCount); - ImGui::Separator(); - - const auto drawPanelRow = [](const HostedPreviewPanelDiagnostics& diagnostics) { - const char* const pathLabel = - GetHostedPreviewPathLabel(diagnostics.nativeRequested, diagnostics.nativePresenterBound); - const char* const stateLabel = GetHostedPreviewStateLabel( - diagnostics.hostedPreviewEnabled, - diagnostics.nativePresenterBound, - diagnostics.presentedThisFrame, - diagnostics.queuedToNativePassThisFrame, - diagnostics.surfaceImageAvailable, - diagnostics.surfaceAllocated, - diagnostics.surfaceReady, - diagnostics.descriptorAvailable); - - ImGui::Text( - "%s [%s] %s", - diagnostics.debugName.c_str(), - diagnostics.visible ? "visible" : "hidden", - stateLabel); - ImGui::TextDisabled("%s", pathLabel); - ImGui::Text( - "source %s | submit %zu lists / %zu cmds | flush %zu lists / %zu cmds", - diagnostics.debugSource.empty() ? "n/a" : diagnostics.debugSource.c_str(), - diagnostics.submittedDrawListCount, - diagnostics.submittedCommandCount, - diagnostics.flushedDrawListCount, - diagnostics.flushedCommandCount); - if (diagnostics.nativePresenterBound) { - ImGui::Text( - "surface %ux%u | logical %.0f x %.0f | descriptor %s | image %s | submit->native %s", - diagnostics.surfaceWidth, - diagnostics.surfaceHeight, - diagnostics.logicalWidth, - diagnostics.logicalHeight, - diagnostics.descriptorAvailable ? "yes" : "no", - diagnostics.surfaceImageAvailable ? "yes" : "no", - diagnostics.queuedToNativePassThisFrame ? "yes" : "no"); - } else { - ImGui::Text( - "legacy present %s | cached native surface %s", - diagnostics.presentedThisFrame ? "yes" : "no", - (diagnostics.surfaceAllocated || diagnostics.surfaceImageAvailable) ? "retained" : "none"); - } - }; - - drawPanelRow(demoDiagnostics); - ImGui::Separator(); - drawPanelRow(layoutLabDiagnostics); - - ImGui::End(); -} - void Application::RenderQueuedHostedPreviews( const ::XCEngine::Rendering::RenderContext& renderContext, const ::XCEngine::Rendering::RenderSurface& surface) { diff --git a/new_editor/src/Application.h b/new_editor/src/Application.h index 551b5e34..e7aa610c 100644 --- a/new_editor/src/Application.h +++ b/new_editor/src/Application.h @@ -420,6 +420,9 @@ private: ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta DispatchShellShortcuts( const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot& snapshot); bool IsNativeWindowHostEnabled() const; + void InitializePanelsForActiveWindowHost(); + void InitializeLegacyImGuiPanels(); + void RenderLegacyImGuiUiFrame(); ::XCEngine::UI::UIDrawData BuildNativeShellDrawData( const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot& shellSnapshot, const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta& shellFrameDelta); diff --git a/new_editor/src/ApplicationLegacyImGui.cpp b/new_editor/src/ApplicationLegacyImGui.cpp new file mode 100644 index 00000000..aa277d65 --- /dev/null +++ b/new_editor/src/ApplicationLegacyImGui.cpp @@ -0,0 +1,446 @@ +#include "Application.h" + +#include "XCUIBackend/LegacyImGuiHostInterop.h" + +#include + +#include +#include + +namespace XCEngine { +namespace NewEditor { + +namespace { + +constexpr float kLegacyHostClearColor[4] = { 0.08f, 0.09f, 0.11f, 1.0f }; + +std::uint64_t MakeLegacyHostFrameTimestampNanoseconds() { + return static_cast( + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count()); +} + +const char* GetHostedPreviewPathLabel(bool nativeRequested, bool nativePresenterBound) { + if (nativeRequested && nativePresenterBound) { + return "native queued offscreen surface"; + } + if (nativeRequested) { + return "native requested, hosted presenter bound"; + } + if (nativePresenterBound) { + return "hosted presenter requested, native presenter still bound"; + } + return "hosted presenter"; +} + +const char* GetHostedPreviewStateLabel( + bool hostedPreviewEnabled, + bool nativePresenterBound, + bool presentedThisFrame, + bool queuedToNativePassThisFrame, + bool surfaceImageAvailable, + bool surfaceAllocated, + bool surfaceReady, + bool descriptorAvailable) { + if (!hostedPreviewEnabled) { + return "disabled"; + } + + if (nativePresenterBound) { + if (surfaceImageAvailable && surfaceReady) { + return "live"; + } + if (queuedToNativePassThisFrame || surfaceAllocated || descriptorAvailable) { + return "warming"; + } + return "awaiting submit"; + } + + if (presentedThisFrame) { + return "live"; + } + return "idle"; +} + +} // namespace + +void Application::FrameLegacyImGuiHost() { + Application::BeginHostedPreviewFrameLifecycle( + m_hostedPreviewQueue, + m_hostedPreviewSurfaceRegistry); + SyncHostedPreviewSurfaces(); + if (m_windowCompositor == nullptr) { + m_xcuiInputSource.ClearFrameTransients(); + return; + } + + m_windowCompositor->RenderFrame( + kLegacyHostClearColor, + [this]() { RenderLegacyImGuiUiFrame(); }, + [this]( + const ::XCEngine::Rendering::RenderContext& renderContext, + const ::XCEngine::Rendering::RenderSurface& surface) { + RenderQueuedHostedPreviews(renderContext, surface); + + if (!IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop) && + !IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) { + return; + } + + MainWindowNativeBackdropRenderer::FrameState frameState = {}; + frameState.elapsedSeconds = static_cast( + std::chrono::duration(std::chrono::steady_clock::now() - m_startTime).count()); + frameState.pulseAccent = IsShellViewToggleEnabled(ShellViewToggleId::PulseAccent); + frameState.drawBackdrop = IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop); + if (IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) { + const float width = static_cast(surface.GetWidth()); + const float height = static_cast(surface.GetHeight()); + const float horizontalMargin = (std::min)(width * 0.14f, 128.0f); + const float topMargin = (std::min)(height * 0.15f, 132.0f); + const float bottomMargin = (std::min)(height * 0.12f, 96.0f); + + ::XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState overlayInput = {}; + overlayInput.canvasRect = ::XCEngine::UI::UIRect( + horizontalMargin, + topMargin, + (std::max)(0.0f, width - horizontalMargin * 2.0f), + (std::max)(0.0f, height - topMargin - bottomMargin)); + overlayInput.pointerPosition = m_xcuiInputSource.GetPointerPosition(); + overlayInput.pointerInside = + overlayInput.pointerPosition.x >= overlayInput.canvasRect.x && + overlayInput.pointerPosition.y >= overlayInput.canvasRect.y && + overlayInput.pointerPosition.x <= overlayInput.canvasRect.x + overlayInput.canvasRect.width && + overlayInput.pointerPosition.y <= overlayInput.canvasRect.y + overlayInput.canvasRect.height; + const auto& overlayFrame = m_nativeOverlayRuntime.Update(overlayInput); + frameState.overlayDrawData = &overlayFrame.drawData; + } + m_nativeBackdropRenderer.Render(renderContext, surface, frameState); + }); + + m_xcuiInputSource.ClearFrameTransients(); +} + +void Application::RenderLegacyImGuiUiFrame() { + ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeCaptureOptions options = {}; + options.timestampNanoseconds = MakeLegacyHostFrameTimestampNanoseconds(); + options.windowFocused = m_xcuiInputSource.IsWindowFocused(); + const auto shellSnapshot = m_xcuiInputSource.CaptureSnapshot(options); + DispatchShellShortcuts(shellSnapshot); + + RenderShellChrome(); + if (m_demoPanel != nullptr) { + m_demoPanel->RenderIfVisible(); + } + if (m_layoutLabPanel != nullptr) { + m_layoutLabPanel->RenderIfVisible(); + } + + bool showImGuiDemoWindow = IsShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow); + if (::XCEngine::Editor::XCUIBackend::RenderLegacyImGuiDemoWindow(showImGuiDemoWindow)) { + SetShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow, showImGuiDemoWindow); + } + + SyncShellChromePanelStateFromPanels(); + SyncHostedPreviewSurfaces(); +} + +void Application::RenderShellChrome() { + SyncShellChromePanelStateFromPanels(); + + ImGuiViewport* viewport = ImGui::GetMainViewport(); + if (viewport == nullptr) { + return; + } + + ImGuiWindowFlags windowFlags = + ImGuiWindowFlags_NoDocking | + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoBringToFrontOnFocus | + ImGuiWindowFlags_NoNavFocus | + ImGuiWindowFlags_MenuBar; + + ImGui::SetNextWindowPos(viewport->WorkPos); + ImGui::SetNextWindowSize(viewport->WorkSize); + ImGui::SetNextWindowViewport(viewport->ID); + ImGui::SetNextWindowBgAlpha(0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + const bool opened = ImGui::Begin("XCNewEditorShell", nullptr, windowFlags); + ImGui::PopStyleVar(3); + + if (opened) { + if (ImGui::BeginMenuBar()) { + if (ImGui::BeginMenu("View")) { + const auto drawCommandMenuItem = + [this](const char* label, const char* shortcut, bool selected, const char* commandId) { + const bool enabled = m_shellCommandRouter.IsCommandEnabled(commandId); + if (ImGui::MenuItem(label, shortcut, selected, enabled)) { + m_shellCommandRouter.InvokeCommand(commandId); + } + }; + + drawCommandMenuItem( + "XCUI Demo", + "Ctrl+1", + TryGetShellPanelState(ShellPanelId::XCUIDemo) != nullptr && + TryGetShellPanelState(ShellPanelId::XCUIDemo)->visible, + ShellCommandIds::ToggleXCUIDemoPanel); + drawCommandMenuItem( + "XCUI Layout Lab", + "Ctrl+2", + TryGetShellPanelState(ShellPanelId::XCUILayoutLab) != nullptr && + TryGetShellPanelState(ShellPanelId::XCUILayoutLab)->visible, + ShellCommandIds::ToggleXCUILayoutLabPanel); + drawCommandMenuItem( + "ImGui Demo", + "Ctrl+3", + IsShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow), + ShellCommandIds::ToggleImGuiDemoWindow); + ImGui::Separator(); + drawCommandMenuItem( + "Native Backdrop", + "Ctrl+Shift+B", + IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop), + ShellCommandIds::ToggleNativeBackdrop); + drawCommandMenuItem( + "Pulse Accent", + "Ctrl+Shift+P", + IsShellViewToggleEnabled(ShellViewToggleId::PulseAccent), + ShellCommandIds::TogglePulseAccent); + drawCommandMenuItem( + "Native XCUI Overlay", + "Ctrl+Shift+O", + IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay), + ShellCommandIds::ToggleNativeXCUIOverlay); + drawCommandMenuItem( + "Hosted Preview HUD", + "Ctrl+Shift+H", + IsShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud), + ShellCommandIds::ToggleHostedPreviewHud); + drawCommandMenuItem( + "Native Demo Panel Preview", + "Ctrl+Alt+1", + IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo), + ShellCommandIds::ToggleNativeDemoPanelPreview); + drawCommandMenuItem( + "Native Layout Lab Preview", + "Ctrl+Alt+2", + IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab), + ShellCommandIds::ToggleNativeLayoutLabPreview); + ImGui::EndMenu(); + } + + ImGui::SeparatorText("XCUI Sandbox"); + const MainWindowNativeBackdropRenderer::OverlayStats& nativeOverlayStats = + m_nativeBackdropRenderer.GetLastOverlayStats(); + const ::XCEngine::Editor::XCUIBackend::XCUILayoutLabFrameStats& overlayFrameStats = + m_nativeOverlayRuntime.GetFrameResult().stats; + const ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewDrainStats& hostedPreviewStats = + m_hostedPreviewQueue.GetLastDrainStats(); + if (IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) { + ImGui::TextDisabled( + "Native XCUI overlay: %s | runtime %zu cmds (%zu fill, %zu outline, %zu text, %zu image, clips %zu/%zu)", + overlayFrameStats.nativeOverlayReady ? "preflight OK" : "preflight issues", + overlayFrameStats.commandCount, + overlayFrameStats.filledRectCommandCount, + overlayFrameStats.rectOutlineCommandCount, + overlayFrameStats.textCommandCount, + overlayFrameStats.imageCommandCount, + overlayFrameStats.clipPushCommandCount, + overlayFrameStats.clipPopCommandCount); + ImGui::TextDisabled( + "%s | supported %zu | unsupported %zu | prev native pass %zu cmds, %zu rendered, %zu skipped", + overlayFrameStats.nativeOverlayStatusMessage.empty() + ? "Overlay diagnostics unavailable" + : overlayFrameStats.nativeOverlayStatusMessage.c_str(), + overlayFrameStats.nativeSupportedCommandCount, + overlayFrameStats.nativeUnsupportedCommandCount, + nativeOverlayStats.commandCount, + nativeOverlayStats.renderedCommandCount, + nativeOverlayStats.skippedCommandCount); + } else { + ImGui::TextDisabled( + IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop) + ? "Transition backend + runtime diagnostics + native backbuffer pass" + : "Transition backend + runtime diagnostics"); + } + ImGui::TextDisabled( + "Hosted preview queue: %zu frames | queued %zu cmds | rendered %zu cmds | skipped %zu cmds", + hostedPreviewStats.queuedFrameCount, + hostedPreviewStats.queuedCommandCount, + hostedPreviewStats.renderedCommandCount, + hostedPreviewStats.skippedCommandCount); + std::size_t allocatedSurfaceCount = 0u; + std::size_t readySurfaceCount = 0u; + for (const HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) { + if (previewSurface.colorTexture != nullptr || previewSurface.colorView != nullptr) { + ++allocatedSurfaceCount; + } + if (previewSurface.IsReady()) { + ++readySurfaceCount; + } + } + ImGui::TextDisabled( + "Hosted surfaces: %zu registry entries | %zu allocated | %zu ready", + m_hostedPreviewSurfaceRegistry.GetDescriptors().size(), + allocatedSurfaceCount, + readySurfaceCount); + if (m_demoPanel != nullptr) { + ImGui::TextDisabled( + "XCUI Demo preview: %s", + IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo) + ? "native offscreen preview surface" + : "hosted presenter"); + } + if (m_layoutLabPanel != nullptr) { + ImGui::TextDisabled( + "Layout Lab preview: %s", + IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab) + ? "native offscreen preview surface" + : "hosted presenter"); + } + ImGui::EndMenuBar(); + } + + ImGui::DockSpace( + ImGui::GetID("XCNewEditorDockSpace"), + ImVec2(0.0f, 0.0f), + ImGuiDockNodeFlags_PassthruCentralNode); + } + + ImGui::End(); + + if (IsShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud)) { + RenderHostedPreviewHud(); + } +} + +void Application::RenderHostedPreviewHud() { + ImGuiViewport* viewport = ImGui::GetMainViewport(); + if (viewport == nullptr) { + return; + } + + const ShellPanelChromeState* demoState = TryGetShellPanelState(ShellPanelId::XCUIDemo); + const ShellPanelChromeState* layoutLabState = TryGetShellPanelState(ShellPanelId::XCUILayoutLab); + const HostedPreviewPanelDiagnostics demoDiagnostics = BuildHostedPreviewPanelDiagnostics( + demoState != nullptr ? demoState->previewDebugName.data() : "XCUI Demo", + demoState != nullptr ? demoState->previewDebugSource.data() : "new_editor.panels.xcui_demo", + demoState != nullptr && demoState->visible, + demoState != nullptr && demoState->hostedPreviewEnabled, + IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo), + m_demoPanel != nullptr && m_demoPanel->IsUsingNativeHostedPreview(), + m_demoPanel != nullptr + ? m_demoPanel->GetLastPreviewStats() + : ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats{}); + const HostedPreviewPanelDiagnostics layoutLabDiagnostics = BuildHostedPreviewPanelDiagnostics( + layoutLabState != nullptr ? layoutLabState->previewDebugName.data() : "XCUI Layout Lab", + layoutLabState != nullptr ? layoutLabState->previewDebugSource.data() : "new_editor.panels.xcui_layout_lab", + layoutLabState != nullptr && layoutLabState->visible, + layoutLabState != nullptr && layoutLabState->hostedPreviewEnabled, + IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab), + m_layoutLabPanel != nullptr && m_layoutLabPanel->IsUsingNativeHostedPreview(), + m_layoutLabPanel != nullptr + ? m_layoutLabPanel->GetLastPreviewStats() + : ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats{}); + + std::size_t allocatedSurfaceCount = 0u; + std::size_t readySurfaceCount = 0u; + for (const HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) { + if (previewSurface.colorTexture != nullptr || previewSurface.colorView != nullptr) { + ++allocatedSurfaceCount; + } + if (previewSurface.IsReady()) { + ++readySurfaceCount; + } + } + + const ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewDrainStats& drainStats = + m_hostedPreviewQueue.GetLastDrainStats(); + + ImGuiWindowFlags windowFlags = + ImGuiWindowFlags_NoDocking | + ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoFocusOnAppearing | + ImGuiWindowFlags_NoNav; + + ImGui::SetNextWindowViewport(viewport->ID); + ImGui::SetNextWindowPos( + ImVec2(viewport->WorkPos.x + viewport->WorkSize.x - 18.0f, viewport->WorkPos.y + 42.0f), + ImGuiCond_Always, + ImVec2(1.0f, 0.0f)); + ImGui::SetNextWindowBgAlpha(0.9f); + if (!ImGui::Begin("XCUI Hosted Preview HUD", nullptr, windowFlags)) { + ImGui::End(); + return; + } + + ImGui::TextUnformatted("XCUI Hosted Preview"); + ImGui::Text( + "Registry %zu | surfaces %zu/%zu ready | last native drain %zu rendered, %zu skipped", + m_hostedPreviewSurfaceRegistry.GetDescriptors().size(), + readySurfaceCount, + allocatedSurfaceCount, + drainStats.renderedFrameCount, + drainStats.skippedFrameCount); + ImGui::Separator(); + + const auto drawPanelRow = [](const HostedPreviewPanelDiagnostics& diagnostics) { + const char* const pathLabel = + GetHostedPreviewPathLabel(diagnostics.nativeRequested, diagnostics.nativePresenterBound); + const char* const stateLabel = GetHostedPreviewStateLabel( + diagnostics.hostedPreviewEnabled, + diagnostics.nativePresenterBound, + diagnostics.presentedThisFrame, + diagnostics.queuedToNativePassThisFrame, + diagnostics.surfaceImageAvailable, + diagnostics.surfaceAllocated, + diagnostics.surfaceReady, + diagnostics.descriptorAvailable); + + ImGui::Text( + "%s [%s] %s", + diagnostics.debugName.c_str(), + diagnostics.visible ? "visible" : "hidden", + stateLabel); + ImGui::TextDisabled("%s", pathLabel); + ImGui::Text( + "source %s | submit %zu lists / %zu cmds | flush %zu lists / %zu cmds", + diagnostics.debugSource.empty() ? "n/a" : diagnostics.debugSource.c_str(), + diagnostics.submittedDrawListCount, + diagnostics.submittedCommandCount, + diagnostics.flushedDrawListCount, + diagnostics.flushedCommandCount); + if (diagnostics.nativePresenterBound) { + ImGui::Text( + "surface %ux%u | logical %.0f x %.0f | descriptor %s | image %s | submit->native %s", + diagnostics.surfaceWidth, + diagnostics.surfaceHeight, + diagnostics.logicalWidth, + diagnostics.logicalHeight, + diagnostics.descriptorAvailable ? "yes" : "no", + diagnostics.surfaceImageAvailable ? "yes" : "no", + diagnostics.queuedToNativePassThisFrame ? "yes" : "no"); + } else { + ImGui::Text( + "legacy present %s | cached native surface %s", + diagnostics.presentedThisFrame ? "yes" : "no", + (diagnostics.surfaceAllocated || diagnostics.surfaceImageAvailable) ? "retained" : "none"); + } + }; + + drawPanelRow(demoDiagnostics); + ImGui::Separator(); + drawPanelRow(layoutLabDiagnostics); + + ImGui::End(); +} + +} // namespace NewEditor +} // namespace XCEngine diff --git a/new_editor/src/XCUIBackend/IWindowUICompositor.h b/new_editor/src/XCUIBackend/IWindowUICompositor.h index 45738c11..bbe41ed7 100644 --- a/new_editor/src/XCUIBackend/IWindowUICompositor.h +++ b/new_editor/src/XCUIBackend/IWindowUICompositor.h @@ -41,6 +41,7 @@ public: }; std::unique_ptr CreateImGuiWindowUICompositor(); +std::unique_ptr CreateNativeWindowUICompositor(); } // namespace XCUIBackend } // namespace Editor diff --git a/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.cpp b/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.cpp new file mode 100644 index 00000000..b9931041 --- /dev/null +++ b/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.cpp @@ -0,0 +1,54 @@ +#include "XCUIBackend/LegacyImGuiHostInterop.h" + +#include "XCUIBackend/ImGuiXCUIPanelCanvasHost.h" +#include "XCUIBackend/ImGuiXCUIHostedPreviewPresenter.h" +#include "XCUIBackend/ImGuiWindowUICompositor.h" +#include "XCUIBackend/XCUIEditorFontSetup.h" + +#include + +namespace XCEngine { +namespace Editor { +namespace XCUIBackend { + +bool ConfigureLegacyImGuiHostFonts() { + ImGuiIO& io = ImGui::GetIO(); + ImFont* uiFont = nullptr; + if (!BuildDefaultXCUIEditorFontAtlas(*io.Fonts, uiFont)) { + return false; + } + + io.FontDefault = uiFont; + return io.FontDefault != nullptr; +} + +std::unique_ptr CreateLegacyImGuiWindowUICompositor() { + return CreateImGuiWindowUICompositor(); +} + +std::unique_ptr CreateLegacyImGuiHostedPreviewPresenter() { + return CreateImGuiXCUIHostedPreviewPresenter(); +} + +std::unique_ptr CreateLegacyImGuiPanelCanvasHost() { + return CreateImGuiXCUIPanelCanvasHost(); +} + +void ApplyLegacyImGuiHostInputCapture(XCUIInputBridgeFrameSnapshot& snapshot) { + const ImGuiIO& io = ImGui::GetIO(); + snapshot.wantCaptureKeyboard = io.WantCaptureKeyboard; + snapshot.wantTextInput = io.WantTextInput; +} + +bool RenderLegacyImGuiDemoWindow(bool& visible) { + if (!visible) { + return false; + } + + ImGui::ShowDemoWindow(&visible); + return true; +} + +} // namespace XCUIBackend +} // namespace Editor +} // namespace XCEngine diff --git a/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.h b/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.h new file mode 100644 index 00000000..0a2aa36f --- /dev/null +++ b/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.h @@ -0,0 +1,28 @@ +#pragma once + +#include "XCUIBackend/XCUIInputBridge.h" +#include "XCUIBackend/IWindowUICompositor.h" +#include "XCUIBackend/XCUIHostedPreviewPresenter.h" +#include "XCUIBackend/XCUIPanelCanvasHost.h" + +#include + +namespace XCEngine { +namespace Editor { +namespace XCUIBackend { + +bool ConfigureLegacyImGuiHostFonts(); + +std::unique_ptr CreateLegacyImGuiWindowUICompositor(); + +std::unique_ptr CreateLegacyImGuiHostedPreviewPresenter(); + +std::unique_ptr CreateLegacyImGuiPanelCanvasHost(); + +void ApplyLegacyImGuiHostInputCapture(XCUIInputBridgeFrameSnapshot& snapshot); + +bool RenderLegacyImGuiDemoWindow(bool& visible); + +} // namespace XCUIBackend +} // namespace Editor +} // namespace XCEngine diff --git a/tests/NewEditor/test_application_shell_command_bindings.cpp b/tests/NewEditor/test_application_shell_command_bindings.cpp index 2c20aa02..4c424b58 100644 --- a/tests/NewEditor/test_application_shell_command_bindings.cpp +++ b/tests/NewEditor/test_application_shell_command_bindings.cpp @@ -319,4 +319,90 @@ TEST(ApplicationShellCommandBindingsTest, HostedPreviewRegistrationGuardsAllowTe EXPECT_FALSE(Application::HasHostedPreviewPublishedTexture(registration)); } +TEST(ApplicationShellCommandBindingsTest, NativeHostedPreviewConsumptionLeavesHostedPathInDirectAppendMode) { + const auto consumption = Application::ResolveNativeHostedPreviewConsumption( + false, + false, + false, + "Pending", + "Waiting"); + + EXPECT_EQ( + consumption.surfaceState, + Application::NativeHostedPreviewSurfaceState::Disabled); + EXPECT_FALSE(consumption.queueRuntimeFrame); + EXPECT_TRUE(consumption.appendRuntimeDrawDataToShell); + EXPECT_FALSE(consumption.showSurfaceImage); + EXPECT_TRUE(consumption.drawRuntimeDebugRects); + EXPECT_TRUE(consumption.placeholderTitle.empty()); + EXPECT_TRUE(consumption.placeholderSubtitle.empty()); + EXPECT_EQ( + Application::ComposeNativeHostedPreviewStatusLine(consumption, "Runtime status"), + "Runtime status"); +} + +TEST(ApplicationShellCommandBindingsTest, NativeHostedPreviewConsumptionQueuesFrameWhileAwaitingFirstSurfacePublish) { + const auto consumption = Application::ResolveNativeHostedPreviewConsumption( + true, + false, + false, + "Native XCUI preview pending", + "Waiting for queued native preview output to publish into the shell card."); + + EXPECT_EQ( + consumption.surfaceState, + Application::NativeHostedPreviewSurfaceState::AwaitingSubmit); + EXPECT_TRUE(consumption.queueRuntimeFrame); + EXPECT_FALSE(consumption.appendRuntimeDrawDataToShell); + EXPECT_FALSE(consumption.showSurfaceImage); + EXPECT_FALSE(consumption.drawRuntimeDebugRects); + EXPECT_EQ(consumption.placeholderTitle, "Native XCUI preview pending"); + EXPECT_EQ( + consumption.placeholderSubtitle, + "Waiting for queued native preview output to publish into the shell card."); + EXPECT_EQ( + Application::ComposeNativeHostedPreviewStatusLine(consumption, "Runtime status"), + "Native surface awaiting submit | Runtime status"); +} + +TEST(ApplicationShellCommandBindingsTest, NativeHostedPreviewConsumptionDistinguishesWarmingFromLiveSurfaceStates) { + const auto warming = Application::ResolveNativeHostedPreviewConsumption( + true, + true, + false, + "Native layout preview pending", + "Waiting for queued native preview output to publish into the layout card."); + const auto live = Application::ResolveNativeHostedPreviewConsumption( + true, + true, + true, + "Native layout preview pending", + "Waiting for queued native preview output to publish into the layout card."); + + EXPECT_EQ( + warming.surfaceState, + Application::NativeHostedPreviewSurfaceState::Warming); + EXPECT_TRUE(warming.queueRuntimeFrame); + EXPECT_FALSE(warming.appendRuntimeDrawDataToShell); + EXPECT_FALSE(warming.showSurfaceImage); + EXPECT_FALSE(warming.drawRuntimeDebugRects); + EXPECT_EQ(warming.placeholderTitle, "Native layout preview pending"); + EXPECT_EQ( + Application::ComposeNativeHostedPreviewStatusLine(warming, "Layout status"), + "Native surface warming | Layout status"); + + EXPECT_EQ( + live.surfaceState, + Application::NativeHostedPreviewSurfaceState::Live); + EXPECT_TRUE(live.queueRuntimeFrame); + EXPECT_FALSE(live.appendRuntimeDrawDataToShell); + EXPECT_TRUE(live.showSurfaceImage); + EXPECT_TRUE(live.drawRuntimeDebugRects); + EXPECT_TRUE(live.placeholderTitle.empty()); + EXPECT_TRUE(live.placeholderSubtitle.empty()); + EXPECT_EQ( + Application::ComposeNativeHostedPreviewStatusLine(live, "Layout status"), + "Native surface live | Layout status"); +} + } // namespace