mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-10 00:41:26 +12:00
Qt: Lazy load game grid icons and optimize paint method for invisible items
This commit is contained in:
parent
b47db88ded
commit
823b23f800
6 changed files with 103 additions and 105 deletions
|
@ -149,10 +149,9 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
|
||||||
m_serials.clear();
|
m_serials.clear();
|
||||||
m_games.pop_all();
|
m_games.pop_all();
|
||||||
});
|
});
|
||||||
connect(&m_repaint_watcher, &QFutureWatcher<movie_item*>::finished, this, &game_list_frame::OnRepaintFinished);
|
|
||||||
connect(this, &game_list_frame::IconReady, this, [this](movie_item* item)
|
connect(this, &game_list_frame::IconReady, this, [this](movie_item* item)
|
||||||
{
|
{
|
||||||
if (!m_is_list_layout || !item) return;
|
if (!item) return;
|
||||||
item->call_icon_func();
|
item->call_icon_func();
|
||||||
});
|
});
|
||||||
connect(this, &game_list_frame::SizeOnDiskReady, this, [this](const game_info& game)
|
connect(this, &game_list_frame::SizeOnDiskReady, this, [this](const game_info& game)
|
||||||
|
@ -882,29 +881,6 @@ void game_list_frame::OnRefreshFinished()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_list_frame::OnRepaintFinished()
|
|
||||||
{
|
|
||||||
if (!m_is_list_layout)
|
|
||||||
{
|
|
||||||
// The game grid needs to be recreated from scratch
|
|
||||||
int games_per_row = 0;
|
|
||||||
|
|
||||||
if (m_icon_size.width() > 0 && m_icon_size.height() > 0)
|
|
||||||
{
|
|
||||||
games_per_row = width() / (m_icon_size.width() + m_icon_size.width() * m_game_grid->getMarginFactor() * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
const int scroll_position = m_game_grid->verticalScrollBar()->value();
|
|
||||||
PopulateGameGrid(games_per_row, m_icon_size, m_icon_color);
|
|
||||||
connect(m_game_grid, &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu);
|
|
||||||
connect(m_game_grid, &QTableWidget::itemSelectionChanged, this, &game_list_frame::ItemSelectionChangedSlot);
|
|
||||||
connect(m_game_grid, &QTableWidget::itemDoubleClicked, this, &game_list_frame::doubleClickedSlot);
|
|
||||||
m_central_widget->addWidget(m_game_grid);
|
|
||||||
m_central_widget->setCurrentWidget(m_game_grid);
|
|
||||||
m_game_grid->verticalScrollBar()->setValue(scroll_position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void game_list_frame::OnCompatFinished()
|
void game_list_frame::OnCompatFinished()
|
||||||
{
|
{
|
||||||
for (const auto& game : m_game_data)
|
for (const auto& game : m_game_data)
|
||||||
|
@ -2409,7 +2385,7 @@ void game_list_frame::RepaintIcons(const bool& from_settings)
|
||||||
QPixmap placeholder(m_icon_size);
|
QPixmap placeholder(m_icon_size);
|
||||||
placeholder.fill(Qt::transparent);
|
placeholder.fill(Qt::transparent);
|
||||||
|
|
||||||
for (auto& game : m_game_data)
|
for (game_info& game : m_game_data)
|
||||||
{
|
{
|
||||||
game->pxmap = placeholder;
|
game->pxmap = placeholder;
|
||||||
|
|
||||||
|
@ -2417,46 +2393,7 @@ void game_list_frame::RepaintIcons(const bool& from_settings)
|
||||||
{
|
{
|
||||||
item->set_icon_load_func([this, game, cancel = item->icon_loading_aborted()]()
|
item->set_icon_load_func([this, game, cancel = item->icon_loading_aborted()]()
|
||||||
{
|
{
|
||||||
if (cancel && cancel->load())
|
IconLoadFunction(game, cancel);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::unordered_set<std::string> warn_once_list;
|
|
||||||
static shared_mutex s_mtx;
|
|
||||||
|
|
||||||
if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(qstr(game->info.icon_path))))
|
|
||||||
{
|
|
||||||
if (game_list_log.warning)
|
|
||||||
{
|
|
||||||
bool logged = false;
|
|
||||||
{
|
|
||||||
std::lock_guard lock(s_mtx);
|
|
||||||
logged = !warn_once_list.emplace(game->info.icon_path).second;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!logged)
|
|
||||||
{
|
|
||||||
game_list_log.warning("Could not load image from path %s", sstr(QDir(qstr(game->info.icon_path)).absolutePath()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!game->item || (cancel && cancel->load()))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QColor color = getGridCompatibilityColor(game->compat.color);
|
|
||||||
{
|
|
||||||
std::lock_guard lock(game->item->pixmap_mutex);
|
|
||||||
game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, game->hasCustomPadConfig, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cancel || !cancel->load())
|
|
||||||
{
|
|
||||||
Q_EMIT IconReady(game->item);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
item->call_icon_func();
|
item->call_icon_func();
|
||||||
}
|
}
|
||||||
|
@ -2471,37 +2408,26 @@ void game_list_frame::RepaintIcons(const bool& from_settings)
|
||||||
|
|
||||||
// Shorten the last section to remove horizontal scrollbar if possible
|
// Shorten the last section to remove horizontal scrollbar if possible
|
||||||
m_game_list->resizeColumnToContents(gui::column_count - 1);
|
m_game_list->resizeColumnToContents(gui::column_count - 1);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
const std::function func = [this](const game_info& game) -> movie_item*
|
|
||||||
{
|
{
|
||||||
static std::unordered_set<std::string> warn_once_list;
|
// The game grid needs to be recreated from scratch
|
||||||
static shared_mutex s_mtx;
|
int games_per_row = 0;
|
||||||
|
|
||||||
if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(qstr(game->info.icon_path))))
|
if (m_icon_size.width() > 0 && m_icon_size.height() > 0)
|
||||||
{
|
{
|
||||||
if (game_list_log.warning)
|
games_per_row = width() / (m_icon_size.width() + m_icon_size.width() * m_game_grid->getMarginFactor() * 2);
|
||||||
{
|
|
||||||
bool logged = false;
|
|
||||||
{
|
|
||||||
std::lock_guard lock(s_mtx);
|
|
||||||
logged = !warn_once_list.emplace(game->info.icon_path).second;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!logged)
|
|
||||||
{
|
|
||||||
game_list_log.warning("Could not load image from path %s", sstr(QDir(qstr(game->info.icon_path)).absolutePath()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QColor color = getGridCompatibilityColor(game->compat.color);
|
const int scroll_position = m_game_grid->verticalScrollBar()->value();
|
||||||
game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, game->hasCustomPadConfig, color);
|
PopulateGameGrid(games_per_row, m_icon_size, m_icon_color);
|
||||||
return game->item;
|
connect(m_game_grid, &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu);
|
||||||
};
|
connect(m_game_grid, &QTableWidget::itemSelectionChanged, this, &game_list_frame::ItemSelectionChangedSlot);
|
||||||
m_repaint_watcher.setFuture(QtConcurrent::mapped(m_game_data, func));
|
connect(m_game_grid, &QTableWidget::itemDoubleClicked, this, &game_list_frame::doubleClickedSlot);
|
||||||
|
m_central_widget->addWidget(m_game_grid);
|
||||||
|
m_central_widget->setCurrentWidget(m_game_grid);
|
||||||
|
m_game_grid->verticalScrollBar()->setValue(scroll_position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_list_frame::SetShowHidden(bool show)
|
void game_list_frame::SetShowHidden(bool show)
|
||||||
|
@ -2647,7 +2573,10 @@ void game_list_frame::PopulateGameList()
|
||||||
|
|
||||||
icon_item->set_icon_func([this, icon_item, game](int)
|
icon_item->set_icon_func([this, icon_item, game](int)
|
||||||
{
|
{
|
||||||
ensure(icon_item && game);
|
if (!icon_item || !game)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (std::shared_ptr<QMovie> movie = icon_item->movie(); movie && icon_item->get_active())
|
if (std::shared_ptr<QMovie> movie = icon_item->movie(); movie && icon_item->get_active())
|
||||||
{
|
{
|
||||||
|
@ -2821,7 +2750,7 @@ void game_list_frame::PopulateGameGrid(int maxCols, const QSize& image_size, con
|
||||||
|
|
||||||
const QString game_icon_path = m_play_hover_movies ? qstr(fs::get_config_dir() + "/Icons/game_icons/") : "";
|
const QString game_icon_path = m_play_hover_movies ? qstr(fs::get_config_dir() + "/Icons/game_icons/") : "";
|
||||||
|
|
||||||
for (const auto& app : matching_apps)
|
for (const game_info& app : matching_apps)
|
||||||
{
|
{
|
||||||
const QString serial = qstr(app->info.serial);
|
const QString serial = qstr(app->info.serial);
|
||||||
const QString title = m_titles.value(serial, qstr(app->info.name));
|
const QString title = m_titles.value(serial, qstr(app->info.name));
|
||||||
|
@ -2831,6 +2760,10 @@ void game_list_frame::PopulateGameGrid(int maxCols, const QSize& image_size, con
|
||||||
ensure(item);
|
ensure(item);
|
||||||
app->item = item;
|
app->item = item;
|
||||||
item->setData(gui::game_role, QVariant::fromValue(app));
|
item->setData(gui::game_role, QVariant::fromValue(app));
|
||||||
|
item->set_icon_load_func([this, app, cancel = item->icon_loading_aborted()]()
|
||||||
|
{
|
||||||
|
IconLoadFunction(app, cancel);
|
||||||
|
});
|
||||||
|
|
||||||
if (!notes.isEmpty())
|
if (!notes.isEmpty())
|
||||||
{
|
{
|
||||||
|
@ -3032,10 +2965,52 @@ std::string game_list_frame::GetGameVersion(const game_info& game)
|
||||||
return game->info.app_ver;
|
return game->info.app_ver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game_list_frame::IconLoadFunction(game_info game, std::shared_ptr<atomic_t<bool>> cancel)
|
||||||
|
{
|
||||||
|
if (cancel && cancel->load())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unordered_set<std::string> warn_once_list;
|
||||||
|
static shared_mutex s_mtx;
|
||||||
|
|
||||||
|
if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(qstr(game->info.icon_path))))
|
||||||
|
{
|
||||||
|
if (game_list_log.warning)
|
||||||
|
{
|
||||||
|
bool logged = false;
|
||||||
|
{
|
||||||
|
std::lock_guard lock(s_mtx);
|
||||||
|
logged = !warn_once_list.emplace(game->info.icon_path).second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!logged)
|
||||||
|
{
|
||||||
|
game_list_log.warning("Could not load image from path %s", sstr(QDir(qstr(game->info.icon_path)).absolutePath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!game->item || (cancel && cancel->load()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QColor color = getGridCompatibilityColor(game->compat.color);
|
||||||
|
{
|
||||||
|
std::lock_guard lock(game->item->pixmap_mutex);
|
||||||
|
game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, game->hasCustomPadConfig, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cancel || !cancel->load())
|
||||||
|
{
|
||||||
|
Q_EMIT IconReady(game->item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void game_list_frame::WaitAndAbortRepaintThreads()
|
void game_list_frame::WaitAndAbortRepaintThreads()
|
||||||
{
|
{
|
||||||
gui::utils::stop_future_watcher(m_repaint_watcher, true);
|
|
||||||
|
|
||||||
for (const game_info& game : m_game_data)
|
for (const game_info& game : m_game_data)
|
||||||
{
|
{
|
||||||
if (game && game->item)
|
if (game && game->item)
|
||||||
|
|
|
@ -80,7 +80,6 @@ public Q_SLOTS:
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void OnRefreshFinished();
|
void OnRefreshFinished();
|
||||||
void OnRepaintFinished();
|
|
||||||
void OnCompatFinished();
|
void OnCompatFinished();
|
||||||
void OnColClicked(int col);
|
void OnColClicked(int col);
|
||||||
void ShowContextMenu(const QPoint &pos);
|
void ShowContextMenu(const QPoint &pos);
|
||||||
|
@ -102,6 +101,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
QPixmap PaintedPixmap(const QPixmap& icon, bool paint_config_icon = false, bool paint_pad_config_icon = false, const QColor& color = QColor()) const;
|
QPixmap PaintedPixmap(const QPixmap& icon, bool paint_config_icon = false, bool paint_pad_config_icon = false, const QColor& color = QColor()) const;
|
||||||
QColor getGridCompatibilityColor(const QString& string) const;
|
QColor getGridCompatibilityColor(const QString& string) const;
|
||||||
|
void IconLoadFunction(game_info game, std::shared_ptr<atomic_t<bool>> cancel);
|
||||||
|
|
||||||
/** Sets the custom config icon. Only call this for list title items. */
|
/** Sets the custom config icon. Only call this for list title items. */
|
||||||
void SetCustomConfigIcon(QTableWidgetItem* title_item, const game_info& game);
|
void SetCustomConfigIcon(QTableWidgetItem* title_item, const game_info& game);
|
||||||
|
@ -174,7 +174,6 @@ private:
|
||||||
QMutex m_games_mutex;
|
QMutex m_games_mutex;
|
||||||
lf_queue<game_info> m_games;
|
lf_queue<game_info> m_games;
|
||||||
QFutureWatcher<void> m_refresh_watcher;
|
QFutureWatcher<void> m_refresh_watcher;
|
||||||
QFutureWatcher<movie_item*> m_repaint_watcher;
|
|
||||||
QSet<QString> m_hidden_list;
|
QSet<QString> m_hidden_list;
|
||||||
bool m_show_hidden{false};
|
bool m_show_hidden{false};
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,10 @@ movie_item* game_list_grid::addItem(const game_info& app, const QString& name, c
|
||||||
|
|
||||||
item->set_icon_func([this, app, item](int)
|
item->set_icon_func([this, app, item](int)
|
||||||
{
|
{
|
||||||
ensure(item);
|
if (!item)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const qreal device_pixel_ratio = devicePixelRatioF();
|
const qreal device_pixel_ratio = devicePixelRatioF();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
#include "game_list_grid_delegate.h"
|
#include "game_list_grid_delegate.h"
|
||||||
|
#include "movie_item.h"
|
||||||
|
|
||||||
|
#include <QTableWidget>
|
||||||
|
|
||||||
game_list_grid_delegate::game_list_grid_delegate(const QSize& size, const qreal& margin_factor, const qreal& text_factor, QObject *parent)
|
game_list_grid_delegate::game_list_grid_delegate(const QSize& size, const qreal& margin_factor, const qreal& text_factor, QObject *parent)
|
||||||
: QStyledItemDelegate(parent), m_size(size), m_margin_factor(margin_factor), m_text_factor(text_factor)
|
: QStyledItemDelegate(parent), m_size(size), m_margin_factor(margin_factor), m_text_factor(text_factor)
|
||||||
|
@ -23,13 +26,31 @@ void game_list_grid_delegate::paint(QPainter* painter, const QStyleOptionViewIte
|
||||||
painter->setRenderHints(QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
|
painter->setRenderHints(QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
|
||||||
painter->eraseRect(r);
|
painter->eraseRect(r);
|
||||||
|
|
||||||
|
// Paint from our stylesheet
|
||||||
|
QStyledItemDelegate::paint(painter, option, index);
|
||||||
|
|
||||||
|
// Check if the item is visible
|
||||||
|
if (const QTableWidget* table = static_cast<const QTableWidget*>(parent()))
|
||||||
|
{
|
||||||
|
if (movie_item* item = static_cast<movie_item*>(table->item(index.row(), index.column())))
|
||||||
|
{
|
||||||
|
if (!table->visibleRegion().intersects(table->visualItemRect(item)))
|
||||||
|
{
|
||||||
|
// Skip all further actions if the item is not visible
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item->icon_loading())
|
||||||
|
{
|
||||||
|
item->call_icon_load_func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get title and image
|
// Get title and image
|
||||||
const QPixmap image = qvariant_cast<QPixmap>(index.data(Qt::DecorationRole));
|
const QPixmap image = qvariant_cast<QPixmap>(index.data(Qt::DecorationRole));
|
||||||
const QString title = index.data(Qt::DisplayRole).toString();
|
const QString title = index.data(Qt::DisplayRole).toString();
|
||||||
|
|
||||||
// Paint from our stylesheet
|
|
||||||
QStyledItemDelegate::paint(painter, option, index);
|
|
||||||
|
|
||||||
// image
|
// image
|
||||||
if (image.isNull() == false)
|
if (image.isNull() == false)
|
||||||
{
|
{
|
||||||
|
|
|
@ -129,7 +129,7 @@ void movie_item::set_size_calc_func(const size_calc_callback_t& func)
|
||||||
|
|
||||||
void movie_item::wait_for_icon_loading(bool abort)
|
void movie_item::wait_for_icon_loading(bool abort)
|
||||||
{
|
{
|
||||||
if (m_icon_load_thread)
|
if (m_icon_load_thread && m_icon_load_thread->isRunning())
|
||||||
{
|
{
|
||||||
*m_icon_loading_aborted = abort;
|
*m_icon_loading_aborted = abort;
|
||||||
m_icon_load_thread->wait();
|
m_icon_load_thread->wait();
|
||||||
|
@ -139,7 +139,7 @@ void movie_item::wait_for_icon_loading(bool abort)
|
||||||
|
|
||||||
void movie_item::wait_for_size_on_disk_loading(bool abort)
|
void movie_item::wait_for_size_on_disk_loading(bool abort)
|
||||||
{
|
{
|
||||||
if (m_size_calc_thread)
|
if (m_size_calc_thread && m_size_calc_thread->isRunning())
|
||||||
{
|
{
|
||||||
*m_size_on_disk_loading_aborted = abort;
|
*m_size_on_disk_loading_aborted = abort;
|
||||||
m_size_calc_thread->wait();
|
m_size_calc_thread->wait();
|
||||||
|
|
|
@ -61,12 +61,12 @@ public:
|
||||||
return m_size_on_disk_loading;
|
return m_size_on_disk_loading;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<atomic_t<bool>> icon_loading_aborted() const
|
[[nodiscard]] std::shared_ptr<atomic_t<bool>> icon_loading_aborted() const
|
||||||
{
|
{
|
||||||
return m_icon_loading_aborted;
|
return m_icon_loading_aborted;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<atomic_t<bool>> size_on_disk_loading_aborted() const
|
[[nodiscard]] std::shared_ptr<atomic_t<bool>> size_on_disk_loading_aborted() const
|
||||||
{
|
{
|
||||||
return m_size_on_disk_loading_aborted;
|
return m_size_on_disk_loading_aborted;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue