Accumulated qt-frontend work from the 2026-08-11/12 sessions (cc), uncommitted until now: the app-wide dynamic background in both modes (AmbientField with GPU/Canvas arms, AppShell layering, ambient palette pipeline), the wave-1 clip/batch sweep across the tree (alpha batches 449 -> 327, reproducible), the rounded-cover and icon-tint arms, and scripts/qt-batches.sh (batches + presents/s sampler). The GPU behaviour of these pieces was finalized in the follow-up pulse commits; see qbz-nix-docs/qt-frontend/2026-08-11-scenegraph-batches/ GPU-COST-INVESTIGATION.md for the full campaign. km
108 lines
4.2 KiB
QML
108 lines
4.2 KiB
QML
// 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
|
|
// The well: surface-elevated @ 0.5 under the dynamic background
|
|
// (SegmentedTabBar.slint:108).
|
|
color: theme.ambientOn ? theme.surfaceElevatedA50 : 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
|
|
// The ACTIVE cell takes surface-main @ 0.5 — surface-MAIN, not
|
|
// elevated, and at the chrome alpha (SegmentedTabBar.slint:29).
|
|
color: active
|
|
? (theme.ambientOn ? theme.surfaceMainA50 : 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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|