Merge branch 'refactor/uniform-menus'
Some checks failed
Build for Windows / build-windows (push) Has been cancelled

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
joren
2026-04-01 00:20:49 +02:00
7 changed files with 112 additions and 28 deletions

View File

@@ -347,7 +347,7 @@ void Tracks::onContextMenu(const QPoint &pos)
m_model->data(index, TrackListModel::PlaylistTrackIdRole).toLongLong();
if (playlistTrackId > 0) {
if (m_userPlaylists.isEmpty()) menu.addSeparator();
auto *remFromPl = menu.addAction(tr("Remove from this playlist"));
auto *remFromPl = menu.addAction(QIcon(":/res/icons/list-remove.svg"), tr("Remove from this playlist"));
const qint64 curPlaylistId = m_playlistId;
const int curRow = index.row();
connect(remFromPl, &QAction::triggered, this, [this, curPlaylistId, playlistTrackId, curRow] {

View File

@@ -347,6 +347,15 @@ void MainWindow::connectToolbarSignals()
connect(m_toolBar, &MainToolBar::albumRequested, this, &MainWindow::onSearchAlbumSelected);
connect(m_toolBar, &MainToolBar::artistRequested, this, &MainWindow::onSearchArtistSelected);
connect(m_toolBar, &MainToolBar::addToPlaylistRequested,
this, [this](qint64 trackId, qint64 playlistId) {
m_backend->addTrackToPlaylist(playlistId, trackId);
statusBar()->showMessage(tr("Adding track to playlist…"), 3000);
});
connect(m_toolBar, &MainToolBar::favTrackRequested,
this, [this](qint64 trackId) {
m_backend->addFavTrack(trackId);
});
}
void MainWindow::setupMenuBar()
@@ -629,4 +638,5 @@ void MainWindow::onUserPlaylistsChanged(const QVector<QPair<qint64, QString>> &p
m_userPlaylists = playlists;
m_content->tracksList()->setUserPlaylists(playlists);
m_sidePanel->searchTab()->setUserPlaylists(playlists);
m_toolBar->setUserPlaylists(playlists);
}

View File

@@ -723,16 +723,28 @@ void GenreBrowserView::onAlbumContextMenu(const QPoint &pos)
const QString albumId = item->data(1, Qt::UserRole).toString();
const qint64 artistId = item->data(2, Qt::UserRole).toLongLong();
const QString albumTitle = item->text(1);
const QString artistName = item->text(2);
QMenu menu(this);
auto *openAlbum = menu.addAction(tr("Open Album"));
auto *openAlbum = menu.addAction(
QIcon(":/res/icons/view-media-album-cover.svg"),
tr("Open album: %1").arg(QString(albumTitle).replace(QLatin1Char('&'), QStringLiteral("&&"))));
connect(openAlbum, &QAction::triggered, this, [this, albumId] {
emit albumSelected(albumId);
});
auto *addFav = menu.addAction(QIcon(":/res/icons/starred-symbolic.svg"), tr("Add to favorites"));
connect(addFav, &QAction::triggered, this, [this, albumId] {
m_backend->addFavAlbum(albumId);
});
if (artistId > 0) {
auto *openArtist = menu.addAction(tr("Open Artist"));
menu.addSeparator();
auto *openArtist = menu.addAction(
QIcon(":/res/icons/view-media-artist.svg"),
tr("Open artist: %1").arg(QString(artistName).replace(QLatin1Char('&'), QStringLiteral("&&"))));
connect(openArtist, &QAction::triggered, this, [this, artistId] {
emit artistSelected(artistId);
});
@@ -762,7 +774,8 @@ void GenreBrowserView::onPlaylistContextMenu(const QPoint &pos)
return;
QMenu menu(this);
auto *openPlaylist = menu.addAction(tr("Open Playlist"));
auto *openPlaylist = menu.addAction(
QIcon(":/res/icons/view-media-playlist.svg"), tr("Open playlist"));
connect(openPlaylist, &QAction::triggered, this, [this, playlistId] {
emit playlistSelected(playlistId);
});

View File

@@ -41,16 +41,63 @@ MainToolBar::MainToolBar(QobuzBackend *backend, PlayQueue *queue, QWidget *paren
connect(m_trackLabel, &QLabel::customContextMenuRequested,
this, [this](const QPoint &pos) {
if (m_currentTrack.isEmpty()) return;
QMenu menu(this);
const qint64 trackId = static_cast<qint64>(m_currentTrack["id"].toDouble());
const QString albumId = m_currentTrack["album"].toObject()["id"].toString();
const QString albumTitle = m_currentTrack["album"].toObject()["title"].toString();
const qint64 artistId = static_cast<qint64>(
m_currentTrack["performer"].toObject()["id"].toDouble());
if (!albumId.isEmpty())
menu.addAction(tr("Go to Album"), this, [this, albumId] { emit albumRequested(albumId); });
if (artistId > 0)
menu.addAction(tr("Go to Artist"), this, [this, artistId] { emit artistRequested(artistId); });
if (!menu.isEmpty())
menu.exec(m_trackLabel->mapToGlobal(pos));
const QString artistName = m_currentTrack["performer"].toObject()["name"].toString();
QMenu menu(this);
auto *playNext = menu.addAction(QIcon(":/res/icons/media-skip-forward.svg"), tr("Play next"));
auto *addQueue = menu.addAction(QIcon(":/res/icons/media-playlist-append.svg"), tr("Add to queue"));
menu.addSeparator();
auto *addFav = menu.addAction(QIcon(":/res/icons/starred-symbolic.svg"), tr("Add to favorites"));
connect(addFav, &QAction::triggered, this, [this, trackId] {
emit favTrackRequested(trackId);
});
if (!albumId.isEmpty() || artistId > 0)
menu.addSeparator();
if (!albumId.isEmpty()) {
auto *openAlbum = menu.addAction(
QIcon(":/res/icons/view-media-album-cover.svg"),
tr("Open album: %1").arg(QString(albumTitle).replace(QLatin1Char('&'), QStringLiteral("&&"))));
connect(openAlbum, &QAction::triggered, this, [this, albumId] {
emit albumRequested(albumId);
});
}
if (artistId > 0) {
auto *openArtist = menu.addAction(
QIcon(":/res/icons/view-media-artist.svg"),
tr("Open artist: %1").arg(QString(artistName).replace(QLatin1Char('&'), QStringLiteral("&&"))));
connect(openArtist, &QAction::triggered, this, [this, artistId] {
emit artistRequested(artistId);
});
}
if (!m_userPlaylists.isEmpty()) {
menu.addSeparator();
auto *plMenu = menu.addMenu(QIcon(":/res/icons/media-playlist-append.svg"), tr("Add to playlist"));
for (const auto &pl : m_userPlaylists) {
auto *act = plMenu->addAction(QString(pl.second).replace(QLatin1Char('&'), QStringLiteral("&&")));
connect(act, &QAction::triggered, this, [this, trackId, plId = pl.first] {
emit addToPlaylistRequested(trackId, plId);
});
}
}
connect(playNext, &QAction::triggered, this, [this] {
m_queue->playNext(m_currentTrack);
});
connect(addQueue, &QAction::triggered, this, [this] {
m_queue->addToQueue(m_currentTrack);
});
menu.exec(m_trackLabel->mapToGlobal(pos));
});
addSeparator();

View File

@@ -12,6 +12,7 @@
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonObject>
#include <QPair>
#include <QVector>
class MainToolBar : public QToolBar
@@ -27,11 +28,15 @@ public:
void setQueueToggleChecked(bool checked);
void setSearchToggleChecked(bool checked);
void setUserPlaylists(const QVector<QPair<qint64, QString>> &playlists) { m_userPlaylists = playlists; }
signals:
void searchToggled(bool visible);
void queueToggled(bool visible);
void albumRequested(const QString &albumId);
void artistRequested(qint64 artistId);
void addToPlaylistRequested(qint64 trackId, qint64 playlistId);
void favTrackRequested(qint64 trackId);
protected:
void resizeEvent(QResizeEvent *event) override;
@@ -93,5 +98,7 @@ private:
qint64 m_pendingSeekStartedMs = 0;
bool m_fetchingAutoplay = false;
QVector<QPair<qint64, QString>> m_userPlaylists;
void requestAutoplaySuggestions();
};

View File

@@ -225,8 +225,8 @@ void QueuePanel::onContextMenu(const QPoint &pos)
const int idx = item->data(UpcomingIndexRole).toInt();
QMenu menu(this);
auto *removeAct = menu.addAction(tr("Remove from queue"));
auto *toTopAct = menu.addAction(tr("Move to top (play next)"));
auto *removeAct = menu.addAction(QIcon(":/res/icons/list-remove.svg"), tr("Remove from queue"));
auto *toTopAct = menu.addAction(QIcon(":/res/icons/go-up.svg"), tr("Move to top (play next)"));
connect(removeAct, &QAction::triggered, this, [this, idx] { m_queue->removeUpcoming(idx); });
connect(toTopAct, &QAction::triggered, this, [this, idx] { m_queue->moveUpcomingToTop(idx); });

View File

@@ -241,12 +241,15 @@ void SearchTab::onTrackContextMenu(const QPoint &pos)
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"));
auto *playNow = menu.addAction(QIcon(":/res/icons/media-playback-start.svg"), tr("Play now"));
auto *playNext = menu.addAction(QIcon(":/res/icons/media-skip-forward.svg"), tr("Play next"));
auto *addQueue = menu.addAction(QIcon(":/res/icons/media-playlist-append.svg"), tr("Add to queue"));
menu.addSeparator();
auto *addFav = menu.addAction(tr("Add to favorites"));
auto *favAction = menu.addAction(QIcon(":/res/icons/starred-symbolic.svg"), tr("Add to favorites"));
connect(favAction, &QAction::triggered, this, [this, trackId] {
m_backend->addFavTrack(trackId);
});
// Open album / artist
const QString albumId = trackJson["album"].toObject()["id"].toString();
@@ -255,15 +258,20 @@ void SearchTab::onTrackContextMenu(const QPoint &pos)
const QString artistName = trackJson["performer"].toObject()["name"].toString();
const QString albumTitle = trackJson["album"].toObject()["title"].toString();
menu.addSeparator();
if (!albumId.isEmpty() || artistId > 0)
menu.addSeparator();
if (!albumId.isEmpty()) {
auto *openAlbum = menu.addAction(tr("Go to album: %1").arg(QString(albumTitle).replace(QLatin1Char('&'), QStringLiteral("&&"))));
auto *openAlbum = menu.addAction(
QIcon(":/res/icons/view-media-album-cover.svg"),
tr("Open album: %1").arg(QString(albumTitle).replace(QLatin1Char('&'), QStringLiteral("&&"))));
connect(openAlbum, &QAction::triggered, this, [this, albumId] {
emit albumSelected(albumId);
});
}
if (artistId > 0) {
auto *openArtist = menu.addAction(tr("Go to artist: %1").arg(QString(artistName).replace(QLatin1Char('&'), QStringLiteral("&&"))));
auto *openArtist = menu.addAction(
QIcon(":/res/icons/view-media-artist.svg"),
tr("Open artist: %1").arg(QString(artistName).replace(QLatin1Char('&'), QStringLiteral("&&"))));
connect(openArtist, &QAction::triggered, this, [this, artistId] {
emit artistSelected(artistId);
});
@@ -272,9 +280,9 @@ void SearchTab::onTrackContextMenu(const QPoint &pos)
// Add to playlist submenu
if (!m_userPlaylists.isEmpty()) {
menu.addSeparator();
auto *plMenu = menu.addMenu(tr("Add to playlist"));
auto *plMenu = menu.addMenu(QIcon(":/res/icons/media-playlist-append.svg"), tr("Add to playlist"));
for (const auto &pl : m_userPlaylists) {
auto *act = plMenu->addAction(pl.second);
auto *act = plMenu->addAction(QString(pl.second).replace(QLatin1Char('&'), QStringLiteral("&&")));
connect(act, &QAction::triggered, this, [this, trackId, plId = pl.first] {
emit addToPlaylistRequested(trackId, plId);
});
@@ -294,9 +302,6 @@ void SearchTab::onTrackContextMenu(const QPoint &pos)
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);
});
@@ -315,15 +320,17 @@ void SearchTab::onAlbumContextMenu(const QPoint &pos)
QMenu menu(this);
auto *openAlbum = menu.addAction(tr("Open album"));
auto *addFav = menu.addAction(tr("Add to favorites"));
auto *openAlbum = menu.addAction(QIcon(":/res/icons/view-media-album-cover.svg"), tr("Open album"));
auto *addFav = menu.addAction(QIcon(":/res/icons/starred-symbolic.svg"), 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(QString(artistName).replace(QLatin1Char('&'), QStringLiteral("&&"))));
auto *openArtist = menu.addAction(
QIcon(":/res/icons/view-media-artist.svg"),
tr("Open artist: %1").arg(QString(artistName).replace(QLatin1Char('&'), QStringLiteral("&&"))));
connect(openArtist, &QAction::triggered, this, [this, artistId] {
emit artistSelected(artistId);
});