diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0a08b653..e8092a6ec 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -174,6 +174,7 @@ set(SOURCES widgets/nowplayingwidget.cpp widgets/osd.cpp widgets/osdpretty.cpp + widgets/prettyimageview.cpp widgets/progressitemdelegate.cpp widgets/sliderwidget.cpp widgets/spinbox.cpp @@ -310,6 +311,7 @@ set(HEADERS widgets/nowplayingwidget.h widgets/osd.h widgets/osdpretty.h + widgets/prettyimageview.h widgets/progressitemdelegate.h widgets/sliderwidget.h widgets/spinbox.h diff --git a/src/songinfo/artistinfoview.cpp b/src/songinfo/artistinfoview.cpp index 175b44a9b..c5a1db6bd 100644 --- a/src/songinfo/artistinfoview.cpp +++ b/src/songinfo/artistinfoview.cpp @@ -17,6 +17,7 @@ #include "artistinfofetcher.h" #include "artistinfoview.h" #include "collapsibleinfopane.h" +#include "widgets/prettyimageview.h" #include #include @@ -24,8 +25,8 @@ #include #include -ArtistInfoView::ArtistInfoView(QWidget *parent) - : SongInfoBase(parent), +ArtistInfoView::ArtistInfoView(NetworkAccessManager* network, QWidget *parent) + : SongInfoBase(network, parent), fetcher_(new ArtistInfoFetcher(this)), current_request_id_(-1), scroll_area_(new QScrollArea), @@ -75,13 +76,16 @@ void ArtistInfoView::Clear() { void ArtistInfoView::Update(const Song& metadata) { Clear(); current_request_id_ = fetcher_->FetchInfo(metadata.artist()); + + image_view_ = new PrettyImageView(network_); + AddChild(image_view_); } void ArtistInfoView::ImageReady(int id, const QUrl& url) { if (id != current_request_id_) return; - qDebug() << "Image" << url; + image_view_->AddImage(url); } void ArtistInfoView::InfoReady(int id, const QString& title, QWidget* widget) { diff --git a/src/songinfo/artistinfoview.h b/src/songinfo/artistinfoview.h index 620bb8e6d..4a395ad85 100644 --- a/src/songinfo/artistinfoview.h +++ b/src/songinfo/artistinfoview.h @@ -20,6 +20,7 @@ #include "songinfobase.h" class ArtistInfoFetcher; +class PrettyImageView; class QScrollArea; class QVBoxLayout; @@ -28,7 +29,7 @@ class ArtistInfoView : public SongInfoBase { Q_OBJECT public: - ArtistInfoView(QWidget* parent = 0); + ArtistInfoView(NetworkAccessManager* network, QWidget* parent = 0); ~ArtistInfoView(); protected: @@ -48,6 +49,8 @@ private: QScrollArea* scroll_area_; QVBoxLayout* container_; + PrettyImageView* image_view_; + QList children_; }; diff --git a/src/songinfo/lyricview.cpp b/src/songinfo/lyricview.cpp index 99e92fa24..40c8626e0 100644 --- a/src/songinfo/lyricview.cpp +++ b/src/songinfo/lyricview.cpp @@ -18,8 +18,8 @@ #include "lyricview.h" #include "ui_lyricview.h" -LyricView::LyricView(QWidget *parent) - : SongInfoBase(parent), +LyricView::LyricView(NetworkAccessManager* network, QWidget *parent) + : SongInfoBase(network, parent), ui_(new Ui_LyricView), fetcher_(NULL), current_request_id_(-1) diff --git a/src/songinfo/lyricview.h b/src/songinfo/lyricview.h index 9bab3b7c5..05c5540b4 100644 --- a/src/songinfo/lyricview.h +++ b/src/songinfo/lyricview.h @@ -28,7 +28,7 @@ class LyricView : public SongInfoBase { Q_OBJECT public: - LyricView(QWidget* parent = 0); + LyricView(NetworkAccessManager* network, QWidget* parent = 0); ~LyricView(); void set_network(NetworkAccessManager* network); diff --git a/src/songinfo/songinfobase.cpp b/src/songinfo/songinfobase.cpp index e95a8e17b..cd7b9e154 100644 --- a/src/songinfo/songinfobase.cpp +++ b/src/songinfo/songinfobase.cpp @@ -16,8 +16,9 @@ #include "songinfobase.h" -SongInfoBase::SongInfoBase(QWidget* parent) +SongInfoBase::SongInfoBase(NetworkAccessManager* network, QWidget* parent) : QWidget(parent), + network_(network), dirty_(false) { } diff --git a/src/songinfo/songinfobase.h b/src/songinfo/songinfobase.h index 0e2faac08..4e3ee3c3a 100644 --- a/src/songinfo/songinfobase.h +++ b/src/songinfo/songinfobase.h @@ -21,11 +21,13 @@ #include "core/song.h" +class NetworkAccessManager; + class SongInfoBase : public QWidget { Q_OBJECT public: - SongInfoBase(QWidget* parent = 0); + SongInfoBase(NetworkAccessManager* network, QWidget* parent = 0); public slots: void SongChanged(const Song& metadata); @@ -36,6 +38,8 @@ protected: virtual void Update(const Song& metadata) {} + NetworkAccessManager* network_; + private: Song queued_metadata_; bool dirty_; diff --git a/src/translations/ar.po b/src/translations/ar.po index b5a89ae74..fbc8eb4bd 100644 --- a/src/translations/ar.po +++ b/src/translations/ar.po @@ -1083,6 +1083,9 @@ msgstr "" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "" diff --git a/src/translations/bg.po b/src/translations/bg.po index 38cac5518..bc776cdc6 100644 --- a/src/translations/bg.po +++ b/src/translations/bg.po @@ -1084,6 +1084,9 @@ msgstr "" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "" diff --git a/src/translations/ca.po b/src/translations/ca.po index ebdfccdab..7473052bc 100644 --- a/src/translations/ca.po +++ b/src/translations/ca.po @@ -1111,6 +1111,9 @@ msgstr "Carregant fluxe" msgid "Loading tracks" msgstr "Carregant pistes" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Carregar fitxers/URLs, substituïnt l'actual llista de reproducció" diff --git a/src/translations/cs.po b/src/translations/cs.po index 11ac5af2c..569158845 100644 --- a/src/translations/cs.po +++ b/src/translations/cs.po @@ -1088,6 +1088,9 @@ msgstr "Načítám kanál" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Nahraj soubory/URL, výměnou za aktuální playlist" diff --git a/src/translations/da.po b/src/translations/da.po index 1792c60c5..008914663 100644 --- a/src/translations/da.po +++ b/src/translations/da.po @@ -1089,6 +1089,9 @@ msgstr "Indlæser stream" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Indlæser filer/URL'er og erstatter nuværende spilleliste" diff --git a/src/translations/de.po b/src/translations/de.po index b2858e432..56cdf71ea 100644 --- a/src/translations/de.po +++ b/src/translations/de.po @@ -1112,6 +1112,9 @@ msgstr "Lade Stream" msgid "Loading tracks" msgstr "Lade Stücke" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Öffne Dateien/URLs und ersetze die Wiedergabeliste" diff --git a/src/translations/el.po b/src/translations/el.po index d5e87dc7a..c6b4f0f75 100644 --- a/src/translations/el.po +++ b/src/translations/el.po @@ -1115,6 +1115,9 @@ msgstr "Φόρτωμα ροής (stream)" msgid "Loading tracks" msgstr "Φόρτωση κομματιών" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Φορτώνει αρχεία/URLs, αντικαθιστώντας την τρέχουσα λίστα αναπαραγωγής" diff --git a/src/translations/en_CA.po b/src/translations/en_CA.po index 757ab3cb8..d4d95508d 100644 --- a/src/translations/en_CA.po +++ b/src/translations/en_CA.po @@ -1087,6 +1087,9 @@ msgstr "Loading stream" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Loads files/URLs, replacing current playlist" diff --git a/src/translations/en_GB.po b/src/translations/en_GB.po index b34527006..664709f2d 100644 --- a/src/translations/en_GB.po +++ b/src/translations/en_GB.po @@ -1085,6 +1085,9 @@ msgstr "Loading stream" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Loads files/URLs, replacing current playlist" diff --git a/src/translations/es.po b/src/translations/es.po index a55e34773..c7904eb32 100644 --- a/src/translations/es.po +++ b/src/translations/es.po @@ -1116,6 +1116,9 @@ msgstr "Cargando flujo" msgid "Loading tracks" msgstr "Cargando pistas" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Cargar archivos/URLs, remplazando la actual lista de reproducción" diff --git a/src/translations/fi.po b/src/translations/fi.po index 802e39e6b..bcb533976 100644 --- a/src/translations/fi.po +++ b/src/translations/fi.po @@ -1085,6 +1085,9 @@ msgstr "" msgid "Loading tracks" msgstr "Ladataan kappaleita" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "" diff --git a/src/translations/fr.po b/src/translations/fr.po index c0f04adc5..615e162f9 100644 --- a/src/translations/fr.po +++ b/src/translations/fr.po @@ -1120,6 +1120,9 @@ msgstr "Chargement du flux" msgid "Loading tracks" msgstr "Chargement des pistes" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Charger des fichiers/URLs, et remplacer la liste de lecture actuelle" diff --git a/src/translations/gl.po b/src/translations/gl.po index 7a563283c..58ee7993b 100644 --- a/src/translations/gl.po +++ b/src/translations/gl.po @@ -1090,6 +1090,9 @@ msgstr "A carregar a stream" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "" diff --git a/src/translations/hu.po b/src/translations/hu.po index 5e154a5ad..bfcce0a22 100644 --- a/src/translations/hu.po +++ b/src/translations/hu.po @@ -1109,6 +1109,9 @@ msgstr "Adatfolyam betöltése" msgid "Loading tracks" msgstr "Számok betöltése" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Fájlok/URL-ek betöltése, lejátszási lista cseréje" diff --git a/src/translations/it.po b/src/translations/it.po index 378830210..9c9a7de9d 100644 --- a/src/translations/it.po +++ b/src/translations/it.po @@ -1119,6 +1119,9 @@ msgstr "Caricamento flusso" msgid "Loading tracks" msgstr "Caricamento delle tracce" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Carica file/URL, sostituendo la scaletta attuale" diff --git a/src/translations/kk.po b/src/translations/kk.po index 528a026c3..cfa9c0410 100644 --- a/src/translations/kk.po +++ b/src/translations/kk.po @@ -1085,6 +1085,9 @@ msgstr "" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "" diff --git a/src/translations/lt.po b/src/translations/lt.po index 207a49b99..3dcc4180f 100644 --- a/src/translations/lt.po +++ b/src/translations/lt.po @@ -1084,6 +1084,9 @@ msgstr "" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "" diff --git a/src/translations/nb.po b/src/translations/nb.po index a19a0cf90..b1c70946e 100644 --- a/src/translations/nb.po +++ b/src/translations/nb.po @@ -1087,6 +1087,9 @@ msgstr "Lader lydstrøm" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "" diff --git a/src/translations/nl.po b/src/translations/nl.po index 388f10b4e..21d26b9ab 100644 --- a/src/translations/nl.po +++ b/src/translations/nl.po @@ -1113,6 +1113,9 @@ msgstr "Radiostream laden" msgid "Loading tracks" msgstr "Tracks laden" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Laadt bestanden/URLs, en vervangt de huidige afspeellijst" diff --git a/src/translations/oc.po b/src/translations/oc.po index 0b31a8428..f55a82a77 100644 --- a/src/translations/oc.po +++ b/src/translations/oc.po @@ -1083,6 +1083,9 @@ msgstr "Cargament del flux" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "" diff --git a/src/translations/pl.po b/src/translations/pl.po index fea192279..9ffc86569 100644 --- a/src/translations/pl.po +++ b/src/translations/pl.po @@ -1111,6 +1111,9 @@ msgstr "Ładowanie strumienia" msgid "Loading tracks" msgstr "Wczytywanie ścieżek" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Wczytywanie plików/adresów URL, zamiana obecnej listy odtwarzania" diff --git a/src/translations/pt.po b/src/translations/pt.po index 082c5a5c4..1d073ecdb 100644 --- a/src/translations/pt.po +++ b/src/translations/pt.po @@ -1111,6 +1111,9 @@ msgstr "Carregando emissão" msgid "Loading tracks" msgstr "Carregando faixas" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Carregar ficheiros/URLs, substituindo a lista atual" diff --git a/src/translations/pt_BR.po b/src/translations/pt_BR.po index 39931abc0..ee36ffbad 100644 --- a/src/translations/pt_BR.po +++ b/src/translations/pt_BR.po @@ -1100,6 +1100,9 @@ msgstr "Carregando transmissão" msgid "Loading tracks" msgstr "Carregando faixas" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Carregar arquivos/sites, substiuindo a lista de reprodução atual" diff --git a/src/translations/ro.po b/src/translations/ro.po index 808805523..2af9277e0 100644 --- a/src/translations/ro.po +++ b/src/translations/ro.po @@ -1084,6 +1084,9 @@ msgstr "Se încarcă fluxul" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "" diff --git a/src/translations/ru.po b/src/translations/ru.po index 402ad0f93..2a4341b38 100644 --- a/src/translations/ru.po +++ b/src/translations/ru.po @@ -1104,6 +1104,9 @@ msgstr "Загрузка потока" msgid "Loading tracks" msgstr "Загрузить композиции" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Загрузить файлы/URLs, заменяя текущий список воспроизведения" diff --git a/src/translations/sk.po b/src/translations/sk.po index 481a510e6..c167d60cb 100644 --- a/src/translations/sk.po +++ b/src/translations/sk.po @@ -1106,6 +1106,9 @@ msgstr "Načítava sa stream" msgid "Loading tracks" msgstr "Načítavajú sa skladby" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Načítať súbory/URLy, nahradiť nimi aktuálny playlist" diff --git a/src/translations/sl.po b/src/translations/sl.po index ae2eb5dc7..06a9e46e7 100644 --- a/src/translations/sl.po +++ b/src/translations/sl.po @@ -1105,6 +1105,9 @@ msgstr "Nalaganje pretoka" msgid "Loading tracks" msgstr "Nalaganje skladb" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Naloži datoteke/povezave in zamenjaj trenutni seznam predvajanja" diff --git a/src/translations/sr.po b/src/translations/sr.po index 096c42373..718f376a3 100644 --- a/src/translations/sr.po +++ b/src/translations/sr.po @@ -1089,6 +1089,9 @@ msgstr "Учитавам ток" msgid "Loading tracks" msgstr "Учитавам нумере" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "" diff --git a/src/translations/sv.po b/src/translations/sv.po index 52b9ffe5d..6f7881e60 100644 --- a/src/translations/sv.po +++ b/src/translations/sv.po @@ -1093,6 +1093,9 @@ msgstr "Laddar ström" msgid "Loading tracks" msgstr "Läser in spår" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Ladda filer/webbadresser, ersätt nuvarande spellista" diff --git a/src/translations/tr.po b/src/translations/tr.po index 36d3611d7..954adf231 100644 --- a/src/translations/tr.po +++ b/src/translations/tr.po @@ -1108,6 +1108,9 @@ msgstr "Yayın akışı yükleniyor" msgid "Loading tracks" msgstr "Parçalar yükleniyor" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Dosyaları/URLleri yükler, mevcut çalma listesinin yerine koyar" diff --git a/src/translations/translations.pot b/src/translations/translations.pot index 03ef91308..f285f5350 100644 --- a/src/translations/translations.pot +++ b/src/translations/translations.pot @@ -1074,6 +1074,9 @@ msgstr "" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "" diff --git a/src/translations/uk.po b/src/translations/uk.po index 36c8024aa..148704851 100644 --- a/src/translations/uk.po +++ b/src/translations/uk.po @@ -1105,6 +1105,9 @@ msgstr "Завнтаження потоку" msgid "Loading tracks" msgstr "Завантаження доріжок" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "Завантажити файли/адреси, замінюючи поточний список відтворення" diff --git a/src/translations/zh_CN.po b/src/translations/zh_CN.po index 1eb73b45a..4f598bf86 100644 --- a/src/translations/zh_CN.po +++ b/src/translations/zh_CN.po @@ -1083,6 +1083,9 @@ msgstr "" msgid "Loading tracks" msgstr "" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "装入文件/URL,替换当前播放列表" diff --git a/src/translations/zh_TW.po b/src/translations/zh_TW.po index 2db03ba55..68f4c4054 100644 --- a/src/translations/zh_TW.po +++ b/src/translations/zh_TW.po @@ -1089,6 +1089,9 @@ msgstr "載入串流" msgid "Loading tracks" msgstr "載入歌曲" +msgid "Loading..." +msgstr "" + msgid "Loads files/URLs, replacing current playlist" msgstr "加載檔案/網址,取代目前的播放清單" diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index f6d297257..f52ffb856 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -142,8 +142,8 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg file_view_(new FileView(this)), radio_view_(new RadioViewContainer(this)), device_view_(new DeviceView(this)), - lyric_view_(new LyricView(this)), - artist_info_view_(new ArtistInfoView(this)), + lyric_view_(new LyricView(network, this)), + artist_info_view_(new ArtistInfoView(network, this)), settings_dialog_(NULL), cover_manager_(NULL), equalizer_(new Equalizer), diff --git a/src/widgets/prettyimageview.cpp b/src/widgets/prettyimageview.cpp new file mode 100644 index 000000000..d47d97fab --- /dev/null +++ b/src/widgets/prettyimageview.cpp @@ -0,0 +1,228 @@ +/* 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 "prettyimageview.h" +#include "core/networkaccessmanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +const int PrettyImageView::kArrowWidth = 35; +const int PrettyImageView::kImageHeight = 160; +const int PrettyImageView::kTotalHeight = 200; +const int PrettyImageView::kBorderHeight = 10; + +const int PrettyImageView::kBaseAnimationDuration = 500; // msec +const int PrettyImageView::kArrowAnimationDuration = 250; // msec + +PrettyImageView::PrettyImageView(NetworkAccessManager* network, QWidget* parent) + : QWidget(parent), + network_(network), + next_image_request_id_(1), + current_index_(0), + base_timeline_(new QTimeLine(kBaseAnimationDuration, this)), + left_timeline_(new QTimeLine(kArrowAnimationDuration, this)), + right_timeline_(new QTimeLine(kArrowAnimationDuration, this)) +{ + setMouseTracking(true); + setMinimumHeight(kTotalHeight); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + connect(base_timeline_, SIGNAL(valueChanged(qreal)), SLOT(update())); + connect(left_timeline_, SIGNAL(valueChanged(qreal)), SLOT(update())); + connect(right_timeline_, SIGNAL(valueChanged(qreal)), SLOT(update())); +} + +QRect PrettyImageView::left() const { + return QRect(0, 0, kArrowWidth, height()); +} + +QRect PrettyImageView::right() const { + return QRect(width() - kArrowWidth, 0, kArrowWidth, height()); +} + +QPolygon PrettyImageView::arrow(const QRect& rect, int direction) const { + QPoint point(direction > 0 ? rect.right() : rect.left(), + (rect.bottom() - rect.top()) / 2 + rect.top()); + + return QPolygon() + << point + << QPoint(point.x() - direction * kArrowWidth, point.y() - kArrowWidth) + << QPoint(point.x() - direction * kArrowWidth, point.y() + kArrowWidth); +} + +void PrettyImageView::Clear() { + images_.clear(); + image_requests_.clear(); + current_index_ = 0; + update(); +} + +void PrettyImageView::AddImage(const QUrl& url) { + const int index = images_.count(); + const int id = next_image_request_id_ ++; + + // Add the image to the list + images_ << Image(url); + + // Start fetching the image + network_->Get(url, this, "ImageFetched", id); + image_requests_[id] = index; +} + +void PrettyImageView::ImageFetched(quint64 id, QNetworkReply* reply) { + reply->deleteLater(); + + if (!image_requests_.contains(id)) + return; + + Image& data = images_[image_requests_.take(id)]; + + QImage image = QImage::fromData(reply->readAll()); + if (image.isNull()) + return; + + data.SetImage(image); + update(); +} + +void PrettyImageView::paintEvent(QPaintEvent*) { + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing, true); + + p.setBrush(palette().color(QPalette::Highlight)); + p.setPen(QPen(palette().color(QPalette::Text), 0.5)); + + // Draw left arrow + p.setOpacity(base_timeline_->currentValue() * (0.5 + left_timeline_->currentValue() * 0.5)); + p.drawConvexPolygon(arrow(left(), -1)); + + // Draw right arrow + p.setOpacity(base_timeline_->currentValue() * (0.5 + right_timeline_->currentValue() * 0.5)); + p.drawConvexPolygon(arrow(right(), +1)); + + // Draw the current image + if (current_index_ >= 0 && current_index_ < images_.count()) { + QRect rect(0, 0, width(), kImageHeight - kBorderHeight); + + p.setOpacity(1.0); + DrawImage(&p, rect, images_[current_index_]); + } +} + +void PrettyImageView::DrawImage(QPainter* p, const QRect& rect, const Image& image) { + const int width = image.image_.isNull() ? rect.height() * 1.6 : + float(image.image_.width()) / image.image_.height() * rect.height(); + + // Center the image in the rectangle + QRect actual_rect(0, kBorderHeight, width, rect.height()); + actual_rect.moveLeft((rect.width() - width) / 2); + + // Draw the main image + DrawThumbnail(p, actual_rect, image); + + // Draw the reflection + // Figure out where to draw it + QRect reflection_rect(actual_rect); + reflection_rect.moveTop(reflection_rect.bottom()); + + // Create the reflected pixmap + QImage reflection(reflection_rect.size(), QImage::Format_ARGB32_Premultiplied); + reflection.fill(palette().color(QPalette::Base).rgba()); + QPainter reflection_painter(&reflection); + + // Set up the transformation + QTransform transform; + transform.scale(1.0, -1.0); + transform.translate(0.0, -actual_rect.height()); + reflection_painter.setTransform(transform); + + QRect fade_rect(reflection.rect().bottomLeft() - QPoint(0, kTotalHeight - kImageHeight), + reflection.rect().bottomRight()); + + // Draw the reflection into the buffer + DrawThumbnail(&reflection_painter, reflection.rect(), image); + + // Make it fade out towards the bottom + QLinearGradient fade_gradient(fade_rect.topLeft(), fade_rect.bottomLeft()); + fade_gradient.setColorAt(0.0, QColor(0, 0, 0, 0)); + fade_gradient.setColorAt(1.0, QColor(0, 0, 0, 128)); + + reflection_painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + reflection_painter.fillRect(fade_rect, fade_gradient); + + reflection_painter.end(); + + // Draw the reflection on the image + p->drawImage(reflection_rect, reflection); +} + +void PrettyImageView::DrawThumbnail(QPainter* p, const QRect& rect, const Image& image) { + if (image.image_.isNull()) { + // Draw an empty box if there's no image to show + p->setPen(palette().color(QPalette::Disabled, QPalette::Text)); + p->drawText(rect, Qt::AlignHCenter | Qt::AlignBottom, tr("Loading...")); + } else { + // Draw the image + p->drawPixmap(rect, image.thumbnail_); + } +} + +void PrettyImageView::mouseReleaseEvent(QMouseEvent* e) { + if (left().contains(e->pos())) + current_index_ = qMax(0, current_index_ - 1); + else if (right().contains(e->pos())) + current_index_ = qMin(images_.count() - 1, current_index_ + 1); + update(); +} + +void PrettyImageView::mouseMoveEvent(QMouseEvent* e) { + SetTimeLineActive(left_timeline_, left().contains(e->pos())); + SetTimeLineActive(right_timeline_, right().contains(e->pos())); +} + +void PrettyImageView::enterEvent(QEvent*) { + SetTimeLineActive(base_timeline_, true); +} + +void PrettyImageView::leaveEvent(QEvent*) { + SetTimeLineActive(base_timeline_, false); +} + +void PrettyImageView::SetTimeLineActive(QTimeLine* timeline, bool active) { + const QTimeLine::Direction direction = + active ? QTimeLine::Forward : QTimeLine::Backward; + + if (timeline->state() == QTimeLine::Running && timeline->direction() == direction) + return; + + timeline->setDirection(direction); + + if (timeline->state() != QTimeLine::Running) + timeline->resume(); +} + +void PrettyImageView::Image::SetImage(const QImage& image) { + image_ = image; + thumbnail_ = QPixmap::fromImage(image_.scaledToHeight( + kImageHeight, Qt::SmoothTransformation)); +} diff --git a/src/widgets/prettyimageview.h b/src/widgets/prettyimageview.h new file mode 100644 index 000000000..f45082414 --- /dev/null +++ b/src/widgets/prettyimageview.h @@ -0,0 +1,92 @@ +/* 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 PRETTYIMAGEVIEW_H +#define PRETTYIMAGEVIEW_H + +#include +#include +#include + +class NetworkAccessManager; + +class QNetworkReply; +class QTimeLine; + +class PrettyImageView : public QWidget { + Q_OBJECT + +public: + PrettyImageView(NetworkAccessManager* network, QWidget* parent = 0); + + static const int kTotalHeight; + static const int kImageHeight; + static const int kBorderHeight; + static const int kArrowWidth; + + static const int kBaseAnimationDuration; + static const int kArrowAnimationDuration; + +public slots: + void Clear(); + void AddImage(const QUrl& url); + +protected: + void paintEvent(QPaintEvent*); + void mouseMoveEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + void enterEvent(QEvent*); + void leaveEvent(QEvent*); + +private: + struct Image { + Image(const QUrl& url) : loading_(false), url_(url) {} + + void SetImage(const QImage& image); + + bool loading_; + QUrl url_; + QImage image_; + QPixmap thumbnail_; + }; + + QRect left() const; + QRect right() const; + QPolygon arrow(const QRect& rect, int direction) const; + + void SetTimeLineActive(QTimeLine* timeline, bool active); + + void DrawImage(QPainter* p, const QRect& rect, const Image& image); + void DrawThumbnail(QPainter* p, const QRect& rect, const Image& image); + +private slots: + void ImageFetched(quint64 id, QNetworkReply* reply); + +private: + NetworkAccessManager* network_; + + QMap image_requests_; + quint64 next_image_request_id_; + + QList images_; + int current_index_; + + QTimeLine* base_timeline_; + QTimeLine* left_timeline_; + QTimeLine* right_timeline_; +}; + +#endif // PRETTYIMAGEVIEW_H