Files
XCEngine/new_editor/src/Application.h

464 lines
20 KiB
C
Raw Normal View History

2026-04-05 04:55:25 +08:00
#pragma once
#include <XCEngine/Input/InputTypes.h>
#include "XCUIBackend/XCUIEditorCommandRouter.h"
#include "XCUIBackend/NativeXCUIPanelCanvasHost.h"
2026-04-05 04:55:25 +08:00
#include "XCUIBackend/XCUIHostedPreviewPresenter.h"
#include "XCUIBackend/XCUIInputBridge.h"
#include "XCUIBackend/XCUILayoutLabRuntime.h"
#include "XCUIBackend/XCUIRHIRenderBackend.h"
#include "XCUIBackend/XCUIShellChromeState.h"
2026-04-05 04:55:25 +08:00
#include "XCUIBackend/XCUIStandaloneTextAtlasProvider.h"
#include "XCUIBackend/UITextureRegistration.h"
#include "Platform/D3D12WindowRenderer.h"
#include "Rendering/MainWindowNativeBackdropRenderer.h"
2026-04-05 04:55:25 +08:00
#include <chrono>
#include <cstdint>
#include <functional>
#include <initializer_list>
2026-04-05 04:55:25 +08:00
#include <memory>
#include <string>
#include <string_view>
2026-04-05 04:55:25 +08:00
#include <vector>
#include <windows.h>
namespace XCEngine {
namespace Editor {
namespace XCUIBackend {
class IWindowUICompositor;
} // namespace XCUIBackend
} // namespace Editor
2026-04-05 04:55:25 +08:00
namespace NewEditor {
class XCUIDemoPanel;
class XCUILayoutLabPanel;
2026-04-05 04:55:25 +08:00
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<bool()> getXCUIDemoPanelVisible = {};
std::function<void(bool)> setXCUIDemoPanelVisible = {};
std::function<bool()> getXCUILayoutLabPanelVisible = {};
std::function<void(bool)> setXCUILayoutLabPanelVisible = {};
std::function<bool()> getNativeBackdropVisible = {};
std::function<void(bool)> setNativeBackdropVisible = {};
std::function<bool()> getPulseAccentEnabled = {};
std::function<void(bool)> setPulseAccentEnabled = {};
std::function<bool()> getNativeXCUIOverlayVisible = {};
std::function<void(bool)> setNativeXCUIOverlayVisible = {};
std::function<bool()> getHostedPreviewHudVisible = {};
std::function<void(bool)> setHostedPreviewHudVisible = {};
std::function<bool()> getNativeDemoPanelPreviewEnabled = {};
std::function<void(bool)> setNativeDemoPanelPreviewEnabled = {};
std::function<bool()> getNativeLayoutLabPreviewEnabled = {};
std::function<void(bool)> setNativeLayoutLabPreviewEnabled = {};
std::function<void()> 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<bool()>& getter,
const std::function<void(bool)>& setter,
std::initializer_list<XCUIEditorCommandAccelerator> accelerators,
const std::function<void()>& afterToggle = {}) {
if (!getter || !setter) {
return;
}
XCUIEditorCommandDefinition definition = {};
definition.commandId = commandId;
definition.isEnabled = [getter, setter]() {
return static_cast<bool>(getter) && static_cast<bool>(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<std::int32_t>(KeyCode::One),
ctrlOnly,
true,
false } });
bindToggleCommand(
ShellCommandIds::ToggleXCUILayoutLabPanel,
bindings.getXCUILayoutLabPanelVisible,
bindings.setXCUILayoutLabPanelVisible,
{ XCUIEditorCommandAccelerator{
static_cast<std::int32_t>(KeyCode::Two),
ctrlOnly,
true,
false } });
bindToggleCommand(
ShellCommandIds::ToggleNativeBackdrop,
bindings.getNativeBackdropVisible,
bindings.setNativeBackdropVisible,
{ XCUIEditorCommandAccelerator{
static_cast<std::int32_t>(KeyCode::B),
ctrlShift,
true,
false } });
bindToggleCommand(
ShellCommandIds::TogglePulseAccent,
bindings.getPulseAccentEnabled,
bindings.setPulseAccentEnabled,
{ XCUIEditorCommandAccelerator{
static_cast<std::int32_t>(KeyCode::P),
ctrlShift,
true,
false } });
bindToggleCommand(
ShellCommandIds::ToggleNativeXCUIOverlay,
bindings.getNativeXCUIOverlayVisible,
bindings.setNativeXCUIOverlayVisible,
{ XCUIEditorCommandAccelerator{
static_cast<std::int32_t>(KeyCode::O),
ctrlShift,
true,
false } });
bindToggleCommand(
ShellCommandIds::ToggleHostedPreviewHud,
bindings.getHostedPreviewHudVisible,
bindings.setHostedPreviewHudVisible,
{ XCUIEditorCommandAccelerator{
static_cast<std::int32_t>(KeyCode::H),
ctrlShift,
true,
false } });
bindToggleCommand(
ShellCommandIds::ToggleNativeDemoPanelPreview,
bindings.getNativeDemoPanelPreviewEnabled,
bindings.setNativeDemoPanelPreviewEnabled,
{ XCUIEditorCommandAccelerator{
static_cast<std::int32_t>(KeyCode::One),
ctrlAlt,
true,
false } },
bindings.onHostedPreviewModeChanged);
bindToggleCommand(
ShellCommandIds::ToggleNativeLayoutLabPreview,
bindings.getNativeLayoutLabPreviewEnabled,
bindings.setNativeLayoutLabPreviewEnabled,
{ XCUIEditorCommandAccelerator{
static_cast<std::int32_t>(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);
}
}
2026-04-05 04:55:25 +08:00
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 = {};
2026-04-05 04:55:25 +08:00
::XCEngine::RHI::ResourceStates colorState = ::XCEngine::RHI::ResourceStates::Common;
bool IsReady() const {
return !debugName.empty() &&
colorTexture != nullptr &&
colorView != nullptr &&
Application::HasHostedPreviewPublishedTexture(textureRegistration) &&
2026-04-05 04:55:25 +08:00
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();
2026-04-05 06:15:24 +08:00
void InitializeWindowCompositor();
void InitializeNativeShell();
2026-04-05 06:15:24 +08:00
void ShutdownWindowCompositor();
2026-04-05 04:55:25 +08:00
void ShutdownRenderer();
void DestroyHostedPreviewSurfaces();
void SyncShellChromePanelStateFromPanels();
2026-04-05 04:55:25 +08:00
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;
2026-04-05 04:55:25 +08:00
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();
2026-04-05 04:55:25 +08:00
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;
2026-04-05 06:15:24 +08:00
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IWindowUICompositor> m_windowCompositor;
2026-04-05 04:55:25 +08:00
std::unique_ptr<XCUIDemoPanel> m_demoPanel;
std::unique_ptr<XCUILayoutLabPanel> m_layoutLabPanel;
::XCEngine::Editor::XCUIBackend::XCUIWin32InputSource m_xcuiInputSource;
::XCEngine::Editor::XCUIBackend::XCUIInputBridge m_shellInputBridge;
::XCEngine::Editor::XCUIBackend::XCUIEditorCommandRouter m_shellCommandRouter;
2026-04-05 04:55:25 +08:00
::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 = {};
2026-04-05 04:55:25 +08:00
std::vector<HostedPreviewOffscreenSurface> 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;
2026-04-05 04:55:25 +08:00
::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