diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-02 17:02:18 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-02 17:02:18 +0200 |
| commit | dd7845d847a3d6f961f0302551c33245d1f12bdc (patch) | |
| tree | f146a0bf84bbd3229e9bf18235314de0bc5f4c5f /src/keymap.cpp | |
| parent | 059fa151856293a8b6bdf9b16d1b72f415f59df3 (diff) | |
| download | qtmaildir-dd7845d847a3d6f961f0302551c33245d1f12bdc.tar.gz qtmaildir-dd7845d847a3d6f961f0302551c33245d1f12bdc.zip | |
feat: add KeyMap with defaults and INI overrides
Maps key sequences to action name strings, with hardcoded vim-style
defaults and QSettings-based [keys] overrides. Unknown actions and
unparseable sequences are collected as warnings rather than treated
as fatal, so a typo in the config cannot silently misbind or crash.
Note: QKeySequence::fromString() on Qt 6.11 does not return an empty
sequence for unparseable input (e.g. "NotAKey++") -- it returns a
non-empty sequence whose toString() is empty. Detection uses that
instead of isEmpty().
Diffstat (limited to 'src/keymap.cpp')
| -rw-r--r-- | src/keymap.cpp | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/src/keymap.cpp b/src/keymap.cpp new file mode 100644 index 0000000..6e43f1b --- /dev/null +++ b/src/keymap.cpp @@ -0,0 +1,82 @@ +#include "keymap.h" + +#include <QSettings> + +QStringList KeyMap::knownActions() +{ + // Keep in sync with the actions MainWindow registers. + return { + QStringLiteral("next_thread"), + QStringLiteral("prev_thread"), + QStringLiteral("open_thread"), + QStringLiteral("archive"), + QStringLiteral("delete"), + QStringLiteral("spam"), + QStringLiteral("toggle_unread"), + QStringLiteral("flag"), + QStringLiteral("focus_query"), + QStringLiteral("toggle_html"), + QStringLiteral("load_remote"), + QStringLiteral("undo"), + QStringLiteral("sync"), + QStringLiteral("quit"), + }; +} + +void KeyMap::loadDefaults() +{ + const QHash<QString, QString> defaults = { + { QStringLiteral("j"), QStringLiteral("next_thread") }, + { QStringLiteral("k"), QStringLiteral("prev_thread") }, + { QStringLiteral("Return"), QStringLiteral("open_thread") }, + { QStringLiteral("a"), QStringLiteral("archive") }, + { QStringLiteral("d"), QStringLiteral("delete") }, + { QStringLiteral("N"), QStringLiteral("toggle_unread") }, + { QStringLiteral("F"), QStringLiteral("flag") }, + { QStringLiteral("/"), QStringLiteral("focus_query") }, + { QStringLiteral("h"), QStringLiteral("toggle_html") }, + { QStringLiteral("u"), QStringLiteral("undo") }, + { QStringLiteral("G"), QStringLiteral("sync") }, + { QStringLiteral("Ctrl+Q"), QStringLiteral("quit") }, + }; + + for (auto it = defaults.cbegin(); it != defaults.cend(); ++it) + m_bindings.insert(QKeySequence::fromString(it.key()), it.value()); +} + +void KeyMap::loadOverrides(QSettings &settings) +{ + const QStringList known = knownActions(); + + settings.beginGroup(QStringLiteral("keys")); + const QStringList keys = settings.childKeys(); + for (const QString &key : keys) { + const QString action = settings.value(key).toString(); + + const QKeySequence sequence = QKeySequence::fromString(key); + // QKeySequence::fromString() does not return an empty sequence for + // unparseable input; it returns a non-empty sequence whose + // toString() is empty (verified on Qt 6.11). Use that to detect + // garbage input instead. + if (sequence.isEmpty() || sequence.toString().isEmpty()) { + m_warnings.append( + QStringLiteral("Unparseable key sequence '%1' in [keys]").arg(key)); + continue; + } + + if (!known.contains(action)) { + m_warnings.append( + QStringLiteral("Unknown action '%1' bound to '%2' in [keys]") + .arg(action, key)); + continue; + } + + m_bindings.insert(sequence, action); + } + settings.endGroup(); +} + +QString KeyMap::actionFor(const QKeySequence &sequence) const +{ + return m_bindings.value(sequence); +} |
