From 8b67ed8d6d727aa99c091c6eb46160ecb6300f72 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 12 Aug 2026 11:55:42 +0200 Subject: feat(rules): open the tagging rules from the Message menu Counts are generation-stamped and dropped when stale or when the dialog has closed: counting every rule against a cold index takes seconds, so an in-flight reply outliving its dialog is ordinary rather than rare. The stamp is its own counter, not m_generation as drafted. That one is the QUERY generation, compared against directly by every thread, tree and message load, so bumping it to count rules would discard whatever the user was opening at the time and blank the message pane for an unrelated reason. Registering an action obliges two more entries, both enforced by tests: the name in KeyMap::knownActions(), and a default binding, since every action carries one. Ctrl+Shift+T, shifted against Ctrl+T for edit_tags the way Ctrl+Shift+U is shifted against Ctrl+U. Co-Authored-By: Claude Opus 5 --- src/mainwindow.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'src/mainwindow.h') diff --git a/src/mainwindow.h b/src/mainwindow.h index 19635dd..adf63b0 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -55,6 +55,7 @@ class MessageView; class MailSync; class NotmuchWorker; class QueryCompleter; +class TagRulesDialog; class MainWindow : public QMainWindow { @@ -292,6 +293,14 @@ private slots: /// Runs a query the user clicked on the placeholder pane. void onPlaceholderQueryRequested(const QString &query); + /// Opens the auto-tagging rules editor, or raises the one already open. + void showTagRulesDialog(); + + /// Message counts for the rules dialog's queries, in the order it asked + /// for them. Does nothing if the dialog has since closed, or if a newer + /// request has superseded this one. + void onRuleCountsReady(const QVector &counts, quint64 generation); + private: void buildUi(); @@ -632,6 +641,28 @@ private: /// and reopened, so an old answer cannot fill in a newer dialog. quint64 m_statsGeneration = 0; + /// The open rules dialog, or null. Held so a counts reply can reach it, + /// and cleared when it closes: a reply that arrives after the dialog is + /// gone must find nothing rather than a dangling pointer. A QPointer for + /// the same reason m_overviewCounts is one, and the window is open wider + /// here, since counting every rule takes seconds. + QPointer m_tagRulesDialog; + + /// Generation of the rules dialog's counts request. + /// + /// Its OWN counter, deliberately not m_generation. That one is the QUERY + /// generation, and onThreadsReady, onQueryFinished, onThreadLoaded, + /// onThreadTreeLoaded and onMessageLoaded all compare against it directly: + /// bumping it here would discard whatever thread load was in flight when + /// the user pressed Count matches, blanking the message pane for a reason + /// that has nothing to do with the query. Not m_countsGeneration either, + /// though that one is closer: it belongs to the placeholder pane's THREAD + /// counts, and sharing it would let each cancel the other's reply. The two + /// arrive on different signals (countsReady against messageCountsReady), + /// so they can never be confused for one another and need not share a + /// counter. + quint64 m_ruleCountGeneration = 0; + /// The generation of a REFRESH query, run after a sync to bring the list /// up to date without disturbing it. /// -- cgit v1.2.3