feat: artist sections, fav indicator, art scaling fix, volume popup fix

- Artist profile: collapsible Albums / EPs & Singles / Other sections
  keyed on release_type; fetches up to 200 albums per artist
- Favorites: starred icon on favorited tracks, context menu shows
  Add or Remove (not both); IDs cached when fav tracks are loaded
- Shuffle button: one-time shuffle via shuffleNow() without touching
  global shuffle flag, so double-click still plays in order
- Now-playing art: replaced setFixedHeight hack with ArtWidget that
  overrides hasHeightForWidth() — scales smoothly up and down, no min-size
- Volume popup: replaced QMenu (laggy, broken drag) with Qt::Popup QFrame;
  appears below button; fixed size locked at 100% label width

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
joren
2026-03-24 17:56:47 +01:00
parent 75429faffe
commit 56473cae6f
15 changed files with 370 additions and 109 deletions

View File

@@ -22,11 +22,7 @@ View::View(QobuzBackend *backend, QWidget *parent)
layout->setContentsMargins(8, 8, 8, 8);
layout->setSpacing(6);
m_albumArt = new QLabel(container);
m_albumArt->setAlignment(Qt::AlignCenter);
m_albumArt->setStyleSheet(QStringLiteral(
"background: #1a1a1a; border-radius: 4px;"));
m_albumArt->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_albumArt = new ArtWidget(container);
layout->addWidget(m_albumArt);
m_title = new QLabel(tr("Not playing"), container);
@@ -45,7 +41,6 @@ View::View(QobuzBackend *backend, QWidget *parent)
layout->addStretch();
setWidget(container);
setMinimumWidth(160);
connect(m_backend, &QobuzBackend::trackChanged, this, &View::onTrackChanged);
}
@@ -60,7 +55,6 @@ void View::onTrackChanged(const QJsonObject &track)
m_title->setText(title.isEmpty() ? tr("Not playing") : title);
m_artist->setText(artist);
// Prefer "large" image, fall back to "small"
const QJsonObject img = track["album"].toObject()["image"].toObject();
QString artUrl = img["large"].toString();
if (artUrl.isEmpty())
@@ -77,26 +71,9 @@ void View::onArtReady(QNetworkReply *reply)
reply->deleteLater();
if (reply->error() != QNetworkReply::NoError)
return;
if (m_artPixmap.loadFromData(reply->readAll()))
scaleArtToWidth();
}
void View::resizeEvent(QResizeEvent *event)
{
QDockWidget::resizeEvent(event);
if (m_artPixmap.isNull()) return;
// Use the new dock width from the event so we don't lag behind the layout
const int side = qMax(32, event->size().width() - 16);
m_albumArt->setFixedHeight(side);
m_albumArt->setPixmap(m_artPixmap.scaled(side, side, Qt::KeepAspectRatio, Qt::SmoothTransformation));
}
void View::scaleArtToWidth()
{
if (m_artPixmap.isNull()) return;
const int side = qMax(32, width() - 16);
m_albumArt->setFixedHeight(side);
m_albumArt->setPixmap(m_artPixmap.scaled(side, side, Qt::KeepAspectRatio, Qt::SmoothTransformation));
QPixmap pix;
if (pix.loadFromData(reply->readAll()))
m_albumArt->setPixmap(pix);
}
} // namespace Context