diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-04 10:03:24 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-04 12:54:32 +0200 |
| commit | 0e149dfb514de8eada2c8c8430352ac984d3c5ba (patch) | |
| tree | f8dac7d1c10d2a3ecf2d0358fc295153429328be /tests | |
| parent | 9016063582c75f6d9a5fbd770b1f0b2f363b3054 (diff) | |
| download | qtmaildir-0e149dfb514de8eada2c8c8430352ac984d3c5ba.tar.gz qtmaildir-0e149dfb514de8eada2c8c8430352ac984d3c5ba.zip | |
fix(query): let the query bar keep Return instead of open_thread
Pressing Return in the query bar moved focus to the thread list and left
the query unrun, so a query typed at the keyboard could not be executed
at all.
Return is bound to open_thread as a Qt::WindowShortcut, and a shortcut is
dispatched before the focused widget ever sees the key. Qt withholds a
plain-LETTER shortcut from an editable widget, which is why every other
binding in the map was safe here, but Return is not a letter and gets no
such protection: the action fired from inside the bar, its handler called
setFocus() on the thread list, and QLineEdit::returnPressed was never
emitted.
Accept the ShortcutOverride for Return and Enter on the query bar, which
tells Qt the focused widget wants the key as ordinary input and stops the
shortcut being dispatched. Narrow by design, one widget and one key, so
open_thread keeps working everywhere else in the window.
Three earlier hypotheses were tested and disproven before this one, and
two synthetic probes wrongly reported the binding as harmless: real input
sends ShortcutOverride first and only dispatches the shortcut if nothing
claims it, while QTest::keyClick skips that round trip entirely. The new
test drives the override exchange rather than the keystroke and fails
against the old code. The comment claiming no filter was needed said the
letter rule covered this case; it did not, and it now says so.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_mainwindow.cpp | 45 |
1 files changed, 45 insertions, 0 deletions
diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp index ab904f9..a66e1c2 100644 --- a/tests/test_mainwindow.cpp +++ b/tests/test_mainwindow.cpp @@ -19,7 +19,10 @@ #include <QtTest> #include <QAction> +#include <QApplication> #include <QDir> +#include <QKeyEvent> +#include <QLineEdit> #include <QFile> #include <QSettings> #include <QStandardPaths> @@ -51,6 +54,7 @@ private slots: void uiStateSurvivesARestart(); void missingUiStateLeavesTheDefaults(); void headerStateFromADifferentColumnLayoutIsDiscarded(); + void returnInTheQueryBarRunsTheQueryNotOpenThread(); }; void TestMainWindow::everyKnownActionIsRegistered() @@ -266,6 +270,47 @@ void TestMainWindow::headerStateFromADifferentColumnLayoutIsDiscarded() QStandardPaths::setTestModeEnabled(false); } +void TestMainWindow::returnInTheQueryBarRunsTheQueryNotOpenThread() +{ + // Return is bound to open_thread as a WindowShortcut, and the query bar has + // to win it back while it has focus. Qt withholds a plain-LETTER shortcut + // from an editable widget, but Return is not a letter and gets no such + // protection, so without an explicit override the action fires, the query + // never runs, and focus jumps to the thread list. + // + // The delivery order matters and is the reason this bug survived earlier + // tests: real input sends ShortcutOverride first and only dispatches the + // shortcut if nothing accepts it. QTest::keyClick() skips that round trip, + // so a test written with it passes against the broken code. + const Config config; + MainWindow window(config); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + auto *edit = window.findChild<QLineEdit *>(); + QVERIFY(edit); + edit->setFocus(); + QTRY_COMPARE(QApplication::focusWidget(), edit); + edit->setText(QStringLiteral("tag:unread")); + + QAction *openThread = window.findChild<QAction *>(QStringLiteral("open_thread")); + QVERIFY(openThread); + bool actionFired = false; + connect(openThread, &QAction::triggered, &window, [&actionFired]() { + actionFired = true; + }); + + // The query bar must claim the override, which is what stops the shortcut + // from ever being dispatched. + QKeyEvent override(QEvent::ShortcutOverride, Qt::Key_Return, Qt::NoModifier); + override.ignore(); + QApplication::sendEvent(edit, &override); + QVERIFY2(override.isAccepted(), + "the query bar let Return through to the open_thread shortcut"); + + QVERIFY(!actionFired); +} + // Constructing a MainWindow needs a QApplication and a platform plugin. The // test has no display under ctest, so it runs offscreen unless the caller // asked for something else. |
