Show a Loading... indicator for the gpodder, lazy load images properly

This commit is contained in:
David Sansome 2012-03-06 16:37:15 +00:00
parent 3a88d8fcda
commit e8a879372d
6 changed files with 146 additions and 20 deletions

@ -32,6 +32,7 @@ AddPodcastDialog::AddPodcastDialog(Application* app, QWidget* parent)
{ {
ui_->setupUi(this); ui_->setupUi(this);
ui_->details->SetApplication(app); ui_->details->SetApplication(app);
ui_->results->SetExpandOnReset(false);
ui_->results_stack->setCurrentWidget(ui_->results_page); ui_->results_stack->setCurrentWidget(ui_->results_page);
fader_ = new WidgetFadeHelper(ui_->details_scroll_area); fader_ = new WidgetFadeHelper(ui_->details_scroll_area);

@ -53,7 +53,7 @@
<item> <item>
<widget class="QStackedWidget" name="results_stack"> <widget class="QStackedWidget" name="results_stack">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>1</number>
</property> </property>
<widget class="QWidget" name="results_page"> <widget class="QWidget" name="results_page">
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
@ -64,7 +64,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QTreeView" name="results"> <widget class="AutoExpandingTreeView" name="results">
<property name="editTriggers"> <property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set> <set>QAbstractItemView::NoEditTriggers</set>
</property> </property>
@ -80,9 +80,40 @@
</widget> </widget>
<widget class="QWidget" name="busy_page"> <widget class="QWidget" name="busy_page">
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="margin"> <property name="margin">
<number>0</number> <number>0</number>
</property> </property>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>192</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item> <item>
<widget class="BusyIndicator" name="widget" native="true"> <widget class="BusyIndicator" name="widget" native="true">
<property name="text" stdset="0"> <property name="text" stdset="0">
@ -90,6 +121,21 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
@ -97,8 +143,8 @@
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>0</width>
<height>367</height> <height>0</height>
</size> </size>
</property> </property>
</spacer> </spacer>
@ -168,6 +214,11 @@
<header>podcasts/podcastinfowidget.h</header> <header>podcasts/podcastinfowidget.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>AutoExpandingTreeView</class>
<extends>QTreeView</extends>
<header>widgets/autoexpandingtreeview.h</header>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections> <connections>

@ -22,6 +22,8 @@
#include <ApiRequest.h> #include <ApiRequest.h>
#include <QMessageBox>
GPodderTopTagsModel::GPodderTopTagsModel(mygpo::ApiRequest* api, Application* app, GPodderTopTagsModel::GPodderTopTagsModel(mygpo::ApiRequest* api, Application* app,
QObject* parent) QObject* parent)
: PodcastDiscoveryModel(app, parent), : PodcastDiscoveryModel(app, parent),
@ -57,24 +59,36 @@ void GPodderTopTagsModel::fetchMore(const QModelIndex& parent) {
} }
setData(parent, true, Role_HasLazyLoaded); setData(parent, true, Role_HasLazyLoaded);
qLog(Debug) << "Fetching podcasts for" << parent.data().toString(); // Create a little Loading... item.
itemFromIndex(parent)->appendRow(CreateLoadingIndicator());
mygpo::PodcastList* list = mygpo::PodcastList* list =
api_->podcastsOfTag(GPodderTopTagsPage::kMaxTagCount, parent.data().toString()); api_->podcastsOfTag(GPodderTopTagsPage::kMaxTagCount, parent.data().toString());
NewClosure(list, SIGNAL(finished()), NewClosure(list, SIGNAL(finished()),
this, SLOT(PodcastsOfTagFinished(QModelIndex,mygpo::PodcastList*)), this, SLOT(PodcastsOfTagFinished(QModelIndex,mygpo::PodcastList*)),
parent, list); parent, list);
NewClosure(list, SIGNAL(parseError()),
this, SLOT(PodcastsOfTagFailed(QModelIndex,mygpo::PodcastList*)),
parent, list);
NewClosure(list, SIGNAL(requestError(QNetworkReply::NetworkError)),
this, SLOT(PodcastsOfTagFailed(QModelIndex,mygpo::PodcastList*)),
parent, list);
} }
void GPodderTopTagsModel::PodcastsOfTagFinished(const QModelIndex& parent, void GPodderTopTagsModel::PodcastsOfTagFinished(const QModelIndex& parent,
mygpo::PodcastList* list) { mygpo::PodcastList* list) {
list->deleteLater(); list->deleteLater();
qLog(Debug) << "Tag list finished";
QStandardItem* parent_item = itemFromIndex(parent); QStandardItem* parent_item = itemFromIndex(parent);
if (!parent_item) if (!parent_item)
return; return;
// Remove the Loading... item.
while (parent_item->hasChildren()) {
parent_item->removeRow(0);
}
foreach (mygpo::PodcastPtr gpo_podcast, list->list()) { foreach (mygpo::PodcastPtr gpo_podcast, list->list()) {
Podcast podcast; Podcast podcast;
podcast.InitFromGpo(gpo_podcast.data()); podcast.InitFromGpo(gpo_podcast.data());
@ -82,3 +96,29 @@ void GPodderTopTagsModel::PodcastsOfTagFinished(const QModelIndex& parent,
parent_item->appendRow(CreatePodcastItem(podcast)); parent_item->appendRow(CreatePodcastItem(podcast));
} }
} }
void GPodderTopTagsModel::PodcastsOfTagFailed(const QModelIndex& parent,
mygpo::PodcastList* list) {
list->deleteLater();
QStandardItem* parent_item = itemFromIndex(parent);
if (!parent_item)
return;
// Remove the Loading... item.
while (parent_item->hasChildren()) {
parent_item->removeRow(0);
}
if (QMessageBox::warning(
NULL, tr("Failed to fetch podcasts"),
tr("There was a problem communicating with gpodder.net"),
QMessageBox::Retry | QMessageBox::Close,
QMessageBox::Retry) != QMessageBox::Retry) {
return;
}
// Try fetching the list again.
setData(parent, false, Role_HasLazyLoaded);
fetchMore(parent);
}

@ -44,6 +44,7 @@ public:
private slots: private slots:
void PodcastsOfTagFinished(const QModelIndex& parent, mygpo::PodcastList* list); void PodcastsOfTagFinished(const QModelIndex& parent, mygpo::PodcastList* list);
void PodcastsOfTagFailed(const QModelIndex& parent, mygpo::PodcastList* list);
private: private:
mygpo::ApiRequest* api_; mygpo::ApiRequest* api_;

@ -37,20 +37,23 @@ PodcastDiscoveryModel::PodcastDiscoveryModel(Application* app, QObject* parent)
connect(this, SIGNAL(modelAboutToBeReset()), SLOT(CancelPendingImages())); connect(this, SIGNAL(modelAboutToBeReset()), SLOT(CancelPendingImages()));
} }
QVariant PodcastDiscoveryModel::data(const QModelIndex& index, int role) const {
if (index.isValid() &&
role == Qt::DecorationRole &&
QStandardItemModel::data(index, Role_Type).toInt() == Type_Podcast &&
QStandardItemModel::data(index, Role_StartedLoadingImage).toBool() == false) {
const_cast<PodcastDiscoveryModel*>(this)->LazyLoadImage(index);
}
return QStandardItemModel::data(index, role);
}
QStandardItem* PodcastDiscoveryModel::CreatePodcastItem(const Podcast& podcast) { QStandardItem* PodcastDiscoveryModel::CreatePodcastItem(const Podcast& podcast) {
QStandardItem* item = new QStandardItem; QStandardItem* item = new QStandardItem;
item->setIcon(default_icon_); item->setIcon(default_icon_);
item->setText(podcast.title()); item->setText(podcast.title());
item->setData(QVariant::fromValue(podcast), Role_Podcast); item->setData(QVariant::fromValue(podcast), Role_Podcast);
item->setData(Type_Podcast, Role_Type); item->setData(Type_Podcast, Role_Type);
if (podcast.image_url().isValid()) {
// Start loading an image for this item.
quint64 id = app_->album_cover_loader()->LoadImageAsync(
cover_options_, podcast.image_url().toString(), QString());
pending_covers_[id] = item;
}
return item; return item;
} }
@ -66,15 +69,37 @@ QStandardItem* PodcastDiscoveryModel::CreateFolder(const QString& name) {
return item; return item;
} }
void PodcastDiscoveryModel::LazyLoadImage(const QModelIndex& index) {
QStandardItem* item = itemFromIndex(index);
item->setData(true, Role_StartedLoadingImage);
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;
}
}
void PodcastDiscoveryModel::ImageLoaded(quint64 id, const QImage& image) { void PodcastDiscoveryModel::ImageLoaded(quint64 id, const QImage& image) {
QStandardItem* item = pending_covers_.take(id); QStandardItem* item = pending_covers_.take(id);
if (!item) if (!item)
return; return;
if (!image.isNull()) {
item->setIcon(QIcon(QPixmap::fromImage(image))); item->setIcon(QIcon(QPixmap::fromImage(image)));
} }
}
void PodcastDiscoveryModel::CancelPendingImages() { void PodcastDiscoveryModel::CancelPendingImages() {
app_->album_cover_loader()->CancelTasks(QSet<quint64>::fromList(pending_covers_.keys())); app_->album_cover_loader()->CancelTasks(QSet<quint64>::fromList(pending_covers_.keys()));
pending_covers_.clear(); pending_covers_.clear();
} }
QStandardItem* PodcastDiscoveryModel::CreateLoadingIndicator() {
QStandardItem* item = new QStandardItem;
item->setText(tr("Loading..."));
item->setData(Type_LoadingIndicator, Role_Type);
return item;
}

@ -33,12 +33,14 @@ public:
enum Type { enum Type {
Type_Folder, Type_Folder,
Type_Podcast Type_Podcast,
Type_LoadingIndicator
}; };
enum Role { enum Role {
Role_Podcast = Qt::UserRole, Role_Podcast = Qt::UserRole,
Role_Type, Role_Type,
Role_StartedLoadingImage,
RoleCount RoleCount
}; };
@ -48,11 +50,17 @@ public:
QStandardItem* CreatePodcastItem(const Podcast& podcast); QStandardItem* CreatePodcastItem(const Podcast& podcast);
QStandardItem* CreateFolder(const QString& name); QStandardItem* CreateFolder(const QString& name);
QStandardItem* CreateLoadingIndicator();
QVariant data(const QModelIndex& index, int role) const;
private slots: private slots:
void CancelPendingImages(); void CancelPendingImages();
void ImageLoaded(quint64 id, const QImage& image); void ImageLoaded(quint64 id, const QImage& image);
private:
void LazyLoadImage(const QModelIndex& index);
private: private:
Application* app_; Application* app_;