From 14842cd136e45c551dde4f276af1176bfdf22023 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Thu, 13 Aug 2026 19:29:18 +0200 Subject: fix(queries): put the Save query button beside the query bar The spec asked for "a Save query button beside the search bar" and what shipped was a menu entry and Ctrl+S. The user went looking for the button where the design said it would be and did not find it. Saving is a thing you decide on while looking at the results, so it belongs where the results came from rather than behind a menu or a remembered chord. The button takes the action through setDefaultAction rather than a second connect, so it inherits the text, icon, tooltip and enabled state and cannot end up offering to save an empty query while the menu entry correctly refuses. The mutation that replaces it with a plain clicked() connection fails the test. Also records item 82: a saved query cannot be edited, unpinned or deleted from the UI. Item 23 specified saving and nothing else, and that is exactly what was built, so the only way to unpin a query is a text editor or retyping it in full under the same name. An action that creates something the UI cannot then change or remove is incomplete, and this was found within minutes of the first hand test. It is filed as a defect rather than an enhancement, and the spec now says so where a reader would otherwise take the design for complete. Co-Authored-By: Claude Opus 5 --- src/mainwindow.cpp | 17 +++++++++++++++++ src/mainwindow.h | 3 +++ 2 files changed, 20 insertions(+) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3966e9f..9eff3d5 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include "mailsync.h" @@ -348,6 +349,12 @@ MainWindow::MainWindow(const Config &config, QWidget *parent) // also set programmatically, by the saved-query buttons and by // recoverStaleThread(), and the action must track those too. if (QAction *save = m_actions.value(QStringLiteral("save_query"))) { + // setDefaultAction, not a second connect: the button then takes the + // action's text, icon, tooltip and ENABLED state, so it cannot end up + // offering to save an empty query while the menu entry refuses. + m_saveQueryButton->setDefaultAction(save); + m_saveQueryButton->setToolButtonStyle(Qt::ToolButtonIconOnly); + auto updateSaveState = [this, save]() { save->setEnabled(!m_queryEdit->text().trimmed().isEmpty()); }; @@ -566,6 +573,16 @@ void MainWindow::buildUi() queryRow->addWidget(m_accountBox); queryRow->addWidget(m_sortOrder); queryRow->addWidget(m_queryEdit, 1); + + // Beside the field, where a user looks for it. The menu entry and Ctrl+S + // were not enough on their own: saving is a thing you decide on while + // looking at the results, so it needs to be visible at the query bar + // rather than remembered. Created here and given its action in the + // constructor, since registerActions() has not run yet. + m_saveQueryButton = new QToolButton(central); + m_saveQueryButton->setObjectName(QStringLiteral("saveQueryButton")); + queryRow->addWidget(m_saveQueryButton); + layout->addLayout(queryRow); buildSavedQueryRow(central, layout); diff --git a/src/mainwindow.h b/src/mainwindow.h index 8b43fd0..e6ee322 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -49,6 +49,7 @@ class QPlainTextEdit; class QSplitter; class QProgressBar; class QTimer; +class QToolButton; class QVBoxLayout; class ThreadListModel; @@ -625,6 +626,8 @@ private: QUndoStack m_undoStack; QLineEdit *m_queryEdit = nullptr; + /// Save query, beside the field. Driven by the save_query action. + QToolButton *m_saveQueryButton = nullptr; QueryCompleter *m_queryCompleter = nullptr; /// Its own type, not the QTreeView base. The strip painting and the /// expander column are ThreadListView's, and holding the base here only -- cgit v1.2.3