Initial implementation of qobuz-qt
- Rust backend (qobuz-backend static lib): Qobuz API client (reqwest/tokio), Symphonia audio decoder, CPAL audio output, extern "C" FFI bridge - Qt 6 frontend mirroring spotify-qt layout: toolbar with playback controls, left library dock, central track list, right search panel - Auth: email/password login with MD5-signed requests; session token persisted via QSettings - Playback: double-click a track → Rust fetches stream URL → Symphonia decodes → CPAL outputs to default audio device - Dark Fusion palette matching spotify-qt feel Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
173
src/view/maintoolbar.cpp
Normal file
173
src/view/maintoolbar.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#include "maintoolbar.hpp"
|
||||
#include "../util/settings.hpp"
|
||||
#include "../model/tracklistmodel.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
MainToolBar::MainToolBar(QobuzBackend *backend, QWidget *parent)
|
||||
: QToolBar(parent)
|
||||
, m_backend(backend)
|
||||
{
|
||||
setMovable(false);
|
||||
setFloatable(false);
|
||||
setContextMenuPolicy(Qt::PreventContextMenu);
|
||||
|
||||
// Previous
|
||||
m_previous = addAction(Icon::previous(), tr("Previous"));
|
||||
connect(m_previous, &QAction::triggered, this, &MainToolBar::onPrevious);
|
||||
|
||||
// Play/Pause
|
||||
m_playPause = addAction(Icon::play(), tr("Play"));
|
||||
m_playPause->setCheckable(false);
|
||||
connect(m_playPause, &QAction::triggered, this, [this](bool) { onPlayPause(false); });
|
||||
|
||||
// Next
|
||||
m_next = addAction(Icon::next(), tr("Next"));
|
||||
connect(m_next, &QAction::triggered, this, &MainToolBar::onNext);
|
||||
|
||||
addSeparator();
|
||||
|
||||
// Track label
|
||||
m_trackLabel = new QLabel(tr("Not playing"), this);
|
||||
m_trackLabel->setMinimumWidth(200);
|
||||
m_trackLabel->setMaximumWidth(320);
|
||||
addWidget(m_trackLabel);
|
||||
|
||||
addSeparator();
|
||||
|
||||
// Progress slider
|
||||
m_progress = new ClickableSlider(Qt::Horizontal, this);
|
||||
m_progress->setRange(0, 1000);
|
||||
m_progress->setValue(0);
|
||||
m_progress->setMinimumWidth(180);
|
||||
m_progress->setMaximumWidth(400);
|
||||
addWidget(m_progress);
|
||||
|
||||
connect(m_progress, &QSlider::sliderPressed, this, [this] { m_seeking = true; });
|
||||
connect(m_progress, &QSlider::sliderReleased, this, &MainToolBar::onProgressReleased);
|
||||
|
||||
// Time label
|
||||
m_timeLabel = new QLabel("0:00 / 0:00", this);
|
||||
addWidget(m_timeLabel);
|
||||
|
||||
addSeparator();
|
||||
|
||||
// Volume button
|
||||
m_volume = new VolumeButton(this);
|
||||
m_volume->setValue(AppSettings::instance().volume());
|
||||
addWidget(m_volume);
|
||||
connect(m_volume, &VolumeButton::volumeChanged, this, &MainToolBar::onVolumeChanged);
|
||||
|
||||
addSeparator();
|
||||
|
||||
// Search toggle
|
||||
m_search = addAction(Icon::search(), tr("Search"));
|
||||
m_search->setCheckable(true);
|
||||
connect(m_search, &QAction::toggled, this, &MainToolBar::searchToggled);
|
||||
|
||||
// Connect to backend signals
|
||||
connect(m_backend, &QobuzBackend::stateChanged, this, &MainToolBar::onBackendStateChanged);
|
||||
connect(m_backend, &QobuzBackend::trackChanged, this, &MainToolBar::onTrackChanged);
|
||||
connect(m_backend, &QobuzBackend::positionChanged, this, &MainToolBar::onPositionChanged);
|
||||
connect(m_backend, &QobuzBackend::trackFinished, this, &MainToolBar::onTrackFinished);
|
||||
}
|
||||
|
||||
void MainToolBar::setPlaying(bool playing)
|
||||
{
|
||||
m_playing = playing;
|
||||
m_playPause->setIcon(playing ? Icon::pause() : Icon::play());
|
||||
m_playPause->setText(playing ? tr("Pause") : tr("Play"));
|
||||
}
|
||||
|
||||
void MainToolBar::setCurrentTrack(const QJsonObject &track)
|
||||
{
|
||||
const QString title = track["title"].toString();
|
||||
const QJsonObject performer = track["performer"].toObject();
|
||||
const QString artist = performer["name"].toString();
|
||||
|
||||
if (title.isEmpty()) {
|
||||
m_trackLabel->setText(tr("Not playing"));
|
||||
} else if (artist.isEmpty()) {
|
||||
m_trackLabel->setText(title);
|
||||
} else {
|
||||
m_trackLabel->setText(QStringLiteral("%1 — %2").arg(artist, title));
|
||||
}
|
||||
}
|
||||
|
||||
void MainToolBar::updateProgress(quint64 position, quint64 duration)
|
||||
{
|
||||
if (m_seeking) return;
|
||||
|
||||
const int sliderPos = duration > 0
|
||||
? static_cast<int>(position * 1000 / duration)
|
||||
: 0;
|
||||
m_progress->blockSignals(true);
|
||||
m_progress->setValue(sliderPos);
|
||||
m_progress->blockSignals(false);
|
||||
|
||||
const QString pos = TrackListModel::formatDuration(static_cast<qint64>(position));
|
||||
const QString dur = TrackListModel::formatDuration(static_cast<qint64>(duration));
|
||||
m_timeLabel->setText(QStringLiteral("%1 / %2").arg(pos, dur));
|
||||
}
|
||||
|
||||
void MainToolBar::setVolume(int volume)
|
||||
{
|
||||
m_volume->setValue(volume);
|
||||
}
|
||||
|
||||
// --- private slots ---
|
||||
|
||||
void MainToolBar::onPlayPause(bool)
|
||||
{
|
||||
if (m_playing) {
|
||||
m_backend->pause();
|
||||
} else {
|
||||
m_backend->resume();
|
||||
}
|
||||
}
|
||||
|
||||
void MainToolBar::onPrevious()
|
||||
{
|
||||
// TODO: wire up to a queue / playlist
|
||||
}
|
||||
|
||||
void MainToolBar::onNext()
|
||||
{
|
||||
// TODO: wire up to a queue / playlist
|
||||
}
|
||||
|
||||
void MainToolBar::onProgressReleased()
|
||||
{
|
||||
m_seeking = false;
|
||||
// Seek is not yet implemented in the Rust player, but we can at least
|
||||
// update the displayed position optimistically.
|
||||
}
|
||||
|
||||
void MainToolBar::onVolumeChanged(int volume)
|
||||
{
|
||||
m_backend->setVolume(volume);
|
||||
AppSettings::instance().setVolume(volume);
|
||||
}
|
||||
|
||||
void MainToolBar::onBackendStateChanged(const QString &state)
|
||||
{
|
||||
setPlaying(state == QStringLiteral("playing"));
|
||||
}
|
||||
|
||||
void MainToolBar::onTrackChanged(const QJsonObject &track)
|
||||
{
|
||||
setCurrentTrack(track);
|
||||
}
|
||||
|
||||
void MainToolBar::onPositionChanged(quint64 position, quint64 duration)
|
||||
{
|
||||
updateProgress(position, duration);
|
||||
}
|
||||
|
||||
void MainToolBar::onTrackFinished()
|
||||
{
|
||||
setPlaying(false);
|
||||
m_progress->setValue(0);
|
||||
m_timeLabel->setText("0:00 / 0:00");
|
||||
}
|
||||
Reference in New Issue
Block a user