From 957713015a9f5873511fca33fa875f1d9ea83fad Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 25 Apr 2023 10:04:17 +0200 Subject: [PATCH] screenshot manager: use flow layout This whole screenshot thing was really janky, as I added it in half a day or so. But this commit should make everything smooth. Sadly there is no real lazy loading yet (icons are loaded async, but indiscriminately). --- rpcs3/rpcs3.vcxproj | 17 ++ rpcs3/rpcs3.vcxproj.filters | 21 ++ rpcs3/rpcs3qt/CMakeLists.txt | 2 + rpcs3/rpcs3qt/flow_layout.cpp | 222 ++++++++++++++++++++ rpcs3/rpcs3qt/flow_layout.h | 86 ++++++++ rpcs3/rpcs3qt/flow_widget.cpp | 64 ++++++ rpcs3/rpcs3qt/flow_widget.h | 31 +++ rpcs3/rpcs3qt/screenshot_manager_dialog.cpp | 190 +++++++++-------- rpcs3/rpcs3qt/screenshot_manager_dialog.h | 76 ++++--- 9 files changed, 588 insertions(+), 121 deletions(-) create mode 100644 rpcs3/rpcs3qt/flow_layout.cpp create mode 100644 rpcs3/rpcs3qt/flow_layout.h create mode 100644 rpcs3/rpcs3qt/flow_widget.cpp create mode 100644 rpcs3/rpcs3qt/flow_widget.h diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 476f5457b7..2a8295228c 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -670,6 +670,8 @@ + + @@ -1159,6 +1161,21 @@ $(QTDIR)\bin\moc.exe;%(FullPath) $(QTDIR)\bin\moc.exe;%(FullPath) + + + + + + + + + + + + + + + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 080136e8de..ff5977b1cf 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -151,6 +151,9 @@ {d60be100-0d7e-4076-a24a-d413d0e91532} + + {9191ae51-8b80-4606-b5bc-966a9588f538} + @@ -951,6 +954,18 @@ Gui\game list + + Gui\flow_layout + + + Gui\flow_layout + + + Generated Files\Debug + + + Generated Files\Release + @@ -1124,6 +1139,9 @@ Gui\game list + + Gui\flow_layout + @@ -1402,6 +1420,9 @@ Gui\game list + + Gui\flow_layout + diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index f6cb298511..6262889249 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -22,6 +22,8 @@ set(SRC_FILES emu_settings.cpp fatal_error_dialog.cpp find_dialog.cpp + flow_layout.cpp + flow_widget.cpp game_compatibility.cpp game_list.cpp game_list_delegate.cpp diff --git a/rpcs3/rpcs3qt/flow_layout.cpp b/rpcs3/rpcs3qt/flow_layout.cpp new file mode 100644 index 0000000000..698999dbed --- /dev/null +++ b/rpcs3/rpcs3qt/flow_layout.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "flow_layout.h" + +flow_layout::flow_layout(QWidget* parent, int margin, int hSpacing, int vSpacing) + : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +flow_layout::flow_layout(int margin, int hSpacing, int vSpacing) + : m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +flow_layout::~flow_layout() +{ + clear(); +} + +void flow_layout::clear() +{ + while (QLayoutItem* item = takeAt(0)) + { + delete item->widget(); + delete item; + } + itemList.clear(); +} + +void flow_layout::addItem(QLayoutItem* item) +{ + itemList.append(item); +} + +int flow_layout::horizontalSpacing() const +{ + if (m_hSpace >= 0) + { + return m_hSpace; + } + + return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); +} + +int flow_layout::verticalSpacing() const +{ + if (m_vSpace >= 0) + { + return m_vSpace; + } + + return smartSpacing(QStyle::PM_LayoutVerticalSpacing); +} + +int flow_layout::count() const +{ + return itemList.size(); +} + +QLayoutItem* flow_layout::itemAt(int index) const +{ + return itemList.value(index); +} + +QLayoutItem* flow_layout::takeAt(int index) +{ + if (index >= 0 && index < itemList.size()) + return itemList.takeAt(index); + + return nullptr; +} + +Qt::Orientations flow_layout::expandingDirections() const +{ + return {}; +} + +bool flow_layout::hasHeightForWidth() const +{ + return true; +} + +int flow_layout::heightForWidth(int width) const +{ + return doLayout(QRect(0, 0, width, 0), true); +} + +void flow_layout::setGeometry(const QRect& rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize flow_layout::sizeHint() const +{ + return minimumSize(); +} + +QSize flow_layout::minimumSize() const +{ + QSize size; + for (const QLayoutItem* item : qAsConst(itemList)) + { + if (item) + { + size = size.expandedTo(item->minimumSize()); + } + } + + const QMargins margins = contentsMargins(); + size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); + return size; +} + +int flow_layout::doLayout(const QRect& rect, bool testOnly) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; + + for (QLayoutItem* item : qAsConst(itemList)) + { + const QWidget* wid = item->widget(); + if (!wid) + continue; + + int spaceX = horizontalSpacing(); + if (spaceX == -1) + spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + + int spaceY = verticalSpacing(); + if (spaceY == -1) + spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); + + int nextX = x + item->sizeHint().width() + spaceX; + if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) + { + x = effectiveRect.x(); + y = y + lineHeight + spaceY; + nextX = x + item->sizeHint().width() + spaceX; + lineHeight = 0; + } + + if (!testOnly) + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + } + return y + lineHeight - rect.y() + bottom; +} + +int flow_layout::smartSpacing(QStyle::PixelMetric pm) const +{ + QObject* parent = this->parent(); + if (!parent) + { + return -1; + } + + if (parent->isWidgetType()) + { + QWidget* pw = static_cast(parent); + return pw->style()->pixelMetric(pm, nullptr, pw); + } + + return static_cast(parent)->spacing(); +} diff --git a/rpcs3/rpcs3qt/flow_layout.h b/rpcs3/rpcs3qt/flow_layout.h new file mode 100644 index 0000000000..b395b2cbcc --- /dev/null +++ b/rpcs3/rpcs3qt/flow_layout.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include +#include +#include + +class flow_layout : public QLayout +{ +public: + explicit flow_layout(QWidget* parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); + explicit flow_layout(int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~flow_layout(); + + void clear(); + + void addItem(QLayoutItem* item) override; + int horizontalSpacing() const; + int verticalSpacing() const; + Qt::Orientations expandingDirections() const override; + bool hasHeightForWidth() const override; + int heightForWidth(int) const override; + int count() const override; + QLayoutItem* itemAt(int index) const override; + QSize minimumSize() const override; + void setGeometry(const QRect& rect) override; + QSize sizeHint() const override; + QLayoutItem* takeAt(int index) override; + +private: + int doLayout(const QRect& rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; + + QList itemList; + int m_hSpace; + int m_vSpace; +}; diff --git a/rpcs3/rpcs3qt/flow_widget.cpp b/rpcs3/rpcs3qt/flow_widget.cpp new file mode 100644 index 0000000000..281b8e9ee7 --- /dev/null +++ b/rpcs3/rpcs3qt/flow_widget.cpp @@ -0,0 +1,64 @@ +#include "flow_widget.h" +#include "flow_layout.h" + +#include +#include + +flow_widget::flow_widget(QWidget* parent) + : QWidget(parent) +{ + m_flow_layout = new flow_layout(); + + QWidget* widget = new QWidget(this); + widget->setLayout(m_flow_layout); + + QScrollArea* scrollArea = new QScrollArea(this); + scrollArea->setWidget(widget); + scrollArea->setWidgetResizable(true); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->addWidget(scrollArea); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); +} + +flow_widget::~flow_widget() +{ +} + +void flow_widget::add_widget(flow_widget_item* widget) +{ + if (widget) + { + m_widgets << widget; + m_flow_layout->addWidget(widget); + } +} + +void flow_widget::clear() +{ + m_widgets.clear(); + m_flow_layout->clear(); +} + +QList& flow_widget::items() +{ + return m_widgets; +} + +void flow_widget_item::paintEvent(QPaintEvent* event) +{ + QWidget::paintEvent(event); + + if (!got_visible && cb_on_first_visibility) + { + if (QWidget* widget = static_cast(parent())) + { + if (widget->visibleRegion().intersects(geometry())) + { + got_visible = true; + cb_on_first_visibility(); + } + } + } +} diff --git a/rpcs3/rpcs3qt/flow_widget.h b/rpcs3/rpcs3qt/flow_widget.h new file mode 100644 index 0000000000..5389f6cfed --- /dev/null +++ b/rpcs3/rpcs3qt/flow_widget.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +class flow_layout; + +class flow_widget_item : public QWidget +{ +public: + using QWidget::QWidget; + void paintEvent(QPaintEvent* event) override; + + bool got_visible{}; + std::function cb_on_first_visibility{}; +}; + +class flow_widget : public QWidget +{ +public: + flow_widget(QWidget* parent); + virtual ~flow_widget(); + + void add_widget(flow_widget_item* widget); + void clear(); + + QList& items(); + +private: + flow_layout* m_flow_layout{}; + QList m_widgets{}; +}; diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp index 61d18179bb..8d0f5acc5f 100644 --- a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "screenshot_manager_dialog.h" #include "screenshot_preview.h" +#include "flow_widget.h" #include "qt_utils.h" #include "Utilities/File.h" #include "Emu/VFS.h" @@ -9,9 +10,7 @@ #include #include #include -#include #include -#include #include #include @@ -23,57 +22,18 @@ screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog( setAttribute(Qt::WA_DeleteOnClose); m_icon_size = QSize(160, 90); + m_flow_widget = new flow_widget(this); + m_flow_widget->setObjectName("m_flow_widget"); - m_grid = new QListWidget(this); - m_grid->setViewMode(QListWidget::IconMode); - m_grid->setMovement(QListWidget::Static); - m_grid->setResizeMode(QListWidget::Adjust); - m_grid->setIconSize(m_icon_size); - m_grid->setGridSize(m_icon_size + QSize(10, 10)); + m_placeholder = QPixmap(m_icon_size); + m_placeholder.fill(Qt::gray); - // Make sure the directory is mounted - vfs::mount("/dev_hdd0", rpcs3::utils::get_hdd0_dir()); - - const std::string screenshot_path_qt = fs::get_config_dir() + "screenshots/"; - const std::string screenshot_path_cell = vfs::get("/dev_hdd0/photo/"); - const QStringList filter{ QStringLiteral("*.png") }; - - QPixmap placeholder(m_icon_size); - placeholder.fill(Qt::gray); - m_placeholder = QIcon(placeholder); - - for (const std::string& path : { screenshot_path_qt, screenshot_path_cell }) - { - if (path.empty()) - { - gui_log.error("Screenshot manager: Trying to load screenshots from empty path!"); - continue; - } - - QDirIterator dir_iter(QString::fromStdString(path), filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); - - while (dir_iter.hasNext()) - { - const QString filepath = dir_iter.next(); - - QListWidgetItem* item = new QListWidgetItem; - 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_icon_loader, &QFutureWatcher::resultReadyAt, this, &screenshot_manager_dialog::update_icon); - - connect(m_grid, &QListWidget::itemDoubleClicked, this, &screenshot_manager_dialog::show_preview); - connect(m_grid->verticalScrollBar(), &QScrollBar::valueChanged, this, &screenshot_manager_dialog::update_icons); + connect(this, &screenshot_manager_dialog::signal_icon_preview, this, &screenshot_manager_dialog::show_preview); + connect(this, &screenshot_manager_dialog::signal_entry_parsed, this, &screenshot_manager_dialog::add_entry); QVBoxLayout* layout = new QVBoxLayout; layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(m_grid); + layout->addWidget(m_flow_widget); setLayout(layout); resize(QGuiApplication::primaryScreen()->availableSize() * 3 / 5); @@ -81,80 +41,130 @@ screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog( screenshot_manager_dialog::~screenshot_manager_dialog() { - gui::utils::stop_future_watcher(m_icon_loader, true); + m_abort_parsing = true; + gui::utils::stop_future_watcher(m_parsing_watcher, true); } -void screenshot_manager_dialog::show_preview(QListWidgetItem* item) +void screenshot_manager_dialog::add_entry(const QString& path) { - if (!item) - { - return; - } + screenshot_item* item = new screenshot_item(m_flow_widget); + ensure(item->label); + item->setToolTip(path); + item->installEventFilter(this); + item->label->setPixmap(m_placeholder); + item->icon_path = path; + item->icon_size = m_icon_size; + connect(item, &screenshot_item::signal_icon_update, this, &screenshot_manager_dialog::update_icon); - const QString filepath = item->data(Qt::UserRole).toString(); + m_flow_widget->add_widget(item); +} - screenshot_preview* preview = new screenshot_preview(filepath); +void screenshot_manager_dialog::show_preview(const QString& path) +{ + screenshot_preview* preview = new screenshot_preview(path); preview->show(); } -void screenshot_manager_dialog::update_icon(int index) const +void screenshot_manager_dialog::update_icon(const QPixmap& pixmap) { - const thumbnail tn = m_icon_loader.resultAt(index); - - if (QListWidgetItem* item = m_grid->item(tn.index)) + if (screenshot_item* item = static_cast(QObject::sender())) { - item->setIcon(tn.icon); - item->setData(item_role::loaded, true); + if (item->label) + { + item->label->setPixmap(pixmap); + } } } -void screenshot_manager_dialog::update_icons(int value) +void screenshot_manager_dialog::reload() { - const QRect visible_rect = rect(); + m_abort_parsing = true; + gui::utils::stop_future_watcher(m_parsing_watcher, true); - QList thumbnails_to_load; + // Make sure the directory is mounted + vfs::mount("/dev_hdd0", rpcs3::utils::get_hdd0_dir()); - const bool forward = value >= m_scrollbar_value; - m_scrollbar_value = value; + const std::string screenshot_path_qt = fs::get_config_dir() + "screenshots/"; + const std::string screenshot_path_cell = vfs::get("/dev_hdd0/photo/"); - 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) + m_flow_widget->clear(); + m_abort_parsing = false; + m_parsing_watcher.setFuture(QtConcurrent::map(m_parsing_threads, [this, screenshot_path_qt, screenshot_path_cell](int index) { - if (QListWidgetItem* item = m_grid->item(i)) + if (index != 0) { - const bool is_loaded = item->data(item_role::loaded).toBool(); - const bool is_visible = visible_rect.intersects(m_grid->visualItemRect(item)); + return; + } - if (is_visible) + const QStringList filter{ QStringLiteral("*.png") }; + + for (const std::string& path : { screenshot_path_qt, screenshot_path_cell }) + { + if (m_abort_parsing) { - if (!is_loaded) - { - thumbnails_to_load.push_back({ QIcon(), item->data(item_role::source).toString() , i }); - } + return; } - else if (is_loaded) + + if (path.empty()) { - item->setIcon(m_placeholder); - item->setData(item_role::loaded, false); + gui_log.error("Screenshot manager: Trying to load screenshots from empty path!"); + continue; } + + QDirIterator dir_iter(QString::fromStdString(path), filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); + + while (dir_iter.hasNext() && !m_abort_parsing) + { + Q_EMIT signal_entry_parsed(dir_iter.next()); + } + } + })); +} + +void screenshot_manager_dialog::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); + reload(); +} + +bool screenshot_manager_dialog::eventFilter(QObject* watched, QEvent* event) +{ + if (event && event->type() == QEvent::MouseButtonDblClick) + { + if (screenshot_item* item = static_cast(watched)) + { + Q_EMIT signal_icon_preview(item->icon_path); + return true; } } - gui::utils::stop_future_watcher(m_icon_loader, true); + return false; +} - const std::function load = [icon_size = m_icon_size](thumbnail tn) -> thumbnail +screenshot_item::screenshot_item(QWidget* parent) + : flow_widget_item(parent) +{ + cb_on_first_visibility = [this]() { - tn.icon = QIcon(gui::utils::get_centered_pixmap(tn.path, icon_size, 0, 0, 1.0)); - return tn; + m_thread.reset(QThread::create([this]() + { + const QPixmap pixmap = gui::utils::get_centered_pixmap(icon_path, icon_size, 0, 0, 1.0); + Q_EMIT signal_icon_update(pixmap); + })); + m_thread->start(); }; - m_icon_loader.setFuture(QtConcurrent::mapped(thumbnails_to_load, load)); + label = new QLabel(this); + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(label); + setLayout(layout); } -void screenshot_manager_dialog::resizeEvent(QResizeEvent* event) +screenshot_item::~screenshot_item() { - QDialog::resizeEvent(event); - update_icons(m_scrollbar_value); + if (m_thread && m_thread->isRunning()) + { + m_thread->wait(); + } } diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.h b/rpcs3/rpcs3qt/screenshot_manager_dialog.h index a67313f843..8eec1aa324 100644 --- a/rpcs3/rpcs3qt/screenshot_manager_dialog.h +++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.h @@ -1,12 +1,15 @@ #pragma once +#include "flow_widget.h"; + #include #include -#include +#include #include - -class QListWidget; -class QListWidgetItem; +#include +#include +#include +#include class screenshot_manager_dialog : public QDialog { @@ -16,38 +19,49 @@ public: screenshot_manager_dialog(QWidget* parent = nullptr); ~screenshot_manager_dialog(); -protected: - void resizeEvent(QResizeEvent* event) override; - -private Q_SLOTS: - void update_icon(int index) const; + bool eventFilter(QObject* watched, QEvent* event) override; Q_SIGNALS: - void signal_icon_change(int index, const QString& path); + void signal_entry_parsed(const QString& path); + void signal_icon_preview(const QString& path); + +public Q_SLOTS: + void update_icon(const QPixmap& pixmap); + +private Q_SLOTS: + void add_entry(const QString& path); + void show_preview(const QString& path); + +protected: + void showEvent(QShowEvent* event) override; private: - static void show_preview(QListWidgetItem* item); - void update_icons(int value); + void reload(); - 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; + bool m_abort_parsing = false; + const std::array m_parsing_threads{0}; + QFutureWatcher m_parsing_watcher; + flow_widget* m_flow_widget = nullptr; QSize m_icon_size; - QIcon m_placeholder; - - int m_scrollbar_value = 0; + QPixmap m_placeholder; +}; + +class screenshot_item : public flow_widget_item +{ + Q_OBJECT + +public: + screenshot_item(QWidget* parent); + virtual ~screenshot_item(); + + QString icon_path; + QSize icon_size; + QLabel* label{}; + +private: + std::unique_ptr m_thread; + +Q_SIGNALS: + void signal_icon_update(const QPixmap& pixmap); };