fix(cards): carry the Qobuz album version in card titles + tooltip when elided
Every album card producer copied `title` raw although the model carries
`version` ("50th Anniversary", "Deluxe"); the album page, info modal and quick
view already used `format_album_title`. The Artist page listed two identical
"All Things Must Pass" cards for two different editions.
- Route the 8 card producers through `album_qt::format_album_title`
(artist page buckets, album carousels, label grid, Home rails, Library ALL
feed, Artist Scene); search_qt drops its private duplicate of the helper.
- AlbumCard.qml: the title is always the full string, elided at the card
width; when Qt reports `truncated`, hovering shows the whole title in a
Controls ToolTip (QualityBadge pattern, no character counting, no ticker).
Verified: qmllint clean, qbz-qt suite 782/782, release build.
cc
This commit is contained in:
7 files changed
+55
-14
No files matched your search
@@ -921,6 +921,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
spacing: 2
|
spacing: 2
|
||||||
Text {
|
Text {
|
||||||
|
id: titleText
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 20
|
height: 20
|
||||||
text: root.title
|
text: root.title
|
||||||
@@ -930,6 +931,48 @@ Rectangle {
|
|||||||
font.weight: theme.weightMedium
|
font.weight: theme.weightMedium
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
|
// The title is ALWAYS the full one (version included) and
|
||||||
|
// elides at the card's width; when it is cut, hovering shows
|
||||||
|
// the whole text in a small bubble above the line. `truncated`
|
||||||
|
// is Qt's own verdict on the elision, so a title that fits
|
||||||
|
// never raises a tooltip — no character counting. Qt's own
|
||||||
|
// ToolTip, like QualityBadge.qml: a recycled delegate deep
|
||||||
|
// inside a view cannot name the shell's tooltip overlay.
|
||||||
|
ToolTip {
|
||||||
|
id: titleTip
|
||||||
|
visible: titleArea.containsMouse && titleText.truncated
|
||||||
|
&& root.title !== ""
|
||||||
|
text: root.title
|
||||||
|
delay: 350
|
||||||
|
timeout: -1
|
||||||
|
padding: 0
|
||||||
|
x: 0
|
||||||
|
y: -height - 4
|
||||||
|
contentWidth: titleTipText.implicitWidth
|
||||||
|
contentHeight: titleTipText.implicitHeight
|
||||||
|
contentItem: Text {
|
||||||
|
id: titleTipText
|
||||||
|
text: titleTip.text
|
||||||
|
color: theme.textPrimary
|
||||||
|
font.pixelSize: 11
|
||||||
|
font.weight: theme.weightMedium
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
width: Math.min(implicitWidth, 300)
|
||||||
|
maximumLineCount: 3
|
||||||
|
elide: Text.ElideRight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
leftPadding: 9
|
||||||
|
rightPadding: 9
|
||||||
|
topPadding: 5
|
||||||
|
bottomPadding: 5
|
||||||
|
}
|
||||||
|
background: Rectangle {
|
||||||
|
color: theme.surfaceElevated
|
||||||
|
radius: theme.radiusSm
|
||||||
|
border.width: 1
|
||||||
|
border.color: theme.borderSubtle
|
||||||
|
}
|
||||||
|
}
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: titleArea
|
id: titleArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -1086,7 +1086,7 @@ fn map_release_card(release: &qbz_models::PageArtistRelease) -> AlbumCardData {
|
|||||||
// Heart from the favourite-id cache (see `AlbumCardData::is_favorite`).
|
// Heart from the favourite-id cache (see `AlbumCardData::is_favorite`).
|
||||||
is_favorite: crate::fav_cache_qt::is_album_favorite(&release.id),
|
is_favorite: crate::fav_cache_qt::is_album_favorite(&release.id),
|
||||||
id: release.id.clone(),
|
id: release.id.clone(),
|
||||||
title: release.title.clone(),
|
title: format_album_title(&release.title, release.version.as_deref()),
|
||||||
artist,
|
artist,
|
||||||
artist_id,
|
artist_id,
|
||||||
genre: release
|
genre: release
|
||||||
@@ -1213,7 +1213,7 @@ pub async fn load_suggestions(
|
|||||||
is_pinned: crate::sidebar_qt::is_pinned("album", &a.id),
|
is_pinned: crate::sidebar_qt::is_pinned("album", &a.id),
|
||||||
is_favorite: crate::fav_cache_qt::is_album_favorite(&a.id),
|
is_favorite: crate::fav_cache_qt::is_album_favorite(&a.id),
|
||||||
id: a.id.clone(),
|
id: a.id.clone(),
|
||||||
title: a.title.clone(),
|
title: format_album_title(&a.title, a.version.as_deref()),
|
||||||
artist,
|
artist,
|
||||||
artist_id: card_artist_id,
|
artist_id: card_artist_id,
|
||||||
genre: a.genre.as_ref().map(|g| g.name.clone()).unwrap_or_default(),
|
genre: a.genre.as_ref().map(|g| g.name.clone()).unwrap_or_default(),
|
||||||
|
|||||||
@@ -442,7 +442,10 @@ pub(crate) fn map_release(release: &PageArtistRelease) -> AlbumCardData {
|
|||||||
// mounts the same AlbumCard as Home (see `AlbumCardData::is_favorite`).
|
// mounts the same AlbumCard as Home (see `AlbumCardData::is_favorite`).
|
||||||
is_favorite: crate::fav_cache_qt::is_album_favorite(&release.id),
|
is_favorite: crate::fav_cache_qt::is_album_favorite(&release.id),
|
||||||
id: release.id.clone(),
|
id: release.id.clone(),
|
||||||
title: release.title.clone(),
|
// The Qobuz `version` ("50th Anniversary", "Deluxe") rides the title
|
||||||
|
// everywhere else in the app (`format_album_title`); the card is not
|
||||||
|
// the exception — a title without it points at the wrong edition.
|
||||||
|
title: crate::album_qt::format_album_title(&release.title, release.version.as_deref()),
|
||||||
artist,
|
artist,
|
||||||
artist_id,
|
artist_id,
|
||||||
genre: release
|
genre: release
|
||||||
|
|||||||
@@ -1115,7 +1115,7 @@ fn map_album(album: &Album) -> SceneAlbum {
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
SceneAlbum {
|
SceneAlbum {
|
||||||
album_id: album.id.clone(),
|
album_id: album.id.clone(),
|
||||||
title: album.title.clone(),
|
title: crate::album_qt::format_album_title(&album.title, album.version.as_deref()),
|
||||||
artist: album.artist.name.clone(),
|
artist: album.artist.name.clone(),
|
||||||
artist_id: album.artist.id.to_string(),
|
artist_id: album.artist.id.to_string(),
|
||||||
// `image.small || image.large` — Tauri's exact order
|
// `image.small || image.large` — Tauri's exact order
|
||||||
|
|||||||
@@ -1797,7 +1797,7 @@ pub(crate) fn map_album(album: DiscoverAlbum) -> HomeCard {
|
|||||||
is_pinned,
|
is_pinned,
|
||||||
is_favorite,
|
is_favorite,
|
||||||
id: album.id,
|
id: album.id,
|
||||||
title: album.title,
|
title: crate::album_qt::format_album_title(&album.title, album.version.as_deref()),
|
||||||
artist,
|
artist,
|
||||||
artist_id,
|
artist_id,
|
||||||
blacklist_artist_ids,
|
blacklist_artist_ids,
|
||||||
@@ -1893,7 +1893,7 @@ fn map_slim(index: usize, album: DiscoverAlbum) -> HomeCard {
|
|||||||
// false. A future SlimCard with a heart inherits a correct row.
|
// false. A future SlimCard with a heart inherits a correct row.
|
||||||
is_favorite: crate::fav_cache_qt::is_album_favorite(&album.id),
|
is_favorite: crate::fav_cache_qt::is_album_favorite(&album.id),
|
||||||
id: album.id,
|
id: album.id,
|
||||||
title: album.title,
|
title: crate::album_qt::format_album_title(&album.title, album.version.as_deref()),
|
||||||
artist: subtitle,
|
artist: subtitle,
|
||||||
artist_id,
|
artist_id,
|
||||||
blacklist_artist_ids,
|
blacklist_artist_ids,
|
||||||
@@ -2101,7 +2101,7 @@ pub(crate) fn map_flat_album(album: Album) -> HomeCard {
|
|||||||
// DiscoverBrowse, so one stamp covers four surfaces.
|
// DiscoverBrowse, so one stamp covers four surfaces.
|
||||||
is_favorite: crate::fav_cache_qt::is_album_favorite(&album.id),
|
is_favorite: crate::fav_cache_qt::is_album_favorite(&album.id),
|
||||||
id: album.id,
|
id: album.id,
|
||||||
title: album.title,
|
title: crate::album_qt::format_album_title(&album.title, album.version.as_deref()),
|
||||||
artist: album.artist.name,
|
artist: album.artist.name,
|
||||||
artist_id: album.artist.id.to_string(),
|
artist_id: album.artist.id.to_string(),
|
||||||
blacklist_artist_ids,
|
blacklist_artist_ids,
|
||||||
|
|||||||
@@ -651,7 +651,7 @@ fn map_album(album: Album, ready_offline_tracks: usize) -> FeedItem {
|
|||||||
group: "favorites".into(),
|
group: "favorites".into(),
|
||||||
source: "qobuz".into(),
|
source: "qobuz".into(),
|
||||||
id: album.id,
|
id: album.id,
|
||||||
title: album.title,
|
title: crate::album_qt::format_album_title(&album.title, album.version.as_deref()),
|
||||||
subtitle: artist.clone(),
|
subtitle: artist.clone(),
|
||||||
artist,
|
artist,
|
||||||
artist_id: album.artist.id.to_string(),
|
artist_id: album.artist.id.to_string(),
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use cxx_qt_lib::QString;
|
use cxx_qt_lib::QString;
|
||||||
pub(crate) use qbz_app::settings::search_service::InteractionAction;
|
pub(crate) use qbz_app::settings::search_service::InteractionAction;
|
||||||
|
use crate::album_qt::format_album_title;
|
||||||
use qbz_app::settings::search_service::SearchService;
|
use qbz_app::settings::search_service::SearchService;
|
||||||
use qbz_app::shell::AppRuntime;
|
use qbz_app::shell::AppRuntime;
|
||||||
use qbz_core::LoggingAdapter;
|
use qbz_core::LoggingAdapter;
|
||||||
@@ -320,12 +321,6 @@ fn year_of(date: Option<&str>) -> String {
|
|||||||
date.and_then(|d| d.get(0..4)).unwrap_or("").to_string()
|
date.and_then(|d| d.get(0..4)).unwrap_or("").to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_album_title(title: &str, version: Option<&str>) -> String {
|
|
||||||
match version.map(str::trim).filter(|v| !v.is_empty()) {
|
|
||||||
Some(v) => format!("{title} ({v})"),
|
|
||||||
None => title.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map_album(album: &Album) -> CardRow {
|
fn map_album(album: &Album) -> CardRow {
|
||||||
CardRow {
|
CardRow {
|
||||||
|
|||||||
Reference in new issue
Block a user