fix(slint): album card overlay, award ribbon, and quality tooltip
Three corrections to the Discover album card:
- Overlay meta: the genre is one point larger and bold, and the year
now sits a single line below it (conditional `if` rows with explicit
heights, instead of `visible` toggles that left a phantom gap)
- Award ribbon: width hugs its label (capped at 200px, below the card
width) instead of spanning the full card — it reads as a ribbon again
- Quality badge: extracted to a QualityBadge primitive that reveals
the exact quality ("Hi-Res: 24-bit / 96 kHz") on hover, mirroring the
Tauri QualityBadge; home.rs derives the label from the discover
audio info
cc
This commit is contained in:
6 files changed
+138
-33
No files matched your search
@@ -12,6 +12,7 @@ import { Radius } from "../foundation/radius.slint";
|
||||
import { Spacing } from "../foundation/spacing.slint";
|
||||
import { AlbumCardItem } from "../state.slint";
|
||||
import { QbzIcon } from "../primitives/QbzIcon.slint";
|
||||
import { QualityBadge } from "../primitives/QualityBadge.slint";
|
||||
|
||||
// Circular hover-overlay action button.
|
||||
component OverlayButton inherits Rectangle {
|
||||
@@ -75,26 +76,29 @@ export component AlbumCard inherits Rectangle {
|
||||
animate opacity { duration: 150ms; }
|
||||
}
|
||||
|
||||
// Hover meta — genre + year, top-left.
|
||||
// Hover meta — genre + year, top-left. The year sits one
|
||||
// single line below the genre.
|
||||
VerticalLayout {
|
||||
x: 12px;
|
||||
y: 12px;
|
||||
spacing: 1px;
|
||||
spacing: 2px;
|
||||
opacity: hover.has-hover ? 1.0 : 0.0;
|
||||
animate opacity { duration: 150ms; }
|
||||
|
||||
Text {
|
||||
if root.album.genre != "": Text {
|
||||
text: root.album.genre;
|
||||
visible: root.album.genre != "";
|
||||
height: 20px;
|
||||
color: #ffffffeb;
|
||||
font-size: 14px;
|
||||
font-weight: Typography.medium;
|
||||
font-size: 15px;
|
||||
font-weight: Typography.bold;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Text {
|
||||
if root.album.year != "": Text {
|
||||
text: root.album.year;
|
||||
visible: root.album.year != "";
|
||||
height: 17px;
|
||||
color: #ffffffcc;
|
||||
font-size: 13px;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,16 +129,21 @@ export component AlbumCard inherits Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// Award ribbon — always visible, above the overlay.
|
||||
// Award ribbon — content-width only (it is a ribbon, not a
|
||||
// full-width band), capped so long labels still elide.
|
||||
if root.album.ribbon != "": Rectangle {
|
||||
x: 0px;
|
||||
y: parent.height - self.height - 8px;
|
||||
height: 20px;
|
||||
// Hug the label; cap below the card width so a runaway
|
||||
// label elides instead of spanning the whole card.
|
||||
width: Math.min(ribbon-row.preferred-width, 200px);
|
||||
background: root.album.ribbon-kind == "press"
|
||||
? #d49511
|
||||
: #000000e0;
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
clip: true;
|
||||
|
||||
// Left accent stripe (not on the gold press ribbon).
|
||||
Rectangle {
|
||||
@@ -144,7 +153,7 @@ export component AlbumCard inherits Rectangle {
|
||||
? #8b5cf6
|
||||
: #eab308;
|
||||
}
|
||||
HorizontalLayout {
|
||||
ribbon-row := HorizontalLayout {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
Text {
|
||||
@@ -202,31 +211,12 @@ export component AlbumCard inherits Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// Icon-only quality badge.
|
||||
// Icon-only quality badge with an exact-quality hover tooltip.
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
|
||||
if root.album.quality-tier == "hires": Image {
|
||||
source: @image-url("../assets/hi-res.svg");
|
||||
width: 42px;
|
||||
height: 28px;
|
||||
image-fit: contain;
|
||||
}
|
||||
if root.album.quality-tier == "cd": Rectangle {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 3px;
|
||||
background: Theme.surface-elevated;
|
||||
border-width: 1px;
|
||||
border-color: Theme.border-subtle;
|
||||
Image {
|
||||
x: (parent.width - self.width) / 2;
|
||||
y: (parent.height - self.height) / 2;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
source: @image-url("../assets/cd.svg");
|
||||
image-fit: contain;
|
||||
}
|
||||
QualityBadge {
|
||||
tier: root.album.quality-tier;
|
||||
label: root.album.quality-label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// Icon-only quality badge with a hover tooltip.
|
||||
//
|
||||
// The square badge is intentionally compact (it buys layout space); the
|
||||
// exact quality — "Hi-Res: 24-bit / 96 kHz" — is revealed on hover, the
|
||||
// same string the Tauri QualityBadge surfaces.
|
||||
|
||||
import { Theme } from "../foundation/semantic-colors.slint";
|
||||
import { Typography } from "../foundation/typography.slint";
|
||||
import { Radius } from "../foundation/radius.slint";
|
||||
|
||||
export component QualityBadge inherits Rectangle {
|
||||
// "hires" | "cd" | "" — empty hides the badge entirely.
|
||||
in property <string> tier;
|
||||
// Exact quality string for the hover tooltip.
|
||||
in property <string> label;
|
||||
|
||||
visible: root.tier != "";
|
||||
width: root.tier == "hires" ? 42px : 30px;
|
||||
height: 30px;
|
||||
|
||||
if root.tier == "hires": Image {
|
||||
source: @image-url("../assets/hi-res.svg");
|
||||
width: 42px;
|
||||
height: 28px;
|
||||
x: (parent.width - self.width) / 2;
|
||||
y: (parent.height - self.height) / 2;
|
||||
image-fit: contain;
|
||||
}
|
||||
if root.tier == "cd": Rectangle {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 3px;
|
||||
background: Theme.surface-elevated;
|
||||
border-width: 1px;
|
||||
border-color: Theme.border-subtle;
|
||||
Image {
|
||||
x: (parent.width - self.width) / 2;
|
||||
y: (parent.height - self.height) / 2;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
source: @image-url("../assets/cd.svg");
|
||||
image-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
// Hover tooltip — floats just above the badge.
|
||||
tip := Rectangle {
|
||||
x: (parent.width - self.width) / 2;
|
||||
y: -self.height - 6px;
|
||||
width: tip-row.preferred-width;
|
||||
height: 24px;
|
||||
background: Theme.surface-elevated;
|
||||
border-radius: Radius.sm;
|
||||
border-width: 1px;
|
||||
border-color: Theme.border-subtle;
|
||||
drop-shadow-blur: 14px;
|
||||
drop-shadow-color: Theme.card-shadow;
|
||||
drop-shadow-offset-y: 3px;
|
||||
opacity: ta.has-hover && root.label != "" ? 1.0 : 0.0;
|
||||
animate opacity { duration: 120ms; }
|
||||
|
||||
tip-row := HorizontalLayout {
|
||||
padding-left: 9px;
|
||||
padding-right: 9px;
|
||||
Text {
|
||||
text: root.label;
|
||||
color: Theme.text-secondary;
|
||||
font-size: 11px;
|
||||
font-weight: Typography.medium;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ta := TouchArea {
|
||||
mouse-cursor: default;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ export struct AlbumCardItem {
|
||||
genre: string, // shown in the hover overlay
|
||||
year: string, // shown in the hover overlay
|
||||
quality-tier: string, // "hires" | "cd" | "" — drives the badge icon
|
||||
quality-label: string, // "Hi-Res: 24-bit / 96 kHz" — badge hover tooltip
|
||||
ribbon: string,
|
||||
ribbon-kind: string, // "press" | "qobuzissime" | "albumOfTheWeek" | ""
|
||||
artwork-url: string,
|
||||
|
||||
Reference in new issue
Block a user