From d7b87856340eefad1fed7d2f246fa49cdcf63d93 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Mon, 3 Aug 2026 14:17:09 +0200 Subject: feat: add menus, a toolbar and a shortcut reference Actions were a QHash of std::function dispatched by an event filter, which nothing could put in a menu. They are QActions now, bound from KeyMap so a [keys] override reaches the menus as well as the keyboard. Menu bar covers every action; the toolbar carries only Sync, Archive, Delete and Undo. Help > Keyboard shortcuts is generated from the actions, so it shows what the keys really do rather than a copy that drifts. spam and load_remote gained defaults, having been unreachable without a hand-written binding. The event filter is gone. Probing showed QAction shortcuts are dispatched before the focused widget sees the key, so they beat QAbstractItemView's type-to-search without one, and Qt already suppresses plain-letter shortcuts while an editable widget has focus. Dropping the filter's blanket guard also lets Ctrl+Q work while the query bar has focus. registeredActionNames() is derived from the actions rather than hand-maintained, so the two drift tests it needed are replaced by checks that a configured binding reaches its action. No confirmation dialogs: tag mutations still answer to undo. --- src/keymap.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'src/keymap.cpp') diff --git a/src/keymap.cpp b/src/keymap.cpp index e53c2bf..42ccd40 100644 --- a/src/keymap.cpp +++ b/src/keymap.cpp @@ -106,17 +106,33 @@ void KeyMap::loadDefaults() QKeySequence KeyMap::sequenceFor(const QString &action) const { - // Several sequences can point at one action (a default the user did not - // remove, plus their own addition). QHash iteration order is unspecified, - // so pick deterministically rather than taking whichever comes first. + // Several sequences can reach one action: the built-in default, which + // loadOverrides() does not remove, plus whatever the user added. Their + // binding is the one to show and to put on the QAction, or configuring + // "Ctrl+Alt+A = archive" would leave the menu still advertising Ctrl+E. + // + // QHash iteration order is unspecified, so ties are broken on the text + // rather than left to chance. + const QKeySequence builtIn = defaultSequenceFor(action); QKeySequence best; + bool bestIsBuiltIn = false; + for (auto it = m_bindings.cbegin(); it != m_bindings.cend(); ++it) { if (it.value() != action) continue; - const QString candidate = it.key().toString(); - if (best.isEmpty() || candidate.size() < best.toString().size() - || (candidate.size() == best.toString().size() - && candidate < best.toString())) { + + const bool isBuiltIn = !builtIn.isEmpty() && it.key() == builtIn; + if (best.isEmpty()) { + best = it.key(); + bestIsBuiltIn = isBuiltIn; + continue; + } + // A user binding always beats the default. + if (bestIsBuiltIn && !isBuiltIn) { + best = it.key(); + bestIsBuiltIn = false; + } else if (bestIsBuiltIn == isBuiltIn + && it.key().toString() < best.toString()) { best = it.key(); } } -- cgit v1.2.3