refactor(qbz-qt): phase 23 part 1 — QML module layout + first five domain bridges

Two fronts of the modularization, both gated green before this commit.

QML leaves the flat qml/ directory for seven modules: cards, controls,
rows, settings, shell, theme, views. Only Main.qml and LoginScreen.qml
stay at the root. build.rs matches the tree exactly (55 files), relative
imports and asset paths were re-anchored for the new depth.

The QbzBridge God-object starts breaking up: QbzSession, QbzShell,
QbzPlayer, QbzQueue and QbzHome are now their own #[cxx_qt::bridge]
singletons, each with its own OnceLock<CxxQtThread> and ui() hop, booted
in order from Main.qml. Bridge files stay directly under src/ because
cxx-qt-build only accepts rust_files from one directory (QTBUG-93443).

bridge.rs keeps 5.5 domains (library, album/artist, settings,
integrations, search, playlist) and dies once they are split out.

cc
This commit is contained in:
vicrodh committed 2026-07-28 11:08:20 -06:00
1 parent 580b72084f
commit 9a4fec6b70
72 files changed
+2152 -1690

No files matched your search

+102
View File
@@ -0,0 +1,102 @@
// QbzTabBar — the segmented tab pill bar (primitives/SegmentedTabBar
// .slint), consolidated in phase 22 from HomeView's discover tab pill and
// LibraryView's SegTab row (+ the playlists sub-tab via `compact`).
// Arms: `tabs` ([{id, label, count?}]), `activeId`, `counts` (11px count
// chip), `underline` (2px accent active cue — the Slint redundant-shape
// cue), `compact` (24px mini variant). Emits selected(id); the host owns
// side effects (scroll resets, refetches).
// Deliberately NOT absorbed (Slint keeps them per-surface too): the
// SearchView underline tab strip, QueuePanel's text tabs, ArtistView's
// scroll-spy jump links.
import QtQuick
import com.blitzfc.qbz
import "../theme"
Rectangle {
id: root
property var tabs: []
property string activeId: ""
property bool counts: false
property bool underline: false
property bool compact: false
signal selected(string id)
QbzTheme { id: theme }
width: segBarRow.width
height: segBarRow.height
color: theme.surfaceElevated
radius: 6
Row {
id: segBarRow
padding: root.compact ? 2 : 3
spacing: 4
Repeater {
model: root.tabs
delegate: Rectangle {
id: segRoot
required property var modelData
readonly property bool active: modelData.id === root.activeId
readonly property int count: modelData.count || 0
width: segRow.implicitWidth
height: segRow.implicitHeight
radius: 4
color: active ? theme.surfaceMain
: segArea.containsMouse ? theme.surfaceHover : "transparent"
Row {
id: segRow
leftPadding: root.compact ? 8 : 12
rightPadding: (root.compact ? 8 : 12) - (root.counts && segRoot.count > 0 ? 4 : 0)
topPadding: root.compact ? 4 : 6
bottomPadding: root.compact ? 4 : 6
spacing: 7
Text {
text: segRoot.modelData.label
color: segRoot.active ? theme.textPrimary : theme.textMuted
font.pixelSize: root.compact ? 12 : theme.fontLegal
font.weight: theme.weightMedium
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
visible: root.counts && segRoot.count > 0
width: Math.max(18, countText.implicitWidth + 10)
height: 16
radius: 8
anchors.verticalCenter: parent.verticalCenter
color: segRoot.active ? "#26ffffff" : "#14ffffff"
Text {
id: countText
anchors.centerIn: parent
text: segRoot.count
color: segRoot.active ? theme.textPrimary : theme.textSecondary
font.pixelSize: 11
font.weight: theme.weightMedium
}
}
}
// Active underline (redundant shape cue, Slint Segment).
Rectangle {
visible: root.underline && segRoot.active
x: 4
width: parent.width - 8
height: 2
y: parent.height - 2
radius: 1
color: theme.accent
}
MouseArea {
id: segArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.selected(segRoot.modelData.id)
}
}
}
}
}