#include "mainwindow.hpp" #include "dialog/login.hpp" #include "dialog/settings.hpp" #include "util/settings.hpp" #include #include #include #include #include #include #include #include #include #include MainWindow::MainWindow(QobuzBackend *backend, QWidget *parent) : QMainWindow(parent) , m_backend(backend) { setWindowTitle(QStringLiteral("Qobuz")); setMinimumSize(800, 500); resize(defaultSize()); // ---- Toolbar ---- m_toolBar = new MainToolBar(m_backend, this); addToolBar(Qt::TopToolBarArea, m_toolBar); // ---- Central content ---- m_content = new MainContent(m_backend, this); setCentralWidget(m_content); // ---- Library dock (left) ---- m_library = new List::Library(m_backend, this); m_libraryDock = new QDockWidget(tr("Library"), this); m_libraryDock->setObjectName(QStringLiteral("libraryDock")); m_libraryDock->setFeatures(QDockWidget::DockWidgetMovable); m_libraryDock->setWidget(m_library); m_libraryDock->setMinimumWidth(200); addDockWidget(Qt::LeftDockWidgetArea, m_libraryDock); // ---- Search side panel (right) ---- m_sidePanel = new SidePanel::View(m_backend, this); m_sidePanel->hide(); addDockWidget(Qt::RightDockWidgetArea, m_sidePanel); // ---- Menu bar ---- setupMenuBar(); // ---- Status bar ---- statusBar()->showMessage(tr("Ready")); // ---- Wire signals ---- connect(m_backend, &QobuzBackend::loginSuccess, this, &MainWindow::onLoginSuccess); connect(m_backend, &QobuzBackend::loginError, this, &MainWindow::onLoginError); connect(m_backend, &QobuzBackend::favTracksLoaded, this, &MainWindow::onFavTracksLoaded); connect(m_backend, &QobuzBackend::favAlbumsLoaded, this, &MainWindow::onFavAlbumsLoaded); connect(m_backend, &QobuzBackend::albumLoaded, this, &MainWindow::onAlbumLoaded); connect(m_backend, &QobuzBackend::artistLoaded, this, &MainWindow::onArtistLoaded); connect(m_backend, &QobuzBackend::playlistLoaded, this, &MainWindow::onPlaylistLoaded); connect(m_backend, &QobuzBackend::error, this, [this](const QString &msg) { statusBar()->showMessage(tr("Error: %1").arg(msg), 5000); }); // Library → backend connect(m_library, &List::Library::favTracksRequested, m_backend, [this] { m_backend->getFavTracks(); statusBar()->showMessage(tr("Loading favorite tracks…")); }); connect(m_library, &List::Library::favAlbumsRequested, m_backend, [this] { m_backend->getFavAlbums(); statusBar()->showMessage(tr("Loading favorite albums…")); }); connect(m_library, &List::Library::favArtistsRequested, m_backend, [this] { m_backend->getFavArtists(); statusBar()->showMessage(tr("Loading favorite artists…")); }); connect(m_library, &List::Library::playlistRequested, this, [this](qint64 id, const QString &name) { m_backend->getPlaylist(id); statusBar()->showMessage(tr("Loading playlist: %1…").arg(name)); }); // Track list → playback connect(m_content->tracksList(), &List::Tracks::playTrackRequested, this, &MainWindow::onPlayTrackRequested); // Search panel → backend connect(m_sidePanel, &SidePanel::View::albumSelected, this, &MainWindow::onSearchAlbumSelected); connect(m_sidePanel, &SidePanel::View::artistSelected, this, &MainWindow::onSearchArtistSelected); connect(m_sidePanel, &SidePanel::View::trackPlayRequested, this, &MainWindow::onPlayTrackRequested); // Toolbar search toggle → side panel visibility connect(m_toolBar, &MainToolBar::searchToggled, this, &MainWindow::onSearchToggled); // Restore or prompt login tryRestoreSession(); } void MainWindow::setupMenuBar() { auto *fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(tr("&Sign in…"), this, &MainWindow::showLoginDialog); fileMenu->addSeparator(); fileMenu->addAction(tr("&Settings…"), this, &MainWindow::showSettingsDialog); fileMenu->addSeparator(); fileMenu->addAction(tr("&Quit"), qApp, &QApplication::quit, QKeySequence::Quit); auto *viewMenu = menuBar()->addMenu(tr("&View")); viewMenu->addAction(m_libraryDock->toggleViewAction()); viewMenu->addAction(m_sidePanel->toggleViewAction()); auto *helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(tr("&About"), this, [this] { QMessageBox::about(this, tr("About Qobuz"), tr("

qobuz-qt

" "

A lightweight Qt client for the Qobuz streaming service.

" "

Audio decoding powered by Symphonia (Rust).

")); }); } void MainWindow::tryRestoreSession() { const QString token = AppSettings::instance().authToken(); if (!token.isEmpty()) { m_backend->setToken(token); m_library->refresh(); const QString name = AppSettings::instance().displayName(); statusBar()->showMessage(tr("Signed in as %1").arg( name.isEmpty() ? AppSettings::instance().userEmail() : name)); } else { // No saved session — prompt login after a short delay so the window is visible first QTimer::singleShot(200, this, &MainWindow::showLoginDialog); } } // ---- slots ---- void MainWindow::showLoginDialog() { auto *dlg = new LoginDialog(this); dlg->setAttribute(Qt::WA_DeleteOnClose); // Pre-fill if remembered if (AppSettings::instance().rememberLogin()) dlg->findChild(); // just show with empty fields connect(dlg, &LoginDialog::loginRequested, this, [this, dlg](const QString &email, const QString &password) { dlg->setBusy(true); m_backend->login(email, password); }); connect(m_backend, &QobuzBackend::loginSuccess, dlg, [dlg](const QString &, const QJsonObject &) { dlg->accept(); }); connect(m_backend, &QobuzBackend::loginError, dlg, [dlg](const QString &err) { dlg->setError(err); }); dlg->exec(); } void MainWindow::showSettingsDialog() { SettingsDialog dlg(this); dlg.exec(); } void MainWindow::onLoginSuccess(const QString &token, const QJsonObject &user) { AppSettings::instance().setAuthToken(token); const QString displayName = user["display_name"].toString(); const QString email = user["email"].toString(); AppSettings::instance().setDisplayName(displayName); AppSettings::instance().setUserEmail(email); statusBar()->showMessage(tr("Signed in as %1").arg( displayName.isEmpty() ? email : displayName)); m_library->refresh(); } void MainWindow::onLoginError(const QString &error) { statusBar()->showMessage(tr("Login failed: %1").arg(error), 6000); } void MainWindow::onFavTracksLoaded(const QJsonObject &result) { m_content->showFavTracks(result); statusBar()->showMessage(tr("Favorite tracks loaded."), 3000); } void MainWindow::onFavAlbumsLoaded(const QJsonObject &result) { // Show albums as a track list (each album as a row) // For now just show the raw tracks from each album // A proper album grid view would go here in a future iteration const QJsonArray albums = result["items"].toArray(); statusBar()->showMessage(tr("%1 favorite albums").arg(albums.size()), 3000); // TODO: show album grid } void MainWindow::onAlbumLoaded(const QJsonObject &album) { m_content->showAlbum(album); const QString title = album["title"].toString(); statusBar()->showMessage(tr("Album: %1").arg(title), 3000); } void MainWindow::onArtistLoaded(const QJsonObject &artist) { // TODO: show artist view with top tracks + discography statusBar()->showMessage(tr("Artist: %1").arg(artist["name"].toString()), 3000); } void MainWindow::onPlaylistLoaded(const QJsonObject &playlist) { m_content->showPlaylist(playlist); const QString name = playlist["name"].toString(); statusBar()->showMessage(tr("Playlist: %1").arg(name), 3000); } void MainWindow::onPlayTrackRequested(qint64 trackId) { const int format = AppSettings::instance().preferredFormat(); m_backend->playTrack(trackId, format); statusBar()->showMessage(tr("Loading track…")); } void MainWindow::onSearchAlbumSelected(const QString &albumId) { m_backend->getAlbum(albumId); statusBar()->showMessage(tr("Loading album…")); } void MainWindow::onSearchArtistSelected(qint64 artistId) { m_backend->getArtist(artistId); statusBar()->showMessage(tr("Loading artist…")); } void MainWindow::onSearchToggled(bool visible) { m_sidePanel->setVisible(visible); }