feat: add deep shuffle in genre browse and tighten toolbar layout
Some checks failed
Build for Windows / build-windows (push) Has been cancelled

This commit is contained in:
joren
2026-03-31 00:37:00 +02:00
parent 96bb21adff
commit 5673d6cd30
3 changed files with 79 additions and 8 deletions

View File

@@ -15,9 +15,10 @@
#include <algorithm> #include <algorithm>
GenreBrowserView::GenreBrowserView(QobuzBackend *backend, QWidget *parent) GenreBrowserView::GenreBrowserView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
: QWidget(parent) : QWidget(parent)
, m_backend(backend) , m_backend(backend)
, m_queue(queue)
{ {
auto *layout = new QVBoxLayout(this); auto *layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
@@ -26,31 +27,34 @@ GenreBrowserView::GenreBrowserView(QobuzBackend *backend, QWidget *parent)
auto *topBar = new QWidget(this); auto *topBar = new QWidget(this);
auto *topLayout = new QHBoxLayout(topBar); auto *topLayout = new QHBoxLayout(topBar);
topLayout->setContentsMargins(8, 6, 8, 6); topLayout->setContentsMargins(8, 6, 8, 6);
topLayout->setSpacing(6);
m_browseLabel = new QLabel(tr("Show:"), this); m_browseLabel = new QLabel(tr("Show:"), this);
topLayout->addWidget(m_browseLabel); topLayout->addWidget(m_browseLabel);
m_kindCombo = new QComboBox(this); m_kindCombo = new QComboBox(this);
m_kindCombo->addItem(tr("Albums"), QStringLiteral("albums")); m_kindCombo->addItem(tr("Albums"), QStringLiteral("albums"));
m_kindCombo->addItem(tr("Playlists"), QStringLiteral("playlists")); m_kindCombo->addItem(tr("Playlists"), QStringLiteral("playlists"));
m_kindCombo->setMinimumWidth(110);
topLayout->addWidget(m_kindCombo); topLayout->addWidget(m_kindCombo);
m_gapAfterKind = new QWidget(this); m_gapAfterKind = new QWidget(this);
m_gapAfterKind->setFixedWidth(12); m_gapAfterKind->setFixedWidth(6);
topLayout->addWidget(m_gapAfterKind); topLayout->addWidget(m_gapAfterKind);
m_genreLabel = new QLabel(tr("Genre:"), this); m_genreLabel = new QLabel(tr("Genre:"), this);
topLayout->addWidget(m_genreLabel); topLayout->addWidget(m_genreLabel);
m_genreCombo = new QComboBox(this); m_genreCombo = new QComboBox(this);
m_genreCombo->setMinimumWidth(160); m_genreCombo->setMinimumWidth(180);
topLayout->addWidget(m_genreCombo); topLayout->addWidget(m_genreCombo);
m_gapAfterGenre = new QWidget(this); m_gapAfterGenre = new QWidget(this);
m_gapAfterGenre->setFixedWidth(16); m_gapAfterGenre->setFixedWidth(10);
topLayout->addWidget(m_gapAfterGenre); topLayout->addWidget(m_gapAfterGenre);
m_typeLabel = new QLabel(tr("Type:"), this); m_typeLabel = new QLabel(tr("Type:"), this);
topLayout->addWidget(m_typeLabel); topLayout->addWidget(m_typeLabel);
m_typeCombo = new QComboBox(this); m_typeCombo = new QComboBox(this);
m_typeCombo->setMinimumWidth(180);
topLayout->addWidget(m_typeCombo); topLayout->addWidget(m_typeCombo);
m_playlistSearchLabel = new QLabel(tr("Search:"), this); m_playlistSearchLabel = new QLabel(tr("Search:"), this);
@@ -61,14 +65,18 @@ GenreBrowserView::GenreBrowserView(QobuzBackend *backend, QWidget *parent)
m_playlistSearchBox->setPlaceholderText(tr("Search playlists...")); m_playlistSearchBox->setPlaceholderText(tr("Search playlists..."));
m_playlistSearchBox->setClearButtonEnabled(true); m_playlistSearchBox->setClearButtonEnabled(true);
m_playlistSearchBox->setVisible(false); m_playlistSearchBox->setVisible(false);
m_playlistSearchBox->setMinimumWidth(180); m_playlistSearchBox->setMinimumWidth(220);
m_playlistSearchBox->setMaximumWidth(320); m_playlistSearchBox->setMaximumWidth(320);
topLayout->addWidget(m_playlistSearchBox); topLayout->addWidget(m_playlistSearchBox);
m_playlistSearchBtn = new QPushButton(tr("Search"), this); m_playlistSearchBtn = new QPushButton(tr("Go"), this);
m_playlistSearchBtn->setVisible(false); m_playlistSearchBtn->setVisible(false);
topLayout->addWidget(m_playlistSearchBtn); topLayout->addWidget(m_playlistSearchBtn);
m_deepShuffleBtn = new QPushButton(tr("⇄ Deep Shuffle"), this);
m_deepShuffleBtn->setVisible(false);
topLayout->addWidget(m_deepShuffleBtn);
topLayout->addStretch(); topLayout->addStretch();
layout->addWidget(topBar); layout->addWidget(topBar);
@@ -137,6 +145,8 @@ GenreBrowserView::GenreBrowserView(QobuzBackend *backend, QWidget *parent)
this, &GenreBrowserView::onSelectionChanged); this, &GenreBrowserView::onSelectionChanged);
connect(m_playlistSearchBtn, &QPushButton::clicked, connect(m_playlistSearchBtn, &QPushButton::clicked,
this, &GenreBrowserView::onSelectionChanged); this, &GenreBrowserView::onSelectionChanged);
connect(m_deepShuffleBtn, &QPushButton::clicked,
this, &GenreBrowserView::onDeepShuffleClicked);
connect(m_albumList, &AlbumListView::albumSelected, connect(m_albumList, &AlbumListView::albumSelected,
this, &GenreBrowserView::albumSelected); this, &GenreBrowserView::albumSelected);
connect(m_albumList, &QTreeWidget::customContextMenuRequested, connect(m_albumList, &QTreeWidget::customContextMenuRequested,
@@ -183,6 +193,7 @@ void GenreBrowserView::refreshModeUi()
m_playlistSearchBox->setVisible(false); m_playlistSearchBox->setVisible(false);
m_playlistSearchLabel->setVisible(false); m_playlistSearchLabel->setVisible(false);
m_playlistSearchBtn->setVisible(false); m_playlistSearchBtn->setVisible(false);
m_deepShuffleBtn->setVisible(m_kindCombo->currentData().toString() == QStringLiteral("albums"));
refreshGenreTypeChoices(); refreshGenreTypeChoices();
return; return;
} }
@@ -194,6 +205,7 @@ void GenreBrowserView::refreshModeUi()
m_playlistSearchLabel->setVisible(true); m_playlistSearchLabel->setVisible(true);
m_playlistSearchBox->setVisible(true); m_playlistSearchBox->setVisible(true);
m_playlistSearchBtn->setVisible(true); m_playlistSearchBtn->setVisible(true);
m_deepShuffleBtn->setVisible(false);
m_resultsStack->setCurrentIndex(1); m_resultsStack->setCurrentIndex(1);
} }
@@ -210,6 +222,7 @@ void GenreBrowserView::refreshGenreTypeChoices()
m_typeCombo->addItem(tr("Discover: Focus"), QStringLiteral("discover-focus")); m_typeCombo->addItem(tr("Discover: Focus"), QStringLiteral("discover-focus"));
m_typeCombo->addItem(tr("Discover: Qobuz Digs"), QStringLiteral("discover-qobuzdigs")); m_typeCombo->addItem(tr("Discover: Qobuz Digs"), QStringLiteral("discover-qobuzdigs"));
m_resultsStack->setCurrentIndex(1); m_resultsStack->setCurrentIndex(1);
m_deepShuffleBtn->setVisible(false);
} else { } else {
m_typeCombo->addItem(tr("New Releases"), QStringLiteral("new-releases")); m_typeCombo->addItem(tr("New Releases"), QStringLiteral("new-releases"));
m_typeCombo->addItem(tr("Best Sellers"), QStringLiteral("best-sellers")); m_typeCombo->addItem(tr("Best Sellers"), QStringLiteral("best-sellers"));
@@ -217,6 +230,7 @@ void GenreBrowserView::refreshGenreTypeChoices()
m_typeCombo->addItem(tr("Editor Picks"), QStringLiteral("editor-picks")); m_typeCombo->addItem(tr("Editor Picks"), QStringLiteral("editor-picks"));
m_typeCombo->addItem(tr("Press Awards"), QStringLiteral("press-awards")); m_typeCombo->addItem(tr("Press Awards"), QStringLiteral("press-awards"));
m_resultsStack->setCurrentIndex(0); m_resultsStack->setCurrentIndex(0);
m_deepShuffleBtn->setVisible(m_mode == BrowseMode::Genres);
} }
m_typeCombo->blockSignals(false); m_typeCombo->blockSignals(false);
@@ -351,6 +365,7 @@ void GenreBrowserView::onSelectionChanged()
m_playlistSearchLabel->setVisible(true); m_playlistSearchLabel->setVisible(true);
m_playlistSearchBox->setVisible(true); m_playlistSearchBox->setVisible(true);
m_playlistSearchBtn->setVisible(true); m_playlistSearchBtn->setVisible(true);
m_deepShuffleBtn->setVisible(false);
const QString query = m_playlistSearchBox->text().trimmed(); const QString query = m_playlistSearchBox->text().trimmed();
if (query.size() < 2) { if (query.size() < 2) {
m_playlistList->clear(); m_playlistList->clear();
@@ -384,10 +399,55 @@ void GenreBrowserView::onSelectionChanged()
m_backend->getFeaturedPlaylists(genreIds, type, 25, 0); m_backend->getFeaturedPlaylists(genreIds, type, 25, 0);
} else { } else {
m_resultsStack->setCurrentIndex(0); m_resultsStack->setCurrentIndex(0);
m_deepShuffleBtn->setVisible(m_mode == BrowseMode::Genres);
m_backend->getFeaturedAlbums(genreIds, type, 50, 0); m_backend->getFeaturedAlbums(genreIds, type, 50, 0);
} }
} }
QStringList GenreBrowserView::currentAlbumIds() const
{
QStringList ids;
for (int i = 0; i < m_albumList->topLevelItemCount(); ++i) {
const QString id = m_albumList->topLevelItem(i)->data(1, Qt::UserRole).toString();
if (!id.isEmpty())
ids.push_back(id);
}
return ids;
}
void GenreBrowserView::onDeepShuffleClicked()
{
const QStringList albumIds = currentAlbumIds();
if (albumIds.isEmpty())
return;
m_waitingDeepShuffle = true;
m_deepShuffleBtn->setEnabled(false);
m_deepShuffleBtn->setText(tr("Loading…"));
m_backend->getAlbumsTracks(albumIds);
}
bool GenreBrowserView::tryHandleDeepShuffleTracks(const QJsonArray &tracks)
{
if (!m_waitingDeepShuffle)
return false;
m_waitingDeepShuffle = false;
m_deepShuffleBtn->setEnabled(true);
m_deepShuffleBtn->setText(tr("⇄ Deep Shuffle"));
if (tracks.isEmpty())
return true;
m_queue->setContext(tracks, 0);
m_queue->shuffleNow();
const QJsonObject first = m_queue->current();
const qint64 id = static_cast<qint64>(first["id"].toDouble());
if (id > 0)
emit playTrackRequested(id);
return true;
}
void GenreBrowserView::onAlbumContextMenu(const QPoint &pos) void GenreBrowserView::onAlbumContextMenu(const QPoint &pos)
{ {
QTreeWidgetItem *item = m_albumList->itemAt(pos); QTreeWidgetItem *item = m_albumList->itemAt(pos);

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "../backend/qobuzbackend.hpp" #include "../backend/qobuzbackend.hpp"
#include "../playqueue.hpp"
#include "albumlistview.hpp" #include "albumlistview.hpp"
#include <QComboBox> #include <QComboBox>
@@ -24,15 +25,17 @@ public:
PlaylistSearch, PlaylistSearch,
}; };
explicit GenreBrowserView(QobuzBackend *backend, QWidget *parent = nullptr); explicit GenreBrowserView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent = nullptr);
void ensureGenresLoaded(); void ensureGenresLoaded();
void setBrowseMode(BrowseMode mode); void setBrowseMode(BrowseMode mode);
bool tryHandleDeepShuffleTracks(const QJsonArray &tracks);
signals: signals:
void albumSelected(const QString &albumId); void albumSelected(const QString &albumId);
void artistSelected(qint64 artistId); void artistSelected(qint64 artistId);
void playlistSelected(qint64 playlistId); void playlistSelected(qint64 playlistId);
void playTrackRequested(qint64 trackId);
private slots: private slots:
void onGenresLoaded(const QJsonObject &result); void onGenresLoaded(const QJsonObject &result);
@@ -44,9 +47,11 @@ private slots:
void onAlbumContextMenu(const QPoint &pos); void onAlbumContextMenu(const QPoint &pos);
void onPlaylistActivated(QTreeWidgetItem *item, int column); void onPlaylistActivated(QTreeWidgetItem *item, int column);
void onPlaylistContextMenu(const QPoint &pos); void onPlaylistContextMenu(const QPoint &pos);
void onDeepShuffleClicked();
private: private:
QobuzBackend *m_backend = nullptr; QobuzBackend *m_backend = nullptr;
PlayQueue *m_queue = nullptr;
QLabel *m_browseLabel = nullptr; QLabel *m_browseLabel = nullptr;
QLabel *m_genreLabel = nullptr; QLabel *m_genreLabel = nullptr;
QLabel *m_typeLabel = nullptr; QLabel *m_typeLabel = nullptr;
@@ -58,6 +63,7 @@ private:
QComboBox *m_typeCombo = nullptr; QComboBox *m_typeCombo = nullptr;
QLineEdit *m_playlistSearchBox = nullptr; QLineEdit *m_playlistSearchBox = nullptr;
QPushButton *m_playlistSearchBtn = nullptr; QPushButton *m_playlistSearchBtn = nullptr;
QPushButton *m_deepShuffleBtn = nullptr;
QStackedWidget *m_resultsStack = nullptr; QStackedWidget *m_resultsStack = nullptr;
AlbumListView *m_albumList = nullptr; AlbumListView *m_albumList = nullptr;
QTreeWidget *m_playlistList = nullptr; QTreeWidget *m_playlistList = nullptr;
@@ -65,10 +71,12 @@ private:
bool m_genresLoaded = false; bool m_genresLoaded = false;
int m_lastGenreComboIndex = 0; int m_lastGenreComboIndex = 0;
QSet<qint64> m_multiGenreIds; QSet<qint64> m_multiGenreIds;
bool m_waitingDeepShuffle = false;
void refreshModeUi(); void refreshModeUi();
void refreshGenreTypeChoices(); void refreshGenreTypeChoices();
QString currentGenreIds() const; QString currentGenreIds() const;
QStringList currentAlbumIds() const;
bool chooseMultiGenres(); bool chooseMultiGenres();
void updateMultiGenreLabel(); void updateMultiGenreLabel();
void setPlaylistItems(const QJsonArray &items); void setPlaylistItems(const QJsonArray &items);

View File

@@ -52,7 +52,7 @@ MainContent::MainContent(QobuzBackend *backend, PlayQueue *queue, QWidget *paren
m_albumList = new AlbumListView(this); m_albumList = new AlbumListView(this);
m_artistList = new ArtistListView(this); m_artistList = new ArtistListView(this);
m_artistView = new ArtistView(backend, queue, this); m_artistView = new ArtistView(backend, queue, this);
m_genreBrowser = new GenreBrowserView(backend, this); m_genreBrowser = new GenreBrowserView(backend, queue, this);
m_stack->addWidget(m_welcome); // 0 m_stack->addWidget(m_welcome); // 0
m_stack->addWidget(tracksPage); // 1 m_stack->addWidget(tracksPage); // 1
@@ -70,6 +70,7 @@ MainContent::MainContent(QobuzBackend *backend, PlayQueue *queue, QWidget *paren
connect(m_genreBrowser, &GenreBrowserView::albumSelected, this, &MainContent::albumRequested); connect(m_genreBrowser, &GenreBrowserView::albumSelected, this, &MainContent::albumRequested);
connect(m_genreBrowser, &GenreBrowserView::artistSelected, this, &MainContent::artistRequested); connect(m_genreBrowser, &GenreBrowserView::artistSelected, this, &MainContent::artistRequested);
connect(m_genreBrowser, &GenreBrowserView::playlistSelected, this, &MainContent::playlistRequested); connect(m_genreBrowser, &GenreBrowserView::playlistSelected, this, &MainContent::playlistRequested);
connect(m_genreBrowser, &GenreBrowserView::playTrackRequested, this, &MainContent::playTrackRequested);
} }
void MainContent::showWelcome() { m_stack->setCurrentIndex(0); } void MainContent::showWelcome() { m_stack->setCurrentIndex(0); }
@@ -132,6 +133,8 @@ void MainContent::setFavArtistIds(const QSet<qint64> &ids)
void MainContent::onDeepShuffleTracks(const QJsonArray &tracks) void MainContent::onDeepShuffleTracks(const QJsonArray &tracks)
{ {
if (m_genreBrowser->tryHandleDeepShuffleTracks(tracks))
return;
m_artistView->onDeepShuffleTracks(tracks); m_artistView->onDeepShuffleTracks(tracks);
} }