aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--TODO.org11
-rw-r--r--include/InputMediator.hpp7
-rw-r--r--include/keymap/KeymapEvaluator.hpp62
-rw-r--r--include/utils.hpp6
-rw-r--r--include/widgets/MainWindow.hpp2
-rw-r--r--spec/KeymapEvaluatorSpec.cpp121
-rw-r--r--spec/testUtils.h2
-rw-r--r--src/InputMediator.cpp8
-rw-r--r--src/keymap/KeymapEvaluator.cpp53
-rw-r--r--src/widgets/MainWindow.cpp24
10 files changed, 232 insertions, 64 deletions
diff --git a/TODO.org b/TODO.org
index d7196c9..f91163e 100644
--- a/TODO.org
+++ b/TODO.org
@@ -1,17 +1,10 @@
** Current
-- [X] Multi tabs
-- [X] Open in new tab/new window
-- [X] Lua for command input
-- [X] Simpler shell-ey/vimscript-ey language for command input
-- [X] Command completion
-- [X] Command/URL input executor
-- [X] Refactor MainWindow
-- [X] Buffer list/search
-- [ ] Multi-window
+- [ ] Process spawning lua (+ stdio)
- [ ] Keybindings (lua api)
- [ ] Tab history navigation
- [ ] Press tab for fill highlighted item in completion
- [ ] Assign ID to each tab (reference in lua api)
+- [ ] Multi-window
** Next
- [ ] Modal keys
diff --git a/include/InputMediator.hpp b/include/InputMediator.hpp
index 4aaa6c1..7194ff9 100644
--- a/include/InputMediator.hpp
+++ b/include/InputMediator.hpp
@@ -4,6 +4,7 @@
#include <QtCore>
#include "LuaRuntime.hpp"
+#include "keymap/KeymapEvaluator.hpp"
#include "utils.hpp"
#include "widgets/WebViewStack.hpp"
@@ -11,13 +12,16 @@ class InputMediator : public QObject {
Q_OBJECT
public:
- InputMediator(WebViewStack *webViewStack, LuaRuntime *luaRuntime);
+ InputMediator(WebViewStack *webViewStack, LuaRuntime *luaRuntime,
+ KeymapEvaluator *keymapEvaluator);
+ ~InputMediator();
DELEGATE(webViewStack, openUrl, openUrl)
DELEGATE(webViewStack, currentUrl, currentUrl)
DELEGATE(webViewStack, next, nextWebView)
DELEGATE(webViewStack, previous, previousWebView)
DELEGATE(webViewStack, closeCurrent, closeCurrentWebView)
+ DELEGATE(keymapEvaluator, evaluate, evaluateKeymap)
private:
void evaluateCommand(QString command);
@@ -25,4 +29,5 @@ private:
private:
WebViewStack *webViewStack;
LuaRuntime *luaRuntime;
+ KeymapEvaluator *keymapEvaluator;
};
diff --git a/include/keymap/KeymapEvaluator.hpp b/include/keymap/KeymapEvaluator.hpp
index 54409b1..db93900 100644
--- a/include/keymap/KeymapEvaluator.hpp
+++ b/include/keymap/KeymapEvaluator.hpp
@@ -1,70 +1,42 @@
#pragma once
-#include "keymap/KeySeqParser.hpp"
#include <QWidget>
#include <QtCore/qmap.h>
#include <QtCore/qnamespace.h>
#include <QtCore>
+#include <functional>
+
+#include "keymap/KeySeqParser.hpp"
+#include "utils.hpp"
+
+typedef std::function<void()> KeyAction;
struct KeyMap {
KeySequence keySequence;
- QString action; // TODO: string for testing
+ KeyAction action;
};
+enum KeyMode { Normal, Insert };
+
class KeymapEvaluator : public QObject {
Q_OBJECT
public:
KeymapEvaluator();
- void addKeymap(QString mode, QString key, QString action) {
- if (!modalKeys.contains(mode))
- modalKeys.insert(mode, {});
-
- auto keySeq = keySeqParser.parse(key);
- // TODO: Add actions
- modalKeys[mode].append((KeyMap){.keySequence = keySeq, .action = action});
- }
-
- void evaluate(Qt::KeyboardModifiers modifiers, Qt::Key key) {
- if (key == Qt::Key_Control || key == Qt::Key_Shift || key == Qt::Key_Meta ||
- key == Qt::Key_Alt)
- return;
+ void addKeymap(KeyMode mode, QString key, KeyAction action);
+ bool evaluate(Qt::KeyboardModifiers modifiers, Qt::Key key);
- auto keymaps = currentModeKeys();
- auto foundPendingMatches = false;
-
- activeKeySequence.append((KeyChord){.mod = modifiers, .key = key});
-
- for (auto &keymap : *keymaps) {
- auto matchType =
- KeySeqParser::keySequenceMatch(keymap.keySequence, activeKeySequence);
-
- if (matchType == KeyMatchType::Match) {
- qDebug() << "Matched!" << keymap.action;
- activeKeySequence.clear();
- return;
- } else if (matchType == KeyMatchType::Pending) {
- foundPendingMatches = true;
- }
- }
-
- if (!foundPendingMatches)
- activeKeySequence.clear();
- }
+ DEFINE_SETTER(setCurrentMode, currentMode)
+ DEFINE_GETTER(getCurrentMode, currentMode)
private:
- const QList<KeyMap> *currentModeKeys() {
- if (!modalKeys.contains(currentMode))
- return new QList<KeyMap>();
-
- return &modalKeys[currentMode];
- }
+ const QList<KeyMap> *currentModeKeys();
+ bool isInsertableMode();
private:
- QMap<QString, QList<KeyMap>> modalKeys;
+ QMap<KeyMode, QList<KeyMap>> modalKeys;
KeySeqParser keySeqParser;
- QString currentMode = "default";
-
+ KeyMode currentMode = KeyMode::Normal;
KeySequence activeKeySequence;
};
diff --git a/include/utils.hpp b/include/utils.hpp
index 65667f7..0650ac2 100644
--- a/include/utils.hpp
+++ b/include/utils.hpp
@@ -3,3 +3,9 @@
template <typename... Args> decltype(auto) METHOD_AS(Args &&...args) { \
return OBJ->METHOD(std::forward<Args>(args)...); \
}
+
+#define DEFINE_SETTER(METHOD, PROPERTY) \
+ template <typename Arg> void METHOD(Arg val) { PROPERTY = val; }
+
+#define DEFINE_GETTER(METHOD, EXPR) \
+ template <typename Arg> decltype(auto) METHOD() { return EXPR; }
diff --git a/include/widgets/MainWindow.hpp b/include/widgets/MainWindow.hpp
index 3e3a6d6..2bbc42e 100644
--- a/include/widgets/MainWindow.hpp
+++ b/include/widgets/MainWindow.hpp
@@ -4,7 +4,6 @@
#include "Configuration.hpp"
#include "InputMediator.hpp"
-#include "keymap/KeymapEvaluator.hpp"
class MainWindow : public QMainWindow {
public:
@@ -17,5 +16,4 @@ private:
private:
InputMediator *inputMediator;
Configuration configuration;
- KeymapEvaluator keymapEvaluator;
};
diff --git a/spec/KeymapEvaluatorSpec.cpp b/spec/KeymapEvaluatorSpec.cpp
new file mode 100644
index 0000000..4c19dfd
--- /dev/null
+++ b/spec/KeymapEvaluatorSpec.cpp
@@ -0,0 +1,121 @@
+#include "testUtils.h"
+#include <QtCore>
+
+#include "keymap/KeymapEvaluator.hpp"
+
+class KeymapEvaluatorSpec : public QObject {
+ Q_OBJECT
+
+private slots:
+ void testEvaluateSingleKeyChord() {
+ context("when the key sequence is mapped");
+ it("calls mapping") {
+ int keymapWasCalled = false;
+ KeymapEvaluator evaluator;
+ evaluator.addKeymap(KeyMode::Normal, "<c-t>",
+ [&keymapWasCalled]() { keymapWasCalled = true; });
+
+ evaluator.evaluate(Qt::ControlModifier, Qt::Key_T);
+
+ QVERIFY(keymapWasCalled);
+ }
+
+ context("when the key sequence is not mapped");
+ it("does not call mapping") {
+ int keymapWasCalled = false;
+ KeymapEvaluator evaluator;
+ evaluator.addKeymap(KeyMode::Normal, "<c-t>",
+ [&keymapWasCalled]() { keymapWasCalled = true; });
+
+ evaluator.evaluate(Qt::ControlModifier, Qt::Key_K);
+
+ QVERIFY(NOT keymapWasCalled);
+ }
+ }
+
+ void testEvaluateMultiKeySequence() {
+ context("when the full key sequence is mapped");
+ it("calls mapping") {
+ int keymapWasCalled = false;
+ KeymapEvaluator evaluator;
+ evaluator.addKeymap(KeyMode::Normal, "<c-t>a",
+ [&keymapWasCalled]() { keymapWasCalled = true; });
+
+ evaluator.evaluate(Qt::ControlModifier, Qt::Key_T);
+ evaluator.evaluate(Qt::NoModifier, Qt::Key_A);
+
+ QVERIFY(keymapWasCalled);
+ }
+
+ context("when only part of a mapped key sequence is entered");
+ it("does not call mapping") {
+ int keymapWasCalled = false;
+ KeymapEvaluator evaluator;
+ evaluator.addKeymap(KeyMode::Normal, "<c-t>a",
+ [&keymapWasCalled]() { keymapWasCalled = true; });
+
+ evaluator.evaluate(Qt::ControlModifier, Qt::Key_T);
+
+ QVERIFY(NOT keymapWasCalled);
+ }
+
+ context("when the key sequence is not mapped");
+ it("does not call mapping") {
+ int keymapWasCalled = false;
+ KeymapEvaluator evaluator;
+ evaluator.addKeymap(KeyMode::Normal, "<c-t>a",
+ [&keymapWasCalled]() { keymapWasCalled = true; });
+
+ evaluator.evaluate(Qt::ControlModifier, Qt::Key_K);
+
+ QVERIFY(NOT keymapWasCalled);
+ }
+
+ context("when the key sequence is not mapped");
+ it("does not call mapping") {
+ int keymapWasCalled = false;
+ KeymapEvaluator evaluator;
+ evaluator.addKeymap(KeyMode::Normal, "<c-t>a",
+ [&keymapWasCalled]() { keymapWasCalled = true; });
+
+ evaluator.evaluate(Qt::ControlModifier, Qt::Key_T);
+ evaluator.evaluate(Qt::NoModifier, Qt::Key_B);
+
+ QVERIFY(NOT keymapWasCalled);
+ }
+
+ context("when an incorrect sequence is entered before the correct one");
+ it("calls mapping") {
+ int keymapWasCalled = false;
+ KeymapEvaluator evaluator;
+ evaluator.addKeymap(KeyMode::Normal, "<c-t>a",
+ [&keymapWasCalled]() { keymapWasCalled = true; });
+
+ evaluator.evaluate(Qt::ControlModifier, Qt::Key_T);
+ evaluator.evaluate(Qt::NoModifier, Qt::Key_B);
+ evaluator.evaluate(Qt::ControlModifier, Qt::Key_T);
+ evaluator.evaluate(Qt::NoModifier, Qt::Key_A);
+
+ QVERIFY(keymapWasCalled);
+ }
+
+ // TODO: maybe fix this behavior
+ context(
+ "when partial mapped sequence is entered before an entire sequence");
+ it("does not call mapping") {
+ int keymapWasCalled = false;
+ KeymapEvaluator evaluator;
+ evaluator.addKeymap(KeyMode::Normal, "<c-t>a",
+ [&keymapWasCalled]() { keymapWasCalled = true; });
+
+ evaluator.evaluate(Qt::ControlModifier, Qt::Key_T);
+ evaluator.evaluate(Qt::ControlModifier, Qt::Key_T);
+ evaluator.evaluate(Qt::NoModifier, Qt::Key_A);
+
+ QVERIFY(NOT keymapWasCalled);
+ }
+ }
+};
+
+QTEST_REGISTER(KeymapEvaluatorSpec)
+#include "KeymapEvaluatorSpec.moc"
diff --git a/spec/testUtils.h b/spec/testUtils.h
index 20b3ba8..fd0c30b 100644
--- a/spec/testUtils.h
+++ b/spec/testUtils.h
@@ -4,6 +4,8 @@
#include <QtTest/qtestcase.h>
#include <cstdio>
+#define NOT !
+
#define ANSI_BOLD "\x1b[1m"
#define COLOR_CONTEXT "\x1b[32m" ANSI_BOLD
#define COLOR_IT "\x1b[36m" ANSI_BOLD
diff --git a/src/InputMediator.cpp b/src/InputMediator.cpp
index 350d8ac..f935ad2 100644
--- a/src/InputMediator.cpp
+++ b/src/InputMediator.cpp
@@ -7,8 +7,10 @@
#include "LuaRuntime.hpp"
#include "widgets/WebViewStack.hpp"
-InputMediator::InputMediator(WebViewStack *webViewStack, LuaRuntime *luaRuntime)
- : QObject(), webViewStack(webViewStack), luaRuntime(luaRuntime) {
+InputMediator::InputMediator(WebViewStack *webViewStack, LuaRuntime *luaRuntime,
+ KeymapEvaluator *keymapEvaluator)
+ : QObject(), webViewStack(webViewStack), luaRuntime(luaRuntime),
+ keymapEvaluator(keymapEvaluator) {
connect(luaRuntime, &LuaRuntime::urlOpened, webViewStack,
&WebViewStack::openUrl);
}
@@ -40,3 +42,5 @@ void InputMediator::evaluateCommand(QString command) {
break;
}
}
+
+InputMediator::~InputMediator() { delete webViewStack; }
diff --git a/src/keymap/KeymapEvaluator.cpp b/src/keymap/KeymapEvaluator.cpp
index cca10e8..baae819 100644
--- a/src/keymap/KeymapEvaluator.cpp
+++ b/src/keymap/KeymapEvaluator.cpp
@@ -4,3 +4,56 @@
#include "keymap/KeymapEvaluator.hpp"
KeymapEvaluator::KeymapEvaluator() : QObject() {}
+
+// TODO: Clear mapping after some time
+
+void KeymapEvaluator::addKeymap(KeyMode mode, QString key, KeyAction action) {
+ if (!modalKeys.contains(mode))
+ modalKeys.insert(mode, {});
+
+ auto keySeq = keySeqParser.parse(key);
+ modalKeys[mode].append((KeyMap){.keySequence = keySeq, .action = action});
+}
+
+bool KeymapEvaluator::evaluate(Qt::KeyboardModifiers modifiers, Qt::Key key) {
+ if (key == Qt::Key_Control || key == Qt::Key_Shift || key == Qt::Key_Meta ||
+ key == Qt::Key_Alt)
+ return true;
+
+ auto keymaps = currentModeKeys();
+ auto foundPendingMatches = false;
+
+ activeKeySequence.append((KeyChord){.mod = modifiers, .key = key});
+
+ for (auto &keymap : *keymaps) {
+ auto matchType =
+ KeySeqParser::keySequenceMatch(keymap.keySequence, activeKeySequence);
+
+ if (matchType == KeyMatchType::Match) {
+ keymap.action();
+ activeKeySequence.clear();
+ return true;
+ } else if (matchType == KeyMatchType::Pending) {
+ foundPendingMatches = true;
+ }
+ }
+
+ if (!foundPendingMatches)
+ activeKeySequence.clear();
+
+ if (isInsertableMode())
+ return foundPendingMatches;
+
+ return true;
+}
+
+bool KeymapEvaluator::isInsertableMode() {
+ return currentMode == KeyMode::Insert;
+}
+
+const QList<KeyMap> *KeymapEvaluator::currentModeKeys() {
+ if (!modalKeys.contains(currentMode))
+ return new QList<KeyMap>();
+
+ return &modalKeys[currentMode];
+}
diff --git a/src/widgets/MainWindow.cpp b/src/widgets/MainWindow.cpp
index 327a77c..32bde30 100644
--- a/src/widgets/MainWindow.cpp
+++ b/src/widgets/MainWindow.cpp
@@ -6,6 +6,7 @@
#include <QtWidgets/qapplication.h>
#include "InputMediator.hpp"
+#include "keymap/KeymapEvaluator.hpp"
#include "widgets/MainWindow.hpp"
#include "widgets/WebViewStack.hpp"
@@ -34,11 +35,23 @@ MainWindow::MainWindow() {
webViewStack->openUrl(QUrl("https://github.com/trending"),
OpenType::OpenUrlInBgTab);
- inputMediator = new InputMediator(webViewStack, LuaRuntime::instance());
+ auto keymapEvaluator = new KeymapEvaluator;
+
+ inputMediator =
+ new InputMediator(webViewStack, LuaRuntime::instance(), keymapEvaluator);
// TODO: remove
- keymapEvaluator.addKeymap("default", "<c-t>a", "Stuff");
- keymapEvaluator.addKeymap("default", "<c-t>b", "Other stuff");
+ keymapEvaluator->addKeymap(KeyMode::Normal, "i", [keymapEvaluator]() {
+ keymapEvaluator->setCurrentMode(KeyMode::Insert);
+ });
+ keymapEvaluator->addKeymap(KeyMode::Insert, "<esc>", [keymapEvaluator]() {
+ keymapEvaluator->setCurrentMode(KeyMode::Normal);
+ });
+
+ keymapEvaluator->addKeymap(KeyMode::Normal, "<c-t>a",
+ []() { qDebug() << "Stuff"; });
+ keymapEvaluator->addKeymap(KeyMode::Normal, "<c-t>b",
+ []() { qDebug() << "Else"; });
}
void MainWindow::keyPressEvent(QKeyEvent *event) {
@@ -61,7 +74,8 @@ bool MainWindow::eventFilter(QObject *object, QEvent *event) {
return false;
auto keyEvent = static_cast<QKeyEvent *>(event);
- keymapEvaluator.evaluate(keyEvent->modifiers(), (Qt::Key)keyEvent->key());
+ bool shouldSkip = inputMediator->evaluateKeymap(keyEvent->modifiers(),
+ (Qt::Key)keyEvent->key());
- return true;
+ return shouldSkip;
}