feat: queue panel skip-to-track and drag reorder; remove visualizer

Queue panel:
- Double-clicking an upcoming track skips to it immediately: drops all
  tracks before it from the queue and starts playback (skipToUpcoming)
- Items can be dragged to reorder; rowsMoved rebuilds the queue via
  setUpcomingOrder()
- Track JSON stored per-item so order survives drag operations
- New PlayQueue methods: skipToUpcoming(), setUpcomingOrder()
- New QueuePanel signal: skipToTrackRequested(qint64) wired to MainWindow

Remove visualizer:
- Drop VisualizerWidget, Qt6::OpenGLWidgets, projectM CMake detection
- Remove qobuz_backend_read_pcm FFI (Rust + C header + Qt wrapper)
- Remove pcm_visualizer from PlayerStatus and PCM tap from AudioOutput

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
joren
2026-03-24 10:12:30 +01:00
parent 74e43b9713
commit 373fc2b43c
6 changed files with 76 additions and 4 deletions

View File

@@ -7,6 +7,7 @@
static constexpr int UpcomingIndexRole = Qt::UserRole + 1;
static constexpr int IsPlayNextRole = Qt::UserRole + 2;
static constexpr int TrackJsonRole = Qt::UserRole + 3;
QueuePanel::QueuePanel(PlayQueue *queue, QWidget *parent)
: QDockWidget(tr("Queue"), parent)
@@ -32,6 +33,8 @@ QueuePanel::QueuePanel(PlayQueue *queue, QWidget *parent)
m_list = new QListWidget(container);
m_list->setAlternatingRowColors(true);
m_list->setContextMenuPolicy(Qt::CustomContextMenu);
m_list->setDragDropMode(QAbstractItemView::InternalMove);
m_list->setDefaultDropAction(Qt::MoveAction);
layout->addWidget(m_list, 1);
setWidget(container);
@@ -45,12 +48,17 @@ QueuePanel::QueuePanel(PlayQueue *queue, QWidget *parent)
this, &QueuePanel::onItemDoubleClicked);
connect(m_list, &QListWidget::customContextMenuRequested,
this, &QueuePanel::onContextMenu);
connect(m_list->model(), &QAbstractItemModel::rowsMoved,
this, &QueuePanel::onRowsMoved);
refresh();
}
void QueuePanel::refresh()
{
if (m_refreshing) return;
m_refreshing = true;
m_list->clear();
const QVector<QJsonObject> upcoming = m_queue->upcomingTracks();
@@ -73,6 +81,7 @@ void QueuePanel::refresh()
auto *item = new QListWidgetItem(text, m_list);
item->setData(UpcomingIndexRole, i);
item->setData(IsPlayNextRole, i < playNextCount);
item->setData(TrackJsonRole, QVariant::fromValue(t));
// "Play Next" tracks shown slightly differently
if (i < playNextCount) {
@@ -81,12 +90,33 @@ void QueuePanel::refresh()
item->setFont(f);
}
}
m_refreshing = false;
}
void QueuePanel::onItemDoubleClicked(QListWidgetItem *item)
{
// Double-clicking an upcoming item is not needed for now (could skip to it later)
Q_UNUSED(item)
const int idx = item->data(UpcomingIndexRole).toInt();
const QJsonObject track = m_queue->skipToUpcoming(idx);
if (track.isEmpty()) return;
const qint64 id = static_cast<qint64>(track["id"].toDouble());
emit skipToTrackRequested(id);
}
void QueuePanel::onRowsMoved()
{
if (m_refreshing) return;
QVector<QJsonObject> newOrder;
newOrder.reserve(m_list->count());
for (int i = 0; i < m_list->count(); ++i) {
const QVariant v = m_list->item(i)->data(TrackJsonRole);
newOrder.append(v.value<QJsonObject>());
}
m_refreshing = true;
m_queue->setUpcomingOrder(newOrder);
m_refreshing = false;
}
void QueuePanel::onContextMenu(const QPoint &pos)