diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-12 11:55:42 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-12 11:55:42 +0200 |
| commit | 8b67ed8d6d727aa99c091c6eb46160ecb6300f72 (patch) | |
| tree | a17578371f2c87c1ffb0730555ae3c3bb03d8f84 /src/mainwindow.cpp | |
| parent | 0afd62360ffa7f28bd24b7a37ddbe53456f94aa9 (diff) | |
| download | qtmaildir-8b67ed8d6d727aa99c091c6eb46160ecb6300f72.tar.gz qtmaildir-8b67ed8d6d727aa99c091c6eb46160ecb6300f72.zip | |
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 <noreply@anthropic.com>
Diffstat (limited to 'src/mainwindow.cpp')
| -rw-r--r-- | src/mainwindow.cpp | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f731511..331354f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -56,6 +56,7 @@ #include "cardlayout.h" #include "tagchip.h" #include "tagdialog.h" +#include "tagrulesdialog.h" #include "threadlistmodel.h" #include "threadlistview.h" #include "version.h" @@ -831,6 +832,10 @@ void MainWindow::registerActions() tr("Add or remove any tag on the selected threads"), [this]() { editTagsOnSelection(); }); + addAction(QStringLiteral("tag_rules"), tr("Tagging &rules..."), + tr("Edit the rules that tag mail as it arrives"), [this]() { + showTagRulesDialog(); + }); addAction(QStringLiteral("toggle_html"), tr("Toggle &HTML"), tr("Switch the thread between HTML and plain text"), [this]() { m_messageView->toggleHtml(); @@ -989,6 +994,10 @@ void MainWindow::buildMenus() messageMenu->addAction(m_actions.value(QStringLiteral("mark_all_read"))); messageMenu->addAction(m_actions.value(QStringLiteral("edit_tags"))); messageMenu->addAction(m_actions.value(QStringLiteral("flag"))); + // Separated from the entries above: those act on the selection, this edits + // a rule store shared with mailctl and changes nothing that is on screen. + messageMenu->addSeparator(); + messageMenu->addAction(m_actions.value(QStringLiteral("tag_rules"))); auto *viewMenu = menuBar()->addMenu(tr("&View")); viewMenu->addAction(m_actions.value(QStringLiteral("prev_thread"))); @@ -1046,6 +1055,10 @@ void MainWindow::buildMenus() { QStringLiteral("toggle_unread"), QStringLiteral("mail-mark-unread") }, { QStringLiteral("mark_all_read"), QStringLiteral("mail-mark-read") }, { QStringLiteral("edit_tags"), QStringLiteral("tag") }, + // NOT "tag", which edit_tags uses: with the toolbar icon-only the icon + // is the whole control, and editing the standing rules is not editing + // the selection's tags. + { QStringLiteral("tag_rules"), QStringLiteral("configure") }, { QStringLiteral("complete_query"), QStringLiteral("edit-find-replace") }, { QStringLiteral("select_all"), QStringLiteral("edit-select-all") }, { QStringLiteral("clear_pane"), QStringLiteral("edit-clear") }, @@ -1287,6 +1300,44 @@ void MainWindow::onDatabaseStatsReady(const DatabaseStats &stats, number(stats.tags))); } +void MainWindow::showTagRulesDialog() +{ + // One dialog. A second would edit a stale copy and the last Save would + // silently win, which is the lost-edit case the atomic write cannot help + // with because both writers are this process. + if (m_tagRulesDialog) { + m_tagRulesDialog->raise(); + m_tagRulesDialog->activateWindow(); + return; + } + + auto *dialog = new TagRulesDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose); + m_tagRulesDialog = dialog; + + connect(dialog, &TagRulesDialog::countsRequested, this, [this, dialog]() { + QMetaObject::invokeMethod( + m_worker, "requestMessageCounts", Qt::QueuedConnection, + Q_ARG(QStringList, dialog->countQueries()), + Q_ARG(quint64, ++m_ruleCountGeneration)); + }); + + dialog->show(); +} + +void MainWindow::onRuleCountsReady(const QVector<int> &counts, + quint64 generation) +{ + // Stale reply, or the dialog closed while the count was in flight. Both + // are ordinary rather than rare: counting every rule against a cold index + // takes seconds, which is long enough for the user to close the dialog or + // press the button again. + if (generation != m_ruleCountGeneration || !m_tagRulesDialog) + return; + + m_tagRulesDialog->setCounts(counts); +} + void MainWindow::showAbout() { QDialog dialog(this); @@ -1360,6 +1411,8 @@ void MainWindow::wireWorker() this, &MainWindow::onCountsReady); connect(m_worker, &NotmuchWorker::databaseStatsReady, this, &MainWindow::onDatabaseStatsReady); + connect(m_worker, &NotmuchWorker::messageCountsReady, + this, &MainWindow::onRuleCountsReady); // A confirmed write clears the pending revert: without this, a later // unrelated error would roll back a change that actually succeeded. |
