#pragma once #include #include "XCUIBackend/XCUIEditorCommandRouter.h" #include "XCUIBackend/NativeXCUIPanelCanvasHost.h" #include "XCUIBackend/XCUIHostedPreviewPresenter.h" #include "XCUIBackend/XCUIInputBridge.h" #include "XCUIBackend/XCUILayoutLabRuntime.h" #include "XCUIBackend/XCUIRHIRenderBackend.h" #include "XCUIBackend/XCUIShellChromeState.h" #include "XCUIBackend/XCUIStandaloneTextAtlasProvider.h" #include "XCUIBackend/UITextureRegistration.h" #include "Platform/D3D12WindowRenderer.h" #include "Rendering/MainWindowNativeBackdropRenderer.h" #include #include #include #include #include #include #include #include #include namespace XCEngine { namespace Editor { namespace XCUIBackend { class IWindowUICompositor; } // namespace XCUIBackend } // namespace Editor namespace NewEditor { class XCUIDemoPanel; class XCUILayoutLabPanel; class Application { public: Application(); ~Application(); using ShellChromeState = ::XCEngine::Editor::XCUIBackend::XCUIShellChromeState; using ShellPanelId = ::XCEngine::Editor::XCUIBackend::XCUIShellPanelId; using ShellViewToggleId = ::XCEngine::Editor::XCUIBackend::XCUIShellViewToggleId; using ShellHostedPreviewMode = ::XCEngine::Editor::XCUIBackend::XCUIShellHostedPreviewMode; using ShellPanelChromeState = ::XCEngine::Editor::XCUIBackend::XCUIShellPanelChromeState; using ShellViewToggleState = ::XCEngine::Editor::XCUIBackend::XCUIShellViewToggleState; enum class WindowHostMode : std::uint8_t { NativeXCUI = 0, CompatibilityHost }; struct ShellCommandIds { static constexpr const char* ToggleXCUIDemoPanel = ::XCEngine::Editor::XCUIBackend::XCUIShellChromeCommandIds::ToggleXCUIDemoPanel; static constexpr const char* ToggleXCUILayoutLabPanel = ::XCEngine::Editor::XCUIBackend::XCUIShellChromeCommandIds::ToggleXCUILayoutLabPanel; static constexpr const char* ToggleNativeBackdrop = ::XCEngine::Editor::XCUIBackend::XCUIShellChromeCommandIds::ToggleNativeBackdrop; static constexpr const char* TogglePulseAccent = ::XCEngine::Editor::XCUIBackend::XCUIShellChromeCommandIds::TogglePulseAccent; static constexpr const char* ToggleNativeXCUIOverlay = ::XCEngine::Editor::XCUIBackend::XCUIShellChromeCommandIds::ToggleNativeXCUIOverlay; static constexpr const char* ToggleHostedPreviewHud = ::XCEngine::Editor::XCUIBackend::XCUIShellChromeCommandIds::ToggleHostedPreviewHud; static constexpr const char* ToggleNativeDemoPanelPreview = ::XCEngine::Editor::XCUIBackend::XCUIShellChromeCommandIds::ToggleNativeDemoPanelPreview; static constexpr const char* ToggleNativeLayoutLabPreview = ::XCEngine::Editor::XCUIBackend::XCUIShellChromeCommandIds::ToggleNativeLayoutLabPreview; }; struct ShellCommandBindings { std::function getXCUIDemoPanelVisible = {}; std::function setXCUIDemoPanelVisible = {}; std::function getXCUILayoutLabPanelVisible = {}; std::function setXCUILayoutLabPanelVisible = {}; std::function getNativeBackdropVisible = {}; std::function setNativeBackdropVisible = {}; std::function getPulseAccentEnabled = {}; std::function setPulseAccentEnabled = {}; std::function getNativeXCUIOverlayVisible = {}; std::function setNativeXCUIOverlayVisible = {}; std::function getHostedPreviewHudVisible = {}; std::function setHostedPreviewHudVisible = {}; std::function getNativeDemoPanelPreviewEnabled = {}; std::function setNativeDemoPanelPreviewEnabled = {}; std::function getNativeLayoutLabPreviewEnabled = {}; std::function setNativeLayoutLabPreviewEnabled = {}; std::function onHostedPreviewModeChanged = {}; }; enum class NativeHostedPreviewSurfaceState : std::uint8_t { Disabled = 0, AwaitingSubmit, Warming, Live }; struct NativeHostedPreviewConsumption { NativeHostedPreviewSurfaceState surfaceState = NativeHostedPreviewSurfaceState::Disabled; bool queueRuntimeFrame = false; bool appendRuntimeDrawDataToShell = true; bool showSurfaceImage = false; bool drawRuntimeDebugRects = true; std::string_view placeholderTitle = {}; std::string_view placeholderSubtitle = {}; }; static ::XCEngine::Editor::XCUIBackend::XCUIEditorCommandInputSnapshot BuildShellShortcutSnapshot( const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta& frameDelta) { ::XCEngine::Editor::XCUIBackend::XCUIEditorCommandInputSnapshot snapshot = {}; snapshot.modifiers = frameDelta.state.modifiers; snapshot.windowFocused = frameDelta.state.windowFocused; snapshot.wantCaptureKeyboard = frameDelta.state.wantCaptureKeyboard; snapshot.wantTextInput = frameDelta.state.wantTextInput; snapshot.keys.reserve( frameDelta.keyboard.pressedKeys.size() + frameDelta.keyboard.repeatedKeys.size()); const auto appendKeyState = [&snapshot](std::int32_t keyCode, bool repeat) { for (auto& existing : snapshot.keys) { if (existing.keyCode != keyCode) { continue; } existing.down = true; existing.repeat = existing.repeat || repeat; return; } ::XCEngine::Editor::XCUIBackend::XCUIEditorCommandKeyState keyState = {}; keyState.keyCode = keyCode; keyState.down = true; keyState.repeat = repeat; snapshot.keys.push_back(keyState); }; for (std::int32_t keyCode : frameDelta.keyboard.pressedKeys) { appendKeyState(keyCode, false); } for (std::int32_t keyCode : frameDelta.keyboard.repeatedKeys) { appendKeyState(keyCode, true); } return snapshot; } static void RegisterShellViewCommands( ::XCEngine::Editor::XCUIBackend::XCUIEditorCommandRouter& router, const ShellCommandBindings& bindings) { using ::XCEngine::Editor::XCUIBackend::XCUIEditorCommandAccelerator; using ::XCEngine::Editor::XCUIBackend::XCUIEditorCommandDefinition; using ::XCEngine::Input::KeyCode; using ModifierState = ::XCEngine::UI::UIInputModifiers; const auto bindToggleCommand = [&router]( const char* commandId, const std::function& getter, const std::function& setter, std::initializer_list accelerators, const std::function& afterToggle = {}) { if (!getter || !setter) { return; } XCUIEditorCommandDefinition definition = {}; definition.commandId = commandId; definition.isEnabled = [getter, setter]() { return static_cast(getter) && static_cast(setter); }; definition.invoke = [getter, setter, afterToggle]() { const bool nextValue = !getter(); setter(nextValue); if (afterToggle) { afterToggle(); } }; definition.accelerators.assign(accelerators.begin(), accelerators.end()); router.RegisterCommand(definition); }; const ModifierState ctrlOnly = { false, true, false, false }; const ModifierState ctrlShift = { true, true, false, false }; const ModifierState ctrlAlt = { false, true, true, false }; bindToggleCommand( ShellCommandIds::ToggleXCUIDemoPanel, bindings.getXCUIDemoPanelVisible, bindings.setXCUIDemoPanelVisible, { XCUIEditorCommandAccelerator{ static_cast(KeyCode::One), ctrlOnly, true, false } }); bindToggleCommand( ShellCommandIds::ToggleXCUILayoutLabPanel, bindings.getXCUILayoutLabPanelVisible, bindings.setXCUILayoutLabPanelVisible, { XCUIEditorCommandAccelerator{ static_cast(KeyCode::Two), ctrlOnly, true, false } }); bindToggleCommand( ShellCommandIds::ToggleNativeBackdrop, bindings.getNativeBackdropVisible, bindings.setNativeBackdropVisible, { XCUIEditorCommandAccelerator{ static_cast(KeyCode::B), ctrlShift, true, false } }); bindToggleCommand( ShellCommandIds::TogglePulseAccent, bindings.getPulseAccentEnabled, bindings.setPulseAccentEnabled, { XCUIEditorCommandAccelerator{ static_cast(KeyCode::P), ctrlShift, true, false } }); bindToggleCommand( ShellCommandIds::ToggleNativeXCUIOverlay, bindings.getNativeXCUIOverlayVisible, bindings.setNativeXCUIOverlayVisible, { XCUIEditorCommandAccelerator{ static_cast(KeyCode::O), ctrlShift, true, false } }); bindToggleCommand( ShellCommandIds::ToggleHostedPreviewHud, bindings.getHostedPreviewHudVisible, bindings.setHostedPreviewHudVisible, { XCUIEditorCommandAccelerator{ static_cast(KeyCode::H), ctrlShift, true, false } }); bindToggleCommand( ShellCommandIds::ToggleNativeDemoPanelPreview, bindings.getNativeDemoPanelPreviewEnabled, bindings.setNativeDemoPanelPreviewEnabled, { XCUIEditorCommandAccelerator{ static_cast(KeyCode::One), ctrlAlt, true, false } }, bindings.onHostedPreviewModeChanged); bindToggleCommand( ShellCommandIds::ToggleNativeLayoutLabPreview, bindings.getNativeLayoutLabPreviewEnabled, bindings.setNativeLayoutLabPreviewEnabled, { XCUIEditorCommandAccelerator{ static_cast(KeyCode::Two), ctrlAlt, true, false } }, bindings.onHostedPreviewModeChanged); } static void BeginHostedPreviewFrameLifecycle( ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewQueue& previewQueue, ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceRegistry& surfaceRegistry) { previewQueue.BeginFrame(); surfaceRegistry.BeginFrame(); } static bool HasHostedPreviewTextureRegistration( const ::XCEngine::Editor::XCUIBackend::UITextureRegistration& registration) { return registration.texture.IsValid() || registration.cpuHandle.ptr != 0u || registration.gpuHandle.ptr != 0u; } static bool HasHostedPreviewPublishedTexture( const ::XCEngine::Editor::XCUIBackend::UITextureRegistration& registration) { return registration.texture.IsValid(); } static NativeHostedPreviewConsumption ResolveNativeHostedPreviewConsumption( bool nativeHostedPreview, bool hasHostedSurfaceDescriptor, bool showHostedSurfaceImage, std::string_view pendingTitle = {}, std::string_view pendingSubtitle = {}) { NativeHostedPreviewConsumption consumption = {}; if (!nativeHostedPreview) { return consumption; } consumption.queueRuntimeFrame = true; consumption.appendRuntimeDrawDataToShell = false; consumption.showSurfaceImage = showHostedSurfaceImage; consumption.drawRuntimeDebugRects = showHostedSurfaceImage; consumption.surfaceState = showHostedSurfaceImage ? NativeHostedPreviewSurfaceState::Live : (hasHostedSurfaceDescriptor ? NativeHostedPreviewSurfaceState::Warming : NativeHostedPreviewSurfaceState::AwaitingSubmit); if (!showHostedSurfaceImage) { consumption.placeholderTitle = pendingTitle; consumption.placeholderSubtitle = pendingSubtitle; } return consumption; } static std::string ComposeNativeHostedPreviewStatusLine( const NativeHostedPreviewConsumption& consumption, std::string_view status) { switch (consumption.surfaceState) { case NativeHostedPreviewSurfaceState::AwaitingSubmit: return std::string("Native surface awaiting submit | ") + std::string(status); case NativeHostedPreviewSurfaceState::Warming: return std::string("Native surface warming | ") + std::string(status); case NativeHostedPreviewSurfaceState::Live: return std::string("Native surface live | ") + std::string(status); case NativeHostedPreviewSurfaceState::Disabled: default: return std::string(status); } } int Run(HINSTANCE instance, int nCmdShow); private: struct HostedPreviewPanelDiagnostics { std::string debugName = {}; std::string debugSource = {}; bool visible = false; bool hostedPreviewEnabled = false; bool nativeRequested = false; bool nativePresenterBound = false; bool descriptorAvailable = false; bool surfaceImageAvailable = false; bool surfaceAllocated = false; bool surfaceReady = false; bool presentedThisFrame = false; bool queuedToNativePassThisFrame = false; std::uint32_t surfaceWidth = 0; std::uint32_t surfaceHeight = 0; float logicalWidth = 0.0f; float logicalHeight = 0.0f; std::size_t queuedFrameIndex = 0; std::size_t submittedDrawListCount = 0; std::size_t submittedCommandCount = 0; std::size_t flushedDrawListCount = 0; std::size_t flushedCommandCount = 0; }; struct HostedPreviewOffscreenSurface { std::string debugName = {}; std::uint32_t width = 0; std::uint32_t height = 0; ::XCEngine::RHI::RHITexture* colorTexture = nullptr; ::XCEngine::RHI::RHIResourceView* colorView = nullptr; ::XCEngine::Editor::XCUIBackend::UITextureRegistration textureRegistration = {}; ::XCEngine::RHI::ResourceStates colorState = ::XCEngine::RHI::ResourceStates::Common; bool IsReady() const { return !debugName.empty() && colorTexture != nullptr && colorView != nullptr && Application::HasHostedPreviewPublishedTexture(textureRegistration) && width > 0u && height > 0u; } }; static LRESULT CALLBACK StaticWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); bool CreateMainWindow(HINSTANCE instance, int nCmdShow); bool InitializeRenderer(); void InitializeWindowCompositor(); void InitializeNativeShell(); void ShutdownWindowCompositor(); void ShutdownRenderer(); void DestroyHostedPreviewSurfaces(); void SyncShellChromePanelStateFromPanels(); void SyncHostedPreviewSurfaces(); std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> CreateHostedPreviewPresenter( bool nativePreview); void ConfigureHostedPreviewPresenters(); const ShellPanelChromeState* TryGetShellPanelState(ShellPanelId panelId) const; bool IsShellViewToggleEnabled(ShellViewToggleId toggleId) const; void SetShellViewToggleEnabled(ShellViewToggleId toggleId, bool enabled); bool IsNativeHostedPreviewEnabled(ShellPanelId panelId) const; HostedPreviewPanelDiagnostics BuildHostedPreviewPanelDiagnostics( const char* debugName, const char* fallbackDebugSource, bool visible, bool hostedPreviewEnabled, bool nativeRequested, bool nativePresenterBound, const ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats& previewStats) const; HostedPreviewOffscreenSurface* FindHostedPreviewSurface(const std::string& debugName); const HostedPreviewOffscreenSurface* FindHostedPreviewSurface(const std::string& debugName) const; HostedPreviewOffscreenSurface& FindOrAddHostedPreviewSurface(const std::string& debugName); bool EnsureHostedPreviewSurface( HostedPreviewOffscreenSurface& previewSurface, std::uint32_t width, std::uint32_t height); bool RenderHostedPreviewOffscreenSurface( HostedPreviewOffscreenSurface& previewSurface, const ::XCEngine::Rendering::RenderContext& renderContext, const ::XCEngine::UI::UIDrawData& drawData); void ResetCompatibilityHostPanels(); void ConfigureShellCommandRouter(); ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta DispatchShellShortcuts( const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot& snapshot); bool IsNativeWindowHostEnabled() const; void InitializePanelsForActiveWindowHost(); void InitializeCompatibilityHostPanels(); void RenderCompatibilityHostUiFrame(); ::XCEngine::UI::UIDrawData BuildNativeShellDrawData( const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot& shellSnapshot, const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta& shellFrameDelta); void FrameCompatibilityHost(); void FrameNativeXCUIHost(); void RenderShellChrome(); void RenderHostedPreviewHud(); void RenderQueuedHostedPreviews( const ::XCEngine::Rendering::RenderContext& renderContext, const ::XCEngine::Rendering::RenderSurface& surface); void Frame(); HWND m_hwnd = nullptr; ::XCEngine::Editor::Platform::D3D12WindowRenderer m_windowRenderer; std::unique_ptr<::XCEngine::Editor::XCUIBackend::IWindowUICompositor> m_windowCompositor; std::unique_ptr m_demoPanel; std::unique_ptr m_layoutLabPanel; ::XCEngine::Editor::XCUIBackend::XCUIWin32InputSource m_xcuiInputSource; ::XCEngine::Editor::XCUIBackend::XCUIInputBridge m_shellInputBridge; ::XCEngine::Editor::XCUIBackend::XCUIEditorCommandRouter m_shellCommandRouter; ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewQueue m_hostedPreviewQueue; ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceRegistry m_hostedPreviewSurfaceRegistry; ::XCEngine::Editor::XCUIBackend::XCUIStandaloneTextAtlasProvider m_hostedPreviewTextAtlasProvider; ::XCEngine::Editor::XCUIBackend::XCUIRHIRenderBackend m_hostedPreviewRenderBackend; ShellChromeState m_shellChromeState = {}; std::vector m_hostedPreviewSurfaces = {}; WindowHostMode m_windowHostMode = WindowHostMode::NativeXCUI; ::XCEngine::Editor::XCUIBackend::NativeXCUIPanelCanvasHost m_nativeDemoCanvasHost; ::XCEngine::Editor::XCUIBackend::NativeXCUIPanelCanvasHost m_nativeLayoutCanvasHost; ShellPanelId m_nativeActivePanel = ShellPanelId::XCUIDemo; bool m_legacyHostDemoWindowVisible = false; ::XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime m_nativeOverlayRuntime; MainWindowNativeBackdropRenderer m_nativeBackdropRenderer; bool m_running = false; bool m_renderReady = false; std::chrono::steady_clock::time_point m_startTime = {}; }; } // namespace NewEditor } // namespace XCEngine