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:
80
src/dialog/login.cpp
Normal file
80
src/dialog/login.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "login.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QFormLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Sign in to Qobuz"));
|
||||
setMinimumWidth(360);
|
||||
setModal(true);
|
||||
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
layout->setSpacing(12);
|
||||
|
||||
// Logo / title
|
||||
auto *title = new QLabel(QStringLiteral("<h2>Qobuz</h2>"), this);
|
||||
title->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(title);
|
||||
|
||||
// Form
|
||||
auto *form = new QFormLayout;
|
||||
form->setLabelAlignment(Qt::AlignRight);
|
||||
|
||||
m_email = new QLineEdit(this);
|
||||
m_password = new QLineEdit(this);
|
||||
m_password->setEchoMode(QLineEdit::Password);
|
||||
|
||||
form->addRow(tr("E-mail:"), m_email);
|
||||
form->addRow(tr("Password:"), m_password);
|
||||
layout->addLayout(form);
|
||||
|
||||
// Remember checkbox
|
||||
m_remember = new QCheckBox(tr("Remember me"), this);
|
||||
m_remember->setChecked(true);
|
||||
layout->addWidget(m_remember);
|
||||
|
||||
// Error label (hidden until needed)
|
||||
m_errorLbl = new QLabel(this);
|
||||
m_errorLbl->setStyleSheet("color: red;");
|
||||
m_errorLbl->setWordWrap(true);
|
||||
m_errorLbl->hide();
|
||||
layout->addWidget(m_errorLbl);
|
||||
|
||||
// Buttons
|
||||
m_loginBtn = new QPushButton(tr("Sign in"), this);
|
||||
m_loginBtn->setDefault(true);
|
||||
auto *cancelBtn = new QPushButton(tr("Cancel"), this);
|
||||
|
||||
auto *btnLayout = new QHBoxLayout;
|
||||
btnLayout->addStretch();
|
||||
btnLayout->addWidget(cancelBtn);
|
||||
btnLayout->addWidget(m_loginBtn);
|
||||
layout->addLayout(btnLayout);
|
||||
|
||||
connect(m_loginBtn, &QPushButton::clicked, this, [this] {
|
||||
m_errorLbl->hide();
|
||||
emit loginRequested(m_email->text().trimmed(), m_password->text());
|
||||
});
|
||||
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
|
||||
|
||||
// Allow pressing Enter in the password field
|
||||
connect(m_password, &QLineEdit::returnPressed, m_loginBtn, &QPushButton::click);
|
||||
}
|
||||
|
||||
void LoginDialog::setError(const QString &msg)
|
||||
{
|
||||
m_errorLbl->setText(msg);
|
||||
m_errorLbl->show();
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
void LoginDialog::setBusy(bool busy)
|
||||
{
|
||||
m_loginBtn->setEnabled(!busy);
|
||||
m_loginBtn->setText(busy ? tr("Signing in…") : tr("Sign in"));
|
||||
m_email->setEnabled(!busy);
|
||||
m_password->setEnabled(!busy);
|
||||
}
|
||||
32
src/dialog/login.hpp
Normal file
32
src/dialog/login.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QCheckBox>
|
||||
|
||||
class LoginDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LoginDialog(QWidget *parent = nullptr);
|
||||
|
||||
QString email() const { return m_email->text(); }
|
||||
QString password() const { return m_password->text(); }
|
||||
bool remember() const { return m_remember->isChecked(); }
|
||||
|
||||
void setError(const QString &msg);
|
||||
void setBusy(bool busy);
|
||||
|
||||
signals:
|
||||
void loginRequested(const QString &email, const QString &password);
|
||||
|
||||
private:
|
||||
QLineEdit *m_email = nullptr;
|
||||
QLineEdit *m_password = nullptr;
|
||||
QCheckBox *m_remember = nullptr;
|
||||
QPushButton *m_loginBtn = nullptr;
|
||||
QLabel *m_errorLbl = nullptr;
|
||||
};
|
||||
54
src/dialog/settings.cpp
Normal file
54
src/dialog/settings.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "settings.hpp"
|
||||
#include "../util/settings.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QFormLayout>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGroupBox>
|
||||
|
||||
SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Settings"));
|
||||
setMinimumWidth(400);
|
||||
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
|
||||
// --- Playback group ---
|
||||
auto *playGroup = new QGroupBox(tr("Playback"), this);
|
||||
auto *playLayout = new QFormLayout(playGroup);
|
||||
|
||||
m_formatBox = new QComboBox(playGroup);
|
||||
m_formatBox->addItem(tr("Hi-Res 24-bit/192kHz"), 27);
|
||||
m_formatBox->addItem(tr("Hi-Res 24-bit/96kHz"), 7);
|
||||
m_formatBox->addItem(tr("CD 16-bit"), 6);
|
||||
m_formatBox->addItem(tr("MP3 320 kbps"), 5);
|
||||
|
||||
const int currentFormat = AppSettings::instance().preferredFormat();
|
||||
for (int i = 0; i < m_formatBox->count(); ++i) {
|
||||
if (m_formatBox->itemData(i).toInt() == currentFormat) {
|
||||
m_formatBox->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
playLayout->addRow(tr("Preferred quality:"), m_formatBox);
|
||||
layout->addWidget(playGroup);
|
||||
|
||||
layout->addStretch();
|
||||
|
||||
auto *buttons = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, [this] {
|
||||
applyChanges();
|
||||
accept();
|
||||
});
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
void SettingsDialog::applyChanges()
|
||||
{
|
||||
AppSettings::instance().setPreferredFormat(
|
||||
m_formatBox->currentData().toInt());
|
||||
}
|
||||
18
src/dialog/settings.hpp
Normal file
18
src/dialog/settings.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
|
||||
class SettingsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SettingsDialog(QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
QComboBox *m_formatBox = nullptr;
|
||||
|
||||
void applyChanges();
|
||||
};
|
||||
Reference in New Issue
Block a user