Files
XCEngine/tests/NewEditor/test_application_shell_command_bindings.cpp

251 lines
12 KiB
C++
Raw Normal View History

#include "Application.h"
#include <XCEngine/Input/InputTypes.h>
#include <gtest/gtest.h>
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<std::size_t>(panelId);
}
struct ShellCommandHarness {
Application::ShellViewToggleState viewToggles = {};
std::array<Application::ShellPanelChromeState, static_cast<std::size_t>(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<std::int32_t>(KeyCode::One));
frameDelta.keyboard.repeatedKeys.push_back(static_cast<std::int32_t>(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<std::int32_t>(KeyCode::One)));
EXPECT_TRUE(snapshot.IsKeyDown(static_cast<std::int32_t>(KeyCode::Two)));
const auto* repeatedKey = snapshot.FindKeyState(static_cast<std::int32_t>(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<std::int32_t>(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<std::int32_t>(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<std::int32_t>(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<std::int32_t>(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<std::int32_t>(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<std::int32_t>(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