Show a Loading... indicator for the gpodder, lazy load images properly
This commit is contained in:
parent
3a88d8fcda
commit
e8a879372d
@ -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_;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user