Files
qbz/crates/qbz-qt/qml/controls/OfflineCacheChoiceModal.qml
T
vicrodh ed7fa588ea fix(qt): elevated controls lift on hover instead of darkening
Buttons that rest on surfaceElevated used surfaceHover for their hover
fill: a 6% white wash meant for rows that rest transparent. Over an
elevated button it lands DARKER than the rest fill — on a modal's dark
ground the Cancel/Save/footer buttons went near-black under the pointer.

QbzTheme gains surfaceElevatedHover (a lift of surfaceElevated, derived
so every palette has it), its ambient half-alpha twin, and the
elevatedFill / elevatedHoverFill pair an ambient-aware control paints.
Every site with the old pairing (65 across modals, toolbars, selects,
circle actions, chips and cards) now hovers to the lift.

cc
2026-09-14 00:49:54 -06:00

252 lines
8.6 KiB
QML

// Shared whole-collection offline-cache preflight. Rust retains the complete
// album/playlist snapshot; this surface receives only counts and labels, so a
// 5,000-track playlist never becomes a QML object graph just to ask one
// question.
import QtQuick
import com.blitzfc.qbz
import "../theme"
Item {
id: root
QbzTheme { id: theme }
readonly property var doc: {
try { return JSON.parse(QbzOffline.collectionChoiceJson || "{}") }
catch (e) { return {} }
}
readonly property bool opened: QbzOffline.collectionChoiceOpen
function tr(source) { return QbzSession.tr(source, QbzSession.trRev) }
function summary() {
return root.tr("{} of {} tracks in “{}” are already available offline.")
.replace("{}", String(root.doc.cachedTracks || 0))
.replace("{}", String(root.doc.totalTracks || 0))
.replace("{}", root.doc.title || "")
}
function restoreShellFocus() {
var p = root
while (p.parent) {
if (p.parent.isQbzShellRoot === true) {
p.parent.forceActiveFocus()
return
}
p = p.parent
}
}
function cancel() {
QbzOffline.cancelCollectionCache()
root.restoreShellFocus()
}
function choose(mode) {
QbzOffline.confirmCollectionCache(mode)
root.restoreShellFocus()
}
visible: root.opened
enabled: root.opened
z: 3150
onOpenedChanged: {
if (root.opened) keyScope.forceActiveFocus()
}
FocusScope {
id: keyScope
anchors.fill: parent
Keys.onEscapePressed: function(event) {
root.cancel()
event.accepted = true
}
}
Rectangle {
anchors.fill: parent
radius: theme.radiusMd
color: "#bf000000"
MouseArea {
anchors.fill: parent
onClicked: root.cancel()
// Wheel-lock (the DiscoverConfigModal rule).
onWheel: function (wheel) { wheel.accepted = true }
}
}
Rectangle {
x: panel.x
y: panel.y + 8
width: panel.width
height: panel.height
radius: theme.radiusMd
color: "#80000000"
opacity: 0.5
}
Rectangle {
id: panel
width: Math.min(root.width - 80, 520)
height: content.implicitHeight + 48
x: Math.round((root.width - width) / 2)
y: Math.round((root.height - height) / 2)
radius: theme.radiusMd
color: theme.surfaceMain
border.width: 1
border.color: theme.borderSubtle
MouseArea {
anchors.fill: parent
// Wheel-lock (the DiscoverConfigModal rule).
onWheel: function (wheel) { wheel.accepted = true }
}
Column {
id: content
x: 24
y: 24
width: parent.width - 48
spacing: 12
Text {
width: parent.width
text: root.tr("Some tracks are already available offline")
color: theme.textPrimary
font.pixelSize: theme.fontHeading
font.weight: theme.weightSemibold
wrapMode: Text.WordWrap
}
Text {
width: parent.width
text: root.summary()
color: theme.textSecondary
font.pixelSize: theme.fontBody
wrapMode: Text.WordWrap
}
Item { width: 1; height: 2 }
Rectangle {
width: parent.width
height: allCol.implicitHeight + 24
radius: theme.radiusSm
color: allArea.containsMouse ? theme.surfaceElevatedHover : theme.surfaceElevated
border.width: 1
border.color: allArea.containsMouse ? theme.accent : theme.borderSubtle
Row {
anchors.fill: parent
anchors.margins: 12
spacing: 12
QbzIcon {
name: "refresh-cw"
width: 18
height: 18
anchors.verticalCenter: parent.verticalCenter
tintName: "accent"
}
Column {
id: allCol
width: parent.width - 30
spacing: 3
Text {
width: parent.width
text: root.doc.kind === "playlist"
? root.tr("Download the entire playlist again")
: root.tr("Download the entire album again")
color: theme.textPrimary
font.pixelSize: theme.fontBody
font.weight: theme.weightSemibold
wrapMode: Text.WordWrap
}
Text {
width: parent.width
text: root.tr("Re-downloads every track and can repair corrupt offline copies.")
color: theme.textSecondary
font.pixelSize: theme.fontLegal
wrapMode: Text.WordWrap
}
}
}
MouseArea {
id: allArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.choose("all")
}
}
Rectangle {
width: parent.width
height: missingCol.implicitHeight + 24
radius: theme.radiusSm
color: missingArea.containsMouse ? theme.surfaceElevatedHover : theme.surfaceElevated
border.width: 1
border.color: missingArea.containsMouse ? theme.accent : theme.borderSubtle
Row {
anchors.fill: parent
anchors.margins: 12
spacing: 12
QbzIcon {
name: "cloud-download"
width: 18
height: 18
anchors.verticalCenter: parent.verticalCenter
tintName: "accent"
}
Column {
id: missingCol
width: parent.width - 30
spacing: 3
Text {
width: parent.width
text: root.tr("Only missing tracks")
color: theme.textPrimary
font.pixelSize: theme.fontBody
font.weight: theme.weightSemibold
}
Text {
width: parent.width
text: root.tr("Keeps existing offline copies and downloads tracks that are not available yet.")
color: theme.textSecondary
font.pixelSize: theme.fontLegal
wrapMode: Text.WordWrap
}
}
}
MouseArea {
id: missingArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.choose("missing")
}
}
Item {
width: parent.width
height: 36
Rectangle {
anchors.right: parent.right
width: cancelLabel.implicitWidth + 32
height: 36
radius: theme.radiusSm
color: cancelArea.containsMouse ? theme.surfaceElevatedHover : theme.surfaceElevated
Text {
id: cancelLabel
anchors.centerIn: parent
text: root.tr("Cancel")
color: theme.textPrimary
font.pixelSize: theme.fontBody
}
MouseArea {
id: cancelArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.cancel()
}
}
}
}
}
}