Add a helper class to load icons asynchronously and set them on QStandardItems
This commit is contained in:
parent
f2885c0319
commit
884080684a
|
@ -305,6 +305,7 @@ set(SOURCES
|
|||
ui/screensaver.cpp
|
||||
ui/settingsdialog.cpp
|
||||
ui/settingspage.cpp
|
||||
ui/standarditemiconloader.cpp
|
||||
ui/systemtrayicon.cpp
|
||||
ui/trackselectiondialog.cpp
|
||||
ui/windows7thumbbar.cpp
|
||||
|
@ -541,6 +542,7 @@ set(HEADERS
|
|||
ui/qtsystemtrayicon.h
|
||||
ui/settingsdialog.h
|
||||
ui/settingspage.h
|
||||
ui/standarditemiconloader.h
|
||||
ui/systemtrayicon.h
|
||||
ui/trackselectiondialog.h
|
||||
ui/windows7thumbbar.h
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
#include "podcast.h"
|
||||
#include "podcastdiscoverymodel.h"
|
||||
#include "core/application.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
#include "ui/iconloader.h"
|
||||
#include "ui/standarditemiconloader.h"
|
||||
|
||||
#include <QIcon>
|
||||
#include <QSet>
|
||||
|
@ -27,14 +27,11 @@
|
|||
PodcastDiscoveryModel::PodcastDiscoveryModel(Application* app, QObject* parent)
|
||||
: QStandardItemModel(parent),
|
||||
app_(app),
|
||||
icon_loader_(new StandardItemIconLoader(app->album_cover_loader(), this)),
|
||||
is_tree_(false),
|
||||
default_icon_(":providers/podcast32.png")
|
||||
default_icon_(":providers/podcast16.png")
|
||||
{
|
||||
cover_options_.desired_height_ = 32;
|
||||
|
||||
connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64,QImage)),
|
||||
SLOT(ImageLoaded(quint64,QImage)));
|
||||
connect(this, SIGNAL(modelAboutToBeReset()), SLOT(CancelPendingImages()));
|
||||
icon_loader_->SetModel(this);
|
||||
}
|
||||
|
||||
QVariant PodcastDiscoveryModel::data(const QModelIndex& index, int role) const {
|
||||
|
@ -76,27 +73,10 @@ void PodcastDiscoveryModel::LazyLoadImage(const QModelIndex& index) {
|
|||
Podcast podcast = index.data(Role_Podcast).value<Podcast>();
|
||||
|
||||
if (podcast.image_url().isValid()) {
|
||||
quint64 id = app_->album_cover_loader()->LoadImageAsync(
|
||||
cover_options_, podcast.image_url().toString(), QString());
|
||||
pending_covers_[id] = item;
|
||||
icon_loader_->LoadIcon(podcast.image_url().toString(), QString(), item);
|
||||
}
|
||||
}
|
||||
|
||||
void PodcastDiscoveryModel::ImageLoaded(quint64 id, const QImage& image) {
|
||||
QStandardItem* item = pending_covers_.take(id);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
if (!image.isNull()) {
|
||||
item->setIcon(QIcon(QPixmap::fromImage(image)));
|
||||
}
|
||||
}
|
||||
|
||||
void PodcastDiscoveryModel::CancelPendingImages() {
|
||||
app_->album_cover_loader()->CancelTasks(QSet<quint64>::fromList(pending_covers_.keys()));
|
||||
pending_covers_.clear();
|
||||
}
|
||||
|
||||
QStandardItem* PodcastDiscoveryModel::CreateLoadingIndicator() {
|
||||
QStandardItem* item = new QStandardItem;
|
||||
item->setText(tr("Loading..."));
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
class Application;
|
||||
class Podcast;
|
||||
class StandardItemIconLoader;
|
||||
|
||||
class PodcastDiscoveryModel : public QStandardItemModel {
|
||||
Q_OBJECT
|
||||
|
@ -54,22 +55,17 @@ public:
|
|||
|
||||
QVariant data(const QModelIndex& index, int role) const;
|
||||
|
||||
private slots:
|
||||
void CancelPendingImages();
|
||||
void ImageLoaded(quint64 id, const QImage& image);
|
||||
|
||||
private:
|
||||
void LazyLoadImage(const QModelIndex& index);
|
||||
|
||||
private:
|
||||
Application* app_;
|
||||
StandardItemIconLoader* icon_loader_;
|
||||
|
||||
bool is_tree_;
|
||||
|
||||
AlbumCoverLoaderOptions cover_options_;
|
||||
QIcon default_icon_;
|
||||
QIcon folder_icon_;
|
||||
QMap<quint64, QStandardItem*> pending_covers_;
|
||||
};
|
||||
|
||||
#endif // PODCASTDISCOVERYMODEL_H
|
||||
|
|
|
@ -19,8 +19,11 @@
|
|||
#include "podcastbackend.h"
|
||||
#include "podcastservice.h"
|
||||
#include "core/application.h"
|
||||
#include "core/logging.h"
|
||||
#include "internet/internetmodel.h"
|
||||
#include "library/libraryview.h"
|
||||
#include "ui/iconloader.h"
|
||||
#include "ui/standarditemiconloader.h"
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
|
@ -29,10 +32,13 @@ const char* PodcastService::kSettingsGroup = "Podcasts";
|
|||
|
||||
PodcastService::PodcastService(Application* app, InternetModel* parent)
|
||||
: InternetService(kServiceName, app, parent, parent),
|
||||
use_pretty_covers_(true),
|
||||
icon_loader_(new StandardItemIconLoader(app->album_cover_loader(), this)),
|
||||
context_menu_(NULL),
|
||||
root_(NULL),
|
||||
backend_(app->podcast_backend())
|
||||
{
|
||||
icon_loader_->SetModel(model());
|
||||
}
|
||||
|
||||
PodcastService::~PodcastService() {
|
||||
|
@ -53,6 +59,10 @@ void PodcastService::LazyPopulate(QStandardItem* parent) {
|
|||
}
|
||||
|
||||
void PodcastService::PopulatePodcastList(QStandardItem* parent) {
|
||||
if (default_icon_.isNull()) {
|
||||
default_icon_ = QIcon(":providers/podcast16.png");
|
||||
}
|
||||
|
||||
foreach (const Podcast& podcast, backend_->GetAllSubscriptions()) {
|
||||
const int unlistened_count = podcast.extra("db:unlistened_count").toInt();
|
||||
QString title = podcast.title();
|
||||
|
@ -70,6 +80,12 @@ void PodcastService::PopulatePodcastList(QStandardItem* parent) {
|
|||
}
|
||||
|
||||
item->setText(podcast.title());
|
||||
item->setIcon(default_icon_);
|
||||
|
||||
// Load the podcast's image if it has one
|
||||
if (podcast.image_url().isValid()) {
|
||||
icon_loader_->LoadIcon(podcast.image_url().toString(), QString(), item);
|
||||
}
|
||||
|
||||
parent->appendRow(item);
|
||||
}
|
||||
|
@ -86,6 +102,14 @@ void PodcastService::ShowContextMenu(const QModelIndex& index,
|
|||
context_menu_->popup(global_pos);
|
||||
}
|
||||
|
||||
void PodcastService::ReloadSettings() {
|
||||
QSettings s;
|
||||
s.beginGroup(LibraryView::kSettingsGroup);
|
||||
|
||||
use_pretty_covers_ = s.value("pretty_covers", true).toBool();
|
||||
// TODO: reload the podcast icons that are already loaded?
|
||||
}
|
||||
|
||||
QModelIndex PodcastService::GetCurrentIndex() {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
class AddPodcastDialog;
|
||||
class PodcastBackend;
|
||||
class StandardItemIconLoader;
|
||||
|
||||
class PodcastService : public InternetService {
|
||||
Q_OBJECT
|
||||
|
@ -46,6 +47,7 @@ public:
|
|||
void LazyPopulate(QStandardItem* parent);
|
||||
|
||||
void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos);
|
||||
void ReloadSettings();
|
||||
|
||||
protected:
|
||||
QModelIndex GetCurrentIndex();
|
||||
|
@ -57,6 +59,10 @@ private:
|
|||
void PopulatePodcastList(QStandardItem* parent);
|
||||
|
||||
private:
|
||||
bool use_pretty_covers_;
|
||||
QIcon default_icon_;
|
||||
StandardItemIconLoader* icon_loader_;
|
||||
|
||||
QMenu* context_menu_;
|
||||
QStandardItem* root_;
|
||||
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2012, David Sansome <me@davidsansome.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "standarditemiconloader.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QSet>
|
||||
#include <QStandardItem>
|
||||
|
||||
StandardItemIconLoader::StandardItemIconLoader(AlbumCoverLoader* cover_loader,
|
||||
QObject* parent)
|
||||
: QObject(parent),
|
||||
cover_loader_(cover_loader),
|
||||
model_(NULL)
|
||||
{
|
||||
cover_options_.desired_height_ = 16;
|
||||
|
||||
connect(cover_loader_, SIGNAL(ImageLoaded(quint64,QImage)),
|
||||
SLOT(ImageLoaded(quint64,QImage)));
|
||||
}
|
||||
|
||||
void StandardItemIconLoader::SetModel(QAbstractItemModel* model) {
|
||||
if (model_) {
|
||||
disconnect(model_, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
||||
this, SLOT(RowsAboutToBeRemoved(QModelIndex,int,int)));
|
||||
}
|
||||
|
||||
model_ = model;
|
||||
|
||||
connect(model_, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
||||
SLOT(RowsAboutToBeRemoved(QModelIndex,int,int)));
|
||||
connect(model_, SIGNAL(modelAboutToBeReset()),
|
||||
SLOT(ModelReset()));
|
||||
}
|
||||
|
||||
void StandardItemIconLoader::LoadIcon(const QString& art_automatic,
|
||||
const QString& art_manual,
|
||||
QStandardItem* for_item) {
|
||||
const quint64 id = cover_loader_->LoadImageAsync(cover_options_, art_automatic, art_manual);
|
||||
pending_covers_[id] = for_item;
|
||||
}
|
||||
|
||||
void StandardItemIconLoader::LoadIcon(const Song& song, QStandardItem* for_item) {
|
||||
const quint64 id = cover_loader_->LoadImageAsync(cover_options_, song);
|
||||
pending_covers_[id] = for_item;
|
||||
}
|
||||
|
||||
void StandardItemIconLoader::RowsAboutToBeRemoved(const QModelIndex& parent, int begin, int end) {
|
||||
for (QMap<quint64, QStandardItem*>::iterator it = pending_covers_.begin() ;
|
||||
it != pending_covers_.end() ; ) {
|
||||
const QStandardItem* item = it.value();
|
||||
const QStandardItem* item_parent = item->parent();
|
||||
|
||||
if (item_parent &&
|
||||
item_parent->index() == parent &&
|
||||
item->index().row() >= begin &&
|
||||
item->index().row() <= end) {
|
||||
cover_loader_->CancelTask(it.key());
|
||||
it = pending_covers_.erase(it);
|
||||
} else {
|
||||
++ it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StandardItemIconLoader::ModelReset() {
|
||||
cover_loader_->CancelTasks(QSet<quint64>::fromList(pending_covers_.keys()));
|
||||
pending_covers_.clear();
|
||||
}
|
||||
|
||||
void StandardItemIconLoader::ImageLoaded(quint64 id, const QImage& image) {
|
||||
QStandardItem* item = pending_covers_.take(id);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
if (!image.isNull()) {
|
||||
item->setIcon(QIcon(QPixmap::fromImage(image)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2012, David Sansome <me@davidsansome.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef STANDARDITEMICONLOADER_H
|
||||
#define STANDARDITEMICONLOADER_H
|
||||
|
||||
#include "covers/albumcoverloaderoptions.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
|
||||
class AlbumCoverLoader;
|
||||
class Song;
|
||||
|
||||
class QAbstractItemModel;
|
||||
class QModelIndex;
|
||||
class QStandardItem;
|
||||
|
||||
// Uses an AlbumCoverLoader to asynchronously load and set an icon on a
|
||||
// QStandardItem.
|
||||
class StandardItemIconLoader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StandardItemIconLoader(AlbumCoverLoader* cover_loader, QObject* parent = 0);
|
||||
|
||||
AlbumCoverLoaderOptions* options() { return &cover_options_; }
|
||||
|
||||
void SetModel(QAbstractItemModel* model);
|
||||
|
||||
void LoadIcon(const QString& art_automatic, const QString& art_manual,
|
||||
QStandardItem* for_item);
|
||||
void LoadIcon(const Song& song, QStandardItem* for_item);
|
||||
|
||||
private slots:
|
||||
void ImageLoaded(quint64 id, const QImage& image);
|
||||
void RowsAboutToBeRemoved(const QModelIndex& parent, int begin, int end);
|
||||
void ModelReset();
|
||||
|
||||
private:
|
||||
AlbumCoverLoader* cover_loader_;
|
||||
AlbumCoverLoaderOptions cover_options_;
|
||||
|
||||
QAbstractItemModel* model_;
|
||||
|
||||
QMap<quint64, QStandardItem*> pending_covers_;
|
||||
};
|
||||
|
||||
#endif // STANDARDITEMICONLOADER_H
|
Loading…
Reference in New Issue