From afa4d0a4e270291c0166fc97147aa59dc39c301e Mon Sep 17 00:00:00 2001 From: David Sansome Date: Wed, 16 Jun 2010 22:00:39 +0000 Subject: [PATCH] Add a Now Playing widget --- src/CMakeLists.txt | 2 + src/ui/mainwindow.cpp | 9 ++ src/ui/mainwindow.ui | 218 +++++++++++++++++-------------- src/widgets/nowplayingwidget.cpp | 157 ++++++++++++++++++++++ src/widgets/nowplayingwidget.h | 83 ++++++++++++ 5 files changed, 369 insertions(+), 100 deletions(-) create mode 100644 src/widgets/nowplayingwidget.cpp create mode 100644 src/widgets/nowplayingwidget.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a9dcd028b..8921a6e0d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -125,6 +125,7 @@ set(SOURCES widgets/fileviewlist.cpp widgets/lineedit.cpp widgets/multiloadingindicator.cpp + widgets/nowplayingwidget.cpp widgets/osd.cpp widgets/osdpretty.cpp widgets/progressitemdelegate.cpp @@ -225,6 +226,7 @@ set(HEADERS widgets/fileviewlist.h widgets/lineedit.h widgets/multiloadingindicator.h + widgets/nowplayingwidget.h widgets/osd.h widgets/osdpretty.h widgets/progressitemdelegate.h diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index 5d2dec9e8..8a133be5c 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -456,6 +456,15 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg ui_->status_bar_stack->setCurrentWidget(ui_->playlist_summary_page); connect(ui_->multi_loading_indicator, SIGNAL(TaskCountChange(int)), SLOT(TaskCountChanged(int))); + // Now playing widget + ui_->now_playing->set_network(network); + ui_->now_playing->set_ideal_height(ui_->status_bar->sizeHint().height() + + ui_->status_bar_line->sizeHint().height() + + ui_->player_controls->sizeHint().height() + + 1); // Don't question the 1 + connect(playlists_, SIGNAL(CurrentSongChanged(Song)), ui_->now_playing, SLOT(NowPlaying(Song))); + connect(player_, SIGNAL(Stopped()), ui_->now_playing, SLOT(Stopped())); + // Load theme StyleSheetLoader* css_loader = new StyleSheetLoader(this); css_loader->SetStyleSheet(this, ":mainwindow.css"); diff --git a/src/ui/mainwindow.ui b/src/ui/mainwindow.ui index 20174c1e8..7e91b3a4b 100644 --- a/src/ui/mainwindow.ui +++ b/src/ui/mainwindow.ui @@ -18,7 +18,7 @@ :/icon.png:/icon.png - + 0 @@ -30,100 +30,112 @@ Qt::Horizontal - - - QTabWidget::North - - - 0 - - - false - - - true - - - false - - - true - - - - Library - - - - 0 - - - 0 - - - - - - - - true - - - QAbstractItemView::DragOnly - - - true - - - QAbstractItemView::ExtendedSelection - - - true - - - false - - - false - - - - - - - - Files - - - - 0 - - - 0 - - - - - - - - - Internet - - - - 0 - - - 0 - - - - - - - + + + 0 + + + + + QTabWidget::North + + + 0 + + + false + + + true + + + false + + + true + + + + Library + + + + 0 + + + 0 + + + + + + + + true + + + QAbstractItemView::DragOnly + + + true + + + QAbstractItemView::ExtendedSelection + + + true + + + false + + + false + + + + + + + + Files + + + + 0 + + + 0 + + + + + + + + + Internet + + + + 0 + + + 0 + + + + + + + + + + + + + + 0 @@ -339,11 +351,11 @@ - - - Qt::Horizontal - - + + + Qt::Horizontal + + @@ -417,7 +429,7 @@ 0 0 804 - 25 + 23 @@ -755,6 +767,12 @@
widgets/multiloadingindicator.h
1 + + NowPlayingWidget + QWidget +
widgets/nowplayingwidget.h
+ 1 +
diff --git a/src/widgets/nowplayingwidget.cpp b/src/widgets/nowplayingwidget.cpp new file mode 100644 index 000000000..8d4536511 --- /dev/null +++ b/src/widgets/nowplayingwidget.cpp @@ -0,0 +1,157 @@ +/* This file is part of Clementine. + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#include "nowplayingwidget.h" +#include "core/albumcoverloader.h" +#include "core/networkaccessmanager.h" + +#include +#include +#include +#include +#include + +// Space between the cover and the details +const int NowPlayingWidget::kPadding = 4; + +NowPlayingWidget::NowPlayingWidget(QWidget *parent) + : QWidget(parent), + cover_loader_(new BackgroundThreadImplementation(this)), + network_(NULL), + visible_(false), + ideal_height_(0), + show_hide_animation_(new QTimeLine(500, this)), + fade_animation_(new QTimeLine(1000, this)), + no_cover_(":nocover.png"), + load_cover_id_(0), + details_(new QTextDocument(this)), + previous_track_opacity_(0.0) +{ + connect(show_hide_animation_, SIGNAL(frameChanged(int)), SLOT(SetHeight(int))); + setMaximumHeight(0); + + connect(fade_animation_, SIGNAL(valueChanged(qreal)), SLOT(FadePreviousTrack(qreal))); + fade_animation_->setDirection(QTimeLine::Backward); // 1.0 -> 0.0 + + cover_loader_->Start(); + connect(cover_loader_, SIGNAL(Initialised()), SLOT(CoverLoaderInitialised())); +} + +void NowPlayingWidget::set_ideal_height(int height) { + ideal_height_= height; + show_hide_animation_->setFrameRange(0, ideal_height_); +} + +QSize NowPlayingWidget::sizeHint() const { + return QSize(ideal_height_, ideal_height_); +} + +void NowPlayingWidget::CoverLoaderInitialised() { + cover_loader_->Worker()->SetNetwork(network_); + cover_loader_->Worker()->SetDesiredHeight(ideal_height_); + cover_loader_->Worker()->SetPadOutputImage(true); + cover_loader_->Worker()->SetDefaultOutputImage(QImage(":nocover.png")); + connect(cover_loader_->Worker().get(), SIGNAL(ImageLoaded(quint64,QImage)), + SLOT(AlbumArtLoaded(quint64,QImage))); +} + +void NowPlayingWidget::NowPlaying(const Song& metadata) { + if (visible_) { + // Cache the current pixmap so we can fade between them + previous_track_ = QPixmap(size()); + previous_track_.fill(palette().background().color()); + previous_track_opacity_ = 1.0; + QPainter p(&previous_track_); + DrawContents(&p); + p.end(); + } + + metadata_ = metadata; + + // Load the cover + cover_ = QPixmap(); + load_cover_id_ = cover_loader_->Worker()->LoadImageAsync( + metadata.art_automatic(), metadata.art_manual()); + + // TODO: Make this configurable + details_->setHtml(QString("%1
%2
%3").arg( + Qt::escape(metadata.title()), Qt::escape(metadata.artist()), + Qt::escape(metadata.album()))); + + SetVisible(true); + update(); +} + +void NowPlayingWidget::Stopped() { + SetVisible(false); +} + +void NowPlayingWidget::AlbumArtLoaded(quint64 id, const QImage& image) { + if (id != load_cover_id_) + return; + + cover_ = QPixmap::fromImage(image); + update(); + + // Were we waiting for this cover to load before we started fading? + if (!previous_track_.isNull()) { + fade_animation_->start(); + } +} + +void NowPlayingWidget::SetHeight(int height) { + setMaximumHeight(height); +} + +void NowPlayingWidget::SetVisible(bool visible) { + if (visible == visible_) + return; + visible_ = visible; + + show_hide_animation_->setDirection(visible ? QTimeLine::Forward : QTimeLine::Backward); + show_hide_animation_->start(); +} + +void NowPlayingWidget::paintEvent(QPaintEvent *e) { + QPainter p(this); + + DrawContents(&p); + + // Draw the previous track's image if we're fading + if (!previous_track_.isNull()) { + p.setOpacity(previous_track_opacity_); + p.drawPixmap(0, 0, previous_track_); + } +} + +void NowPlayingWidget::DrawContents(QPainter *p) { + // Draw the cover + p->drawPixmap(0, 0, ideal_height_, ideal_height_, cover_); + + // Draw the details + p->translate(ideal_height_ + kPadding, 0); + details_->drawContents(p); + p->translate(-ideal_height_ - kPadding, 0); +} + +void NowPlayingWidget::FadePreviousTrack(qreal value) { + previous_track_opacity_ = value; + if (qFuzzyCompare(previous_track_opacity_, 0.0)) { + previous_track_ = QPixmap(); + } + + update(); +} diff --git a/src/widgets/nowplayingwidget.h b/src/widgets/nowplayingwidget.h new file mode 100644 index 000000000..32d014dd3 --- /dev/null +++ b/src/widgets/nowplayingwidget.h @@ -0,0 +1,83 @@ +/* This file is part of Clementine. + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef NOWPLAYINGWIDGET_H +#define NOWPLAYINGWIDGET_H + +#include "core/backgroundthread.h" +#include "core/song.h" + +#include + +class AlbumCoverLoader; +class NetworkAccessManager; + +class QTextDocument; +class QTimeLine; + +class NowPlayingWidget : public QWidget { + Q_OBJECT + +public: + NowPlayingWidget(QWidget* parent = 0); + + static const int kPadding; + + void set_network(NetworkAccessManager* network) { network_ = network; } + void set_ideal_height(int height); + + QSize sizeHint() const; + +public slots: + void NowPlaying(const Song& metadata); + void Stopped(); + +protected: + void paintEvent(QPaintEvent* e); + +private slots: + void CoverLoaderInitialised(); + void AlbumArtLoaded(quint64 id, const QImage& image); + + void SetVisible(bool visible); + void SetHeight(int height); + + void FadePreviousTrack(qreal value); + +private: + void DrawContents(QPainter* p); + +private: + BackgroundThread* cover_loader_; + NetworkAccessManager* network_; + + bool visible_; + int ideal_height_; + QTimeLine* show_hide_animation_; + QTimeLine* fade_animation_; + + QPixmap no_cover_; + + Song metadata_; + quint64 load_cover_id_; + QPixmap cover_; + QTextDocument* details_; + + QPixmap previous_track_; + qreal previous_track_opacity_; +}; + +#endif // NOWPLAYINGWIDGET_H