206 lines
8.2 KiB
C++
206 lines
8.2 KiB
C++
#include "XCUIBackend/XCUIEditorCommandRouter.h"
|
|
|
|
#include <XCEngine/Input/InputTypes.h>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
namespace {
|
|
|
|
using XCEngine::Editor::XCUIBackend::XCUIEditorCommandAccelerator;
|
|
using XCEngine::Editor::XCUIBackend::XCUIEditorCommandDefinition;
|
|
using XCEngine::Editor::XCUIBackend::XCUIEditorCommandInputSnapshot;
|
|
using XCEngine::Editor::XCUIBackend::XCUIEditorCommandKeyState;
|
|
using XCEngine::Editor::XCUIBackend::XCUIEditorCommandRouter;
|
|
using XCEngine::Editor::XCUIBackend::XCUIEditorCommandShortcutMatch;
|
|
using XCEngine::Editor::XCUIBackend::XCUIEditorCommandShortcutQuery;
|
|
using XCEngine::Input::KeyCode;
|
|
|
|
XCUIEditorCommandKeyState MakeKeyState(std::int32_t keyCode, bool down, bool repeat = false) {
|
|
XCUIEditorCommandKeyState state = {};
|
|
state.keyCode = keyCode;
|
|
state.down = down;
|
|
state.repeat = repeat;
|
|
return state;
|
|
}
|
|
|
|
XCUIEditorCommandInputSnapshot MakeSnapshot(
|
|
std::initializer_list<XCUIEditorCommandKeyState> keys,
|
|
const XCEngine::UI::UIInputModifiers& modifiers = {},
|
|
bool windowFocused = true) {
|
|
XCUIEditorCommandInputSnapshot snapshot = {};
|
|
snapshot.keys.assign(keys.begin(), keys.end());
|
|
snapshot.modifiers = modifiers;
|
|
snapshot.windowFocused = windowFocused;
|
|
return snapshot;
|
|
}
|
|
|
|
TEST(XCUIEditorCommandRouterTest, RegisterInvokeAndUnregisterTrackCommandsById) {
|
|
XCUIEditorCommandRouter router = {};
|
|
int invokeCount = 0;
|
|
|
|
XCUIEditorCommandDefinition definition = {};
|
|
definition.commandId = "xcui.file.save";
|
|
definition.invoke = [&invokeCount]() { ++invokeCount; };
|
|
|
|
EXPECT_TRUE(router.RegisterCommand(definition));
|
|
EXPECT_EQ(router.GetCommandCount(), 1u);
|
|
EXPECT_TRUE(router.HasCommand("xcui.file.save"));
|
|
EXPECT_TRUE(router.IsCommandEnabled("xcui.file.save"));
|
|
EXPECT_TRUE(router.InvokeCommand("xcui.file.save"));
|
|
EXPECT_EQ(invokeCount, 1);
|
|
|
|
EXPECT_TRUE(router.UnregisterCommand("xcui.file.save"));
|
|
EXPECT_FALSE(router.HasCommand("xcui.file.save"));
|
|
EXPECT_FALSE(router.InvokeCommand("xcui.file.save"));
|
|
EXPECT_EQ(router.GetCommandCount(), 0u);
|
|
}
|
|
|
|
TEST(XCUIEditorCommandRouterTest, RegisterRejectsMissingIdOrHandlerAndDuplicateIdReplacesEntry) {
|
|
XCUIEditorCommandRouter router = {};
|
|
int invokeCount = 0;
|
|
|
|
XCUIEditorCommandDefinition invalid = {};
|
|
invalid.commandId = "xcui.invalid";
|
|
EXPECT_FALSE(router.RegisterCommand(invalid));
|
|
|
|
invalid = {};
|
|
invalid.invoke = []() {};
|
|
EXPECT_FALSE(router.RegisterCommand(invalid));
|
|
|
|
XCUIEditorCommandDefinition first = {};
|
|
first.commandId = "xcui.edit.rename";
|
|
first.invoke = [&invokeCount]() { invokeCount += 1; };
|
|
first.accelerators.push_back({ static_cast<std::int32_t>(KeyCode::F2), {}, true, false });
|
|
ASSERT_TRUE(router.RegisterCommand(first));
|
|
|
|
XCUIEditorCommandDefinition replacement = {};
|
|
replacement.commandId = "xcui.edit.rename";
|
|
replacement.invoke = [&invokeCount]() { invokeCount += 10; };
|
|
replacement.accelerators.push_back({ static_cast<std::int32_t>(KeyCode::Enter), {}, true, false });
|
|
ASSERT_TRUE(router.RegisterCommand(replacement));
|
|
|
|
EXPECT_EQ(router.GetCommandCount(), 1u);
|
|
EXPECT_TRUE(router.InvokeCommand("xcui.edit.rename"));
|
|
EXPECT_EQ(invokeCount, 10);
|
|
|
|
const XCUIEditorCommandInputSnapshot f2Snapshot =
|
|
MakeSnapshot({ MakeKeyState(static_cast<std::int32_t>(KeyCode::F2), true) });
|
|
const auto f2Match = router.MatchShortcut({ &f2Snapshot });
|
|
EXPECT_FALSE(f2Match.matched);
|
|
|
|
const XCUIEditorCommandInputSnapshot enterSnapshot =
|
|
MakeSnapshot({ MakeKeyState(static_cast<std::int32_t>(KeyCode::Enter), true) });
|
|
const auto enterMatch = router.MatchShortcut({ &enterSnapshot });
|
|
EXPECT_TRUE(enterMatch.matched);
|
|
EXPECT_EQ(enterMatch.commandId, "xcui.edit.rename");
|
|
}
|
|
|
|
TEST(XCUIEditorCommandRouterTest, DisabledPredicateBlocksDirectAndShortcutInvocation) {
|
|
XCUIEditorCommandRouter router = {};
|
|
bool enabled = false;
|
|
int invokeCount = 0;
|
|
|
|
XCUIEditorCommandDefinition definition = {};
|
|
definition.commandId = "xcui.edit.delete";
|
|
definition.invoke = [&invokeCount]() { ++invokeCount; };
|
|
definition.isEnabled = [&enabled]() { return enabled; };
|
|
definition.accelerators.push_back({ static_cast<std::int32_t>(KeyCode::Delete), {}, true, false });
|
|
ASSERT_TRUE(router.RegisterCommand(definition));
|
|
|
|
const XCUIEditorCommandInputSnapshot snapshot =
|
|
MakeSnapshot({ MakeKeyState(static_cast<std::int32_t>(KeyCode::Delete), true) });
|
|
EXPECT_FALSE(router.IsCommandEnabled("xcui.edit.delete"));
|
|
EXPECT_FALSE(router.InvokeCommand("xcui.edit.delete"));
|
|
EXPECT_FALSE(router.MatchShortcut({ &snapshot }).matched);
|
|
EXPECT_FALSE(router.InvokeMatchingShortcut({ &snapshot }));
|
|
EXPECT_EQ(invokeCount, 0);
|
|
|
|
enabled = true;
|
|
EXPECT_TRUE(router.IsCommandEnabled("xcui.edit.delete"));
|
|
EXPECT_TRUE(router.InvokeMatchingShortcut({ &snapshot }));
|
|
EXPECT_EQ(invokeCount, 1);
|
|
}
|
|
|
|
TEST(XCUIEditorCommandRouterTest, ShortcutMatchingRespectsModifiersRepeatAndPolicyFlags) {
|
|
XCUIEditorCommandRouter router = {};
|
|
int invokeCount = 0;
|
|
|
|
XCEngine::UI::UIInputModifiers modifiers = {};
|
|
modifiers.control = true;
|
|
modifiers.shift = true;
|
|
|
|
XCUIEditorCommandDefinition definition = {};
|
|
definition.commandId = "xcui.search.command_palette";
|
|
definition.invoke = [&invokeCount]() { ++invokeCount; };
|
|
definition.accelerators.push_back({
|
|
static_cast<std::int32_t>(KeyCode::P),
|
|
modifiers,
|
|
true,
|
|
false });
|
|
ASSERT_TRUE(router.RegisterCommand(definition));
|
|
|
|
const XCUIEditorCommandInputSnapshot exactSnapshot = MakeSnapshot(
|
|
{ MakeKeyState(static_cast<std::int32_t>(KeyCode::P), true) },
|
|
modifiers);
|
|
auto match = router.MatchShortcut({ &exactSnapshot });
|
|
ASSERT_TRUE(match.matched);
|
|
EXPECT_EQ(match.commandId, "xcui.search.command_palette");
|
|
|
|
const XCUIEditorCommandInputSnapshot repeatedSnapshot = MakeSnapshot(
|
|
{ MakeKeyState(static_cast<std::int32_t>(KeyCode::P), true, true) },
|
|
modifiers);
|
|
EXPECT_FALSE(router.MatchShortcut({ &repeatedSnapshot }).matched);
|
|
|
|
XCUIEditorCommandInputSnapshot captureSnapshot = exactSnapshot;
|
|
captureSnapshot.wantCaptureKeyboard = true;
|
|
EXPECT_FALSE(router.MatchShortcut({ &captureSnapshot }).matched);
|
|
EXPECT_TRUE(router.MatchShortcut({ &captureSnapshot, true, true, false }).matched);
|
|
|
|
XCUIEditorCommandInputSnapshot textInputSnapshot = exactSnapshot;
|
|
textInputSnapshot.wantTextInput = true;
|
|
EXPECT_FALSE(router.MatchShortcut({ &textInputSnapshot }).matched);
|
|
EXPECT_TRUE(router.MatchShortcut({ &textInputSnapshot, true, false, true }).matched);
|
|
|
|
XCUIEditorCommandInputSnapshot unfocusedSnapshot = exactSnapshot;
|
|
unfocusedSnapshot.windowFocused = false;
|
|
EXPECT_FALSE(router.MatchShortcut({ &unfocusedSnapshot }).matched);
|
|
EXPECT_TRUE(router.MatchShortcut({ &unfocusedSnapshot, false, false, false }).matched);
|
|
|
|
XCUIEditorCommandShortcutMatch invokedMatch = {};
|
|
EXPECT_TRUE(router.InvokeMatchingShortcut({ &exactSnapshot }, &invokedMatch));
|
|
EXPECT_TRUE(invokedMatch.matched);
|
|
EXPECT_EQ(invokedMatch.commandId, "xcui.search.command_palette");
|
|
EXPECT_EQ(invokeCount, 1);
|
|
}
|
|
|
|
TEST(XCUIEditorCommandRouterTest, NonExactModifierAcceleratorsAllowAdditionalPressedModifiers) {
|
|
XCUIEditorCommandRouter router = {};
|
|
|
|
XCEngine::UI::UIInputModifiers requiredModifiers = {};
|
|
requiredModifiers.control = true;
|
|
|
|
XCUIEditorCommandDefinition definition = {};
|
|
definition.commandId = "xcui.edit.duplicate";
|
|
definition.invoke = []() {};
|
|
definition.accelerators.push_back({
|
|
static_cast<std::int32_t>(KeyCode::D),
|
|
requiredModifiers,
|
|
false,
|
|
false });
|
|
ASSERT_TRUE(router.RegisterCommand(definition));
|
|
|
|
XCEngine::UI::UIInputModifiers actualModifiers = requiredModifiers;
|
|
actualModifiers.shift = true;
|
|
const XCUIEditorCommandInputSnapshot permissiveSnapshot = MakeSnapshot(
|
|
{ MakeKeyState(static_cast<std::int32_t>(KeyCode::D), true) },
|
|
actualModifiers);
|
|
EXPECT_TRUE(router.MatchShortcut({ &permissiveSnapshot }).matched);
|
|
|
|
XCUIEditorCommandRouter exactRouter = {};
|
|
definition.accelerators.front().exactModifiers = true;
|
|
ASSERT_TRUE(exactRouter.RegisterCommand(definition));
|
|
EXPECT_FALSE(exactRouter.MatchShortcut({ &permissiveSnapshot }).matched);
|
|
}
|
|
|
|
} // namespace
|