From 844f9683ec8c88a08eb3a476c00cabeb1edc4a16 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 24 Mar 2020 23:24:29 +0100 Subject: [PATCH] Qt: add naive lazy loading to screenshot manager --- rpcs3/rpcs3qt/screenshot_manager_dialog.cpp | 118 +++++++++++++++++--- rpcs3/rpcs3qt/screenshot_manager_dialog.h | 37 ++++++ 2 files changed, 140 insertions(+), 15 deletions(-) diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp index 58dff9597d..a915f3bd95 100644 --- a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp @@ -8,47 +8,49 @@ #include #include #include +#include #include +#include screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog(parent) { setWindowTitle(tr("Screenshots")); + m_icon_size = QSize(160, 90); + m_grid = new QListWidget(this); m_grid->setViewMode(QListWidget::IconMode); m_grid->setMovement(QListWidget::Static); m_grid->setResizeMode(QListWidget::Adjust); - m_grid->setIconSize(QSize(160, 90)); - m_grid->setGridSize(m_grid->iconSize() + QSize(10, 10)); + m_grid->setIconSize(m_icon_size); + m_grid->setGridSize(m_icon_size + QSize(10, 10)); const std::string screen_path = fs::get_config_dir() + "screenshots/"; const QStringList filter{ QStringLiteral("*.png") }; QDirIterator dir_iter(QString::fromStdString(screen_path), filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); + QPixmap placeholder(m_icon_size); + placeholder.fill(Qt::gray); + m_placeholder = QIcon(placeholder); + while (dir_iter.hasNext()) { const QString filepath = dir_iter.next(); QListWidgetItem* item = new QListWidgetItem; - item->setData(Qt::UserRole, filepath); - item->setIcon(QIcon(filepath)); + item->setData(item_role::source, filepath); + item->setData(item_role::loaded, false); + item->setIcon(m_placeholder); item->setToolTip(filepath); m_grid->addItem(item); } - connect(m_grid, &QListWidget::itemDoubleClicked, [this](QListWidgetItem* item) - { - if (!item) - { - return; - } + m_icon_loader = new QFutureWatcher(this); + connect(m_icon_loader, &QFutureWatcher::resultReadyAt, this, &screenshot_manager_dialog::update_icon); - const QString filepath = item->data(Qt::UserRole).toString(); - - screenshot_preview* preview = new screenshot_preview(filepath); - preview->show(); - }); + connect(m_grid, &QListWidget::itemDoubleClicked, this, &screenshot_manager_dialog::show_preview); + connect(m_grid->verticalScrollBar(), &QScrollBar::valueChanged, this, &screenshot_manager_dialog::update_icons); QVBoxLayout* layout = new QVBoxLayout; layout->setContentsMargins(0, 0, 0, 0); @@ -57,3 +59,89 @@ screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog( resize(QGuiApplication::primaryScreen()->availableSize() * 3 / 5); } + +screenshot_manager_dialog::~screenshot_manager_dialog() +{ + m_icon_loader->cancel(); + m_icon_loader->waitForFinished(); +} + +void screenshot_manager_dialog::show_preview(QListWidgetItem* item) +{ + if (!item) + { + return; + } + + const QString filepath = item->data(Qt::UserRole).toString(); + + screenshot_preview* preview = new screenshot_preview(filepath); + preview->show(); +} + +void screenshot_manager_dialog::update_icon(int index) +{ + const thumbnail tn = m_icon_loader->resultAt(index); + + if (QListWidgetItem* item = m_grid->item(tn.index)) + { + item->setIcon(tn.icon); + item->setData(item_role::loaded, true); + } +} + +void screenshot_manager_dialog::update_icons(int value) +{ + const QRect visible_rect = rect(); + + QList thumbnails_to_load; + + const bool forward = value >= m_scrollbar_value; + m_scrollbar_value = value; + + const int first = forward ? 0 : (m_grid->count() - 1); + const int last = forward ? (m_grid->count() - 1) : 0; + + for (int i = first; forward ? i <= last : i >= last; forward ? ++i : --i) + { + if (QListWidgetItem* item = m_grid->item(i)) + { + const bool is_loaded = item->data(item_role::loaded).toBool(); + const bool is_visible = visible_rect.intersects(m_grid->visualItemRect(item)); + + if (is_visible) + { + if (!is_loaded) + { + thumbnails_to_load.push_back({ QIcon(), item->data(item_role::source).toString() , i }); + } + } + else if (is_loaded) + { + item->setIcon(m_placeholder); + item->setData(item_role::loaded, false); + } + } + } + + if (m_icon_loader->isRunning()) + { + m_icon_loader->cancel(); + m_icon_loader->waitForFinished(); + } + + std::function load = [icon_size = m_icon_size](thumbnail tn) -> thumbnail + { + QPixmap pixmap(tn.path); + tn.icon = QIcon(pixmap.scaled(icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + return tn; + }; + + m_icon_loader->setFuture(QtConcurrent::mapped(thumbnails_to_load, load)); +} + +void screenshot_manager_dialog::resizeEvent(QResizeEvent* event) +{ + QDialog::resizeEvent(event); + update_icons(m_scrollbar_value); +} diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.h b/rpcs3/rpcs3qt/screenshot_manager_dialog.h index f5b9e3ba4e..1d8a5785e3 100644 --- a/rpcs3/rpcs3qt/screenshot_manager_dialog.h +++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.h @@ -1,8 +1,12 @@ #pragma once #include +#include +#include +#include class QListWidget; +class QListWidgetItem; class screenshot_manager_dialog : public QDialog { @@ -10,7 +14,40 @@ class screenshot_manager_dialog : public QDialog public: screenshot_manager_dialog(QWidget* parent = nullptr); + ~screenshot_manager_dialog(); + +protected: + void resizeEvent(QResizeEvent* event) override; + +private Q_SLOTS: + void update_icon(int index); + +Q_SIGNALS: + void signal_icon_change(int index, const QString& path); private: + void show_preview(QListWidgetItem* item); + void update_icons(int value); + + enum item_role + { + source = Qt::UserRole, + loaded = Qt::UserRole + 1, + }; + + struct thumbnail + { + QIcon icon; + QString path; + int index = 0; + }; + QListWidget* m_grid = nullptr; + + QFutureWatcher* m_icon_loader; + + QSize m_icon_size; + QIcon m_placeholder; + + int m_scrollbar_value = 0; };