From 0e149dfb514de8eada2c8c8430352ac984d3c5ba Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Tue, 4 Aug 2026 10:03:24 +0200 Subject: 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 --- tests/test_mainwindow.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'tests/test_mainwindow.cpp') 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 #include +#include #include +#include +#include #include #include #include @@ -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(); + QVERIFY(edit); + edit->setFocus(); + QTRY_COMPARE(QApplication::focusWidget(), edit); + edit->setText(QStringLiteral("tag:unread")); + + QAction *openThread = window.findChild(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. -- cgit v1.2.3