Integrate XCUI shell state and runtime frame seams

This commit is contained in:
2026-04-05 12:50:55 +08:00
parent ec97445071
commit e5e9f348a3
29 changed files with 3183 additions and 102 deletions

View File

@@ -1,5 +1,8 @@
#pragma once
#include <XCEngine/Input/InputTypes.h>
#include "XCUIBackend/XCUIEditorCommandRouter.h"
#include "panels/XCUIDemoPanel.h"
#include "panels/XCUILayoutLabPanel.h"
@@ -10,10 +13,14 @@
#include "XCUIBackend/XCUIInputBridge.h"
#include "XCUIBackend/XCUILayoutLabRuntime.h"
#include "XCUIBackend/XCUIRHIRenderBackend.h"
#include "XCUIBackend/XCUIShellChromeState.h"
#include "XCUIBackend/XCUIStandaloneTextAtlasProvider.h"
#include <array>
#include <chrono>
#include <cstdint>
#include <functional>
#include <initializer_list>
#include <memory>
#include <string>
#include <vector>
@@ -24,9 +31,252 @@ namespace NewEditor {
class Application {
public:
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;
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* ToggleImGuiDemoWindow =
::XCEngine::Editor::XCUIBackend::XCUIShellChromeCommandIds::ToggleImGuiDemoWindow;
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()> getImGuiDemoWindowVisible = {};
std::function<void(bool)> setImGuiDemoWindowVisible = {};
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 = {};
};
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::ToggleImGuiDemoWindow,
bindings.getImGuiDemoWindowVisible,
bindings.setImGuiDemoWindowVisible,
{ XCUIEditorCommandAccelerator{
static_cast<std::int32_t>(KeyCode::Three),
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);
}
int Run(HINSTANCE instance, int nCmdShow);
private:
using ShellPanelStateArray = std::array<ShellPanelChromeState, static_cast<std::size_t>(ShellPanelId::Count)>;
static constexpr std::size_t GetShellPanelIndex(ShellPanelId panelId) {
return static_cast<std::size_t>(panelId);
}
static ShellPanelStateArray CreateDefaultShellPanelStates() {
ShellPanelStateArray panels = {};
panels[GetShellPanelIndex(ShellPanelId::XCUIDemo)] = {
ShellPanelId::XCUIDemo,
"XCUI Demo",
"XCUI Demo",
"new_editor.panels.xcui_demo",
true,
true,
ShellHostedPreviewMode::NativeOffscreen
};
panels[GetShellPanelIndex(ShellPanelId::XCUILayoutLab)] = {
ShellPanelId::XCUILayoutLab,
"XCUI Layout Lab",
"XCUI Layout Lab",
"new_editor.panels.xcui_layout_lab",
true,
true,
ShellHostedPreviewMode::LegacyImGui
};
return panels;
}
struct HostedPreviewPanelDiagnostics {
std::string debugName = {};
std::string debugSource = {};
@@ -79,10 +329,16 @@ private:
void ShutdownWindowCompositor();
void ShutdownRenderer();
void DestroyHostedPreviewSurfaces();
void SyncShellChromePanelStateFromPanels();
void SyncHostedPreviewSurfaces();
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter> CreateHostedPreviewPresenter(
bool nativePreview);
void ConfigureHostedPreviewPresenters();
ShellPanelChromeState* TryGetShellPanelState(ShellPanelId panelId);
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,
@@ -102,6 +358,8 @@ private:
HostedPreviewOffscreenSurface& previewSurface,
const ::XCEngine::Rendering::RenderContext& renderContext,
const ::XCEngine::UI::UIDrawData& drawData);
void ConfigureShellCommandRouter();
void DispatchShellShortcuts();
void RenderShellChrome();
void RenderHostedPreviewHud();
void RenderQueuedHostedPreviews(
@@ -115,20 +373,17 @@ private:
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;
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewQueue m_hostedPreviewQueue;
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceRegistry m_hostedPreviewSurfaceRegistry;
::XCEngine::Editor::XCUIBackend::XCUIStandaloneTextAtlasProvider m_hostedPreviewTextAtlasProvider;
::XCEngine::Editor::XCUIBackend::XCUIRHIRenderBackend m_hostedPreviewRenderBackend;
ShellViewToggleState m_shellViewToggles = {};
ShellPanelStateArray m_shellPanels = CreateDefaultShellPanelStates();
std::vector<HostedPreviewOffscreenSurface> m_hostedPreviewSurfaces = {};
::XCEngine::Editor::XCUIBackend::XCUILayoutLabRuntime m_nativeOverlayRuntime;
MainWindowNativeBackdropRenderer m_nativeBackdropRenderer;
bool m_showImGuiDemoWindow = false;
bool m_showNativeBackdrop = true;
bool m_pulseNativeBackdropAccent = true;
bool m_showNativeXCUIOverlay = true;
bool m_showHostedPreviewHud = true;
bool m_showNativeDemoPanelPreview = true;
bool m_showNativeLayoutLabPreview = false;
bool m_running = false;
bool m_renderReady = false;
std::chrono::steady_clock::time_point m_startTime = {};