fix(qt): publish the live cortinilla query synchronously

Enter and "View more" submitted the PREVIOUS query. With the dropdown open
and nothing selected, HeaderBar calls clearSearch() - which wipes the input -
and then cortinillaSearchAll(), passing no text; search_all_action and
view_more then took the query from LAST_CORT, i.e. the last SUCCESSFULLY
LOADED payload. Type "beatl", let it land, keep typing "beatles", press
Enter while the fetch is in flight: the results page loads for "beatl".

The reference is immune because it publishes the query synchronously per
keystroke (SearchState.cortinilla-query, set before its debounce starts) and
reads that. This does the same: a cortinillaQuery property plus a Rust-side
CORT_QUERY mirror so the two async consumers can read it without a Qt round
trip, both written by one function so they cannot diverge - the discipline
CORT_OPEN already documents for cortinillaOpen.

Two more sites move onto the live query with it:

  - the overlay's visible gate now requires >= 2 characters, matching the
    controller's own gate, so a 1-char query cannot flash a half-empty panel;
  - the "No results for X" line quotes the live query instead of the
    payload's echo, so the empty state stops naming the previous search
    while the user is reading it.

One window stays open and closes in C3: the debounce still lives in QML, so
the query is published when the 220ms timer fires, not on the keystroke
itself. Enter pressed INSIDE that window still carries the previous text.
C3 moves the debounce into Rust, which is what makes the publish truly
per-keystroke.

Contract: qbz-nix-docs/qt-frontend/2026-08-03-cortinilla-parity, commit C2
(D-BUG-2, rows B3/A3/E6/G9/G15).

Gate: build clean, 256/256 tests, both QML audits OK, offscreen smoke
0 complaints + home published.

cc
This commit is contained in:
vicrodh committed 2026-08-03 16:13:02 -06:00
1 parent 9b6c4d12ac
commit dae6c3bb4f
4 files changed
+54 -5

No files matched your search

+12 -2
View File
@@ -19,7 +19,12 @@ import "../theme"
Item {
id: root
anchors.fill: parent
visible: QbzSearch.cortinillaOpen
// Open AND the live query long enough to have meaningful results — the
// same >= 2 gate the controller applies, so a 1-char query never flashes
// a half-empty panel (Cortinilla.slint:190-191). cortinillaQuery is
// published synchronously per keystroke, so this tracks what the user
// has typed rather than what last finished loading.
visible: QbzSearch.cortinillaOpen && QbzSearch.cortinillaQuery.length >= 2
// The host HeaderBar (for clearSearch on row activation).
property var headerBar: null
@@ -206,7 +211,12 @@ Item {
Text {
x: 14
anchors.verticalCenter: parent.verticalCenter
text: QbzSession.tr("No results for", QbzSession.trRev) + " “" + (root.doc.query || "") + "”"
// The LIVE query, matching the reference
// (Cortinilla.slint:373 reads
// SearchState.cortinilla-query, not the payload's
// echo) — otherwise the empty state quotes the
// previous query while the user reads it.
text: QbzSession.tr("No results for", QbzSession.trRev) + " “" + QbzSearch.cortinillaQuery + "”"
color: theme.textMuted
font.pixelSize: 12
}