Files
qbz/crates/qbz-qt/qml/controls/BrowseGenreButton.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

134 lines
5.2 KiB
QML

// BrowseGenreButton — the "Filter by genre" trigger
// (discover/BrowseHeaderTools.slint:102-145): accent fill + "N genres" while
// the SHARED selection is non-empty, else a surface-elevated pill reading
// "Filter by genre".
//
// Extracted from HomeView.qml's inline copy so the Home toolbar and the two
// browse pages draw the identical control. The one difference the Slint keeps
// is the height: HomeView.slint:85 is 32px, BrowseHeaderTools.slint:108 is
// 34px (the ui-control-alignment-standard toolbar size) — hence `btnHeight`.
//
// THE BADGE READS THE BRIDGE, NOT THE POPUP. `GenreFilterPopup` is declared
// LAST in every host (declaration order is z-order) and a creation-time
// binding that dereferences a not-yet-created id registers NO dependency, so
// it would never re-evaluate. The count is parsed straight off
// `QbzBridge.genreFilterJson` here, per context.
import QtQuick
import com.blitzfc.qbz
import "../theme"
Rectangle {
id: root
/// "discover" | "library-all" — whose selection this button reports.
property string context: "discover"
property int btnHeight: 34
signal clicked()
QbzTheme { id: theme }
readonly property var genreDoc: {
try {
return JSON.parse(QbzBridge.genreFilterJson)
} catch (e) {
return {}
}
}
readonly property int count: (genreDoc.counts || {})[root.context] || 0
readonly property bool active: root.count > 0
// APPLIED-FILTERS TOOLTIP. The selected genre NAMES are already on the wire
// per context (`genre_filter_qt.rs` publishes `names`, expanded to include
// tree descendants), so this surface costs nothing but the wiring — and
// wiring it HERE gives it to all four hosts at once: Home/Discover, the two
// browse pages and Library.
//
// `extraGroups` lets a host append the filters this button knows nothing
// about (a search term, a sort, the source toggles) so one bubble answers
// the whole toolbar instead of one control.
property var extraGroups: []
readonly property var genreNames: (genreDoc.names || {})[root.context] || []
readonly property var summaryGroups: {
var out = []
if (root.genreNames.length > 0)
out.push({
group: QbzSession.tr("Genre", QbzSession.trRev),
values: root.genreNames
})
var ex = root.extraGroups || []
for (var i = 0; i < ex.length; i++)
out.push(ex[i])
return out
}
QbzFilterTip {
id: genreTip
ownerKey: "genre-" + root.context
anchor: root
groups: root.summaryGroups
}
width: genreRow.width
height: btnHeight
radius: 6
// Resting fill translucent under the dynamic background
// (HomeView.slint:89, BrowseHeaderTools.slint:111, PlaylistTagFilter
// .slint:77 — the same chip in three mounts). Accent and hover keep theirs.
color: root.active ? theme.accent
: genreArea.containsMouse
? theme.elevatedHoverFill
: theme.elevatedFill
Row {
id: genreRow
height: parent.height
leftPadding: 12
rightPadding: 14
spacing: 7
QbzIcon {
name: "list-filter"
width: 14
height: 14
anchors.verticalCenter: parent.verticalCenter
// Both halves of this pill take the SAME decision now. They did
// not before: the glyph said "primary" (the legacy alias of a
// literal #ffffff) while the label below said accent-text, so on
// every pale-accent theme the pill drew a black label next to a
// white glyph. discover/BrowseHeaderTools.slint:123 and :132 are
// at least self-consistent — they are BOTH #ffffff — but that
// white measures 1.70:1 on high-contrast, 1.74 on ikari and
// 1.82 on wcag-dark, under 2.6:1 on 16 of the 35 palettes, so
// the port diverges from both lines at once, by measurement.
// See theme/QbzTheme.qml, "ON AN ACCENT FILL".
tintName: root.active ? theme.accentGlyphTint : "secondary"
}
Text {
text: root.count === 0
? QbzSession.tr("Filter by genre", QbzSession.trRev)
: root.count === 1
? QbzSession.tr("1 genre", QbzSession.trRev)
: QbzSession.tr("{} genres", QbzSession.trRev).replace("{}", root.count)
// The colour twin of the glyph's tint (see above) — accent-text
// on 34 of the 35 palettes, so this is a no-op everywhere except
// where accent-text itself falls under 3:1.
color: root.active ? theme.accentGlyphColor : theme.textSecondary
font.pixelSize: 13
font.weight: root.active ? theme.weightMedium : theme.weightRegular
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: genreArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.clicked()
onEntered: genreTip.enter()
onExited: genreTip.exit()
// A click opens the genre popup right over the bubble; drop it first.
onPressed: genreTip.exit()
}
}