2026-04-05 04:55:25 +08:00
|
|
|
#include "Application.h"
|
2026-04-05 13:24:14 +08:00
|
|
|
#include "XCUIBackend/ImGuiXCUIPanelCanvasHost.h"
|
2026-04-05 06:34:15 +08:00
|
|
|
#include "XCUIBackend/ImGuiXCUIHostedPreviewPresenter.h"
|
2026-04-05 06:15:24 +08:00
|
|
|
#include "XCUIBackend/ImGuiWindowUICompositor.h"
|
|
|
|
|
|
2026-04-05 04:55:25 +08:00
|
|
|
#include <XCEngine/Core/Asset/ResourceManager.h>
|
|
|
|
|
|
|
|
|
|
#include <imgui.h>
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
|
|
namespace XCEngine {
|
|
|
|
|
namespace NewEditor {
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
constexpr wchar_t kWindowClassName[] = L"XCNewEditorWindowClass";
|
|
|
|
|
constexpr wchar_t kWindowTitle[] = L"XCNewEditor";
|
|
|
|
|
constexpr float kClearColor[4] = { 0.08f, 0.09f, 0.11f, 1.0f };
|
|
|
|
|
constexpr float kHostedPreviewClearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
|
|
|
|
|
|
|
|
template <typename ResourceType>
|
|
|
|
|
void ShutdownAndDelete(ResourceType*& resource) {
|
|
|
|
|
if (resource == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resource->Shutdown();
|
|
|
|
|
delete 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";
|
|
|
|
|
}
|
|
|
|
|
if (nativeRequested) {
|
2026-04-05 13:24:14 +08:00
|
|
|
return "native requested, hosted presenter bound";
|
2026-04-05 04:55:25 +08:00
|
|
|
}
|
|
|
|
|
if (nativePresenterBound) {
|
2026-04-05 13:24:14 +08:00
|
|
|
return "hosted presenter requested, native presenter still bound";
|
2026-04-05 04:55:25 +08:00
|
|
|
}
|
2026-04-05 13:24:14 +08:00
|
|
|
return "hosted presenter";
|
2026-04-05 04:55:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter>
|
|
|
|
|
Application::CreateHostedPreviewPresenter(bool nativePreview) {
|
|
|
|
|
if (nativePreview) {
|
|
|
|
|
return ::XCEngine::Editor::XCUIBackend::CreateQueuedNativeXCUIHostedPreviewPresenter(
|
|
|
|
|
m_hostedPreviewQueue,
|
|
|
|
|
m_hostedPreviewSurfaceRegistry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIHostedPreviewPresenter();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 12:50:55 +08:00
|
|
|
const Application::ShellPanelChromeState* Application::TryGetShellPanelState(ShellPanelId panelId) const {
|
2026-04-05 13:24:14 +08:00
|
|
|
return m_shellChromeState.TryGetPanelState(panelId);
|
2026-04-05 12:50:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Application::IsShellViewToggleEnabled(ShellViewToggleId toggleId) const {
|
2026-04-05 13:24:14 +08:00
|
|
|
return m_shellChromeState.GetViewToggle(toggleId);
|
2026-04-05 12:50:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Application::SetShellViewToggleEnabled(ShellViewToggleId toggleId, bool enabled) {
|
2026-04-05 13:24:14 +08:00
|
|
|
m_shellChromeState.SetViewToggle(toggleId, enabled);
|
2026-04-05 12:50:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Application::IsNativeHostedPreviewEnabled(ShellPanelId panelId) const {
|
2026-04-05 13:24:14 +08:00
|
|
|
return m_shellChromeState.IsNativeHostedPreviewActive(panelId);
|
2026-04-05 12:50:55 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-05 04:55:25 +08:00
|
|
|
void Application::ConfigureHostedPreviewPresenters() {
|
2026-04-05 12:50:55 +08:00
|
|
|
const ShellPanelChromeState* demoState = TryGetShellPanelState(ShellPanelId::XCUIDemo);
|
2026-04-05 04:55:25 +08:00
|
|
|
if (m_demoPanel != nullptr) {
|
2026-04-05 12:50:55 +08:00
|
|
|
m_demoPanel->SetVisible(demoState != nullptr && demoState->visible);
|
|
|
|
|
m_demoPanel->SetHostedPreviewEnabled(demoState == nullptr || demoState->hostedPreviewEnabled);
|
|
|
|
|
m_demoPanel->SetHostedPreviewPresenter(CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)));
|
2026-04-05 04:55:25 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-05 12:50:55 +08:00
|
|
|
const ShellPanelChromeState* layoutLabState = TryGetShellPanelState(ShellPanelId::XCUILayoutLab);
|
2026-04-05 04:55:25 +08:00
|
|
|
if (m_layoutLabPanel != nullptr) {
|
2026-04-05 12:50:55 +08:00
|
|
|
m_layoutLabPanel->SetVisible(layoutLabState != nullptr && layoutLabState->visible);
|
|
|
|
|
m_layoutLabPanel->SetHostedPreviewEnabled(layoutLabState == nullptr || layoutLabState->hostedPreviewEnabled);
|
|
|
|
|
m_layoutLabPanel->SetHostedPreviewPresenter(
|
|
|
|
|
CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab)));
|
2026-04-05 04:55:25 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 12:50:55 +08:00
|
|
|
void Application::ConfigureShellCommandRouter() {
|
|
|
|
|
m_shellCommandRouter.Clear();
|
|
|
|
|
|
|
|
|
|
ShellCommandBindings bindings = {};
|
|
|
|
|
bindings.getXCUIDemoPanelVisible = [this]() {
|
2026-04-05 13:24:14 +08:00
|
|
|
return m_shellChromeState.IsPanelVisible(ShellPanelId::XCUIDemo);
|
2026-04-05 12:50:55 +08:00
|
|
|
};
|
|
|
|
|
bindings.setXCUIDemoPanelVisible = [this](bool visible) {
|
2026-04-05 13:24:14 +08:00
|
|
|
m_shellChromeState.SetPanelVisible(ShellPanelId::XCUIDemo, visible);
|
2026-04-05 12:50:55 +08:00
|
|
|
if (m_demoPanel != nullptr) {
|
|
|
|
|
m_demoPanel->SetVisible(visible);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
bindings.getXCUILayoutLabPanelVisible = [this]() {
|
2026-04-05 13:24:14 +08:00
|
|
|
return m_shellChromeState.IsPanelVisible(ShellPanelId::XCUILayoutLab);
|
2026-04-05 12:50:55 +08:00
|
|
|
};
|
|
|
|
|
bindings.setXCUILayoutLabPanelVisible = [this](bool visible) {
|
2026-04-05 13:24:14 +08:00
|
|
|
m_shellChromeState.SetPanelVisible(ShellPanelId::XCUILayoutLab, visible);
|
2026-04-05 12:50:55 +08:00
|
|
|
if (m_layoutLabPanel != nullptr) {
|
|
|
|
|
m_layoutLabPanel->SetVisible(visible);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
bindings.getImGuiDemoWindowVisible = [this]() {
|
|
|
|
|
return IsShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow);
|
|
|
|
|
};
|
|
|
|
|
bindings.setImGuiDemoWindowVisible = [this](bool visible) {
|
|
|
|
|
SetShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow, visible);
|
|
|
|
|
};
|
|
|
|
|
bindings.getNativeBackdropVisible = [this]() {
|
|
|
|
|
return IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop);
|
|
|
|
|
};
|
|
|
|
|
bindings.setNativeBackdropVisible = [this](bool visible) {
|
|
|
|
|
SetShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop, visible);
|
|
|
|
|
};
|
|
|
|
|
bindings.getPulseAccentEnabled = [this]() {
|
|
|
|
|
return IsShellViewToggleEnabled(ShellViewToggleId::PulseAccent);
|
|
|
|
|
};
|
|
|
|
|
bindings.setPulseAccentEnabled = [this](bool enabled) {
|
|
|
|
|
SetShellViewToggleEnabled(ShellViewToggleId::PulseAccent, enabled);
|
|
|
|
|
};
|
|
|
|
|
bindings.getNativeXCUIOverlayVisible = [this]() {
|
|
|
|
|
return IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay);
|
|
|
|
|
};
|
|
|
|
|
bindings.setNativeXCUIOverlayVisible = [this](bool visible) {
|
|
|
|
|
SetShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay, visible);
|
|
|
|
|
};
|
|
|
|
|
bindings.getHostedPreviewHudVisible = [this]() {
|
|
|
|
|
return IsShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud);
|
|
|
|
|
};
|
|
|
|
|
bindings.setHostedPreviewHudVisible = [this](bool visible) {
|
|
|
|
|
SetShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud, visible);
|
|
|
|
|
};
|
|
|
|
|
bindings.getNativeDemoPanelPreviewEnabled = [this]() {
|
|
|
|
|
return IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo);
|
|
|
|
|
};
|
|
|
|
|
bindings.setNativeDemoPanelPreviewEnabled = [this](bool enabled) {
|
2026-04-05 13:24:14 +08:00
|
|
|
m_shellChromeState.SetHostedPreviewMode(
|
|
|
|
|
ShellPanelId::XCUIDemo,
|
|
|
|
|
enabled ? ShellHostedPreviewMode::NativeOffscreen : ShellHostedPreviewMode::HostedPresenter);
|
2026-04-05 12:50:55 +08:00
|
|
|
};
|
|
|
|
|
bindings.getNativeLayoutLabPreviewEnabled = [this]() {
|
|
|
|
|
return IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab);
|
|
|
|
|
};
|
|
|
|
|
bindings.setNativeLayoutLabPreviewEnabled = [this](bool enabled) {
|
2026-04-05 13:24:14 +08:00
|
|
|
m_shellChromeState.SetHostedPreviewMode(
|
|
|
|
|
ShellPanelId::XCUILayoutLab,
|
|
|
|
|
enabled ? ShellHostedPreviewMode::NativeOffscreen : ShellHostedPreviewMode::HostedPresenter);
|
2026-04-05 12:50:55 +08:00
|
|
|
};
|
|
|
|
|
bindings.onHostedPreviewModeChanged = [this]() { ConfigureHostedPreviewPresenters(); };
|
|
|
|
|
|
|
|
|
|
Application::RegisterShellViewCommands(m_shellCommandRouter, bindings);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Application::DispatchShellShortcuts() {
|
|
|
|
|
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeCaptureOptions options = {};
|
|
|
|
|
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot snapshot =
|
|
|
|
|
m_xcuiInputSource.CaptureSnapshot(options);
|
|
|
|
|
|
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
|
snapshot.wantCaptureKeyboard = io.WantCaptureKeyboard;
|
|
|
|
|
snapshot.wantTextInput = io.WantTextInput;
|
|
|
|
|
|
|
|
|
|
const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta frameDelta =
|
|
|
|
|
m_shellInputBridge.Translate(snapshot);
|
|
|
|
|
const ::XCEngine::Editor::XCUIBackend::XCUIEditorCommandInputSnapshot commandSnapshot =
|
|
|
|
|
Application::BuildShellShortcutSnapshot(frameDelta);
|
|
|
|
|
m_shellCommandRouter.InvokeMatchingShortcut({ &commandSnapshot });
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 04:55:25 +08:00
|
|
|
Application::HostedPreviewPanelDiagnostics Application::BuildHostedPreviewPanelDiagnostics(
|
|
|
|
|
const char* debugName,
|
|
|
|
|
const char* fallbackDebugSource,
|
|
|
|
|
bool visible,
|
|
|
|
|
bool hostedPreviewEnabled,
|
|
|
|
|
bool nativeRequested,
|
|
|
|
|
bool nativePresenterBound,
|
|
|
|
|
const ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats& previewStats) const {
|
|
|
|
|
HostedPreviewPanelDiagnostics diagnostics = {};
|
|
|
|
|
if (debugName != nullptr) {
|
|
|
|
|
diagnostics.debugName = debugName;
|
|
|
|
|
}
|
|
|
|
|
if (fallbackDebugSource != nullptr) {
|
|
|
|
|
diagnostics.debugSource = fallbackDebugSource;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
diagnostics.visible = visible;
|
|
|
|
|
diagnostics.hostedPreviewEnabled = hostedPreviewEnabled;
|
|
|
|
|
diagnostics.nativeRequested = nativeRequested;
|
|
|
|
|
diagnostics.nativePresenterBound = nativePresenterBound;
|
|
|
|
|
diagnostics.presentedThisFrame = previewStats.presented;
|
|
|
|
|
diagnostics.queuedToNativePassThisFrame = previewStats.queuedToNativePass;
|
|
|
|
|
diagnostics.submittedDrawListCount = previewStats.submittedDrawListCount;
|
|
|
|
|
diagnostics.submittedCommandCount = previewStats.submittedCommandCount;
|
|
|
|
|
diagnostics.flushedDrawListCount = previewStats.flushedDrawListCount;
|
|
|
|
|
diagnostics.flushedCommandCount = previewStats.flushedCommandCount;
|
|
|
|
|
|
|
|
|
|
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceDescriptor descriptor = {};
|
|
|
|
|
if (!diagnostics.debugName.empty() &&
|
|
|
|
|
m_hostedPreviewSurfaceRegistry.TryGetSurfaceDescriptor(diagnostics.debugName.c_str(), descriptor)) {
|
|
|
|
|
diagnostics.descriptorAvailable = true;
|
|
|
|
|
diagnostics.surfaceImageAvailable = descriptor.image.IsValid();
|
|
|
|
|
diagnostics.surfaceWidth = descriptor.image.surfaceWidth;
|
|
|
|
|
diagnostics.surfaceHeight = descriptor.image.surfaceHeight;
|
|
|
|
|
diagnostics.logicalWidth = descriptor.logicalSize.width;
|
|
|
|
|
diagnostics.logicalHeight = descriptor.logicalSize.height;
|
|
|
|
|
diagnostics.queuedFrameIndex = descriptor.queuedFrameIndex;
|
|
|
|
|
if (!descriptor.debugSource.empty()) {
|
|
|
|
|
diagnostics.debugSource = descriptor.debugSource;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!diagnostics.debugName.empty()) {
|
|
|
|
|
const HostedPreviewOffscreenSurface* previewSurface = FindHostedPreviewSurface(diagnostics.debugName);
|
|
|
|
|
if (previewSurface != nullptr) {
|
|
|
|
|
diagnostics.surfaceAllocated =
|
|
|
|
|
previewSurface->colorTexture != nullptr ||
|
|
|
|
|
previewSurface->colorView != nullptr ||
|
|
|
|
|
previewSurface->width > 0u ||
|
|
|
|
|
previewSurface->height > 0u;
|
|
|
|
|
diagnostics.surfaceReady = previewSurface->IsReady();
|
|
|
|
|
if (diagnostics.surfaceWidth == 0u) {
|
|
|
|
|
diagnostics.surfaceWidth = previewSurface->width;
|
|
|
|
|
}
|
|
|
|
|
if (diagnostics.surfaceHeight == 0u) {
|
|
|
|
|
diagnostics.surfaceHeight = previewSurface->height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return diagnostics;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Application::Run(HINSTANCE instance, int nCmdShow) {
|
|
|
|
|
if (!CreateMainWindow(instance, nCmdShow)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_xcuiInputSource.Reset();
|
|
|
|
|
|
|
|
|
|
auto& resourceManager = ::XCEngine::Resources::ResourceManager::Get();
|
|
|
|
|
resourceManager.Initialize();
|
|
|
|
|
resourceManager.SetResourceRoot(XCENGINE_NEW_EDITOR_REPO_ROOT);
|
|
|
|
|
|
|
|
|
|
if (!InitializeRenderer()) {
|
|
|
|
|
resourceManager.Shutdown();
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 06:15:24 +08:00
|
|
|
InitializeWindowCompositor();
|
2026-04-05 04:55:25 +08:00
|
|
|
m_demoPanel = std::make_unique<XCUIDemoPanel>(
|
|
|
|
|
&m_xcuiInputSource,
|
2026-04-05 13:24:14 +08:00
|
|
|
CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)),
|
|
|
|
|
::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost());
|
2026-04-05 04:55:25 +08:00
|
|
|
m_layoutLabPanel = std::make_unique<XCUILayoutLabPanel>(
|
|
|
|
|
&m_xcuiInputSource,
|
2026-04-05 13:24:14 +08:00
|
|
|
CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab)),
|
|
|
|
|
::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost());
|
2026-04-05 12:50:55 +08:00
|
|
|
ConfigureHostedPreviewPresenters();
|
|
|
|
|
m_shellInputBridge.Reset();
|
|
|
|
|
ConfigureShellCommandRouter();
|
2026-04-05 04:55:25 +08:00
|
|
|
m_running = true;
|
|
|
|
|
m_renderReady = true;
|
|
|
|
|
|
|
|
|
|
MSG message = {};
|
|
|
|
|
while (m_running) {
|
|
|
|
|
while (PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE)) {
|
|
|
|
|
if (message.message == WM_QUIT) {
|
|
|
|
|
m_running = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TranslateMessage(&message);
|
|
|
|
|
DispatchMessageW(&message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m_running) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Frame();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_demoPanel.reset();
|
|
|
|
|
m_layoutLabPanel.reset();
|
2026-04-05 06:15:24 +08:00
|
|
|
ShutdownWindowCompositor();
|
2026-04-05 04:55:25 +08:00
|
|
|
ShutdownRenderer();
|
|
|
|
|
resourceManager.Shutdown();
|
|
|
|
|
return static_cast<int>(message.wParam);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LRESULT CALLBACK Application::StaticWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
|
|
|
|
|
Application* app = reinterpret_cast<Application*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
|
|
|
|
|
if (message == WM_NCCREATE) {
|
|
|
|
|
const CREATESTRUCTW* createStruct = reinterpret_cast<const CREATESTRUCTW*>(lParam);
|
|
|
|
|
app = reinterpret_cast<Application*>(createStruct->lpCreateParams);
|
|
|
|
|
SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(app));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return app != nullptr
|
|
|
|
|
? app->WndProc(hwnd, message, wParam, lParam)
|
|
|
|
|
: DefWindowProcW(hwnd, message, wParam, lParam);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LRESULT Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
|
|
|
|
|
m_xcuiInputSource.HandleWindowMessage(hwnd, message, wParam, lParam);
|
|
|
|
|
|
2026-04-05 06:15:24 +08:00
|
|
|
if (m_windowCompositor != nullptr &&
|
|
|
|
|
m_windowCompositor->HandleWindowMessage(hwnd, message, wParam, lParam)) {
|
2026-04-05 04:55:25 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (message) {
|
|
|
|
|
case WM_SIZE:
|
|
|
|
|
if (wParam != SIZE_MINIMIZED && m_renderReady) {
|
|
|
|
|
m_windowRenderer.Resize(static_cast<int>(LOWORD(lParam)), static_cast<int>(HIWORD(lParam)));
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
case WM_CLOSE:
|
|
|
|
|
DestroyWindow(hwnd);
|
|
|
|
|
return 0;
|
|
|
|
|
case WM_DESTROY:
|
|
|
|
|
PostQuitMessage(0);
|
|
|
|
|
return 0;
|
|
|
|
|
case WM_PAINT: {
|
|
|
|
|
PAINTSTRUCT paintStruct = {};
|
|
|
|
|
BeginPaint(hwnd, &paintStruct);
|
|
|
|
|
if (m_renderReady) {
|
|
|
|
|
Frame();
|
|
|
|
|
}
|
|
|
|
|
EndPaint(hwnd, &paintStruct);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return DefWindowProcW(hwnd, message, wParam, lParam);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Application::CreateMainWindow(HINSTANCE instance, int nCmdShow) {
|
|
|
|
|
WNDCLASSEXW windowClass = {};
|
|
|
|
|
windowClass.cbSize = sizeof(windowClass);
|
|
|
|
|
windowClass.style = CS_HREDRAW | CS_VREDRAW;
|
|
|
|
|
windowClass.lpfnWndProc = &Application::StaticWndProc;
|
|
|
|
|
windowClass.hInstance = instance;
|
|
|
|
|
windowClass.hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
|
|
|
|
windowClass.lpszClassName = kWindowClassName;
|
|
|
|
|
if (!RegisterClassExW(&windowClass)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_hwnd = CreateWindowExW(
|
|
|
|
|
0,
|
|
|
|
|
kWindowClassName,
|
|
|
|
|
kWindowTitle,
|
|
|
|
|
WS_OVERLAPPEDWINDOW,
|
|
|
|
|
CW_USEDEFAULT,
|
|
|
|
|
CW_USEDEFAULT,
|
|
|
|
|
1360,
|
|
|
|
|
840,
|
|
|
|
|
nullptr,
|
|
|
|
|
nullptr,
|
|
|
|
|
instance,
|
|
|
|
|
this);
|
|
|
|
|
if (m_hwnd == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ShowWindow(m_hwnd, nCmdShow);
|
|
|
|
|
UpdateWindow(m_hwnd);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Application::InitializeRenderer() {
|
|
|
|
|
RECT clientRect = {};
|
|
|
|
|
if (!GetClientRect(m_hwnd, &clientRect)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int width = clientRect.right - clientRect.left;
|
|
|
|
|
const int height = clientRect.bottom - clientRect.top;
|
|
|
|
|
const bool initialized = width > 0 &&
|
|
|
|
|
height > 0 &&
|
|
|
|
|
m_windowRenderer.Initialize(m_hwnd, width, height);
|
|
|
|
|
if (initialized) {
|
|
|
|
|
m_startTime = std::chrono::steady_clock::now();
|
|
|
|
|
}
|
|
|
|
|
return initialized;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 06:15:24 +08:00
|
|
|
void Application::InitializeWindowCompositor() {
|
|
|
|
|
m_windowCompositor = ::XCEngine::Editor::XCUIBackend::CreateImGuiWindowUICompositor();
|
|
|
|
|
if (m_windowCompositor != nullptr) {
|
|
|
|
|
m_windowCompositor->Initialize(
|
|
|
|
|
m_hwnd,
|
|
|
|
|
m_windowRenderer,
|
|
|
|
|
[]() { ConfigureFonts(); });
|
|
|
|
|
}
|
2026-04-05 04:55:25 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-05 06:15:24 +08:00
|
|
|
void Application::ShutdownWindowCompositor() {
|
2026-04-05 04:55:25 +08:00
|
|
|
DestroyHostedPreviewSurfaces();
|
2026-04-05 06:15:24 +08:00
|
|
|
if (m_windowCompositor != nullptr) {
|
|
|
|
|
m_windowCompositor->Shutdown();
|
|
|
|
|
m_windowCompositor.reset();
|
|
|
|
|
}
|
2026-04-05 04:55:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Application::ShutdownRenderer() {
|
|
|
|
|
m_renderReady = false;
|
|
|
|
|
m_hostedPreviewRenderBackend.Shutdown();
|
|
|
|
|
m_nativeBackdropRenderer.Shutdown();
|
|
|
|
|
m_windowRenderer.Shutdown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Application::DestroyHostedPreviewSurfaces() {
|
|
|
|
|
for (HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) {
|
2026-04-05 06:41:32 +08:00
|
|
|
if (previewSurface.textureRegistration.cpuHandle.ptr != 0) {
|
2026-04-05 06:15:24 +08:00
|
|
|
if (m_windowCompositor != nullptr) {
|
2026-04-05 06:41:32 +08:00
|
|
|
m_windowCompositor->FreeTextureDescriptor(previewSurface.textureRegistration);
|
2026-04-05 06:15:24 +08:00
|
|
|
}
|
2026-04-05 04:55:25 +08:00
|
|
|
}
|
|
|
|
|
ShutdownAndDelete(previewSurface.colorView);
|
|
|
|
|
ShutdownAndDelete(previewSurface.colorTexture);
|
|
|
|
|
previewSurface = {};
|
|
|
|
|
}
|
|
|
|
|
m_hostedPreviewSurfaces.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 12:50:55 +08:00
|
|
|
void Application::SyncShellChromePanelStateFromPanels() {
|
2026-04-05 13:24:14 +08:00
|
|
|
m_shellChromeState.SetPanelVisible(
|
|
|
|
|
ShellPanelId::XCUIDemo,
|
|
|
|
|
m_demoPanel != nullptr && m_demoPanel->IsVisible());
|
|
|
|
|
m_shellChromeState.SetPanelVisible(
|
|
|
|
|
ShellPanelId::XCUILayoutLab,
|
|
|
|
|
m_layoutLabPanel != nullptr && m_layoutLabPanel->IsVisible());
|
2026-04-05 12:50:55 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-05 04:55:25 +08:00
|
|
|
void Application::SyncHostedPreviewSurfaces() {
|
|
|
|
|
const auto isNativePreviewEnabled = [this](const std::string& debugName) {
|
2026-04-05 12:50:55 +08:00
|
|
|
const ShellPanelChromeState* demoState = TryGetShellPanelState(ShellPanelId::XCUIDemo);
|
|
|
|
|
if (demoState != nullptr &&
|
|
|
|
|
debugName == demoState->previewDebugName &&
|
|
|
|
|
IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ShellPanelChromeState* layoutLabState = TryGetShellPanelState(ShellPanelId::XCUILayoutLab);
|
|
|
|
|
return layoutLabState != nullptr &&
|
|
|
|
|
debugName == layoutLabState->previewDebugName &&
|
|
|
|
|
IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab);
|
2026-04-05 04:55:25 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const auto syncSurfaceForNameAndSize =
|
|
|
|
|
[this, &isNativePreviewEnabled](
|
|
|
|
|
const std::string& debugName,
|
|
|
|
|
const ::XCEngine::UI::UISize& logicalSize) {
|
|
|
|
|
if (!isNativePreviewEnabled(debugName)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::uint32_t width = logicalSize.width > 1.0f
|
|
|
|
|
? static_cast<std::uint32_t>(std::ceil(logicalSize.width))
|
|
|
|
|
: 0u;
|
|
|
|
|
const std::uint32_t height = logicalSize.height > 1.0f
|
|
|
|
|
? static_cast<std::uint32_t>(std::ceil(logicalSize.height))
|
|
|
|
|
: 0u;
|
|
|
|
|
if (width == 0u || height == 0u) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HostedPreviewOffscreenSurface& previewSurface = FindOrAddHostedPreviewSurface(debugName);
|
|
|
|
|
EnsureHostedPreviewSurface(previewSurface, width, height);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (const auto& descriptor : m_hostedPreviewSurfaceRegistry.GetDescriptors()) {
|
|
|
|
|
syncSurfaceForNameAndSize(descriptor.debugName, descriptor.logicalSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto& queuedFrame : m_hostedPreviewQueue.GetQueuedFrames()) {
|
|
|
|
|
syncSurfaceForNameAndSize(queuedFrame.debugName, queuedFrame.logicalSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Application::HostedPreviewOffscreenSurface* Application::FindHostedPreviewSurface(const std::string& debugName) {
|
|
|
|
|
for (HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) {
|
|
|
|
|
if (previewSurface.debugName == debugName) {
|
|
|
|
|
return &previewSurface;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Application::HostedPreviewOffscreenSurface* Application::FindHostedPreviewSurface(const std::string& debugName) const {
|
|
|
|
|
for (const HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) {
|
|
|
|
|
if (previewSurface.debugName == debugName) {
|
|
|
|
|
return &previewSurface;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Application::HostedPreviewOffscreenSurface& Application::FindOrAddHostedPreviewSurface(const std::string& debugName) {
|
|
|
|
|
HostedPreviewOffscreenSurface* existingSurface = FindHostedPreviewSurface(debugName);
|
|
|
|
|
if (existingSurface != nullptr) {
|
|
|
|
|
return *existingSurface;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HostedPreviewOffscreenSurface previewSurface = {};
|
|
|
|
|
previewSurface.debugName = debugName;
|
|
|
|
|
m_hostedPreviewSurfaces.push_back(std::move(previewSurface));
|
|
|
|
|
return m_hostedPreviewSurfaces.back();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Application::EnsureHostedPreviewSurface(
|
|
|
|
|
HostedPreviewOffscreenSurface& previewSurface,
|
|
|
|
|
std::uint32_t width,
|
|
|
|
|
std::uint32_t height) {
|
|
|
|
|
if (previewSurface.IsReady() && previewSurface.width == width && previewSurface.height == height) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 06:41:32 +08:00
|
|
|
if (previewSurface.textureRegistration.cpuHandle.ptr != 0) {
|
2026-04-05 06:15:24 +08:00
|
|
|
if (m_windowCompositor != nullptr) {
|
2026-04-05 06:41:32 +08:00
|
|
|
m_windowCompositor->FreeTextureDescriptor(previewSurface.textureRegistration);
|
2026-04-05 06:15:24 +08:00
|
|
|
}
|
2026-04-05 04:55:25 +08:00
|
|
|
}
|
|
|
|
|
ShutdownAndDelete(previewSurface.colorView);
|
|
|
|
|
ShutdownAndDelete(previewSurface.colorTexture);
|
2026-04-05 06:41:32 +08:00
|
|
|
previewSurface.textureRegistration = {};
|
2026-04-05 04:55:25 +08:00
|
|
|
previewSurface.width = width;
|
|
|
|
|
previewSurface.height = height;
|
|
|
|
|
previewSurface.colorState = ::XCEngine::RHI::ResourceStates::Common;
|
|
|
|
|
|
|
|
|
|
if (width == 0u || height == 0u) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::XCEngine::RHI::TextureDesc colorDesc = {};
|
|
|
|
|
colorDesc.width = width;
|
|
|
|
|
colorDesc.height = height;
|
|
|
|
|
colorDesc.depth = 1;
|
|
|
|
|
colorDesc.mipLevels = 1;
|
|
|
|
|
colorDesc.arraySize = 1;
|
|
|
|
|
colorDesc.format = static_cast<std::uint32_t>(::XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
|
|
|
colorDesc.textureType = static_cast<std::uint32_t>(::XCEngine::RHI::TextureType::Texture2D);
|
|
|
|
|
colorDesc.sampleCount = 1;
|
|
|
|
|
previewSurface.colorTexture = m_windowRenderer.GetRHIDevice()->CreateTexture(colorDesc);
|
|
|
|
|
if (previewSurface.colorTexture == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::XCEngine::RHI::ResourceViewDesc colorViewDesc = {};
|
|
|
|
|
colorViewDesc.format = static_cast<std::uint32_t>(::XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
|
|
|
colorViewDesc.dimension = ::XCEngine::RHI::ResourceViewDimension::Texture2D;
|
|
|
|
|
previewSurface.colorView =
|
|
|
|
|
m_windowRenderer.GetRHIDevice()->CreateRenderTargetView(previewSurface.colorTexture, colorViewDesc);
|
|
|
|
|
if (previewSurface.colorView == nullptr) {
|
|
|
|
|
ShutdownAndDelete(previewSurface.colorTexture);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 06:15:24 +08:00
|
|
|
if (m_windowCompositor == nullptr ||
|
|
|
|
|
!m_windowCompositor->CreateTextureDescriptor(
|
2026-04-05 04:55:25 +08:00
|
|
|
m_windowRenderer.GetRHIDevice(),
|
|
|
|
|
previewSurface.colorTexture,
|
2026-04-05 06:41:32 +08:00
|
|
|
previewSurface.textureRegistration)) {
|
2026-04-05 04:55:25 +08:00
|
|
|
ShutdownAndDelete(previewSurface.colorView);
|
|
|
|
|
ShutdownAndDelete(previewSurface.colorTexture);
|
2026-04-05 06:41:32 +08:00
|
|
|
previewSurface.textureRegistration = {};
|
2026-04-05 04:55:25 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Application::RenderHostedPreviewOffscreenSurface(
|
|
|
|
|
HostedPreviewOffscreenSurface& previewSurface,
|
|
|
|
|
const ::XCEngine::Rendering::RenderContext& renderContext,
|
|
|
|
|
const ::XCEngine::UI::UIDrawData& drawData) {
|
|
|
|
|
if (!previewSurface.IsReady() || renderContext.commandList == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::XCEngine::Rendering::RenderSurface renderSurface(previewSurface.width, previewSurface.height);
|
|
|
|
|
renderSurface.SetColorAttachment(previewSurface.colorView);
|
|
|
|
|
renderSurface.SetAutoTransitionEnabled(false);
|
|
|
|
|
renderSurface.SetColorStateBefore(::XCEngine::RHI::ResourceStates::RenderTarget);
|
|
|
|
|
renderSurface.SetColorStateAfter(::XCEngine::RHI::ResourceStates::RenderTarget);
|
|
|
|
|
|
|
|
|
|
renderContext.commandList->TransitionBarrier(
|
|
|
|
|
previewSurface.colorView,
|
|
|
|
|
previewSurface.colorState,
|
|
|
|
|
::XCEngine::RHI::ResourceStates::RenderTarget);
|
|
|
|
|
renderContext.commandList->SetRenderTargets(1, &previewSurface.colorView, nullptr);
|
|
|
|
|
renderContext.commandList->ClearRenderTarget(previewSurface.colorView, kHostedPreviewClearColor);
|
|
|
|
|
|
|
|
|
|
if (!m_hostedPreviewRenderBackend.Render(renderContext, renderSurface, drawData)) {
|
|
|
|
|
previewSurface.colorState = ::XCEngine::RHI::ResourceStates::RenderTarget;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderContext.commandList->TransitionBarrier(
|
|
|
|
|
previewSurface.colorView,
|
|
|
|
|
::XCEngine::RHI::ResourceStates::RenderTarget,
|
|
|
|
|
::XCEngine::RHI::ResourceStates::PixelShaderResource);
|
|
|
|
|
previewSurface.colorState = ::XCEngine::RHI::ResourceStates::PixelShaderResource;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Application::RenderShellChrome() {
|
2026-04-05 12:50:55 +08:00
|
|
|
SyncShellChromePanelStateFromPanels();
|
|
|
|
|
|
2026-04-05 04:55:25 +08:00
|
|
|
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")) {
|
2026-04-05 12:50:55 +08:00
|
|
|
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);
|
2026-04-05 04:55:25 +08:00
|
|
|
ImGui::Separator();
|
2026-04-05 12:50:55 +08:00
|
|
|
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);
|
2026-04-05 04:55:25 +08:00
|
|
|
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();
|
2026-04-05 12:50:55 +08:00
|
|
|
if (IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) {
|
2026-04-05 04:55:25 +08:00
|
|
|
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(
|
2026-04-05 12:50:55 +08:00
|
|
|
IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop)
|
2026-04-05 04:55:25 +08:00
|
|
|
? "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",
|
2026-04-05 12:50:55 +08:00
|
|
|
IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)
|
|
|
|
|
? "native offscreen preview surface"
|
2026-04-05 13:24:14 +08:00
|
|
|
: "hosted presenter");
|
2026-04-05 04:55:25 +08:00
|
|
|
}
|
|
|
|
|
if (m_layoutLabPanel != nullptr) {
|
|
|
|
|
ImGui::TextDisabled(
|
|
|
|
|
"Layout Lab preview: %s",
|
2026-04-05 12:50:55 +08:00
|
|
|
IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab)
|
|
|
|
|
? "native offscreen preview surface"
|
2026-04-05 13:24:14 +08:00
|
|
|
: "hosted presenter");
|
2026-04-05 04:55:25 +08:00
|
|
|
}
|
|
|
|
|
ImGui::EndMenuBar();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImGui::DockSpace(
|
|
|
|
|
ImGui::GetID("XCNewEditorDockSpace"),
|
|
|
|
|
ImVec2(0.0f, 0.0f),
|
|
|
|
|
ImGuiDockNodeFlags_PassthruCentralNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImGui::End();
|
|
|
|
|
|
2026-04-05 12:50:55 +08:00
|
|
|
if (IsShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud)) {
|
2026-04-05 04:55:25 +08:00
|
|
|
RenderHostedPreviewHud();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Application::RenderHostedPreviewHud() {
|
|
|
|
|
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
|
|
|
|
if (viewport == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 12:50:55 +08:00
|
|
|
const ShellPanelChromeState* demoState = TryGetShellPanelState(ShellPanelId::XCUIDemo);
|
|
|
|
|
const ShellPanelChromeState* layoutLabState = TryGetShellPanelState(ShellPanelId::XCUILayoutLab);
|
2026-04-05 04:55:25 +08:00
|
|
|
const HostedPreviewPanelDiagnostics demoDiagnostics = BuildHostedPreviewPanelDiagnostics(
|
2026-04-05 12:50:55 +08:00
|
|
|
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),
|
2026-04-05 04:55:25 +08:00
|
|
|
m_demoPanel != nullptr && m_demoPanel->IsUsingNativeHostedPreview(),
|
|
|
|
|
m_demoPanel != nullptr
|
|
|
|
|
? m_demoPanel->GetLastPreviewStats()
|
|
|
|
|
: ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats{});
|
|
|
|
|
const HostedPreviewPanelDiagnostics layoutLabDiagnostics = BuildHostedPreviewPanelDiagnostics(
|
2026-04-05 12:50:55 +08:00
|
|
|
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),
|
2026-04-05 04:55:25 +08:00
|
|
|
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) {
|
|
|
|
|
(void)surface;
|
|
|
|
|
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewDrainStats drainStats = {};
|
|
|
|
|
const auto& queuedFrames = m_hostedPreviewQueue.GetQueuedFrames();
|
|
|
|
|
drainStats.queuedFrameCount = queuedFrames.size();
|
|
|
|
|
for (const auto& queuedFrame : queuedFrames) {
|
|
|
|
|
drainStats.queuedDrawListCount += queuedFrame.drawData.GetDrawListCount();
|
|
|
|
|
drainStats.queuedCommandCount += queuedFrame.drawData.GetTotalCommandCount();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (queuedFrames.empty()) {
|
|
|
|
|
m_hostedPreviewQueue.SetLastDrainStats(drainStats);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_hostedPreviewRenderBackend.SetTextAtlasProvider(&m_hostedPreviewTextAtlasProvider);
|
|
|
|
|
std::size_t queuedFrameIndex = 0u;
|
|
|
|
|
for (const auto& queuedFrame : queuedFrames) {
|
|
|
|
|
m_hostedPreviewSurfaceRegistry.RecordQueuedFrame(queuedFrame, queuedFrameIndex);
|
|
|
|
|
if (queuedFrame.debugName.empty()) {
|
|
|
|
|
++drainStats.skippedFrameCount;
|
|
|
|
|
++queuedFrameIndex;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::uint32_t expectedWidth = queuedFrame.logicalSize.width > 1.0f
|
|
|
|
|
? static_cast<std::uint32_t>(std::ceil(queuedFrame.logicalSize.width))
|
|
|
|
|
: 0u;
|
|
|
|
|
const std::uint32_t expectedHeight = queuedFrame.logicalSize.height > 1.0f
|
|
|
|
|
? static_cast<std::uint32_t>(std::ceil(queuedFrame.logicalSize.height))
|
|
|
|
|
: 0u;
|
|
|
|
|
if (expectedWidth == 0u || expectedHeight == 0u) {
|
|
|
|
|
++drainStats.skippedFrameCount;
|
|
|
|
|
++queuedFrameIndex;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HostedPreviewOffscreenSurface& previewSurface = FindOrAddHostedPreviewSurface(queuedFrame.debugName);
|
|
|
|
|
if (!EnsureHostedPreviewSurface(previewSurface, expectedWidth, expectedHeight) ||
|
|
|
|
|
!RenderHostedPreviewOffscreenSurface(previewSurface, renderContext, queuedFrame.drawData)) {
|
|
|
|
|
++drainStats.skippedFrameCount;
|
|
|
|
|
++queuedFrameIndex;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++drainStats.renderedFrameCount;
|
|
|
|
|
const auto& overlayStats = m_hostedPreviewRenderBackend.GetLastOverlayStats();
|
|
|
|
|
drainStats.renderedDrawListCount += overlayStats.drawListCount;
|
|
|
|
|
drainStats.renderedCommandCount += overlayStats.renderedCommandCount;
|
|
|
|
|
drainStats.skippedCommandCount += overlayStats.skippedCommandCount;
|
|
|
|
|
m_hostedPreviewSurfaceRegistry.UpdateSurface(
|
|
|
|
|
queuedFrame.debugName,
|
2026-04-05 06:41:32 +08:00
|
|
|
previewSurface.textureRegistration.texture,
|
2026-04-05 04:55:25 +08:00
|
|
|
::XCEngine::UI::UIRect(
|
|
|
|
|
0.0f,
|
|
|
|
|
0.0f,
|
|
|
|
|
static_cast<float>(previewSurface.width),
|
|
|
|
|
static_cast<float>(previewSurface.height)));
|
|
|
|
|
++queuedFrameIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_hostedPreviewQueue.SetLastDrainStats(drainStats);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Application::Frame() {
|
|
|
|
|
if (!m_renderReady || !m_windowRenderer.BeginFrame()) {
|
|
|
|
|
m_xcuiInputSource.ClearFrameTransients();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_hostedPreviewQueue.BeginFrame();
|
|
|
|
|
m_hostedPreviewSurfaceRegistry.BeginFrame();
|
|
|
|
|
SyncHostedPreviewSurfaces();
|
2026-04-05 06:15:24 +08:00
|
|
|
if (m_windowCompositor == nullptr) {
|
|
|
|
|
m_xcuiInputSource.ClearFrameTransients();
|
|
|
|
|
return;
|
2026-04-05 04:55:25 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-05 06:15:24 +08:00
|
|
|
m_windowCompositor->RenderFrame(
|
2026-04-05 04:55:25 +08:00
|
|
|
kClearColor,
|
2026-04-05 06:15:24 +08:00
|
|
|
[this]() {
|
2026-04-05 12:50:55 +08:00
|
|
|
DispatchShellShortcuts();
|
2026-04-05 06:15:24 +08:00
|
|
|
RenderShellChrome();
|
|
|
|
|
if (m_demoPanel) {
|
|
|
|
|
m_demoPanel->RenderIfVisible();
|
|
|
|
|
}
|
|
|
|
|
if (m_layoutLabPanel) {
|
|
|
|
|
m_layoutLabPanel->RenderIfVisible();
|
|
|
|
|
}
|
2026-04-05 13:24:14 +08:00
|
|
|
bool showImGuiDemoWindow = IsShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow);
|
|
|
|
|
if (showImGuiDemoWindow) {
|
|
|
|
|
ImGui::ShowDemoWindow(&showImGuiDemoWindow);
|
|
|
|
|
SetShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow, showImGuiDemoWindow);
|
2026-04-05 06:15:24 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-05 12:50:55 +08:00
|
|
|
SyncShellChromePanelStateFromPanels();
|
2026-04-05 06:15:24 +08:00
|
|
|
SyncHostedPreviewSurfaces();
|
|
|
|
|
},
|
2026-04-05 04:55:25 +08:00
|
|
|
[this](
|
|
|
|
|
const ::XCEngine::Rendering::RenderContext& renderContext,
|
|
|
|
|
const ::XCEngine::Rendering::RenderSurface& surface) {
|
|
|
|
|
RenderQueuedHostedPreviews(renderContext, surface);
|
|
|
|
|
|
2026-04-05 12:50:55 +08:00
|
|
|
if (!IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop) &&
|
|
|
|
|
!IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) {
|
2026-04-05 04:55:25 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MainWindowNativeBackdropRenderer::FrameState frameState = {};
|
|
|
|
|
frameState.elapsedSeconds = static_cast<float>(
|
|
|
|
|
std::chrono::duration<double>(std::chrono::steady_clock::now() - m_startTime).count());
|
2026-04-05 12:50:55 +08:00
|
|
|
frameState.pulseAccent = IsShellViewToggleEnabled(ShellViewToggleId::PulseAccent);
|
|
|
|
|
frameState.drawBackdrop = IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop);
|
|
|
|
|
if (IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) {
|
2026-04-05 04:55:25 +08:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace NewEditor
|
|
|
|
|
} // namespace XCEngine
|