fix: section alignment, pagination, fav state, playback error handling
**Section toggles left-aligned** - Replace QToolButton with flat QPushButton for all section headers; QPushButton properly respects text-align: left in stylesheets **Pagination via "Load More" button** - QTreeWidget expands to fit all items so the scrollbar-based infinite scroll never triggered; replaced with an explicit "Load more…" button that appears when has_more is true and emits loadMoreRequested **Favourite button reflects actual state** - MainWindow preloads fav artist IDs on session restore (getFavArtists) and caches them in m_favArtistIds - ArtistView receives the full set via setFavArtistIds() and checks it on every setArtist() call so the button starts gold if already faved - Toggling updates the local cache immediately for back/forward nav **Playback error → queue advances** - player_loop now sets track_finished on Err (was only set on Ok(None)), so the toolbar's onTrackFinished handler advances to the next track instead of stalling on an unplayable track Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
|||||||
build/
|
build/
|
||||||
build-*/
|
build-*/
|
||||||
target/
|
target/
|
||||||
|
src/visualizer/
|
||||||
.cache/
|
.cache/
|
||||||
*.user
|
*.user
|
||||||
*.autosave
|
*.autosave
|
||||||
|
|||||||
@@ -215,6 +215,9 @@ fn player_loop(rx: std::sync::mpsc::Receiver<PlayerCommand>, status: PlayerStatu
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("playback error: {e}");
|
eprintln!("playback error: {e}");
|
||||||
*status.state.lock().unwrap() = PlayerState::Error(e.to_string());
|
*status.state.lock().unwrap() = PlayerState::Error(e.to_string());
|
||||||
|
// Signal track end so the queue advances to the next track
|
||||||
|
// instead of stalling on an unplayable track.
|
||||||
|
status.track_finished.store(true, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ MainWindow::MainWindow(QobuzBackend *backend, QWidget *parent)
|
|||||||
statusBar()->showMessage(tr("Loading favorite albums…"));
|
statusBar()->showMessage(tr("Loading favorite albums…"));
|
||||||
});
|
});
|
||||||
connect(m_library, &List::Library::favArtistsRequested, this, [this] {
|
connect(m_library, &List::Library::favArtistsRequested, this, [this] {
|
||||||
|
m_showFavArtistsOnLoad = true;
|
||||||
m_backend->getFavArtists();
|
m_backend->getFavArtists();
|
||||||
statusBar()->showMessage(tr("Loading favorite artists…"));
|
statusBar()->showMessage(tr("Loading favorite artists…"));
|
||||||
});
|
});
|
||||||
@@ -233,6 +234,8 @@ void MainWindow::tryRestoreSession()
|
|||||||
m_backend->getUser(); // userLoaded will call m_library->refresh()
|
m_backend->getUser(); // userLoaded will call m_library->refresh()
|
||||||
else
|
else
|
||||||
m_library->refresh();
|
m_library->refresh();
|
||||||
|
// Preload fav artists so the artist page fav button works immediately
|
||||||
|
m_backend->getFavArtists();
|
||||||
const QString name = AppSettings::instance().displayName();
|
const QString name = AppSettings::instance().displayName();
|
||||||
statusBar()->showMessage(tr("Signed in as %1").arg(
|
statusBar()->showMessage(tr("Signed in as %1").arg(
|
||||||
name.isEmpty() ? AppSettings::instance().userEmail() : name));
|
name.isEmpty() ? AppSettings::instance().userEmail() : name));
|
||||||
@@ -339,10 +342,23 @@ void MainWindow::onFavAlbumsLoaded(const QJsonObject &result)
|
|||||||
|
|
||||||
void MainWindow::onFavArtistsLoaded(const QJsonObject &result)
|
void MainWindow::onFavArtistsLoaded(const QJsonObject &result)
|
||||||
{
|
{
|
||||||
|
// Always cache fav artist IDs (needed by the artist page fav button)
|
||||||
|
m_favArtistIds.clear();
|
||||||
|
const QJsonArray items = result["items"].toArray();
|
||||||
|
for (const QJsonValue &v : items) {
|
||||||
|
const qint64 id = static_cast<qint64>(v.toObject()["id"].toDouble());
|
||||||
|
if (id > 0) m_favArtistIds.insert(id);
|
||||||
|
}
|
||||||
|
m_content->setFavArtistIds(m_favArtistIds);
|
||||||
|
|
||||||
|
// Only navigate to the fav artists page if the user explicitly requested it
|
||||||
|
if (m_showFavArtistsOnLoad) {
|
||||||
|
m_showFavArtistsOnLoad = false;
|
||||||
m_content->showFavArtists(result);
|
m_content->showFavArtists(result);
|
||||||
statusBar()->showMessage(
|
statusBar()->showMessage(
|
||||||
tr("%1 favorite artists").arg(result["total"].toInt()), 4000);
|
tr("%1 favorite artists").arg(result["total"].toInt()), 4000);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::onAlbumLoaded(const QJsonObject &album)
|
void MainWindow::onAlbumLoaded(const QJsonObject &album)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ private:
|
|||||||
QobuzBackend *m_backend = nullptr;
|
QobuzBackend *m_backend = nullptr;
|
||||||
PlayQueue *m_queue = nullptr;
|
PlayQueue *m_queue = nullptr;
|
||||||
QVector<QPair<qint64, QString>> m_userPlaylists;
|
QVector<QPair<qint64, QString>> m_userPlaylists;
|
||||||
|
QSet<qint64> m_favArtistIds;
|
||||||
|
bool m_showFavArtistsOnLoad = false;
|
||||||
MainToolBar *m_toolBar = nullptr;
|
MainToolBar *m_toolBar = nullptr;
|
||||||
MainContent *m_content = nullptr;
|
MainContent *m_content = nullptr;
|
||||||
List::Library *m_library = nullptr;
|
List::Library *m_library = nullptr;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QScrollBar>
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
// Shared button style (mirrors TrackContextHeader)
|
// Shared button style (mirrors TrackContextHeader)
|
||||||
@@ -18,11 +17,12 @@ static const QString kBtnBase = QStringLiteral(
|
|||||||
"QPushButton { padding: 5px 16px; border-radius: 4px; font-weight: bold; }"
|
"QPushButton { padding: 5px 16px; border-radius: 4px; font-weight: bold; }"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Section-toggle style: text-only, left-aligned, bottom separator
|
// Section-toggle style: flat QPushButton, truly left-aligned
|
||||||
static const QString kToggleStyle = QStringLiteral(
|
static const QString kToggleStyle = QStringLiteral(
|
||||||
"QToolButton { text-align: left; font-weight: bold; padding: 4px 6px;"
|
"QPushButton { text-align: left; font-weight: bold; font-size: 13px;"
|
||||||
" border: none; border-bottom: 1px solid #333; }"
|
" padding: 6px 8px; border: none; border-bottom: 1px solid #333;"
|
||||||
"QToolButton:hover { background: #1e1e1e; }"
|
" background: transparent; }"
|
||||||
|
"QPushButton:hover { background: #1e1e1e; }"
|
||||||
);
|
);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -38,10 +38,10 @@ ArtistSection::ArtistSection(const QString &title, const QString &releaseType, Q
|
|||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
layout->setSpacing(0);
|
layout->setSpacing(0);
|
||||||
|
|
||||||
m_toggle = new QToolButton(this);
|
m_toggle = new QPushButton(this);
|
||||||
m_toggle->setCheckable(true);
|
m_toggle->setCheckable(true);
|
||||||
m_toggle->setChecked(true);
|
m_toggle->setChecked(true);
|
||||||
m_toggle->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
m_toggle->setFlat(true);
|
||||||
m_toggle->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
m_toggle->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
m_toggle->setStyleSheet(kToggleStyle);
|
m_toggle->setStyleSheet(kToggleStyle);
|
||||||
layout->addWidget(m_toggle);
|
layout->addWidget(m_toggle);
|
||||||
@@ -49,16 +49,26 @@ ArtistSection::ArtistSection(const QString &title, const QString &releaseType, Q
|
|||||||
m_list = new AlbumListView(this);
|
m_list = new AlbumListView(this);
|
||||||
layout->addWidget(m_list);
|
layout->addWidget(m_list);
|
||||||
|
|
||||||
connect(m_toggle, &QToolButton::toggled, m_list, &AlbumListView::setVisible);
|
// "Load more" button (shown when has_more is true)
|
||||||
connect(m_list, &AlbumListView::albumSelected, this, &ArtistSection::albumSelected);
|
m_loadMoreBtn = new QPushButton(tr("Load more…"), this);
|
||||||
|
m_loadMoreBtn->setStyleSheet(QStringLiteral(
|
||||||
|
"QPushButton { text-align: left; color: #FFB232; background: transparent;"
|
||||||
|
" border: none; padding: 6px 8px; }"
|
||||||
|
"QPushButton:hover { background: #1e1e1e; }"));
|
||||||
|
m_loadMoreBtn->setCursor(Qt::PointingHandCursor);
|
||||||
|
m_loadMoreBtn->setVisible(false);
|
||||||
|
layout->addWidget(m_loadMoreBtn);
|
||||||
|
|
||||||
// Infinite scroll: emit loadMoreRequested when scrolled to bottom
|
connect(m_toggle, &QPushButton::toggled, this, [this](bool checked) {
|
||||||
connect(m_list->verticalScrollBar(), &QScrollBar::valueChanged, this, [this](int val) {
|
m_list->setVisible(checked);
|
||||||
const int max = m_list->verticalScrollBar()->maximum();
|
m_loadMoreBtn->setVisible(checked && m_hasMore);
|
||||||
if (m_hasMore && max > 0 && val >= max) {
|
updateToggleText();
|
||||||
m_hasMore = false; // prevent double-fire until next page arrives
|
});
|
||||||
|
connect(m_list, &AlbumListView::albumSelected, this, &ArtistSection::albumSelected);
|
||||||
|
connect(m_loadMoreBtn, &QPushButton::clicked, this, [this] {
|
||||||
|
m_loadMoreBtn->setEnabled(false);
|
||||||
|
m_loadMoreBtn->setText(tr("Loading…"));
|
||||||
emit loadMoreRequested(m_releaseType, m_loadedCount);
|
emit loadMoreRequested(m_releaseType, m_loadedCount);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
updateToggleText();
|
updateToggleText();
|
||||||
@@ -69,6 +79,9 @@ void ArtistSection::setAlbums(const QJsonArray &albums, bool hasMore)
|
|||||||
m_list->setAlbums(albums);
|
m_list->setAlbums(albums);
|
||||||
m_loadedCount = albums.size();
|
m_loadedCount = albums.size();
|
||||||
m_hasMore = hasMore;
|
m_hasMore = hasMore;
|
||||||
|
m_loadMoreBtn->setVisible(hasMore && m_toggle->isChecked());
|
||||||
|
m_loadMoreBtn->setEnabled(true);
|
||||||
|
m_loadMoreBtn->setText(tr("Load more…"));
|
||||||
updateToggleText();
|
updateToggleText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +90,9 @@ void ArtistSection::appendAlbums(const QJsonArray &albums, bool hasMore)
|
|||||||
m_list->addAlbums(albums);
|
m_list->addAlbums(albums);
|
||||||
m_loadedCount += albums.size();
|
m_loadedCount += albums.size();
|
||||||
m_hasMore = hasMore;
|
m_hasMore = hasMore;
|
||||||
|
m_loadMoreBtn->setVisible(hasMore && m_toggle->isChecked());
|
||||||
|
m_loadMoreBtn->setEnabled(true);
|
||||||
|
m_loadMoreBtn->setText(tr("Load more…"));
|
||||||
updateToggleText();
|
updateToggleText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,10 +108,6 @@ void ArtistSection::updateToggleText()
|
|||||||
? QStringLiteral("%1%2 (%3)").arg(arrow, m_baseTitle).arg(m_loadedCount)
|
? QStringLiteral("%1%2 (%3)").arg(arrow, m_baseTitle).arg(m_loadedCount)
|
||||||
: arrow + m_baseTitle;
|
: arrow + m_baseTitle;
|
||||||
m_toggle->setText(text);
|
m_toggle->setText(text);
|
||||||
|
|
||||||
disconnect(m_toggle, &QToolButton::toggled, nullptr, nullptr);
|
|
||||||
connect(m_toggle, &QToolButton::toggled, m_list, &AlbumListView::setVisible);
|
|
||||||
connect(m_toggle, &QToolButton::toggled, this, [this](bool) { updateToggleText(); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -194,16 +206,16 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
|||||||
sectLayout->setContentsMargins(0, 0, 0, 0);
|
sectLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
sectLayout->setSpacing(0);
|
sectLayout->setSpacing(0);
|
||||||
|
|
||||||
// Popular Tracks — same collapsible header style as ArtistSection
|
// Popular Tracks section — same toggle style as release sections
|
||||||
m_topTracksSection = new QWidget(content);
|
m_topTracksSection = new QWidget(content);
|
||||||
auto *ttLayout = new QVBoxLayout(m_topTracksSection);
|
auto *ttLayout = new QVBoxLayout(m_topTracksSection);
|
||||||
ttLayout->setContentsMargins(0, 0, 0, 0);
|
ttLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
ttLayout->setSpacing(0);
|
ttLayout->setSpacing(0);
|
||||||
|
|
||||||
m_topTracksToggle = new QToolButton(m_topTracksSection);
|
m_topTracksToggle = new QPushButton(m_topTracksSection);
|
||||||
m_topTracksToggle->setCheckable(true);
|
m_topTracksToggle->setCheckable(true);
|
||||||
m_topTracksToggle->setChecked(true);
|
m_topTracksToggle->setChecked(true);
|
||||||
m_topTracksToggle->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
m_topTracksToggle->setFlat(true);
|
||||||
m_topTracksToggle->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
m_topTracksToggle->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
m_topTracksToggle->setStyleSheet(kToggleStyle);
|
m_topTracksToggle->setStyleSheet(kToggleStyle);
|
||||||
ttLayout->addWidget(m_topTracksToggle);
|
ttLayout->addWidget(m_topTracksToggle);
|
||||||
@@ -212,7 +224,7 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
|||||||
m_topTracks->setMaximumHeight(320);
|
m_topTracks->setMaximumHeight(320);
|
||||||
ttLayout->addWidget(m_topTracks);
|
ttLayout->addWidget(m_topTracks);
|
||||||
|
|
||||||
connect(m_topTracksToggle, &QToolButton::toggled, m_topTracks, &List::Tracks::setVisible);
|
connect(m_topTracksToggle, &QPushButton::toggled, m_topTracks, &QWidget::setVisible);
|
||||||
connect(m_topTracks, &List::Tracks::playTrackRequested, this, &ArtistView::playTrackRequested);
|
connect(m_topTracks, &List::Tracks::playTrackRequested, this, &ArtistView::playTrackRequested);
|
||||||
|
|
||||||
sectLayout->addWidget(m_topTracksSection);
|
sectLayout->addWidget(m_topTracksSection);
|
||||||
@@ -242,10 +254,13 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
|||||||
connect(m_favBtn, &QPushButton::clicked, this, [this] {
|
connect(m_favBtn, &QPushButton::clicked, this, [this] {
|
||||||
if (m_artistId <= 0) return;
|
if (m_artistId <= 0) return;
|
||||||
m_isFaved = !m_isFaved;
|
m_isFaved = !m_isFaved;
|
||||||
if (m_isFaved)
|
if (m_isFaved) {
|
||||||
m_backend->addFavArtist(m_artistId);
|
m_backend->addFavArtist(m_artistId);
|
||||||
else
|
m_favArtistIds.insert(m_artistId);
|
||||||
|
} else {
|
||||||
m_backend->removeFavArtist(m_artistId);
|
m_backend->removeFavArtist(m_artistId);
|
||||||
|
m_favArtistIds.remove(m_artistId);
|
||||||
|
}
|
||||||
setFaved(m_isFaved);
|
setFaved(m_isFaved);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -256,7 +271,7 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
|||||||
connect(m_secCompilations, &ArtistSection::albumSelected, this, &ArtistView::albumSelected);
|
connect(m_secCompilations, &ArtistSection::albumSelected, this, &ArtistView::albumSelected);
|
||||||
connect(m_secOther, &ArtistSection::albumSelected, this, &ArtistView::albumSelected);
|
connect(m_secOther, &ArtistSection::albumSelected, this, &ArtistView::albumSelected);
|
||||||
|
|
||||||
// Load-more connections: when a section scrolls to bottom, request next page
|
// Load-more connections
|
||||||
auto connectLoadMore = [this](ArtistSection *sec) {
|
auto connectLoadMore = [this](ArtistSection *sec) {
|
||||||
connect(sec, &ArtistSection::loadMoreRequested, this,
|
connect(sec, &ArtistSection::loadMoreRequested, this,
|
||||||
[this](const QString &releaseType, int nextOffset) {
|
[this](const QString &releaseType, int nextOffset) {
|
||||||
@@ -274,8 +289,7 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
|||||||
void ArtistView::setArtist(const QJsonObject &artist)
|
void ArtistView::setArtist(const QJsonObject &artist)
|
||||||
{
|
{
|
||||||
m_artistId = static_cast<qint64>(artist["id"].toDouble());
|
m_artistId = static_cast<qint64>(artist["id"].toDouble());
|
||||||
m_isFaved = false;
|
setFaved(m_favArtistIds.contains(m_artistId));
|
||||||
setFaved(false);
|
|
||||||
|
|
||||||
m_nameLabel->setText(artist["name"].toObject()["display"].toString());
|
m_nameLabel->setText(artist["name"].toObject()["display"].toString());
|
||||||
|
|
||||||
@@ -298,7 +312,6 @@ void ArtistView::setArtist(const QJsonObject &artist)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Artist portrait: images.portrait.hash + format → CDN URL
|
// Artist portrait: images.portrait.hash + format → CDN URL
|
||||||
// URL format: https://static.qobuz.com/images/artists/covers/large/{hash}.{format}
|
|
||||||
const QJsonObject portrait = artist["images"].toObject()["portrait"].toObject();
|
const QJsonObject portrait = artist["images"].toObject()["portrait"].toObject();
|
||||||
const QString hash = portrait["hash"].toString();
|
const QString hash = portrait["hash"].toString();
|
||||||
const QString format = portrait["format"].toString();
|
const QString format = portrait["format"].toString();
|
||||||
@@ -307,7 +320,6 @@ void ArtistView::setArtist(const QJsonObject &artist)
|
|||||||
artUrl = QStringLiteral("https://static.qobuz.com/images/artists/covers/large/%1.%2")
|
artUrl = QStringLiteral("https://static.qobuz.com/images/artists/covers/large/%1.%2")
|
||||||
.arg(hash, format.isEmpty() ? QStringLiteral("jpg") : format);
|
.arg(hash, format.isEmpty() ? QStringLiteral("jpg") : format);
|
||||||
} else {
|
} else {
|
||||||
// Fallback: try direct image field
|
|
||||||
const QJsonObject img = artist["image"].toObject();
|
const QJsonObject img = artist["image"].toObject();
|
||||||
artUrl = img["large"].toString();
|
artUrl = img["large"].toString();
|
||||||
if (artUrl.isEmpty()) artUrl = img["small"].toString();
|
if (artUrl.isEmpty()) artUrl = img["small"].toString();
|
||||||
@@ -325,9 +337,9 @@ void ArtistView::setArtist(const QJsonObject &artist)
|
|||||||
m_topTracks->loadTracks(topTracks);
|
m_topTracks->loadTracks(topTracks);
|
||||||
|
|
||||||
const int ttCount = topTracks.size();
|
const int ttCount = topTracks.size();
|
||||||
disconnect(m_topTracksToggle, &QToolButton::toggled, nullptr, nullptr);
|
disconnect(m_topTracksToggle, &QPushButton::toggled, nullptr, nullptr);
|
||||||
connect(m_topTracksToggle, &QToolButton::toggled, m_topTracks, &List::Tracks::setVisible);
|
connect(m_topTracksToggle, &QPushButton::toggled, m_topTracks, &QWidget::setVisible);
|
||||||
connect(m_topTracksToggle, &QToolButton::toggled, this, [this, ttCount](bool open) {
|
connect(m_topTracksToggle, &QPushButton::toggled, this, [this, ttCount](bool open) {
|
||||||
const QString a = open ? QStringLiteral("▼ ") : QStringLiteral("▶ ");
|
const QString a = open ? QStringLiteral("▼ ") : QStringLiteral("▶ ");
|
||||||
m_topTracksToggle->setText(ttCount > 0
|
m_topTracksToggle->setText(ttCount > 0
|
||||||
? QStringLiteral("%1Popular Tracks (%2)").arg(a).arg(ttCount)
|
? QStringLiteral("%1Popular Tracks (%2)").arg(a).arg(ttCount)
|
||||||
@@ -340,7 +352,7 @@ void ArtistView::setArtist(const QJsonObject &artist)
|
|||||||
: QStringLiteral("▼ Popular Tracks"));
|
: QStringLiteral("▼ Popular Tracks"));
|
||||||
m_topTracksSection->setVisible(!topTracks.isEmpty());
|
m_topTracksSection->setVisible(!topTracks.isEmpty());
|
||||||
|
|
||||||
// Clear release sections — populated asynchronously via setReleases
|
// Clear release sections
|
||||||
for (ArtistSection *sec : {m_secAlbums, m_secEps, m_secLive, m_secCompilations, m_secOther}) {
|
for (ArtistSection *sec : {m_secAlbums, m_secEps, m_secLive, m_secCompilations, m_secOther}) {
|
||||||
sec->setAlbums({});
|
sec->setAlbums({});
|
||||||
sec->setVisible(false);
|
sec->setVisible(false);
|
||||||
@@ -365,6 +377,14 @@ void ArtistView::setReleases(const QString &releaseType, const QJsonArray &items
|
|||||||
sec->setVisible(!sec->isEmpty());
|
sec->setVisible(!sec->isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ArtistView::setFavArtistIds(const QSet<qint64> &ids)
|
||||||
|
{
|
||||||
|
m_favArtistIds = ids;
|
||||||
|
// Update current state if we're showing an artist
|
||||||
|
if (m_artistId > 0)
|
||||||
|
setFaved(ids.contains(m_artistId));
|
||||||
|
}
|
||||||
|
|
||||||
void ArtistView::setFaved(bool faved)
|
void ArtistView::setFaved(bool faved)
|
||||||
{
|
{
|
||||||
m_isFaved = faved;
|
m_isFaved = faved;
|
||||||
|
|||||||
@@ -8,12 +8,11 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QToolButton>
|
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QScrollBar>
|
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
class AlbumListView;
|
class AlbumListView;
|
||||||
|
|
||||||
@@ -22,7 +21,6 @@ class ArtistSection : public QWidget
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
/// releaseType is the API string ("album", "epSingle", "live", "compilation").
|
|
||||||
explicit ArtistSection(const QString &title, const QString &releaseType, QWidget *parent = nullptr);
|
explicit ArtistSection(const QString &title, const QString &releaseType, QWidget *parent = nullptr);
|
||||||
|
|
||||||
void setAlbums(const QJsonArray &albums, bool hasMore = false);
|
void setAlbums(const QJsonArray &albums, bool hasMore = false);
|
||||||
@@ -31,23 +29,21 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void albumSelected(const QString &albumId);
|
void albumSelected(const QString &albumId);
|
||||||
/// Emitted when the user scrolls to the bottom and has_more is true.
|
|
||||||
void loadMoreRequested(const QString &releaseType, int nextOffset);
|
void loadMoreRequested(const QString &releaseType, int nextOffset);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_baseTitle;
|
QString m_baseTitle;
|
||||||
QString m_releaseType;
|
QString m_releaseType;
|
||||||
QToolButton *m_toggle = nullptr;
|
QPushButton *m_toggle = nullptr;
|
||||||
AlbumListView *m_list = nullptr;
|
AlbumListView *m_list = nullptr;
|
||||||
|
QPushButton *m_loadMoreBtn = nullptr;
|
||||||
bool m_hasMore = false;
|
bool m_hasMore = false;
|
||||||
int m_loadedCount = 0;
|
int m_loadedCount = 0;
|
||||||
|
|
||||||
void updateToggleText();
|
void updateToggleText();
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Artist detail page: portrait header (mirrors TrackContextHeader),
|
/// Artist detail page.
|
||||||
/// then collapsible sections for popular tracks, albums, EPs, live,
|
|
||||||
/// compilations, and other releases.
|
|
||||||
class ArtistView : public QWidget
|
class ArtistView : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -58,6 +54,7 @@ public:
|
|||||||
void setArtist(const QJsonObject &artist);
|
void setArtist(const QJsonObject &artist);
|
||||||
void setReleases(const QString &releaseType, const QJsonArray &items,
|
void setReleases(const QString &releaseType, const QJsonArray &items,
|
||||||
bool hasMore = false, int offset = 0);
|
bool hasMore = false, int offset = 0);
|
||||||
|
void setFavArtistIds(const QSet<qint64> &ids);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void albumSelected(const QString &albumId);
|
void albumSelected(const QString &albumId);
|
||||||
@@ -77,10 +74,11 @@ private:
|
|||||||
QNetworkAccessManager *m_nam = nullptr;
|
QNetworkAccessManager *m_nam = nullptr;
|
||||||
QString m_currentArtUrl;
|
QString m_currentArtUrl;
|
||||||
bool m_isFaved = false;
|
bool m_isFaved = false;
|
||||||
|
QSet<qint64> m_favArtistIds;
|
||||||
|
|
||||||
// Popular tracks section
|
// Popular tracks section
|
||||||
QWidget *m_topTracksSection = nullptr;
|
QWidget *m_topTracksSection = nullptr;
|
||||||
QToolButton *m_topTracksToggle = nullptr;
|
QPushButton *m_topTracksToggle = nullptr;
|
||||||
List::Tracks *m_topTracks = nullptr;
|
List::Tracks *m_topTracks = nullptr;
|
||||||
|
|
||||||
// Release sections
|
// Release sections
|
||||||
|
|||||||
@@ -112,3 +112,8 @@ void MainContent::updateArtistReleases(const QString &releaseType, const QJsonAr
|
|||||||
{
|
{
|
||||||
m_artistView->setReleases(releaseType, items, hasMore, offset);
|
m_artistView->setReleases(releaseType, items, hasMore, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainContent::setFavArtistIds(const QSet<qint64> &ids)
|
||||||
|
{
|
||||||
|
m_artistView->setFavArtistIds(ids);
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public:
|
|||||||
void showFavArtists(const QJsonObject &result);
|
void showFavArtists(const QJsonObject &result);
|
||||||
void showArtist(const QJsonObject &artist);
|
void showArtist(const QJsonObject &artist);
|
||||||
void updateArtistReleases(const QString &releaseType, const QJsonArray &items, bool hasMore, int offset);
|
void updateArtistReleases(const QString &releaseType, const QJsonArray &items, bool hasMore, int offset);
|
||||||
|
void setFavArtistIds(const QSet<qint64> &ids);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void albumRequested(const QString &albumId);
|
void albumRequested(const QString &albumId);
|
||||||
|
|||||||
Reference in New Issue
Block a user