diff --git a/src/view/genrebrowser.cpp b/src/view/genrebrowser.cpp index 3f0a0d7..8087dc6 100644 --- a/src/view/genrebrowser.cpp +++ b/src/view/genrebrowser.cpp @@ -15,9 +15,10 @@ #include -GenreBrowserView::GenreBrowserView(QobuzBackend *backend, QWidget *parent) +GenreBrowserView::GenreBrowserView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent) : QWidget(parent) , m_backend(backend) + , m_queue(queue) { auto *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -26,31 +27,34 @@ GenreBrowserView::GenreBrowserView(QobuzBackend *backend, QWidget *parent) auto *topBar = new QWidget(this); auto *topLayout = new QHBoxLayout(topBar); topLayout->setContentsMargins(8, 6, 8, 6); + topLayout->setSpacing(6); m_browseLabel = new QLabel(tr("Show:"), this); topLayout->addWidget(m_browseLabel); m_kindCombo = new QComboBox(this); m_kindCombo->addItem(tr("Albums"), QStringLiteral("albums")); m_kindCombo->addItem(tr("Playlists"), QStringLiteral("playlists")); + m_kindCombo->setMinimumWidth(110); topLayout->addWidget(m_kindCombo); m_gapAfterKind = new QWidget(this); - m_gapAfterKind->setFixedWidth(12); + m_gapAfterKind->setFixedWidth(6); topLayout->addWidget(m_gapAfterKind); m_genreLabel = new QLabel(tr("Genre:"), this); topLayout->addWidget(m_genreLabel); m_genreCombo = new QComboBox(this); - m_genreCombo->setMinimumWidth(160); + m_genreCombo->setMinimumWidth(180); topLayout->addWidget(m_genreCombo); m_gapAfterGenre = new QWidget(this); - m_gapAfterGenre->setFixedWidth(16); + m_gapAfterGenre->setFixedWidth(10); topLayout->addWidget(m_gapAfterGenre); m_typeLabel = new QLabel(tr("Type:"), this); topLayout->addWidget(m_typeLabel); m_typeCombo = new QComboBox(this); + m_typeCombo->setMinimumWidth(180); topLayout->addWidget(m_typeCombo); 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->setClearButtonEnabled(true); m_playlistSearchBox->setVisible(false); - m_playlistSearchBox->setMinimumWidth(180); + m_playlistSearchBox->setMinimumWidth(220); m_playlistSearchBox->setMaximumWidth(320); topLayout->addWidget(m_playlistSearchBox); - m_playlistSearchBtn = new QPushButton(tr("Search"), this); + m_playlistSearchBtn = new QPushButton(tr("Go"), this); m_playlistSearchBtn->setVisible(false); topLayout->addWidget(m_playlistSearchBtn); + m_deepShuffleBtn = new QPushButton(tr("⇄ Deep Shuffle"), this); + m_deepShuffleBtn->setVisible(false); + topLayout->addWidget(m_deepShuffleBtn); + topLayout->addStretch(); layout->addWidget(topBar); @@ -137,6 +145,8 @@ GenreBrowserView::GenreBrowserView(QobuzBackend *backend, QWidget *parent) this, &GenreBrowserView::onSelectionChanged); connect(m_playlistSearchBtn, &QPushButton::clicked, this, &GenreBrowserView::onSelectionChanged); + connect(m_deepShuffleBtn, &QPushButton::clicked, + this, &GenreBrowserView::onDeepShuffleClicked); connect(m_albumList, &AlbumListView::albumSelected, this, &GenreBrowserView::albumSelected); connect(m_albumList, &QTreeWidget::customContextMenuRequested, @@ -183,6 +193,7 @@ void GenreBrowserView::refreshModeUi() m_playlistSearchBox->setVisible(false); m_playlistSearchLabel->setVisible(false); m_playlistSearchBtn->setVisible(false); + m_deepShuffleBtn->setVisible(m_kindCombo->currentData().toString() == QStringLiteral("albums")); refreshGenreTypeChoices(); return; } @@ -194,6 +205,7 @@ void GenreBrowserView::refreshModeUi() m_playlistSearchLabel->setVisible(true); m_playlistSearchBox->setVisible(true); m_playlistSearchBtn->setVisible(true); + m_deepShuffleBtn->setVisible(false); 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: Qobuz Digs"), QStringLiteral("discover-qobuzdigs")); m_resultsStack->setCurrentIndex(1); + m_deepShuffleBtn->setVisible(false); } else { m_typeCombo->addItem(tr("New Releases"), QStringLiteral("new-releases")); 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("Press Awards"), QStringLiteral("press-awards")); m_resultsStack->setCurrentIndex(0); + m_deepShuffleBtn->setVisible(m_mode == BrowseMode::Genres); } m_typeCombo->blockSignals(false); @@ -351,6 +365,7 @@ void GenreBrowserView::onSelectionChanged() m_playlistSearchLabel->setVisible(true); m_playlistSearchBox->setVisible(true); m_playlistSearchBtn->setVisible(true); + m_deepShuffleBtn->setVisible(false); const QString query = m_playlistSearchBox->text().trimmed(); if (query.size() < 2) { m_playlistList->clear(); @@ -384,10 +399,55 @@ void GenreBrowserView::onSelectionChanged() m_backend->getFeaturedPlaylists(genreIds, type, 25, 0); } else { m_resultsStack->setCurrentIndex(0); + m_deepShuffleBtn->setVisible(m_mode == BrowseMode::Genres); 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(first["id"].toDouble()); + if (id > 0) + emit playTrackRequested(id); + return true; +} + void GenreBrowserView::onAlbumContextMenu(const QPoint &pos) { QTreeWidgetItem *item = m_albumList->itemAt(pos); diff --git a/src/view/genrebrowser.hpp b/src/view/genrebrowser.hpp index 84f0919..8f144ba 100644 --- a/src/view/genrebrowser.hpp +++ b/src/view/genrebrowser.hpp @@ -1,6 +1,7 @@ #pragma once #include "../backend/qobuzbackend.hpp" +#include "../playqueue.hpp" #include "albumlistview.hpp" #include @@ -24,15 +25,17 @@ public: PlaylistSearch, }; - explicit GenreBrowserView(QobuzBackend *backend, QWidget *parent = nullptr); + explicit GenreBrowserView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent = nullptr); void ensureGenresLoaded(); void setBrowseMode(BrowseMode mode); + bool tryHandleDeepShuffleTracks(const QJsonArray &tracks); signals: void albumSelected(const QString &albumId); void artistSelected(qint64 artistId); void playlistSelected(qint64 playlistId); + void playTrackRequested(qint64 trackId); private slots: void onGenresLoaded(const QJsonObject &result); @@ -44,9 +47,11 @@ private slots: void onAlbumContextMenu(const QPoint &pos); void onPlaylistActivated(QTreeWidgetItem *item, int column); void onPlaylistContextMenu(const QPoint &pos); + void onDeepShuffleClicked(); private: QobuzBackend *m_backend = nullptr; + PlayQueue *m_queue = nullptr; QLabel *m_browseLabel = nullptr; QLabel *m_genreLabel = nullptr; QLabel *m_typeLabel = nullptr; @@ -58,6 +63,7 @@ private: QComboBox *m_typeCombo = nullptr; QLineEdit *m_playlistSearchBox = nullptr; QPushButton *m_playlistSearchBtn = nullptr; + QPushButton *m_deepShuffleBtn = nullptr; QStackedWidget *m_resultsStack = nullptr; AlbumListView *m_albumList = nullptr; QTreeWidget *m_playlistList = nullptr; @@ -65,10 +71,12 @@ private: bool m_genresLoaded = false; int m_lastGenreComboIndex = 0; QSet m_multiGenreIds; + bool m_waitingDeepShuffle = false; void refreshModeUi(); void refreshGenreTypeChoices(); QString currentGenreIds() const; + QStringList currentAlbumIds() const; bool chooseMultiGenres(); void updateMultiGenreLabel(); void setPlaylistItems(const QJsonArray &items); diff --git a/src/view/maincontent.cpp b/src/view/maincontent.cpp index 006936e..f449358 100644 --- a/src/view/maincontent.cpp +++ b/src/view/maincontent.cpp @@ -52,7 +52,7 @@ MainContent::MainContent(QobuzBackend *backend, PlayQueue *queue, QWidget *paren m_albumList = new AlbumListView(this); m_artistList = new ArtistListView(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(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::artistSelected, this, &MainContent::artistRequested); connect(m_genreBrowser, &GenreBrowserView::playlistSelected, this, &MainContent::playlistRequested); + connect(m_genreBrowser, &GenreBrowserView::playTrackRequested, this, &MainContent::playTrackRequested); } void MainContent::showWelcome() { m_stack->setCurrentIndex(0); } @@ -132,6 +133,8 @@ void MainContent::setFavArtistIds(const QSet &ids) void MainContent::onDeepShuffleTracks(const QJsonArray &tracks) { + if (m_genreBrowser->tryHandleDeepShuffleTracks(tracks)) + return; m_artistView->onDeepShuffleTracks(tracks); }