Split legacy ImGui shell out of Application

This commit is contained in:
2026-04-05 15:28:42 +08:00
parent 6fd3ed434d
commit 050502cf78
9 changed files with 656 additions and 407 deletions

View File

@@ -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 `<imgui.h>`, 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 `<imgui.h>`, reducing default-path compile-time coupling before the larger compat-target split
## Phase Risks Still Open

View File

@@ -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

View File

@@ -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 <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/UI/DrawData.h>
#include <imgui.h>
#include <algorithm>
#include <cmath>
#include <sstream>
@@ -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<XCUIDemoPanel>(
&m_xcuiInputSource,
CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)),
::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiPanelCanvasHost());
m_layoutLabPanel = std::make_unique<XCUILayoutLabPanel>(
&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<XCUIDemoPanel>(
&m_xcuiInputSource,
CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)),
::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost());
m_layoutLabPanel = std::make_unique<XCUILayoutLabPanel>(
&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<float>(
std::chrono::duration<double>(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<float>(surface.GetWidth());
const float height = static_cast<float>(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) {

View File

@@ -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);

View File

@@ -0,0 +1,446 @@
#include "Application.h"
#include "XCUIBackend/LegacyImGuiHostInterop.h"
#include <imgui.h>
#include <algorithm>
#include <chrono>
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::uint64_t>(
std::chrono::duration_cast<std::chrono::nanoseconds>(
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<float>(
std::chrono::duration<double>(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<float>(surface.GetWidth());
const float height = static_cast<float>(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

View File

@@ -41,6 +41,7 @@ public:
};
std::unique_ptr<IWindowUICompositor> CreateImGuiWindowUICompositor();
std::unique_ptr<IWindowUICompositor> CreateNativeWindowUICompositor();
} // namespace XCUIBackend
} // namespace Editor

View File

@@ -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 <imgui.h>
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<IWindowUICompositor> CreateLegacyImGuiWindowUICompositor() {
return CreateImGuiWindowUICompositor();
}
std::unique_ptr<IXCUIHostedPreviewPresenter> CreateLegacyImGuiHostedPreviewPresenter() {
return CreateImGuiXCUIHostedPreviewPresenter();
}
std::unique_ptr<IXCUIPanelCanvasHost> 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

View File

@@ -0,0 +1,28 @@
#pragma once
#include "XCUIBackend/XCUIInputBridge.h"
#include "XCUIBackend/IWindowUICompositor.h"
#include "XCUIBackend/XCUIHostedPreviewPresenter.h"
#include "XCUIBackend/XCUIPanelCanvasHost.h"
#include <memory>
namespace XCEngine {
namespace Editor {
namespace XCUIBackend {
bool ConfigureLegacyImGuiHostFonts();
std::unique_ptr<IWindowUICompositor> CreateLegacyImGuiWindowUICompositor();
std::unique_ptr<IXCUIHostedPreviewPresenter> CreateLegacyImGuiHostedPreviewPresenter();
std::unique_ptr<IXCUIPanelCanvasHost> CreateLegacyImGuiPanelCanvasHost();
void ApplyLegacyImGuiHostInputCapture(XCUIInputBridgeFrameSnapshot& snapshot);
bool RenderLegacyImGuiDemoWindow(bool& visible);
} // namespace XCUIBackend
} // namespace Editor
} // namespace XCEngine

View File

@@ -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