#include "Application.h" #include #include namespace { using XCEngine::Editor::XCUIBackend::XCUIEditorCommandRouter; using XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta; using XCEngine::Input::KeyCode; using XCEngine::NewEditor::Application; constexpr std::size_t ToPanelIndex(Application::ShellPanelId panelId) { return static_cast(panelId); } struct ShellCommandHarness { Application::ShellViewToggleState viewToggles = {}; std::array(Application::ShellPanelId::Count)> panels = {}; int hostedPreviewReconfigureCount = 0; ShellCommandHarness() { panels[ToPanelIndex(Application::ShellPanelId::XCUIDemo)] = { Application::ShellPanelId::XCUIDemo, "XCUI Demo", "XCUI Demo", "new_editor.panels.xcui_demo", true, true, Application::ShellHostedPreviewMode::NativeOffscreen }; panels[ToPanelIndex(Application::ShellPanelId::XCUILayoutLab)] = { Application::ShellPanelId::XCUILayoutLab, "XCUI Layout Lab", "XCUI Layout Lab", "new_editor.panels.xcui_layout_lab", true, true, Application::ShellHostedPreviewMode::LegacyImGui }; } Application::ShellPanelChromeState& Panel(Application::ShellPanelId panelId) { return panels[ToPanelIndex(panelId)]; } const Application::ShellPanelChromeState& Panel(Application::ShellPanelId panelId) const { return panels[ToPanelIndex(panelId)]; } Application::ShellCommandBindings BuildBindings() { Application::ShellCommandBindings bindings = {}; bindings.getXCUIDemoPanelVisible = [this]() { return Panel(Application::ShellPanelId::XCUIDemo).visible; }; bindings.setXCUIDemoPanelVisible = [this](bool visible) { Panel(Application::ShellPanelId::XCUIDemo).visible = visible; }; bindings.getXCUILayoutLabPanelVisible = [this]() { return Panel(Application::ShellPanelId::XCUILayoutLab).visible; }; bindings.setXCUILayoutLabPanelVisible = [this](bool visible) { Panel(Application::ShellPanelId::XCUILayoutLab).visible = visible; }; bindings.getImGuiDemoWindowVisible = [this]() { return viewToggles.imguiDemoWindowVisible; }; bindings.setImGuiDemoWindowVisible = [this](bool visible) { viewToggles.imguiDemoWindowVisible = visible; }; bindings.getNativeBackdropVisible = [this]() { return viewToggles.nativeBackdropVisible; }; bindings.setNativeBackdropVisible = [this](bool visible) { viewToggles.nativeBackdropVisible = visible; }; bindings.getPulseAccentEnabled = [this]() { return viewToggles.pulseAccentEnabled; }; bindings.setPulseAccentEnabled = [this](bool enabled) { viewToggles.pulseAccentEnabled = enabled; }; bindings.getNativeXCUIOverlayVisible = [this]() { return viewToggles.nativeXCUIOverlayVisible; }; bindings.setNativeXCUIOverlayVisible = [this](bool visible) { viewToggles.nativeXCUIOverlayVisible = visible; }; bindings.getHostedPreviewHudVisible = [this]() { return viewToggles.hostedPreviewHudVisible; }; bindings.setHostedPreviewHudVisible = [this](bool visible) { viewToggles.hostedPreviewHudVisible = visible; }; bindings.getNativeDemoPanelPreviewEnabled = [this]() { return Panel(Application::ShellPanelId::XCUIDemo).previewMode == Application::ShellHostedPreviewMode::NativeOffscreen; }; bindings.setNativeDemoPanelPreviewEnabled = [this](bool enabled) { Panel(Application::ShellPanelId::XCUIDemo).previewMode = enabled ? Application::ShellHostedPreviewMode::NativeOffscreen : Application::ShellHostedPreviewMode::LegacyImGui; }; bindings.getNativeLayoutLabPreviewEnabled = [this]() { return Panel(Application::ShellPanelId::XCUILayoutLab).previewMode == Application::ShellHostedPreviewMode::NativeOffscreen; }; bindings.setNativeLayoutLabPreviewEnabled = [this](bool enabled) { Panel(Application::ShellPanelId::XCUILayoutLab).previewMode = enabled ? Application::ShellHostedPreviewMode::NativeOffscreen : Application::ShellHostedPreviewMode::LegacyImGui; }; bindings.onHostedPreviewModeChanged = [this]() { ++hostedPreviewReconfigureCount; }; return bindings; } }; TEST(ApplicationShellCommandBindingsTest, RegisterShellViewCommandsInvokesBoundToggleHandlers) { ShellCommandHarness harness = {}; XCUIEditorCommandRouter router = {}; Application::RegisterShellViewCommands(router, harness.BuildBindings()); EXPECT_TRUE(router.HasCommand(Application::ShellCommandIds::ToggleXCUIDemoPanel)); EXPECT_TRUE(router.HasCommand(Application::ShellCommandIds::ToggleXCUILayoutLabPanel)); EXPECT_TRUE(router.HasCommand(Application::ShellCommandIds::ToggleImGuiDemoWindow)); EXPECT_TRUE(router.HasCommand(Application::ShellCommandIds::ToggleNativeBackdrop)); EXPECT_TRUE(router.HasCommand(Application::ShellCommandIds::TogglePulseAccent)); EXPECT_TRUE(router.HasCommand(Application::ShellCommandIds::ToggleNativeXCUIOverlay)); EXPECT_TRUE(router.HasCommand(Application::ShellCommandIds::ToggleHostedPreviewHud)); EXPECT_TRUE(router.HasCommand(Application::ShellCommandIds::ToggleNativeDemoPanelPreview)); EXPECT_TRUE(router.HasCommand(Application::ShellCommandIds::ToggleNativeLayoutLabPreview)); EXPECT_TRUE(router.InvokeCommand(Application::ShellCommandIds::ToggleXCUIDemoPanel)); EXPECT_FALSE(harness.Panel(Application::ShellPanelId::XCUIDemo).visible); EXPECT_TRUE(router.InvokeCommand(Application::ShellCommandIds::ToggleImGuiDemoWindow)); EXPECT_TRUE(harness.viewToggles.imguiDemoWindowVisible); EXPECT_TRUE(router.InvokeCommand(Application::ShellCommandIds::ToggleNativeBackdrop)); EXPECT_FALSE(harness.viewToggles.nativeBackdropVisible); EXPECT_TRUE(router.InvokeCommand(Application::ShellCommandIds::ToggleNativeXCUIOverlay)); EXPECT_FALSE(harness.viewToggles.nativeXCUIOverlayVisible); EXPECT_TRUE(router.InvokeCommand(Application::ShellCommandIds::ToggleHostedPreviewHud)); EXPECT_FALSE(harness.viewToggles.hostedPreviewHudVisible); } TEST(ApplicationShellCommandBindingsTest, PreviewModeCommandsTriggerHostedPreviewReconfigureCallback) { ShellCommandHarness harness = {}; XCUIEditorCommandRouter router = {}; Application::RegisterShellViewCommands(router, harness.BuildBindings()); EXPECT_TRUE(router.InvokeCommand(Application::ShellCommandIds::ToggleNativeDemoPanelPreview)); EXPECT_EQ( harness.Panel(Application::ShellPanelId::XCUIDemo).previewMode, Application::ShellHostedPreviewMode::LegacyImGui); EXPECT_EQ(harness.hostedPreviewReconfigureCount, 1); EXPECT_TRUE(router.InvokeCommand(Application::ShellCommandIds::ToggleNativeLayoutLabPreview)); EXPECT_EQ( harness.Panel(Application::ShellPanelId::XCUILayoutLab).previewMode, Application::ShellHostedPreviewMode::NativeOffscreen); EXPECT_EQ(harness.hostedPreviewReconfigureCount, 2); } TEST(ApplicationShellCommandBindingsTest, BuildShellShortcutSnapshotCarriesBridgeStateAndKeyboardEdges) { XCUIInputBridgeFrameDelta frameDelta = {}; frameDelta.state.windowFocused = true; frameDelta.state.wantCaptureKeyboard = true; frameDelta.state.wantTextInput = true; frameDelta.state.modifiers.control = true; frameDelta.state.modifiers.shift = true; frameDelta.keyboard.pressedKeys.push_back(static_cast(KeyCode::One)); frameDelta.keyboard.repeatedKeys.push_back(static_cast(KeyCode::Two)); const auto snapshot = Application::BuildShellShortcutSnapshot(frameDelta); EXPECT_TRUE(snapshot.windowFocused); EXPECT_TRUE(snapshot.wantCaptureKeyboard); EXPECT_TRUE(snapshot.wantTextInput); EXPECT_TRUE(snapshot.modifiers.control); EXPECT_TRUE(snapshot.modifiers.shift); EXPECT_TRUE(snapshot.IsKeyDown(static_cast(KeyCode::One))); EXPECT_TRUE(snapshot.IsKeyDown(static_cast(KeyCode::Two))); const auto* repeatedKey = snapshot.FindKeyState(static_cast(KeyCode::Two)); ASSERT_NE(repeatedKey, nullptr); EXPECT_TRUE(repeatedKey->repeat); } TEST(ApplicationShellCommandBindingsTest, RegisteredShellShortcutsMatchExpectedViewCommands) { ShellCommandHarness harness = {}; XCUIEditorCommandRouter router = {}; Application::RegisterShellViewCommands(router, harness.BuildBindings()); XCUIInputBridgeFrameDelta frameDelta = {}; frameDelta.state.windowFocused = true; frameDelta.state.modifiers.control = true; frameDelta.keyboard.pressedKeys.push_back(static_cast(KeyCode::One)); const auto demoSnapshot = Application::BuildShellShortcutSnapshot(frameDelta); const auto demoMatch = router.MatchShortcut({ &demoSnapshot }); ASSERT_TRUE(demoMatch.matched); EXPECT_EQ(demoMatch.commandId, Application::ShellCommandIds::ToggleXCUIDemoPanel); XCUIInputBridgeFrameDelta previewDelta = {}; previewDelta.state.windowFocused = true; previewDelta.state.modifiers.control = true; previewDelta.state.modifiers.alt = true; previewDelta.keyboard.pressedKeys.push_back(static_cast(KeyCode::Two)); const auto previewSnapshot = Application::BuildShellShortcutSnapshot(previewDelta); const auto previewMatch = router.MatchShortcut({ &previewSnapshot }); ASSERT_TRUE(previewMatch.matched); EXPECT_EQ(previewMatch.commandId, Application::ShellCommandIds::ToggleNativeLayoutLabPreview); } TEST(ApplicationShellCommandBindingsTest, RegisteredShellShortcutsRespectCaptureAndRepeatGuards) { ShellCommandHarness harness = {}; XCUIEditorCommandRouter router = {}; Application::RegisterShellViewCommands(router, harness.BuildBindings()); XCUIInputBridgeFrameDelta capturedDelta = {}; capturedDelta.state.windowFocused = true; capturedDelta.state.wantCaptureKeyboard = true; capturedDelta.state.modifiers.control = true; capturedDelta.keyboard.pressedKeys.push_back(static_cast(KeyCode::One)); const auto capturedSnapshot = Application::BuildShellShortcutSnapshot(capturedDelta); EXPECT_FALSE(router.MatchShortcut({ &capturedSnapshot }).matched); XCUIInputBridgeFrameDelta textInputDelta = {}; textInputDelta.state.windowFocused = true; textInputDelta.state.wantTextInput = true; textInputDelta.state.modifiers.control = true; textInputDelta.keyboard.pressedKeys.push_back(static_cast(KeyCode::One)); const auto textInputSnapshot = Application::BuildShellShortcutSnapshot(textInputDelta); EXPECT_FALSE(router.MatchShortcut({ &textInputSnapshot }).matched); XCUIInputBridgeFrameDelta repeatedDelta = {}; repeatedDelta.state.windowFocused = true; repeatedDelta.state.modifiers.control = true; repeatedDelta.keyboard.repeatedKeys.push_back(static_cast(KeyCode::One)); const auto repeatedSnapshot = Application::BuildShellShortcutSnapshot(repeatedDelta); EXPECT_FALSE(router.MatchShortcut({ &repeatedSnapshot }).matched); } TEST(ApplicationShellCommandBindingsTest, PreviewShortcutInvokesCommandHandlerAndReconfigureCallback) { ShellCommandHarness harness = {}; XCUIEditorCommandRouter router = {}; Application::RegisterShellViewCommands(router, harness.BuildBindings()); XCUIInputBridgeFrameDelta previewDelta = {}; previewDelta.state.windowFocused = true; previewDelta.state.modifiers.control = true; previewDelta.state.modifiers.alt = true; previewDelta.keyboard.pressedKeys.push_back(static_cast(KeyCode::One)); const auto previewSnapshot = Application::BuildShellShortcutSnapshot(previewDelta); EXPECT_TRUE(router.InvokeMatchingShortcut({ &previewSnapshot })); EXPECT_EQ( harness.Panel(Application::ShellPanelId::XCUIDemo).previewMode, Application::ShellHostedPreviewMode::LegacyImGui); EXPECT_EQ(harness.hostedPreviewReconfigureCount, 1); } } // namespace