mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 18:53:28 +01:00
Qt: multithreaded trophy icon refresh
This commit is contained in:
parent
4e6b37ca04
commit
20331a77ce
@ -204,12 +204,12 @@ bool TROPUSRLoader::Generate(const std::string& filepath, const std::string& con
|
||||
return Save(filepath);
|
||||
}
|
||||
|
||||
u32 TROPUSRLoader::GetTrophiesCount()
|
||||
u32 TROPUSRLoader::GetTrophiesCount() const
|
||||
{
|
||||
return ::size32(m_table6);
|
||||
}
|
||||
|
||||
u32 TROPUSRLoader::GetUnlockedTrophiesCount()
|
||||
u32 TROPUSRLoader::GetUnlockedTrophiesCount() const
|
||||
{
|
||||
u32 count = 0;
|
||||
for (const auto& trophy : m_table6)
|
||||
@ -296,7 +296,7 @@ u32 TROPUSRLoader::GetUnlockedPlatinumID(u32 trophy_id, const std::string& confi
|
||||
return pid;
|
||||
}
|
||||
|
||||
u32 TROPUSRLoader::GetTrophyGrade(u32 id)
|
||||
u32 TROPUSRLoader::GetTrophyGrade(u32 id) const
|
||||
{
|
||||
if (id >= m_table4.size())
|
||||
{
|
||||
@ -307,7 +307,7 @@ u32 TROPUSRLoader::GetTrophyGrade(u32 id)
|
||||
return m_table4[id].trophy_grade; // Let's assume the trophies are stored ordered
|
||||
}
|
||||
|
||||
u32 TROPUSRLoader::GetTrophyUnlockState(u32 id)
|
||||
u32 TROPUSRLoader::GetTrophyUnlockState(u32 id) const
|
||||
{
|
||||
if (id >= m_table6.size())
|
||||
{
|
||||
@ -318,7 +318,7 @@ u32 TROPUSRLoader::GetTrophyUnlockState(u32 id)
|
||||
return m_table6[id].trophy_state; // Let's assume the trophies are stored ordered
|
||||
}
|
||||
|
||||
u64 TROPUSRLoader::GetTrophyTimestamp(u32 id)
|
||||
u64 TROPUSRLoader::GetTrophyTimestamp(u32 id) const
|
||||
{
|
||||
if (id >= m_table6.size())
|
||||
{
|
||||
|
@ -89,14 +89,14 @@ public:
|
||||
virtual load_result Load(const std::string& filepath, const std::string& configpath);
|
||||
virtual bool Save(const std::string& filepath);
|
||||
|
||||
virtual u32 GetTrophiesCount();
|
||||
virtual u32 GetUnlockedTrophiesCount();
|
||||
virtual u32 GetTrophiesCount() const;
|
||||
virtual u32 GetUnlockedTrophiesCount() const;
|
||||
|
||||
virtual u32 GetUnlockedPlatinumID(u32 trophy_id, const std::string& config_path);
|
||||
|
||||
virtual u32 GetTrophyGrade(u32 id);
|
||||
virtual u32 GetTrophyUnlockState(u32 id);
|
||||
virtual u64 GetTrophyTimestamp(u32 id);
|
||||
virtual u32 GetTrophyGrade(u32 id) const;
|
||||
virtual u32 GetTrophyUnlockState(u32 id) const;
|
||||
virtual u64 GetTrophyTimestamp(u32 id) const;
|
||||
|
||||
virtual bool UnlockTrophy(u32 id, u64 timestamp1, u64 timestamp2);
|
||||
};
|
||||
|
@ -111,6 +111,7 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
|
||||
m_trophy_table->setHorizontalHeaderLabels(QStringList{ tr("Icon"), tr("Name"), tr("Description"), tr("Type"), tr("Status"), tr("ID"), tr("Platinum Relevant") });
|
||||
m_trophy_table->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
|
||||
m_trophy_table->horizontalHeader()->setStretchLastSection(true);
|
||||
m_trophy_table->horizontalHeader()->setSectionResizeMode(TrophyColumns::Icon, QHeaderView::Fixed);
|
||||
m_trophy_table->verticalHeader()->setVisible(false);
|
||||
m_trophy_table->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
||||
m_trophy_table->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
@ -338,6 +339,24 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
|
||||
m_game_combo->setCurrentText(item->text());
|
||||
});
|
||||
|
||||
connect(&m_trophy_repaint_watcher, &QFutureWatcher<QPixmap>::finished, this, &trophy_manager_dialog::ReadjustTrophyTable);
|
||||
connect(&m_trophy_repaint_watcher, &QFutureWatcher<QPixmap>::resultReadyAt, this, [this](int index)
|
||||
{
|
||||
if (QTableWidgetItem* icon_item = m_trophy_table->item(index, TrophyColumns::Icon))
|
||||
{
|
||||
icon_item->setData(Qt::DecorationRole, m_trophy_repaint_watcher.resultAt(index));
|
||||
}
|
||||
});
|
||||
|
||||
connect(&m_game_repaint_watcher, &QFutureWatcher<QPixmap>::finished, this, &trophy_manager_dialog::ReadjustGameTable);
|
||||
connect(&m_game_repaint_watcher, &QFutureWatcher<QPixmap>::resultReadyAt, this, [this](int index)
|
||||
{
|
||||
if (QTableWidgetItem* icon_item = m_game_table->item(index, GameColumns::GameIcon))
|
||||
{
|
||||
icon_item->setData(Qt::DecorationRole, m_game_repaint_watcher.resultAt(index));
|
||||
}
|
||||
});
|
||||
|
||||
RepaintUI(true);
|
||||
|
||||
StartTrophyLoadThreads();
|
||||
@ -486,12 +505,33 @@ QPixmap trophy_manager_dialog::GetResizedGameIcon(int index) const
|
||||
QTableWidgetItem* item = m_game_table->item(index, GameColumns::GameIcon);
|
||||
if (!item)
|
||||
{
|
||||
return QPixmap();
|
||||
QPixmap placeholder(m_game_icon_size);
|
||||
placeholder.fill(Qt::transparent);
|
||||
return placeholder;
|
||||
}
|
||||
const QPixmap icon = item->data(Qt::UserRole).value<QPixmap>();
|
||||
|
||||
QPixmap icon;
|
||||
|
||||
if (!item->data(GameUserRole::GamePixmapLoaded).toBool())
|
||||
{
|
||||
// Load game icon
|
||||
const int trophy_index = item->data(GameUserRole::GameIndex).toInt();
|
||||
const std::string icon_path = m_trophies_db[trophy_index]->path + "ICON0.PNG";
|
||||
if (!icon.load(qstr(icon_path)))
|
||||
{
|
||||
gui_log.warning("Could not load trophy game icon from path %s", icon_path);
|
||||
}
|
||||
item->setData(GameUserRole::GamePixmapLoaded, true);
|
||||
item->setData(GameUserRole::GamePixmap, icon);
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = item->data(GameUserRole::GamePixmap).value<QPixmap>();
|
||||
}
|
||||
|
||||
const qreal dpr = devicePixelRatioF();
|
||||
|
||||
QPixmap new_icon = QPixmap(icon.size() * dpr);
|
||||
QPixmap new_icon(icon.size() * dpr);
|
||||
new_icon.setDevicePixelRatio(dpr);
|
||||
new_icon.fill(m_game_icon_color);
|
||||
|
||||
@ -511,26 +551,34 @@ void trophy_manager_dialog::ResizeGameIcons()
|
||||
if (m_game_combo->count() <= 0)
|
||||
return;
|
||||
|
||||
if (m_game_repaint_watcher.isRunning())
|
||||
{
|
||||
m_game_repaint_watcher.cancel();
|
||||
m_game_repaint_watcher.waitForFinished();
|
||||
}
|
||||
|
||||
QPixmap placeholder(m_game_icon_size);
|
||||
placeholder.fill(Qt::transparent);
|
||||
|
||||
QList<int> indices;
|
||||
for (int i = 0; i < m_game_table->rowCount(); ++i)
|
||||
{
|
||||
indices.append(i);
|
||||
|
||||
if (QTableWidgetItem* icon_item = m_game_table->item(i, GameColumns::GameIcon))
|
||||
{
|
||||
icon_item->setData(Qt::DecorationRole, placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
const std::function<QPixmap(const int&)> get_scaled = [this](const int& i)
|
||||
{
|
||||
return GetResizedGameIcon(i);
|
||||
};
|
||||
|
||||
// NOTE: Due to a Qt bug in Qt 5.15.2, QtConcurrent::blockingMapped has a high risk of deadlocking. So let's just use QtConcurrent::mapped.
|
||||
const QList<QPixmap> scaled = QtConcurrent::mapped(indices, get_scaled).results();
|
||||
|
||||
for (int i = 0; i < m_game_table->rowCount() && i < scaled.count(); ++i)
|
||||
{
|
||||
QTableWidgetItem* icon_item = m_game_table->item(i, TrophyColumns::Icon);
|
||||
if (icon_item)
|
||||
icon_item->setData(Qt::DecorationRole, scaled[i]);
|
||||
}
|
||||
|
||||
ReadjustGameTable();
|
||||
|
||||
m_game_repaint_watcher.setFuture(QtConcurrent::mapped(indices, get_scaled));
|
||||
}
|
||||
|
||||
void trophy_manager_dialog::ResizeTrophyIcons()
|
||||
@ -538,10 +586,19 @@ void trophy_manager_dialog::ResizeTrophyIcons()
|
||||
if (m_game_combo->count() <= 0)
|
||||
return;
|
||||
|
||||
if (m_trophy_repaint_watcher.isRunning())
|
||||
{
|
||||
m_trophy_repaint_watcher.cancel();
|
||||
m_trophy_repaint_watcher.waitForFinished();
|
||||
}
|
||||
|
||||
const int db_pos = m_game_combo->currentData().toInt();
|
||||
const qreal dpr = devicePixelRatioF();
|
||||
const int new_height = m_icon_height * dpr;
|
||||
|
||||
QPixmap placeholder(m_icon_height, m_icon_height);
|
||||
placeholder.fill(Qt::transparent);
|
||||
|
||||
QList<int> trophy_ids;
|
||||
for (int i = 0; i < m_trophy_table->rowCount(); ++i)
|
||||
{
|
||||
@ -549,11 +606,20 @@ void trophy_manager_dialog::ResizeTrophyIcons()
|
||||
{
|
||||
trophy_ids.append(item->text().toInt());
|
||||
}
|
||||
|
||||
if (QTableWidgetItem* icon_item = m_trophy_table->item(i, TrophyColumns::Icon))
|
||||
{
|
||||
icon_item->setData(Qt::DecorationRole, placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
const std::function<QPixmap(const int&)> get_scaled = [this, data = m_trophies_db.at(db_pos).get(), dpr, new_height](const int& trophy_id)
|
||||
ReadjustTrophyTable();
|
||||
|
||||
const std::function<QPixmap(const int&)> get_scaled = [this, data = m_trophies_db.at(db_pos).get(), dpr, new_height](const int& trophy_id) -> QPixmap
|
||||
{
|
||||
QPixmap icon;
|
||||
|
||||
if (data)
|
||||
{
|
||||
std::scoped_lock lock(m_trophies_db_mtx);
|
||||
if (data->trophy_images.contains(trophy_id))
|
||||
@ -570,7 +636,7 @@ void trophy_manager_dialog::ResizeTrophyIcons()
|
||||
}
|
||||
}
|
||||
|
||||
QPixmap new_icon = QPixmap(icon.size() * dpr);
|
||||
QPixmap new_icon(icon.size() * dpr);
|
||||
new_icon.setDevicePixelRatio(dpr);
|
||||
new_icon.fill(m_game_icon_color);
|
||||
|
||||
@ -585,18 +651,7 @@ void trophy_manager_dialog::ResizeTrophyIcons()
|
||||
return new_icon.scaledToHeight(new_height, Qt::SmoothTransformation);
|
||||
};
|
||||
|
||||
|
||||
// NOTE: Due to a Qt bug in Qt 5.15.2, QtConcurrent::blockingMapped has a high risk of deadlocking. So let's just use QtConcurrent::mapped.
|
||||
const QList<QPixmap> scaled = QtConcurrent::mapped(trophy_ids, get_scaled).results();
|
||||
|
||||
for (int i = 0; i < m_trophy_table->rowCount() && i < scaled.count(); ++i)
|
||||
{
|
||||
QTableWidgetItem* icon_item = m_trophy_table->item(i, TrophyColumns::Icon);
|
||||
if (icon_item)
|
||||
icon_item->setData(Qt::DecorationRole, scaled.at(i));
|
||||
}
|
||||
|
||||
ReadjustTrophyTable();
|
||||
m_trophy_repaint_watcher.setFuture(QtConcurrent::mapped(trophy_ids, get_scaled));
|
||||
}
|
||||
|
||||
void trophy_manager_dialog::ApplyFilter()
|
||||
@ -605,10 +660,10 @@ void trophy_manager_dialog::ApplyFilter()
|
||||
return;
|
||||
|
||||
const int db_pos = m_game_combo->currentData().toInt();
|
||||
if (db_pos + 0u >= m_trophies_db.size() || !m_trophies_db[db_pos])
|
||||
if (db_pos < 0 || static_cast<usz>(db_pos) >= m_trophies_db.size() || !m_trophies_db[db_pos])
|
||||
return;
|
||||
|
||||
const auto trop_usr = m_trophies_db[db_pos]->trop_usr.get();
|
||||
const TROPUSRLoader* trop_usr = m_trophies_db[db_pos]->trop_usr.get();
|
||||
if (!trop_usr)
|
||||
return;
|
||||
|
||||
@ -679,6 +734,16 @@ void trophy_manager_dialog::ShowContextMenu(const QPoint& pos)
|
||||
|
||||
void trophy_manager_dialog::StartTrophyLoadThreads()
|
||||
{
|
||||
if (m_game_repaint_watcher.isRunning())
|
||||
{
|
||||
m_game_repaint_watcher.waitForFinished();
|
||||
}
|
||||
|
||||
if (m_trophy_repaint_watcher.isRunning())
|
||||
{
|
||||
m_trophy_repaint_watcher.waitForFinished();
|
||||
}
|
||||
|
||||
m_trophies_db.clear();
|
||||
|
||||
const QString trophy_path = qstr(vfs::get(m_trophy_dir));
|
||||
@ -738,6 +803,12 @@ void trophy_manager_dialog::StartTrophyLoadThreads()
|
||||
|
||||
void trophy_manager_dialog::PopulateGameTable()
|
||||
{
|
||||
if (m_game_repaint_watcher.isRunning())
|
||||
{
|
||||
m_game_repaint_watcher.cancel();
|
||||
m_game_repaint_watcher.waitForFinished();
|
||||
}
|
||||
|
||||
m_game_table->setSortingEnabled(false); // Disable sorting before using setItem calls
|
||||
|
||||
m_game_table->clearContents();
|
||||
@ -749,20 +820,8 @@ void trophy_manager_dialog::PopulateGameTable()
|
||||
for (usz i = 0; i < m_trophies_db.size(); ++i)
|
||||
indices.append(static_cast<int>(i));
|
||||
|
||||
const std::function<QPixmap(const int&)> get_icon = [this](const int& i)
|
||||
{
|
||||
// Load game icon
|
||||
QPixmap icon;
|
||||
const std::string icon_path = m_trophies_db[i]->path + "ICON0.PNG";
|
||||
if (!icon.load(qstr(icon_path)))
|
||||
{
|
||||
gui_log.warning("Could not load trophy game icon from path %s", icon_path);
|
||||
}
|
||||
return icon;
|
||||
};
|
||||
|
||||
// NOTE: Due to a Qt bug in Qt 5.15.2, QtConcurrent::blockingMapped has a high risk of deadlocking. So let's just use QtConcurrent::mapped.
|
||||
const QList<QPixmap> icons = QtConcurrent::mapped(indices, get_icon).results();
|
||||
QPixmap placeholder(m_game_icon_size);
|
||||
placeholder.fill(Qt::transparent);
|
||||
|
||||
for (int i = 0; i < indices.count(); ++i)
|
||||
{
|
||||
@ -773,8 +832,10 @@ void trophy_manager_dialog::PopulateGameTable()
|
||||
const QString name = qstr(m_trophies_db[i]->game_name).simplified();
|
||||
|
||||
custom_table_widget_item* icon_item = new custom_table_widget_item;
|
||||
if (icons.count() > i)
|
||||
icon_item->setData(Qt::UserRole, icons[i]);
|
||||
icon_item->setData(Qt::DecorationRole, placeholder);
|
||||
icon_item->setData(GameUserRole::GameIndex, i);
|
||||
icon_item->setData(GameUserRole::GamePixmapLoaded, false);
|
||||
icon_item->setData(GameUserRole::GamePixmap, QPixmap());
|
||||
|
||||
m_game_table->setItem(i, GameColumns::GameIcon, icon_item);
|
||||
m_game_table->setItem(i, GameColumns::GameName, new custom_table_widget_item(name));
|
||||
@ -785,10 +846,10 @@ void trophy_manager_dialog::PopulateGameTable()
|
||||
|
||||
m_game_combo->model()->sort(0, Qt::AscendingOrder);
|
||||
|
||||
ResizeGameIcons();
|
||||
|
||||
m_game_table->setSortingEnabled(true); // Enable sorting only after using setItem calls
|
||||
|
||||
ResizeGameIcons();
|
||||
|
||||
gui::utils::resize_combo_box_view(m_game_combo);
|
||||
}
|
||||
|
||||
@ -821,6 +882,9 @@ void trophy_manager_dialog::PopulateTrophyTable()
|
||||
return;
|
||||
}
|
||||
|
||||
QPixmap placeholder(m_icon_height, m_icon_height);
|
||||
placeholder.fill(Qt::transparent);
|
||||
|
||||
int i = 0;
|
||||
for (std::shared_ptr<rXmlNode> n = trophy_base->GetChildren(); n; n = n->GetNext())
|
||||
{
|
||||
@ -876,6 +940,7 @@ void trophy_manager_dialog::PopulateTrophyTable()
|
||||
|
||||
custom_table_widget_item* icon_item = new custom_table_widget_item();
|
||||
icon_item->setData(Qt::UserRole, hidden, true);
|
||||
icon_item->setData(Qt::DecorationRole, placeholder);
|
||||
|
||||
custom_table_widget_item* type_item = new custom_table_widget_item(trophy_type);
|
||||
type_item->setData(Qt::UserRole, static_cast<uint>(details.trophyGrade), true);
|
||||
@ -920,7 +985,6 @@ void trophy_manager_dialog::ReadjustTrophyTable() const
|
||||
|
||||
// Resize and fixate icon column
|
||||
m_trophy_table->resizeColumnToContents(TrophyColumns::Icon);
|
||||
m_trophy_table->horizontalHeader()->setSectionResizeMode(TrophyColumns::Icon, QHeaderView::Fixed);
|
||||
|
||||
// Shorten the last section to remove horizontal scrollbar if possible
|
||||
m_trophy_table->resizeColumnToContents(TrophyColumns::Count - 1);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QFutureWatcher>
|
||||
#include <QLabel>
|
||||
#include <QPixmap>
|
||||
#include <QTableWidget>
|
||||
@ -50,6 +51,13 @@ enum GameColumns
|
||||
GameColumnsCount
|
||||
};
|
||||
|
||||
enum GameUserRole
|
||||
{
|
||||
GameIndex = Qt::UserRole,
|
||||
GamePixmapLoaded,
|
||||
GamePixmap
|
||||
};
|
||||
|
||||
class trophy_manager_dialog : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -103,6 +111,8 @@ private:
|
||||
QSplitter* m_splitter; //! Contains the game and trophy tables
|
||||
game_list* m_trophy_table; //! UI element to display trophy stuff.
|
||||
QTableWidget* m_game_table; //! UI element to display games.
|
||||
QFutureWatcher<QPixmap> m_game_repaint_watcher;
|
||||
QFutureWatcher<QPixmap> m_trophy_repaint_watcher;
|
||||
|
||||
bool m_show_hidden_trophies = false;
|
||||
bool m_show_unlocked_trophies = true;
|
||||
|
Loading…
Reference in New Issue
Block a user