diff --git a/shadPS4/gui/custom_table_widget_item.cpp b/shadPS4/gui/custom_table_widget_item.cpp index 6b418046..26a78815 100644 --- a/shadPS4/gui/custom_table_widget_item.cpp +++ b/shadPS4/gui/custom_table_widget_item.cpp @@ -3,7 +3,7 @@ #include custom_table_widget_item::custom_table_widget_item(const std::string& text, int sort_role, const QVariant& sort_value) - : QTableWidgetItem(QString::fromStdString(text).simplified()) // simplified() forces single line text + : game_list_item(QString::fromStdString(text).simplified()) // simplified() forces single line text { if (sort_role != Qt::DisplayRole) { @@ -12,7 +12,7 @@ custom_table_widget_item::custom_table_widget_item(const std::string& text, int } custom_table_widget_item::custom_table_widget_item(const QString& text, int sort_role, const QVariant& sort_value) - : QTableWidgetItem(text.simplified()) // simplified() forces single line text + : game_list_item(text.simplified()) // simplified() forces single line text { if (sort_role != Qt::DisplayRole) { diff --git a/shadPS4/gui/custom_table_widget_item.h b/shadPS4/gui/custom_table_widget_item.h index 00ffc18a..0ca88e48 100644 --- a/shadPS4/gui/custom_table_widget_item.h +++ b/shadPS4/gui/custom_table_widget_item.h @@ -1,7 +1,8 @@ #pragma once +#include "game_list_item.h" #include -class custom_table_widget_item : public QTableWidgetItem +class custom_table_widget_item : public game_list_item { private: int m_sort_role = Qt::DisplayRole; diff --git a/shadPS4/gui/game_list_frame.cpp b/shadPS4/gui/game_list_frame.cpp index 177f5224..409a5441 100644 --- a/shadPS4/gui/game_list_frame.cpp +++ b/shadPS4/gui/game_list_frame.cpp @@ -1,8 +1,10 @@ #include "game_list_frame.h" #include "gui_settings.h" #include "custom_table_widget_item.h" +#include "qt_utils.h" #include "../emulator/fileFormat/PSF.h" #include +#include game_list_frame::game_list_frame(std::shared_ptr gui_settings, QWidget* parent) : QWidget(parent) @@ -107,10 +109,19 @@ game_list_frame::game_list_frame(std::shared_ptr gui_settings, QWi configure->exec(m_game_list->horizontalHeader()->viewport()->mapToGlobal(pos)); }); connect(m_game_list->horizontalHeader(), &QHeaderView::sectionClicked, this, &game_list_frame::OnHeaderColumnClicked); + connect(&m_repaint_watcher, &QFutureWatcher::resultReadyAt, this, [this](int index) + { + if (!m_is_list_layout) return; + if (game_list_item* item = m_repaint_watcher.resultAt(index)) + { + item->call_icon_func(); + } + }); Refresh();//TODO remove when watchers added } game_list_frame::~game_list_frame() { + gui::utils::stop_future_watcher(m_repaint_watcher, true); SaveSettings(); } void game_list_frame::FixNarrowColumns() const @@ -368,7 +379,7 @@ void game_list_frame::PopulateGameList() int row = 0; int index = -1; - RepaintIcons();//hackish + //RepaintIcons();//hackish for (const auto& game : m_game_data) { index++; @@ -376,7 +387,12 @@ void game_list_frame::PopulateGameList() // Icon custom_table_widget_item* icon_item = new custom_table_widget_item; - icon_item->setData(Qt::DecorationRole, game->pxmap); + game->item = icon_item; + icon_item->set_icon_func([this, icon_item, game](int) + { + icon_item->setData(Qt::DecorationRole, game->pxmap); + game->pxmap = {}; + }); icon_item->setData(Qt::UserRole, index, true); icon_item->setData(gui::custom_roles::game_role, QVariant::fromValue(game)); @@ -448,12 +464,44 @@ std::string game_list_frame::CurrentSelectionPath() void game_list_frame::RepaintIcons(const bool& from_settings) { - for (auto& game : m_game_data) + gui::utils::stop_future_watcher(m_repaint_watcher, true); + + if (from_settings) { - game->icon.load(QString::fromStdString(game->info.icon_path)); - game->pxmap = PaintedPixmap(game->icon); + //TODO m_icon_color = gui::utils::get_label_color("gamelist_icon_background_color"); } - + + if (m_is_list_layout) + { + QPixmap placeholder(m_icon_size); + placeholder.fill(Qt::transparent); + + for (auto& game : m_game_data) + { + game->pxmap = placeholder; + } + + // Fixate vertical header and row height + m_game_list->verticalHeader()->setMinimumSectionSize(m_icon_size.height()); + m_game_list->verticalHeader()->setMaximumSectionSize(m_icon_size.height()); + + // Resize the icon column + m_game_list->resizeColumnToContents(gui::column_icon); + + // Shorten the last section to remove horizontal scrollbar if possible + m_game_list->resizeColumnToContents(gui::column_count - 1); + } + + const std::function func = [this](const game_info& game) -> game_list_item* + { + if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(QString::fromStdString(game->info.icon_path)))) + { + //TODO added warning message if no found + } + game->pxmap = PaintedPixmap(game->icon); + return game->item; + }; + m_repaint_watcher.setFuture(QtConcurrent::mapped(m_game_data, func)); } QPixmap game_list_frame::PaintedPixmap(const QPixmap& icon) const diff --git a/shadPS4/gui/game_list_frame.h b/shadPS4/gui/game_list_frame.h index 29ad1ee6..16af942c 100644 --- a/shadPS4/gui/game_list_frame.h +++ b/shadPS4/gui/game_list_frame.h @@ -3,11 +3,13 @@ #include "game_list_table.h" #include "shadps4gui.h" #include "game_list_grid.h" - +#include "game_list_item.h" #include #include #include #include +#include +#include class game_list_frame : public QWidget { @@ -60,6 +62,7 @@ private: QList m_game_data; std::vector m_path_list; std::deque m_games; + QFutureWatcher m_repaint_watcher; // Icons QSize m_icon_size; diff --git a/shadPS4/gui/game_list_grid.cpp b/shadPS4/gui/game_list_grid.cpp index aeef3e50..d6b0f23b 100644 --- a/shadPS4/gui/game_list_grid.cpp +++ b/shadPS4/gui/game_list_grid.cpp @@ -1,6 +1,6 @@ #include "game_list_grid.h" #include "game_list_grid_delegate.h" - +#include "game_list_item.h" #include #include @@ -58,45 +58,47 @@ void game_list_grid::setIconSize(const QSize& size) const QTableWidgetItem* game_list_grid::addItem(const game_info& app, const QString& name, const QString& movie_path, const int& row, const int& col) { - const qreal device_pixel_ratio = devicePixelRatioF(); - - // define size of expanded image, which is raw image size + margins - QSizeF exp_size_f; - if (m_text_enabled) + game_list_item* item = new game_list_item; + item->set_icon_func([this, app, item](int) { - exp_size_f = m_icon_size + QSizeF(m_icon_size.width() * m_margin_factor * 2, m_icon_size.height() * m_margin_factor * (m_text_factor + 1)); - } - else - { - exp_size_f = m_icon_size + m_icon_size * m_margin_factor * 2; - } + const qreal device_pixel_ratio = devicePixelRatioF(); - // define offset for raw image placement - QPoint offset(m_icon_size.width() * m_margin_factor, m_icon_size.height() * m_margin_factor); - const QSize exp_size = (exp_size_f * device_pixel_ratio).toSize(); + // define size of expanded image, which is raw image size + margins + QSizeF exp_size_f; + if (m_text_enabled) + { + exp_size_f = m_icon_size + QSizeF(m_icon_size.width() * m_margin_factor * 2, m_icon_size.height() * m_margin_factor * (m_text_factor + 1)); + } + else + { + exp_size_f = m_icon_size + m_icon_size * m_margin_factor * 2; + } - // create empty canvas for expanded image - QImage exp_img(exp_size, QImage::Format_ARGB32); - exp_img.setDevicePixelRatio(device_pixel_ratio); - exp_img.fill(Qt::transparent); + // define offset for raw image placement + QPoint offset(m_icon_size.width() * m_margin_factor, m_icon_size.height() * m_margin_factor); + const QSize exp_size = (exp_size_f * device_pixel_ratio).toSize(); - // create background for image - QImage bg_img(app->pxmap.size(), QImage::Format_ARGB32); - bg_img.setDevicePixelRatio(device_pixel_ratio); - bg_img.fill(m_icon_color); + // create empty canvas for expanded image + QImage exp_img(exp_size, QImage::Format_ARGB32); + exp_img.setDevicePixelRatio(device_pixel_ratio); + exp_img.fill(Qt::transparent); - // place raw image inside expanded image - QPainter painter(&exp_img); - painter.setRenderHint(QPainter::SmoothPixmapTransform); - painter.drawImage(offset, bg_img); - painter.drawPixmap(offset, app->pxmap); - app->pxmap = {}; - painter.end(); + // create background for image + QImage bg_img(app->pxmap.size(), QImage::Format_ARGB32); + bg_img.setDevicePixelRatio(device_pixel_ratio); + bg_img.fill(m_icon_color); - // create item with expanded image, title and position - QTableWidgetItem* item = new QTableWidgetItem(); - item->setData(Qt::ItemDataRole::DecorationRole, QPixmap::fromImage(exp_img)); + // place raw image inside expanded image + QPainter painter(&exp_img); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.drawImage(offset, bg_img); + painter.drawPixmap(offset, app->pxmap); + app->pxmap = {}; + painter.end(); + // create item with expanded image, title and position + item->setData(Qt::ItemDataRole::DecorationRole, QPixmap::fromImage(exp_img)); + }); if (m_text_enabled) { item->setData(Qt::ItemDataRole::DisplayRole, name); diff --git a/shadPS4/gui/game_list_item.h b/shadPS4/gui/game_list_item.h new file mode 100644 index 00000000..e000be0a --- /dev/null +++ b/shadPS4/gui/game_list_item.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#include + +using icon_callback_t = std::function; + +class game_list_item : public QTableWidgetItem +{ +public: + game_list_item() : QTableWidgetItem() + { + } + game_list_item(const QString& text, int type = Type) : QTableWidgetItem(text, type) + { + } + game_list_item(const QIcon& icon, const QString& text, int type = Type) : QTableWidgetItem(icon, text, type) + { + } + + ~game_list_item() + { + + } + + void call_icon_func() const + { + if (m_icon_callback) + { + m_icon_callback(0); + } + } + + void set_icon_func(const icon_callback_t& func) + { + m_icon_callback = func; + call_icon_func(); + } + +private: + icon_callback_t m_icon_callback = nullptr; +}; diff --git a/shadPS4/gui/game_list_table.h b/shadPS4/gui/game_list_table.h index 3678a08c..26a16bf7 100644 --- a/shadPS4/gui/game_list_table.h +++ b/shadPS4/gui/game_list_table.h @@ -3,12 +3,14 @@ #include #include #include "../emulator/gameInfo.h" +#include "game_list_item.h" struct gui_game_info { GameInfo info{}; QPixmap icon; QPixmap pxmap; + game_list_item* item = nullptr; }; typedef std::shared_ptr game_info; diff --git a/shadPS4/gui/qt_utils.h b/shadPS4/gui/qt_utils.h new file mode 100644 index 00000000..00ad829b --- /dev/null +++ b/shadPS4/gui/qt_utils.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace gui +{ + namespace utils + { + template + void stop_future_watcher(QFutureWatcher& watcher, bool cancel) + { + if (watcher.isStarted() || watcher.isRunning()) + { + if (cancel) + { + watcher.cancel(); + } + watcher.waitForFinished(); + } + } + } // utils +} // gui + diff --git a/shadPS4/shadPS4.vcxproj b/shadPS4/shadPS4.vcxproj index 28e790c5..5338f302 100644 --- a/shadPS4/shadPS4.vcxproj +++ b/shadPS4/shadPS4.vcxproj @@ -45,10 +45,12 @@ + + @@ -73,12 +75,12 @@ 6.4.2 - core;gui;widgets + core;gui;widgets;concurrent debug 6.4.2 - core;gui;widgets + core;gui;widgets;concurrent release