From 45e93dcb2906618249d56ec07732ebf23bbe8e11 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 24 Mar 2020 20:56:47 +0100 Subject: [PATCH 1/4] Qt: move open_dir to qt_utils --- rpcs3/rpcs3qt/game_list_frame.cpp | 32 ++++--------------------------- rpcs3/rpcs3qt/qt_utils.cpp | 32 +++++++++++++++++++++++++++++++ rpcs3/rpcs3qt/qt_utils.h | 6 ++++++ 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index d3d00d9c9c93..da8c8e71baf8 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -780,30 +780,6 @@ void game_list_frame::SaveSettings() m_gui_settings->SetValue(gui::gl_state, m_game_list->horizontalHeader()->saveState()); } -static void open_dir(const std::string& spath) -{ - fs::create_dir(spath); - const QString path = qstr(spath); - - if (fs::is_file(spath)) - { - // open directory and select file - // https://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt -#ifdef _WIN32 - QProcess::startDetached("explorer.exe", { "/select,", QDir::toNativeSeparators(path) }); -#elif defined(__APPLE__) - QProcess::execute("/usr/bin/osascript", { "-e", "tell application \"Finder\" to reveal POSIX file \"" + path + "\"" }); - QProcess::execute("/usr/bin/osascript", { "-e", "tell application \"Finder\" to activate" }); -#else - // open parent directory - QDesktopServices::openUrl(QUrl("file:///" + qstr(fs::get_parent_dir(spath)))); -#endif - return; - } - - QDesktopServices::openUrl(QUrl("file:///" + path)); -} - void game_list_frame::doubleClickedSlot(QTableWidgetItem *item) { if (!item) @@ -983,12 +959,12 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) const std::string new_config_path = Emulator::GetCustomConfigPath(current_game.serial); if (fs::is_file(new_config_path)) - open_dir(new_config_path); + gui::utils::open_dir(new_config_path); const std::string old_config_path = Emulator::GetCustomConfigPath(current_game.serial, true); if (fs::is_file(old_config_path)) - open_dir(old_config_path); + gui::utils::open_dir(old_config_path); }); } if (fs::is_dir(data_base_dir)) @@ -996,7 +972,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) QAction* open_data_dir = menu.addAction(tr("&Open Data Folder")); connect(open_data_dir, &QAction::triggered, [=, this]() { - open_dir(data_base_dir); + gui::utils::open_dir(data_base_dir); }); } menu.addSeparator(); @@ -1106,7 +1082,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) }); connect(open_game_folder, &QAction::triggered, [=, this]() { - open_dir(current_game.path); + gui::utils::open_dir(current_game.path); }); connect(check_compat, &QAction::triggered, [=, this] { diff --git a/rpcs3/rpcs3qt/qt_utils.cpp b/rpcs3/rpcs3qt/qt_utils.cpp index f8ada24aba5b..e368a4dd02a6 100644 --- a/rpcs3/rpcs3qt/qt_utils.cpp +++ b/rpcs3/rpcs3qt/qt_utils.cpp @@ -1,9 +1,12 @@ #include "qt_utils.h" #include #include +#include #include #include +#include #include +#include #include "Emu/System.h" @@ -276,5 +279,34 @@ namespace gui // if nothing was found reset the icon to default return QApplication::windowIcon(); } + + void open_dir(const std::string& spath) + { + fs::create_dir(spath); + const QString path = qstr(spath); + + if (fs::is_file(spath)) + { + // open directory and select file + // https://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt +#ifdef _WIN32 + QProcess::startDetached("explorer.exe", { "/select,", QDir::toNativeSeparators(path) }); +#elif defined(__APPLE__) + QProcess::execute("/usr/bin/osascript", { "-e", "tell application \"Finder\" to reveal POSIX file \"" + path + "\"" }); + QProcess::execute("/usr/bin/osascript", { "-e", "tell application \"Finder\" to activate" }); +#else + // open parent directory + QDesktopServices::openUrl(QUrl("file:///" + qstr(fs::get_parent_dir(spath)))); +#endif + return; + } + + QDesktopServices::openUrl(QUrl("file:///" + path)); + } + + void open_dir(const QString& path) + { + open_dir(sstr(path)); + } } // utils } // gui diff --git a/rpcs3/rpcs3qt/qt_utils.h b/rpcs3/rpcs3qt/qt_utils.h index e71fd9ec3a82..bb45bfd329c5 100644 --- a/rpcs3/rpcs3qt/qt_utils.h +++ b/rpcs3/rpcs3qt/qt_utils.h @@ -56,5 +56,11 @@ namespace gui // Loads the app icon from path and embeds it centered into an empty square icon QIcon get_app_icon_from_path(const std::string& path, const std::string& title_id); + + // Open a path in the explorer and mark the file + void open_dir(const std::string& spath); + + // Open a path in the explorer and mark the file + void open_dir(const QString& path); } // utils } // gui From 3b8971721fe3e54cc707f66f003a8aa862eefbf2 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 24 Mar 2020 21:10:03 +0100 Subject: [PATCH 2/4] Qt: add open file location to screenshot preview Remove duplicate slash from screenshot path --- rpcs3/rpcs3qt/gs_frame.cpp | 2 +- rpcs3/rpcs3qt/screenshot_manager_dialog.cpp | 2 +- rpcs3/rpcs3qt/screenshot_preview.cpp | 3 ++- rpcs3/rpcs3qt/screenshot_preview.h | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index b576e22d5e36..9fabd6312bf3 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -301,7 +301,7 @@ void gs_frame::take_screenshot(const std::vector sshot_data, const u32 sshot std::thread( [sshot_width, sshot_height](const std::vector sshot_data) { - std::string screen_path = fs::get_config_dir() + "/screenshots/"; + std::string screen_path = fs::get_config_dir() + "screenshots/"; if (!fs::create_dir(screen_path) && fs::g_tls_error != fs::error::exist) { diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp index 0f8df5511e2f..58dff9597d46 100644 --- a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp @@ -21,7 +21,7 @@ screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog( m_grid->setIconSize(QSize(160, 90)); m_grid->setGridSize(m_grid->iconSize() + QSize(10, 10)); - const std::string screen_path = fs::get_config_dir() + "/screenshots/"; + 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); diff --git a/rpcs3/rpcs3qt/screenshot_preview.cpp b/rpcs3/rpcs3qt/screenshot_preview.cpp index 979f38ebbceb..f91ffe0732cf 100644 --- a/rpcs3/rpcs3qt/screenshot_preview.cpp +++ b/rpcs3/rpcs3qt/screenshot_preview.cpp @@ -27,13 +27,14 @@ screenshot_preview::screenshot_preview(const QString& filepath, QWidget* parent) connect(this, &screenshot_preview::customContextMenuRequested, this, &screenshot_preview::show_context_menu); } -#include void screenshot_preview::show_context_menu(const QPoint & pos) { QMenu* menu = new QMenu(); menu->addAction(tr("&Copy"), [this]() { QGuiApplication::clipboard()->setImage(m_image); }); menu->addSeparator(); + menu->addAction(tr("&Open file location"), [this]() { gui::utils::open_dir(m_filepath); }); + menu->addSeparator(); QAction* reset_act = menu->addAction(tr("To &Normal Size"), [this]() { scale(m_image.size()); }); reset_act->setEnabled(pixmap()->size() != m_image.size()); diff --git a/rpcs3/rpcs3qt/screenshot_preview.h b/rpcs3/rpcs3qt/screenshot_preview.h index f9a813997a06..a02990a447e9 100644 --- a/rpcs3/rpcs3qt/screenshot_preview.h +++ b/rpcs3/rpcs3qt/screenshot_preview.h @@ -11,7 +11,7 @@ class screenshot_preview : public QLabel screenshot_preview(const QString& filepath, QWidget* parent = nullptr); protected: - void resizeEvent(QResizeEvent* event); + void resizeEvent(QResizeEvent* event) override; private Q_SLOTS: void show_context_menu(const QPoint& pos); From b0e4b82b58b97f739b25365484e117a327c3c123 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 24 Mar 2020 23:24:29 +0100 Subject: [PATCH 3/4] 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 58dff9597d46..a915f3bd951c 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; - } - - const QString filepath = item->data(Qt::UserRole).toString(); + m_icon_loader = new QFutureWatcher(this); + connect(m_icon_loader, &QFutureWatcher::resultReadyAt, this, &screenshot_manager_dialog::update_icon); - 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 f5b9e3ba4e53..1d8a5785e367 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; }; From 718ab07648b8d3f73105cc99e657a1fba6a42e5c Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 25 Mar 2020 02:58:13 +0100 Subject: [PATCH 4/4] Qt: fix mem leaks in screenshot and save managers --- rpcs3/rpcs3qt/save_manager_dialog.cpp | 1 + rpcs3/rpcs3qt/screenshot_manager_dialog.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/rpcs3/rpcs3qt/save_manager_dialog.cpp b/rpcs3/rpcs3qt/save_manager_dialog.cpp index c1ffd98e839c..d6b7cc5756eb 100644 --- a/rpcs3/rpcs3qt/save_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/save_manager_dialog.cpp @@ -98,6 +98,7 @@ save_manager_dialog::save_manager_dialog(std::shared_ptr gui_setti { setWindowTitle(tr("Save Manager")); setMinimumSize(QSize(400, 400)); + setAttribute(Qt::WA_DeleteOnClose); Init(dir); } diff --git a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp index a915f3bd951c..17d0a474bb3a 100644 --- a/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/screenshot_manager_dialog.cpp @@ -15,6 +15,7 @@ screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog(parent) { setWindowTitle(tr("Screenshots")); + setAttribute(Qt::WA_DeleteOnClose); m_icon_size = QSize(160, 90);