feat: artist portrait, race condition fix, and uniform button styling
- Load artist portrait from images.portrait.hash via QNetworkAccessManager
- Fix race condition: fire getArtistReleases after setArtist() clears sections,
not before (from onArtistLoaded instead of onSearchArtistSelected)
- Apply uniform gold (#FFB232) play/shuffle button style matching album view
- Make biography scrollable (QTextEdit with max height + scroll on overflow)
- Extend track artist name parsing to handle top_tracks {display:...} format
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,11 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QScrollArea>
|
||||
#include <QTextEdit>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QPixmap>
|
||||
#include <QUrl>
|
||||
#include <QFont>
|
||||
#include <QJsonValue>
|
||||
#include <QRegularExpression>
|
||||
@@ -76,6 +81,10 @@ void ArtistSection::updateToggleText(int count)
|
||||
// ArtistView
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static const QString kBtnBase = QStringLiteral(
|
||||
"QPushButton { padding: 5px 16px; border-radius: 4px; font-weight: bold; }"
|
||||
);
|
||||
|
||||
ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
@@ -83,20 +92,49 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
||||
outerLayout->setContentsMargins(8, 8, 8, 8);
|
||||
outerLayout->setSpacing(6);
|
||||
|
||||
m_nameLabel = new QLabel(this);
|
||||
// --- Artist header: portrait + name + bio ---
|
||||
auto *headerRow = new QHBoxLayout;
|
||||
headerRow->setSpacing(12);
|
||||
|
||||
m_artLabel = new QLabel(this);
|
||||
m_artLabel->setFixedSize(100, 100);
|
||||
m_artLabel->setScaledContents(true);
|
||||
m_artLabel->setAlignment(Qt::AlignCenter);
|
||||
m_artLabel->setStyleSheet(QStringLiteral("background: #1a1a1a; border-radius: 50px;"));
|
||||
headerRow->addWidget(m_artLabel, 0, Qt::AlignTop);
|
||||
|
||||
auto *headerInfo = new QWidget(this);
|
||||
auto *headerInfoLayout = new QVBoxLayout(headerInfo);
|
||||
headerInfoLayout->setContentsMargins(0, 0, 0, 0);
|
||||
headerInfoLayout->setSpacing(4);
|
||||
|
||||
m_nameLabel = new QLabel(headerInfo);
|
||||
QFont f = m_nameLabel->font();
|
||||
f.setPointSize(f.pointSize() + 4);
|
||||
f.setBold(true);
|
||||
m_nameLabel->setFont(f);
|
||||
outerLayout->addWidget(m_nameLabel);
|
||||
headerInfoLayout->addWidget(m_nameLabel);
|
||||
|
||||
m_bioEdit = new QTextEdit(this);
|
||||
m_bioEdit = new QTextEdit(headerInfo);
|
||||
m_bioEdit->setReadOnly(true);
|
||||
m_bioEdit->setFrameShape(QFrame::NoFrame);
|
||||
m_bioEdit->setMaximumHeight(110);
|
||||
m_bioEdit->setMaximumHeight(80);
|
||||
m_bioEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
m_bioEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
outerLayout->addWidget(m_bioEdit);
|
||||
headerInfoLayout->addWidget(m_bioEdit);
|
||||
|
||||
headerRow->addWidget(headerInfo, 1);
|
||||
outerLayout->addLayout(headerRow);
|
||||
|
||||
m_nam = new QNetworkAccessManager(this);
|
||||
QObject::connect(m_nam, &QNetworkAccessManager::finished,
|
||||
this, [this](QNetworkReply *reply) {
|
||||
reply->deleteLater();
|
||||
if (reply->error() != QNetworkReply::NoError) return;
|
||||
QPixmap pix;
|
||||
if (pix.loadFromData(reply->readAll()))
|
||||
m_artLabel->setPixmap(pix);
|
||||
});
|
||||
|
||||
// Scrollable sections area
|
||||
auto *scroll = new QScrollArea(this);
|
||||
@@ -126,8 +164,16 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
||||
ttLabel->setFont(ttFont);
|
||||
ttHeaderLayout->addWidget(ttLabel, 1);
|
||||
|
||||
auto *playBtn = new QPushButton(tr("▶ Play"), ttHeader);
|
||||
auto *shuffleBtn = new QPushButton(tr("⇀ Shuffle"), ttHeader);
|
||||
auto *playBtn = new QPushButton(tr("▶ Play"), ttHeader);
|
||||
playBtn->setStyleSheet(kBtnBase +
|
||||
QStringLiteral("QPushButton { background: #FFB232; color: #000; }"
|
||||
"QPushButton:pressed { background: #e09e28; }"));
|
||||
|
||||
auto *shuffleBtn = new QPushButton(tr("⇄ Shuffle"), ttHeader);
|
||||
shuffleBtn->setStyleSheet(kBtnBase +
|
||||
QStringLiteral("QPushButton { background: #2a2a2a; color: #FFB232; border: 1px solid #FFB232; }"
|
||||
"QPushButton:pressed { background: #333; }"));
|
||||
|
||||
ttHeaderLayout->addWidget(playBtn);
|
||||
ttHeaderLayout->addWidget(shuffleBtn);
|
||||
|
||||
@@ -191,6 +237,21 @@ void ArtistView::setArtist(const QJsonObject &artist)
|
||||
m_bioEdit->setVisible(false);
|
||||
}
|
||||
|
||||
// Artist portrait: images.portrait.hash → CDN URL
|
||||
const QString hash = artist["images"].toObject()["portrait"].toObject()["hash"].toString();
|
||||
if (!hash.isEmpty() && hash.length() >= 4) {
|
||||
const QString p1 = hash.right(2);
|
||||
const QString p2 = hash.mid(hash.length() - 4, 2);
|
||||
const QString url = QStringLiteral("https://static.qobuz.com/images/artists/%1/%2/%3_600.jpg")
|
||||
.arg(p1, p2, hash);
|
||||
if (url != m_currentArtUrl) {
|
||||
m_currentArtUrl = url;
|
||||
m_nam->get(QNetworkRequest(QUrl(url)));
|
||||
}
|
||||
} else {
|
||||
m_artLabel->setPixmap(QPixmap());
|
||||
}
|
||||
|
||||
// top_tracks is a flat array in the artist/page response
|
||||
const QJsonArray topTracks = artist["top_tracks"].toArray();
|
||||
m_topTracks->loadTracks(topTracks);
|
||||
|
||||
Reference in New Issue
Block a user