aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.cpp36
-rw-r--r--src/mainwindow.cpp62
-rw-r--r--src/mainwindow.h26
3 files changed, 96 insertions, 28 deletions
diff --git a/src/config.cpp b/src/config.cpp
index 0b65053..ece1fb9 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -482,22 +482,16 @@ void Config::loadSavedQueries(const QString &configPath, QSettings &settings)
}
settings.endGroup();
- // Sent was a hardcoded button beside the saved queries and becomes an
- // ordinary row here, so it can be reordered, renamed, unpinned or
- // removed like any other. It stays GENERATED, so it still follows the
- // accounts. Appended last, where the button already sat.
+ // Sent is NOT migrated into queries.json any more. It used to become an
+ // ordinary saved query here, so the hardcoded button could be
+ // reordered, renamed or removed; item 93 makes it one of four built-in
+ // filters instead, which are shipped rather than stored. Migrating it
+ // as well would put two Sent buttons on the row, one of them the user's
+ // to edit and one not.
//
- // Only when an account actually configures a sent folder: the button
- // was hidden entirely otherwise, and migrating a row that always finds
- // nothing would be worse than what it replaces.
- if (!allSentQuery().isEmpty()) {
- SavedQuery sent;
- sent.name = QStringLiteral("Sent");
- sent.generated = QStringLiteral("sent");
- sent.pinned = true;
- sent.flat = true;
- m_savedQueries.append(sent);
- }
+ // Nothing is lost: the built-in Sent resolves through the same
+ // generator, so it still follows the accounts, and it now composes with
+ // the account dropdown rather than resetting it.
// Order is alphabetical here because childKeys() is genuinely all the
// INI knows. The user reorders once and it sticks from then on.
@@ -585,6 +579,18 @@ void Config::loadSavedQueries(const QString &configPath, QSettings &settings)
continue;
}
+ // A stored entry naming a generator now duplicates a BUILT-IN filter of
+ // the same name, since item 93 ships all four rather than storing them.
+ // 0.19.0 migrated the hardcoded Sent button into exactly such an entry,
+ // so every existing install has one.
+ //
+ // Unpinned, never dropped: the row would otherwise carry two Sent
+ // buttons, one the user's to edit and one not. Deleting it would be
+ // data loss on a file whose readers are supposed to preserve what they
+ // do not own, and an unpin is reversible from the UI.
+ if (query.isGenerated() && isKnownGenerator(query.generated))
+ query.pinned = false;
+
for (auto it = object.begin(); it != object.end(); ++it) {
static const QStringList known = {
QStringLiteral("name"), QStringLiteral("query"),
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index c6df95c..6bd91d9 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1717,12 +1717,31 @@ void MainWindow::buildSavedQueryRow(QWidget *parent, QVBoxLayout *layout)
auto *box = new QHBoxLayout(row);
box->setContentsMargins(0, 0, 0, 0);
- // Sent is an ordinary row here, not a hardcoded button beside the others.
- // It is still GENERATED, so its query is composed from the accounts' `sent`
- // keys at click time and correcting a folder name stays a config edit and
- // nothing else; what changed is that the entry can now be reordered,
- // renamed, unpinned or removed like every other, instead of being the one
- // control on the row the user did not own.
+ // The built-in filters come first, in their own fixed order, and they are
+ // not saved queries: they are shipped, they are not in queries.json, and
+ // the user cannot edit or delete them (item 93). They are what the row is
+ // FOR; the pinned saved queries below them are the transitional half that
+ // item 94 removes.
+ for (const SavedQuery &filter : Config::builtinFilters()) {
+ // Sent with no account configuring a sent folder finds nothing by
+ // construction. Hidden rather than present and empty, which is what the
+ // hardcoded Sent button did and is worth keeping: a control that always
+ // returns nothing reads as broken rather than as absent.
+ if (m_config.resolvedQuery(filter, QString())
+ == Config::matchNothingQuery())
+ continue;
+
+ auto *button = new QPushButton(filter.name, row);
+ // A stable object name per filter, so a test finds the button without
+ // depending on the label, which is translated.
+ button->setObjectName(filter.generated + QStringLiteral("Button"));
+ connect(button, &QPushButton::clicked, this,
+ [this, filter]() { runFilter(filter); });
+ box->addWidget(button);
+ }
+
+ // The user's own saved queries. A pinned one is still a button, beside the
+ // filters, until item 94 makes the menu their only home.
QList<SavedQuery> unpinned;
for (const SavedQuery &saved : m_config.savedQueries()) {
// A generator whose accounts configure nothing produces a button that
@@ -1736,10 +1755,9 @@ void MainWindow::buildSavedQueryRow(QWidget *parent, QVBoxLayout *layout)
continue;
}
auto *button = new QPushButton(saved.name, row);
- // The generated entries keep a stable object name so a test can find
- // the sent button without depending on what the user renamed it to.
- if (saved.generated == QStringLiteral("sent"))
- button->setObjectName(QStringLiteral("sentButton"));
+ // No object name here any more. "sentButton" now belongs to the BUILT-IN
+ // Sent filter, and a migrated Sent entry claiming it too would give two
+ // buttons one name, so findChild() would return whichever came first.
connect(button, &QPushButton::clicked, this,
[this, saved]() { runSavedQuery(saved); });
addSavedQueryActions(button, saved);
@@ -1928,6 +1946,22 @@ void MainWindow::runSavedQuery(const SavedQuery &saved)
runQuery(saved.flat ? FlatResult::Yes : FlatResult::No);
}
+void MainWindow::runFilter(const SavedQuery &filter)
+{
+ // The account box is READ and never written. That is the whole difference
+ // from runSavedQuery(), and it is item 90's defect: a filter narrows what
+ // the user is already looking at, so the dropdown is its input rather than
+ // something it resets on the way past.
+ const QString accountKey = m_accountBox->currentData().toString();
+
+ // Resolved here, in the account's scope, and put in the bar so what ran is
+ // visible and editable. runQuery() is told not to scope it again.
+ m_queryEdit->setText(m_config.resolvedQuery(filter, accountKey));
+
+ runQuery(filter.flat ? FlatResult::Yes : FlatResult::No,
+ AccountScope::AlreadyScoped);
+}
+
void MainWindow::saveCurrentQuery()
{
const QString query = m_queryEdit->text().trimmed();
@@ -2007,7 +2041,7 @@ void MainWindow::rebuildSavedQueryRow()
}
}
-void MainWindow::runQuery(FlatResult flat)
+void MainWindow::runQuery(FlatResult flat, AccountScope scope)
{
// Set on EVERY run, not only when Yes. This is the line that stops flat
// mode leaking: any query that is not the Sent button restores the tree,
@@ -2017,8 +2051,12 @@ void MainWindow::runQuery(FlatResult flat)
QString query = m_queryEdit->text().trimmed();
+ // A built-in filter arrives already resolved in the selected account's
+ // scope, because a generator has to be asked for the account's own query
+ // rather than have its all-accounts query wrapped. Scoping again here would
+ // put path:"work/Sent/**" inside path:"work/**".
const QString accountKey = m_accountBox->currentData().toString();
- if (!accountKey.isEmpty())
+ if (scope == AccountScope::Apply && !accountKey.isEmpty())
query = m_config.account(accountKey).scopedQuery(query);
if (query.isEmpty())
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 80c7d2b..bb1ba75 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -274,12 +274,23 @@ public:
enum class FlatResult { No, Yes };
Q_ENUM(FlatResult)
+ /// Whether runQuery() applies the selected account's scope to the bar text.
+ ///
+ /// Apply is right for anything the user typed or a saved query put there.
+ /// AlreadyScoped is for a built-in filter, whose text was resolved through
+ /// Config::resolvedQuery(query, accountKey) and already carries the scope:
+ /// scoping it a second time would wrap path:"work/Sent/**" in
+ /// path:"work/**", which is the double scope item 93 exists to avoid.
+ enum class AccountScope { Apply, AlreadyScoped };
+ Q_ENUM(AccountScope)
+
private:
/// The real query runner. Kept off the slot list deliberately: a slot with
/// a defaulted argument does not satisfy QObject::connect, which matches
/// signal and slot arity at compile time, so the zero-argument slot below
/// is what widgets connect to.
- void runQuery(FlatResult flat);
+ void runQuery(FlatResult flat,
+ AccountScope scope = AccountScope::Apply);
/// Builds the row of saved-query buttons, the overflow menu and Sent.
///
@@ -295,6 +306,19 @@ private:
/// twice. Setting the dropdown also shows the user what scope they are in.
void runSavedQuery(const SavedQuery &saved);
+ /// Runs a built-in filter in whatever account scope is currently selected.
+ ///
+ /// The opposite of runSavedQuery() in the one way that matters: it does NOT
+ /// touch the account box. A filter narrows what the user is already looking
+ /// at, so the dropdown is its input rather than something it overwrites,
+ /// which is item 90's defect and item 93's design.
+ ///
+ /// The query text is resolved here rather than left to runQuery()'s own
+ /// scoping, because a generator must be asked for the account's own query:
+ /// Sent wrapped in a scope double-scopes and works only by accident of
+ /// path: being hierarchical. See Config::resolvedQuery(query, accountKey).
+ void runFilter(const SavedQuery &filter);
+
/// Names the current query and stores it in queries.json.
void saveCurrentQuery();