The "In library" Albums section gains the release buckets' sort select (same five keys, persisted by artist_prefs under the pseudo release type "library"; rows sorted in QML off the document's seed). Every album section — each catalog bucket and the library grid — gains a split button right of its sort (controls/QbzSplitButton.qml, sized like the sm select): the body plays the whole section in its sort order as one queue replacing the current one (`QbzPlayer.playAlbums` -> `playback_qt::play_albums`, each album resolved through the single-album path, failures skipped), the chevron offers Play random (one album of the section) and Play selected, which arms a per-section select mode: the cards show their check bottom-right inside the art (`AlbumCard.selectMarkBottom`), the button reads "Play selected (n)" and plays the checked albums in sort order, and the menu swaps that row for Cancel selection. One section is armed at a time; changing artist or tab disarms. No queue options on purpose. cc
171 lines
5.5 KiB
QML
171 lines
5.5 KiB
QML
// QbzSplitButton — a toolbar button that LOOKS like a select: the body runs
|
|
// the primary action, the chevron cell opens a menu of alternatives.
|
|
//
|
|
// +--------------+-+
|
|
// | Play all |v|
|
|
// +--------------+-+
|
|
//
|
|
// Sized and painted like `QbzSelect { sm: true }` so it sits flush beside one
|
|
// in a section header (30 tall, r6, elevated fill, no outline, 12px label,
|
|
// 14px chevron): the album-section "Play all / Play random / Play selected"
|
|
// control on the artist page is the first host. The menu is the shared
|
|
// controls/QbzContextMenu opened below-right of the whole control, the
|
|
// QbzSelect / sort-menu convention.
|
|
//
|
|
// `menuItems` is `[{ label, action, icon? }]`; a pick closes the menu and
|
|
// emits `picked(action)`. The body emits `clicked()` and never opens the
|
|
// menu — the two halves are separate hit zones with their own hover fill,
|
|
// and the 1px hairline between them is the split.
|
|
|
|
import QtQuick
|
|
import com.blitzfc.qbz
|
|
import "../theme"
|
|
|
|
Rectangle {
|
|
id: root
|
|
|
|
property string label: ""
|
|
property var menuItems: []
|
|
property int menuWidth: 180
|
|
property bool btnEnabled: true
|
|
signal clicked()
|
|
signal picked(string action)
|
|
|
|
QbzTheme { id: theme }
|
|
|
|
readonly property int chevronWidth: 24
|
|
|
|
height: 30
|
|
radius: 6
|
|
implicitWidth: bodyLabel.implicitWidth + 24 + root.chevronWidth
|
|
width: implicitWidth
|
|
// Same resting fill as QbzSelect sm (translucent under the dynamic
|
|
// background); hover is painted per zone below.
|
|
color: theme.ambientOn ? theme.surfaceElevatedA50 : theme.surfaceElevated
|
|
opacity: root.btnEnabled ? 1.0 : 0.4
|
|
|
|
// --- Body: the primary action ------------------------------------------
|
|
Rectangle {
|
|
id: body
|
|
x: 0
|
|
y: 0
|
|
width: parent.width - root.chevronWidth
|
|
height: parent.height
|
|
radius: 6
|
|
color: bodyArea.containsMouse && root.btnEnabled ? theme.surfaceHover : "transparent"
|
|
// Square the inner corners so the hover fill meets the hairline.
|
|
Rectangle {
|
|
x: parent.width - 6
|
|
y: 0
|
|
width: 6
|
|
height: parent.height
|
|
color: parent.color
|
|
}
|
|
Text {
|
|
id: bodyLabel
|
|
anchors.centerIn: parent
|
|
text: root.label
|
|
color: theme.textPrimary
|
|
font.pixelSize: 12
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
MouseArea {
|
|
id: bodyArea
|
|
anchors.fill: parent
|
|
enabled: root.btnEnabled
|
|
hoverEnabled: true
|
|
cursorShape: root.btnEnabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
onClicked: root.clicked()
|
|
}
|
|
}
|
|
|
|
// --- The split ----------------------------------------------------------
|
|
Rectangle {
|
|
x: parent.width - root.chevronWidth
|
|
y: 7
|
|
width: 1
|
|
height: parent.height - 14
|
|
color: theme.borderSubtle
|
|
}
|
|
|
|
// --- Chevron: the menu --------------------------------------------------
|
|
Rectangle {
|
|
id: chevron
|
|
x: parent.width - root.chevronWidth
|
|
y: 0
|
|
width: root.chevronWidth
|
|
height: parent.height
|
|
radius: 6
|
|
color: chevronArea.containsMouse && root.btnEnabled ? theme.surfaceHover : "transparent"
|
|
Rectangle {
|
|
x: 0
|
|
y: 0
|
|
width: 6
|
|
height: parent.height
|
|
color: parent.color
|
|
}
|
|
QbzIcon {
|
|
name: "chevron-down"
|
|
width: 14
|
|
height: 14
|
|
anchors.centerIn: parent
|
|
tintName: chevronArea.containsMouse ? "textPrimary" : "secondary"
|
|
}
|
|
MouseArea {
|
|
id: chevronArea
|
|
anchors.fill: parent
|
|
enabled: root.btnEnabled
|
|
hoverEnabled: true
|
|
cursorShape: root.btnEnabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
onClicked: menu.openBelowRight(root)
|
|
}
|
|
}
|
|
|
|
QbzContextMenu {
|
|
id: menu
|
|
menuWidth: root.menuWidth
|
|
Repeater {
|
|
model: root.menuItems
|
|
delegate: Rectangle {
|
|
required property var modelData
|
|
width: parent ? parent.width : 0
|
|
height: 33
|
|
radius: 5
|
|
color: itemArea.containsMouse ? theme.surfaceHover : "transparent"
|
|
Row {
|
|
anchors.fill: parent
|
|
anchors.leftMargin: 8
|
|
spacing: 8
|
|
QbzIcon {
|
|
visible: (modelData.icon || "") !== ""
|
|
name: modelData.icon || ""
|
|
width: visible ? 15 : 0
|
|
height: 15
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
tintName: "secondary"
|
|
}
|
|
Text {
|
|
height: parent.height
|
|
width: parent.width - 23
|
|
text: modelData.label
|
|
color: theme.textSecondary
|
|
font.pixelSize: 13
|
|
verticalAlignment: Text.AlignVCenter
|
|
elide: Text.ElideRight
|
|
}
|
|
}
|
|
MouseArea {
|
|
id: itemArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: {
|
|
menu.close()
|
|
root.picked(modelData.action)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|