feat: context menus on search panel, track info dialog, separate shuffle buttons
- Add right-click context menus to search panel tracks (play, queue, favorites, playlist, go to album/artist, track info) and albums (open, favorite, go to artist) - Add "Track info..." dialog showing metadata (title, performer, composer, album, quality, hi-res status) — available everywhere: playlists, albums, favorites, search results - Split artist page shuffle into "Shuffle" (popular tracks) and "Shuffle All" (deep shuffle across all releases) - Remove magnifying glass emoji from welcome text Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "tracks.hpp"
|
||||
#include "../util/settings.hpp"
|
||||
#include "../util/trackinfo.hpp"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
@@ -237,6 +238,13 @@ void Tracks::onContextMenu(const QPoint &pos)
|
||||
}
|
||||
}
|
||||
|
||||
// Track info
|
||||
menu.addSeparator();
|
||||
auto *infoAction = menu.addAction(tr("Track info..."));
|
||||
connect(infoAction, &QAction::triggered, this, [this, trackJson] {
|
||||
TrackInfoDialog::show(trackJson, this);
|
||||
});
|
||||
|
||||
menu.exec(viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ MainWindow::MainWindow(QobuzBackend *backend, QWidget *parent)
|
||||
addDockWidget(Qt::RightDockWidgetArea, m_queuePanel);
|
||||
|
||||
// ---- Search side panel (right) ----
|
||||
m_sidePanel = new SidePanel::View(m_backend, this);
|
||||
m_sidePanel = new SidePanel::View(m_backend, m_queue, this);
|
||||
m_sidePanel->hide();
|
||||
addDockWidget(Qt::RightDockWidgetArea, m_sidePanel);
|
||||
|
||||
@@ -155,6 +155,11 @@ MainWindow::MainWindow(QobuzBackend *backend, QWidget *parent)
|
||||
this, &MainWindow::onSearchArtistSelected);
|
||||
connect(m_sidePanel, &SidePanel::View::trackPlayRequested,
|
||||
this, &MainWindow::onPlayTrackRequested);
|
||||
connect(m_sidePanel, &SidePanel::View::addToPlaylistRequested,
|
||||
this, [this](qint64 trackId, qint64 playlistId) {
|
||||
m_backend->addTrackToPlaylist(playlistId, trackId);
|
||||
statusBar()->showMessage(tr("Adding track to playlist..."), 3000);
|
||||
});
|
||||
|
||||
// ---- Album / artist navigation from content views ----
|
||||
connect(m_content, &MainContent::albumRequested,
|
||||
@@ -410,5 +415,6 @@ void MainWindow::onUserPlaylistsChanged(const QVector<QPair<qint64, QString>> &p
|
||||
{
|
||||
m_userPlaylists = playlists;
|
||||
m_content->tracksList()->setUserPlaylists(playlists);
|
||||
m_sidePanel->searchTab()->setUserPlaylists(playlists);
|
||||
}
|
||||
|
||||
|
||||
85
src/util/trackinfo.hpp
Normal file
85
src/util/trackinfo.hpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QFormLayout>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QJsonObject>
|
||||
#include <QWidget>
|
||||
|
||||
namespace TrackInfoDialog
|
||||
{
|
||||
|
||||
inline void show(const QJsonObject &track, QWidget *parent)
|
||||
{
|
||||
auto *dlg = new QDialog(parent);
|
||||
dlg->setWindowTitle(QObject::tr("Track Info"));
|
||||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dlg->setMinimumWidth(360);
|
||||
|
||||
auto *form = new QFormLayout(dlg);
|
||||
|
||||
auto addRow = [&](const QString &label, const QString &value) {
|
||||
if (value.isEmpty()) return;
|
||||
auto *val = new QLabel(value, dlg);
|
||||
val->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
val->setWordWrap(true);
|
||||
form->addRow(QStringLiteral("<b>%1</b>").arg(label), val);
|
||||
};
|
||||
|
||||
const QString title = track["title"].toString();
|
||||
const QString version = track["version"].toString().trimmed();
|
||||
addRow(QObject::tr("Title"),
|
||||
version.isEmpty() ? title : title + QStringLiteral(" (%1)").arg(version));
|
||||
|
||||
addRow(QObject::tr("Performer"), track["performer"].toObject()["name"].toString());
|
||||
|
||||
const QJsonObject composer = track["composer"].toObject();
|
||||
if (!composer.isEmpty())
|
||||
addRow(QObject::tr("Composer"), composer["name"].toString());
|
||||
|
||||
const QJsonObject album = track["album"].toObject();
|
||||
addRow(QObject::tr("Album"), album["title"].toString());
|
||||
addRow(QObject::tr("Album artist"), album["artist"].toObject()["name"].toString());
|
||||
|
||||
const int trackNum = track["track_number"].toInt();
|
||||
const int discNum = track["media_number"].toInt();
|
||||
if (trackNum > 0) {
|
||||
const QString pos = discNum > 1
|
||||
? QStringLiteral("%1-%2").arg(discNum).arg(trackNum)
|
||||
: QString::number(trackNum);
|
||||
addRow(QObject::tr("Track #"), pos);
|
||||
}
|
||||
|
||||
const qint64 dur = static_cast<qint64>(track["duration"].toDouble());
|
||||
if (dur > 0) {
|
||||
const int m = static_cast<int>(dur / 60);
|
||||
const int s = static_cast<int>(dur % 60);
|
||||
addRow(QObject::tr("Duration"),
|
||||
QStringLiteral("%1:%2").arg(m).arg(s, 2, 10, QLatin1Char('0')));
|
||||
}
|
||||
|
||||
const int bitDepth = track["maximum_bit_depth"].toInt();
|
||||
const double sampleRate = track["maximum_sampling_rate"].toDouble();
|
||||
if (bitDepth > 0 && sampleRate > 0) {
|
||||
addRow(QObject::tr("Quality"),
|
||||
QStringLiteral("%1-bit / %2 kHz").arg(bitDepth).arg(sampleRate, 0, 'f', 1));
|
||||
} else if (bitDepth > 0) {
|
||||
addRow(QObject::tr("Bit depth"), QStringLiteral("%1-bit").arg(bitDepth));
|
||||
}
|
||||
|
||||
const bool hiRes = track["hires_streamable"].toBool() || track["hires"].toBool();
|
||||
addRow(QObject::tr("Hi-Res"), hiRes ? QObject::tr("Yes") : QObject::tr("No"));
|
||||
|
||||
const bool streamable = track["streamable"].toBool(true);
|
||||
if (!streamable)
|
||||
addRow(QObject::tr("Streamable"), QObject::tr("No"));
|
||||
|
||||
auto *buttons = new QDialogButtonBox(QDialogButtonBox::Close, dlg);
|
||||
form->addRow(buttons);
|
||||
QObject::connect(buttons, &QDialogButtonBox::rejected, dlg, &QDialog::close);
|
||||
|
||||
dlg->show();
|
||||
}
|
||||
|
||||
} // namespace TrackInfoDialog
|
||||
@@ -148,15 +148,20 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
||||
btnRow->setSpacing(8);
|
||||
btnRow->setContentsMargins(0, 4, 0, 0);
|
||||
|
||||
static const QString kOutlineBtn = kBtnBase +
|
||||
QStringLiteral("QPushButton { background: #2a2a2a; color: #FFB232; border: 1px solid #FFB232; }"
|
||||
"QPushButton:pressed { background: #333; }");
|
||||
|
||||
m_playBtn = new QPushButton(tr("▶ Play"), info);
|
||||
m_playBtn->setStyleSheet(kBtnBase +
|
||||
QStringLiteral("QPushButton { background: #FFB232; color: #000; }"
|
||||
"QPushButton:pressed { background: #e09e28; }"));
|
||||
|
||||
m_shuffleTopBtn = new QPushButton(tr("⇄ Shuffle"), info);
|
||||
m_shuffleTopBtn->setStyleSheet(kOutlineBtn);
|
||||
|
||||
m_shuffleBtn = new QPushButton(tr("⇄ Shuffle All"), info);
|
||||
m_shuffleBtn->setStyleSheet(kBtnBase +
|
||||
QStringLiteral("QPushButton { background: #2a2a2a; color: #FFB232; border: 1px solid #FFB232; }"
|
||||
"QPushButton:pressed { background: #333; }"));
|
||||
m_shuffleBtn->setStyleSheet(kOutlineBtn);
|
||||
|
||||
m_favBtn = new QPushButton(tr("♡ Favourite"), info);
|
||||
m_favBtn->setStyleSheet(kBtnBase +
|
||||
@@ -164,6 +169,7 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
||||
"QPushButton:pressed { background: #333; }"));
|
||||
|
||||
btnRow->addWidget(m_playBtn);
|
||||
btnRow->addWidget(m_shuffleTopBtn);
|
||||
btnRow->addWidget(m_shuffleBtn);
|
||||
btnRow->addWidget(m_favBtn);
|
||||
btnRow->addStretch();
|
||||
@@ -246,17 +252,14 @@ ArtistView::ArtistView(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
||||
scroll->setWidget(content);
|
||||
outerLayout->addWidget(scroll, 1);
|
||||
|
||||
// Play top tracks
|
||||
// Play / shuffle top tracks
|
||||
connect(m_playBtn, &QPushButton::clicked, m_topTracks, [this] { m_topTracks->playAll(false); });
|
||||
connect(m_shuffleTopBtn, &QPushButton::clicked, m_topTracks, [this] { m_topTracks->playAll(true); });
|
||||
|
||||
// Deep shuffle: fetch all album tracks, combine, shuffle, play
|
||||
connect(m_shuffleBtn, &QPushButton::clicked, this, [this] {
|
||||
const QStringList ids = allAlbumIds();
|
||||
if (ids.isEmpty()) {
|
||||
// Fallback: just shuffle popular tracks
|
||||
m_topTracks->playAll(true);
|
||||
return;
|
||||
}
|
||||
if (ids.isEmpty()) return;
|
||||
m_shuffleBtn->setEnabled(false);
|
||||
m_shuffleBtn->setText(tr("Loading…"));
|
||||
m_backend->getAlbumsTracks(ids);
|
||||
|
||||
@@ -68,6 +68,7 @@ private:
|
||||
QLabel *m_nameLabel = nullptr;
|
||||
QTextEdit *m_bioEdit = nullptr;
|
||||
QPushButton *m_playBtn = nullptr;
|
||||
QPushButton *m_shuffleTopBtn = nullptr;
|
||||
QPushButton *m_shuffleBtn = nullptr;
|
||||
QPushButton *m_favBtn = nullptr;
|
||||
QNetworkAccessManager *m_nam = nullptr;
|
||||
|
||||
@@ -16,7 +16,7 @@ MainContent::MainContent(QobuzBackend *backend, PlayQueue *queue, QWidget *paren
|
||||
m_welcome = new QLabel(
|
||||
tr("<h2>Welcome to Qobuz</h2>"
|
||||
"<p>Select something from the library on the left to get started,<br>"
|
||||
"or use the search panel (🔍) to find music.</p>"),
|
||||
"or use the search panel to find music.</p>"),
|
||||
this);
|
||||
m_welcome->setAlignment(Qt::AlignCenter);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "view.hpp"
|
||||
#include "../../util/trackinfo.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
@@ -6,18 +7,21 @@
|
||||
#include <QHeaderView>
|
||||
#include <QFont>
|
||||
#include <QJsonArray>
|
||||
#include <QMenu>
|
||||
|
||||
static constexpr int IdRole = Qt::UserRole + 1;
|
||||
static constexpr int TypeRole = Qt::UserRole + 2;
|
||||
static constexpr int JsonRole = Qt::UserRole + 3;
|
||||
|
||||
namespace SidePanel
|
||||
{
|
||||
|
||||
// ---- SearchTab ----
|
||||
|
||||
SearchTab::SearchTab(QobuzBackend *backend, QWidget *parent)
|
||||
SearchTab::SearchTab(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_backend(backend)
|
||||
, m_queue(queue)
|
||||
{
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(4, 4, 4, 4);
|
||||
@@ -25,7 +29,7 @@ SearchTab::SearchTab(QobuzBackend *backend, QWidget *parent)
|
||||
// Search bar
|
||||
auto *barLayout = new QHBoxLayout;
|
||||
m_searchBox = new QLineEdit(this);
|
||||
m_searchBox->setPlaceholderText(tr("Search Qobuz…"));
|
||||
m_searchBox->setPlaceholderText(tr("Search Qobuz..."));
|
||||
m_searchBox->setClearButtonEnabled(true);
|
||||
auto *searchBtn = new QPushButton(tr("Go"), this);
|
||||
barLayout->addWidget(m_searchBox);
|
||||
@@ -38,6 +42,7 @@ SearchTab::SearchTab(QobuzBackend *backend, QWidget *parent)
|
||||
m_trackResults = new QTreeWidget(this);
|
||||
m_trackResults->setHeaderLabels({tr("Title"), tr("Artist"), tr("Album")});
|
||||
m_trackResults->setRootIsDecorated(false);
|
||||
m_trackResults->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
m_albumResults = new QTreeWidget(this);
|
||||
m_albumResults->setHeaderLabels({tr(""), tr("Album"), tr("Artist")});
|
||||
@@ -46,6 +51,7 @@ SearchTab::SearchTab(QobuzBackend *backend, QWidget *parent)
|
||||
m_albumResults->header()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||
m_albumResults->header()->setSectionResizeMode(2, QHeaderView::Stretch);
|
||||
m_albumResults->header()->setStretchLastSection(false);
|
||||
m_albumResults->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
m_artistResults = new QTreeWidget(this);
|
||||
m_artistResults->setHeaderLabels({tr("Artist")});
|
||||
@@ -64,6 +70,17 @@ SearchTab::SearchTab(QobuzBackend *backend, QWidget *parent)
|
||||
connect(m_trackResults, &QTreeWidget::itemDoubleClicked, this, &SearchTab::onItemDoubleClicked);
|
||||
connect(m_albumResults, &QTreeWidget::itemDoubleClicked, this, &SearchTab::onItemDoubleClicked);
|
||||
connect(m_artistResults, &QTreeWidget::itemDoubleClicked, this, &SearchTab::onItemDoubleClicked);
|
||||
|
||||
// Context menus
|
||||
connect(m_trackResults, &QTreeWidget::customContextMenuRequested,
|
||||
this, &SearchTab::onTrackContextMenu);
|
||||
connect(m_albumResults, &QTreeWidget::customContextMenuRequested,
|
||||
this, &SearchTab::onAlbumContextMenu);
|
||||
}
|
||||
|
||||
void SearchTab::setUserPlaylists(const QVector<QPair<qint64, QString>> &playlists)
|
||||
{
|
||||
m_userPlaylists = playlists;
|
||||
}
|
||||
|
||||
void SearchTab::onSearchSubmit()
|
||||
@@ -86,6 +103,7 @@ void SearchTab::onSearchResult(const QJsonObject &result)
|
||||
QStringList{t["title"].toString(), performer, album});
|
||||
item->setData(0, IdRole, static_cast<qint64>(t["id"].toDouble()));
|
||||
item->setData(0, TypeRole, QStringLiteral("track"));
|
||||
item->setData(0, JsonRole, t);
|
||||
}
|
||||
|
||||
// Populate albums
|
||||
@@ -109,8 +127,9 @@ void SearchTab::onSearchResult(const QJsonObject &result)
|
||||
item->setFont(0, hiResFont);
|
||||
item->setTextAlignment(0, Qt::AlignCenter);
|
||||
}
|
||||
item->setData(0, TypeRole, QStringLiteral("album")); // handler reads col 0
|
||||
item->setData(0, TypeRole, QStringLiteral("album"));
|
||||
item->setData(1, IdRole, a["id"].toString());
|
||||
item->setData(0, JsonRole, a);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,20 +159,135 @@ void SearchTab::onItemDoubleClicked(QTreeWidgetItem *item, int)
|
||||
}
|
||||
}
|
||||
|
||||
void SearchTab::onTrackContextMenu(const QPoint &pos)
|
||||
{
|
||||
auto *item = m_trackResults->itemAt(pos);
|
||||
if (!item) return;
|
||||
|
||||
const qint64 trackId = item->data(0, IdRole).toLongLong();
|
||||
const QJsonObject trackJson = item->data(0, JsonRole).toJsonObject();
|
||||
if (trackId <= 0) return;
|
||||
|
||||
QMenu menu(this);
|
||||
|
||||
auto *playNow = menu.addAction(tr("Play now"));
|
||||
auto *playNext = menu.addAction(tr("Play next"));
|
||||
auto *addQueue = menu.addAction(tr("Add to queue"));
|
||||
menu.addSeparator();
|
||||
|
||||
auto *addFav = menu.addAction(tr("Add to favorites"));
|
||||
|
||||
// Open album / artist
|
||||
const QString albumId = trackJson["album"].toObject()["id"].toString();
|
||||
const qint64 artistId = static_cast<qint64>(
|
||||
trackJson["performer"].toObject()["id"].toDouble());
|
||||
const QString artistName = trackJson["performer"].toObject()["name"].toString();
|
||||
const QString albumTitle = trackJson["album"].toObject()["title"].toString();
|
||||
|
||||
menu.addSeparator();
|
||||
if (!albumId.isEmpty()) {
|
||||
auto *openAlbum = menu.addAction(tr("Go to album: %1").arg(albumTitle));
|
||||
connect(openAlbum, &QAction::triggered, this, [this, albumId] {
|
||||
emit albumSelected(albumId);
|
||||
});
|
||||
}
|
||||
if (artistId > 0) {
|
||||
auto *openArtist = menu.addAction(tr("Go to artist: %1").arg(artistName));
|
||||
connect(openArtist, &QAction::triggered, this, [this, artistId] {
|
||||
emit artistSelected(artistId);
|
||||
});
|
||||
}
|
||||
|
||||
// Add to playlist submenu
|
||||
if (!m_userPlaylists.isEmpty()) {
|
||||
menu.addSeparator();
|
||||
auto *plMenu = menu.addMenu(tr("Add to playlist"));
|
||||
for (const auto &pl : m_userPlaylists) {
|
||||
auto *act = plMenu->addAction(pl.second);
|
||||
connect(act, &QAction::triggered, this, [this, trackId, plId = pl.first] {
|
||||
emit addToPlaylistRequested(trackId, plId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Track info
|
||||
menu.addSeparator();
|
||||
auto *info = menu.addAction(tr("Track info..."));
|
||||
|
||||
connect(playNow, &QAction::triggered, this, [this, trackId] {
|
||||
emit trackPlayRequested(trackId);
|
||||
});
|
||||
connect(playNext, &QAction::triggered, this, [this, trackJson] {
|
||||
m_queue->playNext(trackJson);
|
||||
});
|
||||
connect(addQueue, &QAction::triggered, this, [this, trackJson] {
|
||||
m_queue->addToQueue(trackJson);
|
||||
});
|
||||
connect(addFav, &QAction::triggered, this, [this, trackId] {
|
||||
m_backend->addFavTrack(trackId);
|
||||
});
|
||||
connect(info, &QAction::triggered, this, [this, trackJson] {
|
||||
showTrackInfo(trackJson);
|
||||
});
|
||||
|
||||
menu.exec(m_trackResults->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void SearchTab::onAlbumContextMenu(const QPoint &pos)
|
||||
{
|
||||
auto *item = m_albumResults->itemAt(pos);
|
||||
if (!item) return;
|
||||
|
||||
const QString albumId = item->data(1, IdRole).toString();
|
||||
const QJsonObject albumJson = item->data(0, JsonRole).toJsonObject();
|
||||
if (albumId.isEmpty()) return;
|
||||
|
||||
QMenu menu(this);
|
||||
|
||||
auto *openAlbum = menu.addAction(tr("Open album"));
|
||||
auto *addFav = menu.addAction(tr("Add to favorites"));
|
||||
|
||||
const qint64 artistId = static_cast<qint64>(
|
||||
albumJson["artist"].toObject()["id"].toDouble());
|
||||
const QString artistName = albumJson["artist"].toObject()["name"].toString();
|
||||
if (artistId > 0) {
|
||||
menu.addSeparator();
|
||||
auto *openArtist = menu.addAction(tr("Go to artist: %1").arg(artistName));
|
||||
connect(openArtist, &QAction::triggered, this, [this, artistId] {
|
||||
emit artistSelected(artistId);
|
||||
});
|
||||
}
|
||||
|
||||
connect(openAlbum, &QAction::triggered, this, [this, albumId] {
|
||||
emit albumSelected(albumId);
|
||||
});
|
||||
connect(addFav, &QAction::triggered, this, [this, albumId] {
|
||||
m_backend->addFavAlbum(albumId);
|
||||
});
|
||||
|
||||
menu.exec(m_albumResults->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void SearchTab::showTrackInfo(const QJsonObject &track)
|
||||
{
|
||||
TrackInfoDialog::show(track, this);
|
||||
}
|
||||
|
||||
// ---- View ----
|
||||
|
||||
View::View(QobuzBackend *backend, QWidget *parent)
|
||||
View::View(QobuzBackend *backend, PlayQueue *queue, QWidget *parent)
|
||||
: QDockWidget(tr("Search"), parent)
|
||||
{
|
||||
setObjectName(QStringLiteral("searchPanel"));
|
||||
setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable);
|
||||
|
||||
m_search = new SearchTab(backend, this);
|
||||
m_search = new SearchTab(backend, queue, this);
|
||||
setWidget(m_search);
|
||||
|
||||
connect(m_search, &SearchTab::albumSelected, this, &View::albumSelected);
|
||||
connect(m_search, &SearchTab::artistSelected, this, &View::artistSelected);
|
||||
connect(m_search, &SearchTab::trackPlayRequested, this, &View::trackPlayRequested);
|
||||
connect(m_search, &SearchTab::addToPlaylistRequested, this, &View::addToPlaylistRequested);
|
||||
}
|
||||
|
||||
} // namespace SidePanel
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../backend/qobuzbackend.hpp"
|
||||
#include "../../playqueue.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QDockWidget>
|
||||
@@ -8,6 +9,8 @@
|
||||
#include <QLineEdit>
|
||||
#include <QTreeWidget>
|
||||
#include <QJsonObject>
|
||||
#include <QVector>
|
||||
#include <QPair>
|
||||
|
||||
namespace SidePanel
|
||||
{
|
||||
@@ -15,12 +18,15 @@ namespace SidePanel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SearchTab(QobuzBackend *backend, QWidget *parent = nullptr);
|
||||
explicit SearchTab(QobuzBackend *backend, PlayQueue *queue, QWidget *parent = nullptr);
|
||||
|
||||
void setUserPlaylists(const QVector<QPair<qint64, QString>> &playlists);
|
||||
|
||||
signals:
|
||||
void albumSelected(const QString &albumId);
|
||||
void artistSelected(qint64 artistId);
|
||||
void trackPlayRequested(qint64 trackId);
|
||||
void addToPlaylistRequested(qint64 trackId, qint64 playlistId);
|
||||
|
||||
private slots:
|
||||
void onSearchResult(const QJsonObject &result);
|
||||
@@ -29,18 +35,24 @@ namespace SidePanel
|
||||
|
||||
private:
|
||||
QobuzBackend *m_backend = nullptr;
|
||||
PlayQueue *m_queue = nullptr;
|
||||
QLineEdit *m_searchBox = nullptr;
|
||||
QTabWidget *m_resultTabs = nullptr;
|
||||
QTreeWidget *m_trackResults = nullptr;
|
||||
QTreeWidget *m_albumResults = nullptr;
|
||||
QTreeWidget *m_artistResults = nullptr;
|
||||
QVector<QPair<qint64, QString>> m_userPlaylists;
|
||||
|
||||
void onTrackContextMenu(const QPoint &pos);
|
||||
void onAlbumContextMenu(const QPoint &pos);
|
||||
void showTrackInfo(const QJsonObject &track);
|
||||
};
|
||||
|
||||
class View : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit View(QobuzBackend *backend, QWidget *parent = nullptr);
|
||||
explicit View(QobuzBackend *backend, PlayQueue *queue, QWidget *parent = nullptr);
|
||||
|
||||
SearchTab *searchTab() const { return m_search; }
|
||||
|
||||
@@ -48,6 +60,7 @@ namespace SidePanel
|
||||
void albumSelected(const QString &albumId);
|
||||
void artistSelected(qint64 artistId);
|
||||
void trackPlayRequested(qint64 trackId);
|
||||
void addToPlaylistRequested(qint64 trackId, qint64 playlistId);
|
||||
|
||||
private:
|
||||
SearchTab *m_search = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user