refactor(qt): extract search_bridge.rs from the QbzBridge god-object
QbzBridge carried five domains (Settings, Search, Playlist, Genre filter,
Discover config); its own boot() comment called the split "TEMP (phase 23)".
TRACK-RULES section 2 requires one cxx-qt bridge per domain. This moves the
Search domain out verbatim, ahead of the cortinilla parity work, so the
refactor and the behaviour change stay separately reviewable.
QbzSearch (search_bridge.rs) takes 7 properties and 11 invokables. Two
deliberate changes ride the move:
- selected_index -> cortinilla_selected_index. It is cortinilla-only
state and the bare name on an app-wide singleton is a collision hazard.
- cortinilla_scroll_y f32 -> f64, unifying with the immersive twin in
immersive_bridge.rs, which was already f64.
cortinilla_query is declared here but not yet wired; C2 wires it, which is
what makes Enter carry the text the user actually typed instead of the last
successfully loaded payload's query.
Every Rust writer of a moving property was re-pointed BY HAND, never by
regex over the multi-line ui() closures: the seven crate::ui( sites in
search_qt.rs plus two that live outside it and that the QML-only xref audit
cannot see - settings_qt.rs (the live Settings > Appearance path) and
main.rs toggle_intelligent_search. All 48 QML consumer sites across
Cortinilla.qml, HeaderBar.qml and SearchView.qml were updated, including the
one Connections block, whose onSelectedIndexChanged handler was renamed with
its property.
A stale 6.0 base in flat_index_content_y is left in place and annotated: it
mirrors a Flickable viewport margin rather than content space, so every row
reports 6px low. C4 corrects it with its unit test; this commit is a move.
Contract: qbz-nix-docs/qt-frontend/2026-08-03-cortinilla-parity, commit C0.
Gate: build clean (no new warnings), 256/256 tests, qml_resolution_audit
214 files, qml_singleton_xref 28 bridges with QbzSearch resolving, offscreen
smoke 0 complaints + home published.
cc
This commit is contained in:
11 files changed
+309
-172
No files matched your search
@@ -4,11 +4,11 @@
|
||||
// pointer and would kill typing in the header field, the exact reason the
|
||||
// Slint uses a plain Rectangle too).
|
||||
//
|
||||
// Payload: QbzBridge.cortinillaJson (search_qt.rs CortinillaData: query /
|
||||
// Payload: QbzSearch.cortinillaJson (search_qt.rs CortinillaData: query /
|
||||
// top / sections with controller flat indices). Keyboard selection rides
|
||||
// selectedIndex + cortinillaScrollY (content-space top-y of the selected
|
||||
// row — scrolled into view here). Row activation bubbles to the
|
||||
// QbzBridge.cortinilla* invokables; the field clear goes through
|
||||
// QbzSearch.cortinilla* invokables; the field clear goes through
|
||||
// root.header.clearSearch().
|
||||
|
||||
import QtQuick
|
||||
@@ -19,7 +19,7 @@ import "../theme"
|
||||
Item {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
visible: QbzBridge.cortinillaOpen
|
||||
visible: QbzSearch.cortinillaOpen
|
||||
|
||||
// The host HeaderBar (for clearSearch on row activation).
|
||||
property var headerBar: null
|
||||
@@ -29,14 +29,14 @@ Item {
|
||||
readonly property var doc: parseDoc()
|
||||
function parseDoc() {
|
||||
try {
|
||||
return JSON.parse(QbzBridge.cortinillaJson)
|
||||
return JSON.parse(QbzSearch.cortinillaJson)
|
||||
} catch (e) {
|
||||
return ({})
|
||||
}
|
||||
}
|
||||
readonly property var sections: doc.sections || []
|
||||
readonly property bool hasTop: doc.top !== undefined && doc.top !== null && (doc.top.id || "") !== ""
|
||||
readonly property bool empty: !hasTop && sections.length === 0 && !QbzBridge.cortinillaLoading
|
||||
readonly property bool empty: !hasTop && sections.length === 0 && !QbzSearch.cortinillaLoading
|
||||
|
||||
// The centered header search box width (HeaderBar's responsive rule) —
|
||||
// the click-through gaps below leave it free.
|
||||
@@ -52,17 +52,17 @@ Item {
|
||||
interval: 4500
|
||||
repeat: false
|
||||
running: root.visible && !root.panelHovered
|
||||
onTriggered: QbzBridge.cortinillaDismiss()
|
||||
onTriggered: QbzSearch.cortinillaDismiss()
|
||||
}
|
||||
Connections {
|
||||
target: QbzBridge
|
||||
target: QbzSearch
|
||||
// Activity restarts the countdown (keystroke or arrow move).
|
||||
function onCortinillaJsonChanged() { if (root.visible && !root.panelHovered) idleClose.restart() }
|
||||
function onSelectedIndexChanged() { if (root.visible && !root.panelHovered) idleClose.restart() }
|
||||
function onCortinillaSelectedIndexChanged() { if (root.visible && !root.panelHovered) idleClose.restart() }
|
||||
// Keyboard scroll-into-view: the controller publishes the selected
|
||||
// row's content-space top-y; nudge the Flickable so it is visible.
|
||||
function onCortinillaScrollYChanged() {
|
||||
var y = QbzBridge.cortinillaScrollY
|
||||
var y = QbzSearch.cortinillaScrollY
|
||||
if (y < bodyFlick.contentY) {
|
||||
bodyFlick.contentY = Math.max(0, y)
|
||||
} else if (y + 56 > bodyFlick.contentY + bodyFlick.height) {
|
||||
@@ -74,7 +74,7 @@ Item {
|
||||
Connections {
|
||||
target: QbzShell
|
||||
// A page change dismisses (AppShell's tracked-view close hook).
|
||||
function onCurrentViewChanged() { QbzBridge.cortinillaDismiss() }
|
||||
function onCurrentViewChanged() { QbzSearch.cortinillaDismiss() }
|
||||
}
|
||||
|
||||
// --- Click-outside scrims (any click outside the panel dismisses) ----
|
||||
@@ -85,7 +85,7 @@ Item {
|
||||
y: 42
|
||||
width: root.width
|
||||
height: root.height - 42
|
||||
onClicked: QbzBridge.cortinillaDismiss()
|
||||
onClicked: QbzSearch.cortinillaDismiss()
|
||||
}
|
||||
// 2) header strip LEFT of the search box.
|
||||
MouseArea {
|
||||
@@ -94,7 +94,7 @@ Item {
|
||||
y: 0
|
||||
width: (root.width - root.searchBoxWidth) / 2
|
||||
height: 42
|
||||
onClicked: QbzBridge.cortinillaDismiss()
|
||||
onClicked: QbzSearch.cortinillaDismiss()
|
||||
}
|
||||
// 3) header strip RIGHT of the search box.
|
||||
MouseArea {
|
||||
@@ -103,7 +103,7 @@ Item {
|
||||
y: 0
|
||||
width: root.width - x
|
||||
height: 42
|
||||
onClicked: QbzBridge.cortinillaDismiss()
|
||||
onClicked: QbzSearch.cortinillaDismiss()
|
||||
}
|
||||
|
||||
// --- The panel ----------------------------------------------------------
|
||||
@@ -143,7 +143,7 @@ Item {
|
||||
// --- Loading skeleton (no cached instant-paint — one clean
|
||||
// apply, Cortinilla.slint) -------------------------------
|
||||
Column {
|
||||
visible: QbzBridge.cortinillaLoading
|
||||
visible: QbzSearch.cortinillaLoading
|
||||
width: parent.width
|
||||
topPadding: 4
|
||||
property bool pulse: false
|
||||
@@ -195,7 +195,7 @@ Item {
|
||||
|
||||
// --- Loaded content ----------------------------------------
|
||||
Column {
|
||||
visible: !QbzBridge.cortinillaLoading
|
||||
visible: !QbzSearch.cortinillaLoading
|
||||
width: parent.width
|
||||
|
||||
// No results.
|
||||
@@ -269,7 +269,7 @@ Item {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.headerBar) root.headerBar.clearSearch()
|
||||
QbzBridge.cortinillaViewMore(modelData.kind)
|
||||
QbzSearch.cortinillaViewMore(modelData.kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,7 +298,7 @@ Item {
|
||||
width: parent ? parent.width : 0
|
||||
height: 56
|
||||
radius: theme.radiusSm
|
||||
readonly property bool active: QbzBridge.selectedIndex === (row.flatIndex ?? -2)
|
||||
readonly property bool active: QbzSearch.cortinillaSelectedIndex === (row.flatIndex ?? -2)
|
||||
color: (active || rowArea.containsMouse) ? theme.surfaceHover : "transparent"
|
||||
|
||||
// Accent bar on the keyboard-active row (distinct from plain hover).
|
||||
@@ -354,7 +354,7 @@ Item {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.headerBar) root.headerBar.clearSearch()
|
||||
QbzBridge.cortinillaRowClicked(row.flatIndex)
|
||||
QbzSearch.cortinillaRowClicked(row.flatIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user