From dd6590a0fda614fa2fb0ca9e4ad11b086393372e Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sun, 16 Mar 2025 20:53:33 +0530 Subject: Add simple key sequence parser --- include/keymap/KeySeqParser.hpp | 20 +++++++++++ spec/KeySeqParserSpec.cpp | 52 +++++++++++++++++++++++++++ src/keymap/KeySeqParser.cpp | 79 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 include/keymap/KeySeqParser.hpp create mode 100644 spec/KeySeqParserSpec.cpp create mode 100644 src/keymap/KeySeqParser.cpp diff --git a/include/keymap/KeySeqParser.hpp b/include/keymap/KeySeqParser.hpp new file mode 100644 index 0000000..aefcb62 --- /dev/null +++ b/include/keymap/KeySeqParser.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +struct KeyChord { + Qt::KeyboardModifiers mod; + Qt::Key key; +}; +bool operator==(const KeyChord a, const KeyChord b); + +class KeySeqParser { +public: + KeySeqParser(); + + QList parse(QString keySequence); + +private: + Qt::Key parseKey(QString keyName); +}; diff --git a/spec/KeySeqParserSpec.cpp b/spec/KeySeqParserSpec.cpp new file mode 100644 index 0000000..a29c97e --- /dev/null +++ b/spec/KeySeqParserSpec.cpp @@ -0,0 +1,52 @@ +#include "testUtils.h" +#include +#include + +#include "keymap/KeySeqParser.hpp" + +class KeySeqParserSpec : public QObject { + Q_OBJECT + +private slots: + void testThings() { + context("when mix of upper/lower cases"); + it("parses keys ignoring the casing") { + KeySeqParser parser; + + QList keys = parser.parse(""); + + QList expectedKeys = { + {.mod = Qt::ControlModifier, .key = Qt::Key_Tab}, + {.mod = Qt::ControlModifier, .key = Qt::Key_Tab}, + {.mod = Qt::ControlModifier, .key = Qt::Key_Tab}, + }; + QCOMPARE(keys, expectedKeys); + } + + context("when input contains individual keys"); + it("parses the characters") { + KeySeqParser parser; + + QList keys = parser.parse("abz"); + + for (auto &k : keys) { + qDebug() << k.key << k.mod; + } + + QList expectedKeys = { + {.mod = Qt::NoModifier, .key = Qt::Key_A}, + {.mod = Qt::NoModifier, .key = Qt::Key_B}, + {.mod = Qt::NoModifier, .key = Qt::Key_C}, + {.mod = Qt::NoModifier, .key = Qt::Key_S}, + {.mod = Qt::NoModifier, .key = Qt::Key_Space}, + {.mod = Qt::NoModifier, .key = Qt::Key_Tab}, + {.mod = Qt::NoModifier, .key = Qt::Key_Escape}, + {.mod = Qt::NoModifier, .key = Qt::Key_Z}, + }; + QCOMPARE(keys, expectedKeys); + } + } +}; + +QTEST_REGISTER(KeySeqParserSpec) +#include "KeySeqParserSpec.moc" diff --git a/src/keymap/KeySeqParser.cpp b/src/keymap/KeySeqParser.cpp new file mode 100644 index 0000000..c5534eb --- /dev/null +++ b/src/keymap/KeySeqParser.cpp @@ -0,0 +1,79 @@ +#include +#include +#include + +#include "keymap/KeySeqParser.hpp" + +bool operator==(const KeyChord a, const KeyChord b) { + return a.mod == b.mod && a.key == b.key; +} + +KeySeqParser::KeySeqParser() {} + +QList KeySeqParser::parse(QString keySequence) { + QList keyChords; + KeyChord lastKey; + + // TODO: Refactor + // TODO: Support + keySequence = keySequence.toLower(); + while (!keySequence.isEmpty()) { + int skipCount = 1; + if (keySequence.startsWith("'); + auto keyName = keySequence.sliced(3, nextClosing - 3); + lastKey.mod = lastKey.mod | Qt::KeyboardModifier::ControlModifier; + lastKey.key = parseKey(keyName); + skipCount = nextClosing + 1; + } else if (keySequence.startsWith("'); + auto keyName = keySequence.sliced(3, nextClosing - 3); + lastKey.mod = lastKey.mod | Qt::KeyboardModifier::ShiftModifier; + lastKey.key = parseKey(keyName); + skipCount = nextClosing + 1; + } else if (keySequence.startsWith("<")) { + int nextClosing = keySequence.indexOf('>'); + auto keyName = keySequence.sliced(1, nextClosing - 1); + lastKey.mod = Qt::KeyboardModifier::NoModifier; + lastKey.key = parseKey(keyName); + skipCount = nextClosing + 1; + } else { + auto keyName = keySequence.first(1); + lastKey.mod = Qt::KeyboardModifier::NoModifier; + lastKey.key = parseKey(keyName); + } + + keySequence.slice(std::max(1, skipCount)); + keyChords.push_back(lastKey); + lastKey = KeyChord(); + } + + return keyChords; +} + +Qt::Key KeySeqParser::parseKey(QString keyName) { + if (keyName.length() == 0) + return Qt::Key_T; // TODO: tmp + + if (keyName.length() == 1) { + char c = keyName.toStdString().at(0); + return Qt::Key(Qt::Key_A + (c - 'a')); + } + + if (keyName == "space") + return Qt::Key_Space; + + if (keyName == "cr") + return Qt::Key_Return; + + if (keyName == "esc") + return Qt::Key_Escape; + + if (keyName == "bs") + return Qt::Key_Backspace; + + if (keyName == "tab") + return Qt::Key_Tab; + + return Qt::Key_T; +} -- cgit v1.3.1