Merge branch 'master' into rsx_capture

This commit is contained in:
Megamouse 2025-12-01 14:49:52 +01:00 committed by GitHub
commit a02c503536
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 460 additions and 140 deletions

View file

@ -8,11 +8,12 @@ Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Bu
### Windows 10 or later ### Windows 10 or later
The following tools are required to build RPCS3 on Windows 10 or later: The following tools are required to build RPCS3 on Windows 10 or later:
- [Visual Studio 2022](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community) - [Visual Studio 2022/2026](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community)
- **Optional** - [CMake 3.28.0+](https://www.cmake.org/download/) (add to PATH) - **Optional** - [CMake 3.28.0+](https://www.cmake.org/download/) (add to PATH)
**NOTES:** **NOTES:**
- **Visual Studio 2022** integrates **CMake 3.29+** and it also supports both the `sln` solution (`.sln`, `.vcxproj`) and `CMake` solution (`CMakeLists.txt`, `CMakePresets.json`). - **Visual Studio 2026** needs at least **CMake 4.2.0+**.
- **Visual Studio 2022/2026** integrates **CMake 3.29+** and it also supports both the `sln` solution (`.sln`, `.vcxproj`) and `CMake` solution (`CMakeLists.txt`, `CMakePresets.json`).
See sections [Building with Visual Studio sln solution](#building-with-visual-studio-sln-solution) and [Building with Visual Studio CMake solution](#building-with-visual-studio-cmake-solution) See sections [Building with Visual Studio sln solution](#building-with-visual-studio-sln-solution) and [Building with Visual Studio CMake solution](#building-with-visual-studio-cmake-solution)
on how to build the project with **Visual Studio**. on how to build the project with **Visual Studio**.
- Install and use this standalone **CMake** tool just in case of your preference. See section [Building with standalone CMake tool](#building-with-standalone-cmake-tool) on how to build the project - Install and use this standalone **CMake** tool just in case of your preference. See section [Building with standalone CMake tool](#building-with-standalone-cmake-tool) on how to build the project

View file

@ -720,9 +720,19 @@ void spu_cache::initialize(bool build_existing_cache)
} }
// SPU cache file (version + block size type) // SPU cache file (version + block size type)
const std::string loc = ppu_cache + "spu-" + fmt::to_lower(g_cfg.core.spu_block_size.to_string()) + "-v1-tane.dat"; const std::string filename = "spu-" + fmt::to_lower(g_cfg.core.spu_block_size.to_string()) + "-v1-tane.dat";
const std::string loc = ppu_cache + filename;
const std::string loc_debug = fs::get_cache_dir() + "DEBUG/" + filename;
spu_cache cache(loc); bool is_debug = false;
if (fs::is_file(loc_debug))
{
spu_log.success("SPU Cache override applied!");
is_debug = true;
}
spu_cache cache(is_debug ? loc_debug : loc);
if (!cache) if (!cache)
{ {
@ -4963,6 +4973,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
u32 lsa_last_pc = SPU_LS_SIZE; // PC of first LSA write u32 lsa_last_pc = SPU_LS_SIZE; // PC of first LSA write
u32 get_pc = SPU_LS_SIZE; // PC of GETLLAR u32 get_pc = SPU_LS_SIZE; // PC of GETLLAR
u32 put_pc = SPU_LS_SIZE; // PC of PUTLLC u32 put_pc = SPU_LS_SIZE; // PC of PUTLLC
u32 rdatomic_pc = SPU_LS_SIZE; // PC of last RdAtomcStat read
reg_state_t ls{}; // state of LS load/store address register reg_state_t ls{}; // state of LS load/store address register
reg_state_t ls_offs = reg_state_t::from_value(0); // Added value to ls reg_state_t ls_offs = reg_state_t::from_value(0); // Added value to ls
reg_state_t lsa{}; // state of LSA register on GETLLAR reg_state_t lsa{}; // state of LSA register on GETLLAR
@ -5008,7 +5019,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
ls_invalid = true; ls_invalid = true;
ls_write |= write; ls_write |= write;
if (write) if (ls_write)
{ {
return discard(); return discard();
} }
@ -6323,6 +6334,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
break; break;
} }
atomic16->rdatomic_pc = pos;
const auto it = atomic16_all.find(pos); const auto it = atomic16_all.find(pos);
if (it == atomic16_all.end()) if (it == atomic16_all.end())
@ -7263,7 +7276,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
for (const auto& [pc_commited, pattern] : atomic16_all) for (const auto& [pc_commited, pattern] : atomic16_all)
{ {
if (!pattern.active) if (!pattern.active || pattern.lsa_pc >= pattern.rdatomic_pc)
{ {
continue; continue;
} }
@ -7273,6 +7286,17 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
continue; continue;
} }
std::string pattern_hash;
{
sha1_context ctx;
u8 output[20]{};
sha1_starts(&ctx);
sha1_update(&ctx, reinterpret_cast<const u8*>(result.data.data()) + (pattern.lsa_pc - result.lower_bound), pattern.rdatomic_pc - pattern.lsa_pc);
sha1_finish(&ctx, output);
fmt::append(pattern_hash, "%s", fmt::base57(output));
}
union putllc16_or_0_info union putllc16_or_0_info
{ {
u64 data; u64 data;
@ -7295,8 +7319,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
value.required_pc = pattern.required_pc; value.required_pc = pattern.required_pc;
} }
spu_log.success("PUTLLC0 Pattern Detected! (put_pc=0x%x, %s) (putllc0=%d, putllc16+0=%d, all=%d)", pattern.put_pc, func_hash, ++stats.nowrite, ++stats.single, +stats.all); // spu_log.success("PUTLLC0 Pattern Detected! (put_pc=0x%x, %s) (putllc0=%d, putllc16+0=%d, all=%d)", pattern.put_pc, func_hash, ++stats.nowrite, ++stats.single, +stats.all);
add_pattern(false, inst_attr::putllc0, pattern.put_pc - lsa, value.data); // add_pattern(false, inst_attr::putllc0, pattern.put_pc - lsa, value.data);
continue; continue;
} }
@ -7363,16 +7387,35 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
value.reg2 = pattern.reg2; value.reg2 = pattern.reg2;
} }
bool allow_pattern = true;
if (g_cfg.core.spu_accurate_reservations) if (g_cfg.core.spu_accurate_reservations)
{ {
// Because enabling it is a hack, as it turns out // The problem with PUTLLC16 optimization, that it is in theory correct at the bounds of the spu function.
// continue; // But if the SPU code reuses the cache line data observed, it is not truly atomic.
// So we may enable it only for known cases where SPU atomic data is not used after the function leaves.
// So the two options are:
// 1. Atomic compare exchange 16 bytes operation. (rest of data is not read) -> good for RPCS3 to optimize.
// 2. Fetch 128 bytes (read them later), modify only 16 bytes. -> Bad for RPCS3 to optimize.
// This difference cannot be known at analyzer time but from observing callers.
static constexpr std::initializer_list<std::string_view> allowed_patterns =
{
"620oYSe8uQqq9eTkhWfMqoEXX0us"sv, // CellSpurs JobChain acquire pattern
};
allow_pattern = std::any_of(allowed_patterns.begin(), allowed_patterns.end(), FN(pattern_hash == x));
} }
if (allow_pattern)
{
add_pattern(false, inst_attr::putllc16, pattern.put_pc - result.entry_point, value.data); add_pattern(false, inst_attr::putllc16, pattern.put_pc - result.entry_point, value.data);
}
spu_log.success("PUTLLC16 Pattern Detected! (mem_count=%d, put_pc=0x%x, pc_rel=%d, offset=0x%x, const=%u, two_regs=%d, reg=%u, runtime=%d, 0x%x-%s) (putllc0=%d, putllc16+0=%d, all=%d)" spu_log.success("PUTLLC16 Pattern Detected! (mem_count=%d, put_pc=0x%x, pc_rel=%d, offset=0x%x, const=%u, two_regs=%d, reg=%u, runtime=%d, 0x%x-%s, pattern-hash=%s) (putllc0=%d, putllc16+0=%d, all=%d)"
, pattern.mem_count, pattern.put_pc, value.type == v_relative, value.off18, value.type == v_const, value.type == v_reg2, value.reg, value.runtime16_select, entry_point, func_hash, +stats.nowrite, ++stats.single, +stats.all); , pattern.mem_count, pattern.put_pc, value.type == v_relative, value.off18, value.type == v_const, value.type == v_reg2, value.reg, value.runtime16_select, entry_point, func_hash, pattern_hash, +stats.nowrite, ++stats.single, +stats.all);
} }
for (const auto& [read_pc, pattern] : rchcnt_loop_all) for (const auto& [read_pc, pattern] : rchcnt_loop_all)

View file

@ -668,10 +668,9 @@ void emu_settings::EnhanceSpinBox(QSpinBox* spinbox, emu_settings_type type, con
spinbox->setRange(min, max); spinbox->setRange(min, max);
spinbox->setValue(val); spinbox->setValue(val);
connect(spinbox, &QSpinBox::textChanged, this, [type, spinbox, this](const QString& /* text*/) connect(spinbox, &QSpinBox::valueChanged, this, [type, this](int value)
{ {
if (!spinbox) return; SetSetting(type, fmt::format("%d", value));
SetSetting(type, spinbox->cleanText().toStdString());
}); });
connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]() connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]()
@ -724,10 +723,9 @@ void emu_settings::EnhanceDoubleSpinBox(QDoubleSpinBox* spinbox, emu_settings_ty
spinbox->setRange(min, max); spinbox->setRange(min, max);
spinbox->setValue(val); spinbox->setValue(val);
connect(spinbox, &QDoubleSpinBox::textChanged, this, [type, spinbox, this](const QString& /* text*/) connect(spinbox, &QDoubleSpinBox::valueChanged, this, [type, this](double value)
{ {
if (!spinbox) return; SetSetting(type, fmt::format("%f", value));
SetSetting(type, spinbox->cleanText().toStdString());
}); });
connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]() connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]()

View file

@ -14,16 +14,16 @@ game_list::game_list() : QTableWidget(), game_list_base()
}; };
} }
void game_list::sync_header_actions(QList<QAction*>& actions, std::function<bool(int)> get_visibility) void game_list::sync_header_actions(std::map<int, QAction*>& actions, std::function<bool(int)> get_visibility)
{ {
ensure(get_visibility); ensure(get_visibility);
bool is_dirty = false; bool is_dirty = false;
for (int col = 0; col < actions.count(); ++col) for (auto& [col, action] : actions)
{ {
const bool is_hidden = !get_visibility(col); const bool is_hidden = !get_visibility(col);
actions[col]->setChecked(!is_hidden); action->setChecked(!is_hidden);
if (isColumnHidden(col) != is_hidden) if (isColumnHidden(col) != is_hidden)
{ {
@ -38,7 +38,7 @@ void game_list::sync_header_actions(QList<QAction*>& actions, std::function<bool
} }
} }
void game_list::create_header_actions(QList<QAction*>& actions, std::function<bool(int)> get_visibility, std::function<void(int, bool)> set_visibility) void game_list::create_header_actions(std::map<int, QAction*>& actions, std::function<bool(int)> get_visibility, std::function<void(int, bool)> set_visibility)
{ {
ensure(get_visibility); ensure(get_visibility);
ensure(set_visibility); ensure(set_visibility);
@ -48,27 +48,30 @@ void game_list::create_header_actions(QList<QAction*>& actions, std::function<bo
connect(horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this, &actions](const QPoint& pos) connect(horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this, &actions](const QPoint& pos)
{ {
QMenu* configure = new QMenu(this); QMenu* configure = new QMenu(this);
configure->addActions(actions); for (auto& [col, action] : actions)
{
configure->addAction(action);
}
configure->exec(horizontalHeader()->viewport()->mapToGlobal(pos)); configure->exec(horizontalHeader()->viewport()->mapToGlobal(pos));
}); });
for (int col = 0; col < actions.count(); ++col) for (auto& [col, action] : actions)
{ {
actions[col]->setCheckable(true); action->setCheckable(true);
connect(actions[col], &QAction::triggered, this, [this, &actions, get_visibility, set_visibility, col](bool checked) connect(action, &QAction::triggered, this, [this, &actions, get_visibility, set_visibility, col](bool checked)
{ {
if (!checked) // be sure to have at least one column left so you can call the context menu at all time if (!checked) // be sure to have at least one column left so you can call the context menu at all time
{ {
int c = 0; int c = 0;
for (int i = 0; i < actions.count(); ++i) for (auto& [col, action] : actions)
{ {
if (get_visibility(i) && ++c > 1) if (get_visibility(col) && ++c > 1)
break; break;
} }
if (c < 2) if (c < 2)
{ {
actions[col]->setChecked(true); // re-enable the checkbox if we don't change the actual state ::at32(actions, col)->setChecked(true); // re-enable the checkbox if we don't change the actual state
return; return;
} }
} }

View file

@ -23,8 +23,8 @@ class game_list : public QTableWidget, public game_list_base
public: public:
game_list(); game_list();
void sync_header_actions(QList<QAction*>& actions, std::function<bool(int)> get_visibility); void sync_header_actions(std::map<int, QAction*>& actions, std::function<bool(int)> get_visibility);
void create_header_actions(QList<QAction*>& actions, std::function<bool(int)> get_visibility, std::function<void(int, bool)> set_visibility); void create_header_actions(std::map<int, QAction*>& actions, std::function<bool(int)> get_visibility, std::function<void(int, bool)> set_visibility);
void clear_list() override; // Use this instead of clearContents void clear_list() override; // Use this instead of clearContents

View file

@ -100,27 +100,28 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
m_game_dock->setCentralWidget(m_central_widget); m_game_dock->setCentralWidget(m_central_widget);
// Actions regarding showing/hiding columns // Actions regarding showing/hiding columns
auto add_column = [this](gui::game_list_columns col, const QString& header_text, const QString& action_text) const auto add_column = [this](gui::game_list_columns col)
{ {
m_game_list->setHorizontalHeaderItem(static_cast<int>(col), new QTableWidgetItem(header_text)); const int column = static_cast<int>(col);
m_columnActs.append(new QAction(action_text, this)); m_game_list->setHorizontalHeaderItem(column, new QTableWidgetItem(get_header_text(column)));
m_column_acts[column] = new QAction(get_action_text(column), this);
}; };
add_column(gui::game_list_columns::icon, tr("Icon"), tr("Show Icons")); add_column(gui::game_list_columns::icon);
add_column(gui::game_list_columns::name, tr("Name"), tr("Show Names")); add_column(gui::game_list_columns::name);
add_column(gui::game_list_columns::serial, tr("Serial"), tr("Show Serials")); add_column(gui::game_list_columns::serial);
add_column(gui::game_list_columns::firmware, tr("Firmware"), tr("Show Firmwares")); add_column(gui::game_list_columns::firmware);
add_column(gui::game_list_columns::version, tr("Version"), tr("Show Versions")); add_column(gui::game_list_columns::version);
add_column(gui::game_list_columns::category, tr("Category"), tr("Show Categories")); add_column(gui::game_list_columns::category);
add_column(gui::game_list_columns::path, tr("Path"), tr("Show Paths")); add_column(gui::game_list_columns::path);
add_column(gui::game_list_columns::move, tr("PlayStation Move"), tr("Show PlayStation Move")); add_column(gui::game_list_columns::move);
add_column(gui::game_list_columns::resolution, tr("Supported Resolutions"), tr("Show Supported Resolutions")); add_column(gui::game_list_columns::resolution);
add_column(gui::game_list_columns::sound, tr("Sound Formats"), tr("Show Sound Formats")); add_column(gui::game_list_columns::sound);
add_column(gui::game_list_columns::parental, tr("Parental Level"), tr("Show Parental Levels")); add_column(gui::game_list_columns::parental);
add_column(gui::game_list_columns::last_play, tr("Last Played"), tr("Show Last Played")); add_column(gui::game_list_columns::last_play);
add_column(gui::game_list_columns::playtime, tr("Time Played"), tr("Show Time Played")); add_column(gui::game_list_columns::playtime);
add_column(gui::game_list_columns::compat, tr("Compatibility"), tr("Show Compatibility")); add_column(gui::game_list_columns::compat);
add_column(gui::game_list_columns::dir_size, tr("Space On Disk"), tr("Show Space On Disk")); add_column(gui::game_list_columns::dir_size);
m_progress_dialog = new progress_dialog(tr("Loading games"), tr("Loading games, please wait..."), tr("Cancel"), 0, 0, false, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); m_progress_dialog = new progress_dialog(tr("Loading games"), tr("Loading games, please wait..."), tr("Cancel"), 0, 0, false, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
m_progress_dialog->setMinimumDuration(200); // Only show the progress dialog after some time has passed m_progress_dialog->setMinimumDuration(200); // Only show the progress dialog after some time has passed
@ -211,7 +212,7 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
connect(m_game_list, &game_list::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar); connect(m_game_list, &game_list::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar);
connect(m_game_grid, &game_list_grid::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar); connect(m_game_grid, &game_list_grid::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar);
m_game_list->create_header_actions(m_columnActs, m_game_list->create_header_actions(m_column_acts,
[this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast<gui::game_list_columns>(col)); }, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast<gui::game_list_columns>(col)); },
[this](int col, bool visible) { m_gui_settings->SetGamelistColVisibility(static_cast<gui::game_list_columns>(col), visible); }); [this](int col, bool visible) { m_gui_settings->SetGamelistColVisibility(static_cast<gui::game_list_columns>(col), visible); });
} }
@ -227,7 +228,7 @@ void game_list_frame::LoadSettings()
m_show_custom_icons = m_gui_settings->GetValue(gui::gl_custom_icon).toBool(); m_show_custom_icons = m_gui_settings->GetValue(gui::gl_custom_icon).toBool();
m_play_hover_movies = m_gui_settings->GetValue(gui::gl_hover_gifs).toBool(); m_play_hover_movies = m_gui_settings->GetValue(gui::gl_hover_gifs).toBool();
m_game_list->sync_header_actions(m_columnActs, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast<gui::game_list_columns>(col)); }); m_game_list->sync_header_actions(m_column_acts, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast<gui::game_list_columns>(col)); });
} }
game_list_frame::~game_list_frame() game_list_frame::~game_list_frame()
@ -238,6 +239,54 @@ game_list_frame::~game_list_frame()
gui::utils::stop_future_watcher(m_refresh_watcher, true); gui::utils::stop_future_watcher(m_refresh_watcher, true);
} }
QString game_list_frame::get_header_text(int col) const
{
switch (static_cast<gui::game_list_columns>(col))
{
case gui::game_list_columns::icon: return tr("Icon");
case gui::game_list_columns::name: return tr("Name");
case gui::game_list_columns::serial: return tr("Serial");
case gui::game_list_columns::firmware: return tr("Firmware");
case gui::game_list_columns::version: return tr("Version");
case gui::game_list_columns::category: return tr("Category");
case gui::game_list_columns::path: return tr("Path");
case gui::game_list_columns::move: return tr("PlayStation Move");
case gui::game_list_columns::resolution: return tr("Supported Resolutions");
case gui::game_list_columns::sound: return tr("Sound Formats");
case gui::game_list_columns::parental: return tr("Parental Level");
case gui::game_list_columns::last_play: return tr("Last Played");
case gui::game_list_columns::playtime: return tr("Time Played");
case gui::game_list_columns::compat: return tr("Compatibility");
case gui::game_list_columns::dir_size: return tr("Space On Disk");
case gui::game_list_columns::count: break;
}
return {};
}
QString game_list_frame::get_action_text(int col) const
{
switch (static_cast<gui::game_list_columns>(col))
{
case gui::game_list_columns::icon: return tr("Show Icons");
case gui::game_list_columns::name: return tr("Show Names");
case gui::game_list_columns::serial: return tr("Show Serials");
case gui::game_list_columns::firmware: return tr("Show Firmwares");
case gui::game_list_columns::version: return tr("Show Versions");
case gui::game_list_columns::category: return tr("Show Categories");
case gui::game_list_columns::path: return tr("Show Paths");
case gui::game_list_columns::move: return tr("Show PlayStation Move");
case gui::game_list_columns::resolution: return tr("Show Supported Resolutions");
case gui::game_list_columns::sound: return tr("Show Sound Formats");
case gui::game_list_columns::parental: return tr("Show Parental Levels");
case gui::game_list_columns::last_play: return tr("Show Last Played");
case gui::game_list_columns::playtime: return tr("Show Time Played");
case gui::game_list_columns::compat: return tr("Show Compatibility");
case gui::game_list_columns::dir_size: return tr("Show Space On Disk");
case gui::game_list_columns::count: break;
}
return {};
}
void game_list_frame::OnColClicked(int col) void game_list_frame::OnColClicked(int col)
{ {
if (col == static_cast<int>(gui::game_list_columns::icon)) return; // Don't "sort" icons. if (col == static_cast<int>(gui::game_list_columns::icon)) return; // Don't "sort" icons.
@ -417,6 +466,21 @@ void game_list_frame::Refresh(const bool from_drive, const std::vector<std::stri
m_progress_dialog->SetValue(0); m_progress_dialog->SetValue(0);
} }
// Update headers
for (int col = 0; col < m_game_list->horizontalHeader()->count(); col++)
{
if (auto item = m_game_list->horizontalHeaderItem(col))
{
item->setText(get_header_text(col));
}
}
// Update actions
for (auto& [col, action] : m_column_acts)
{
action->setText(get_action_text(col));
}
const std::string games_dir = rpcs3::utils::get_games_dir(); const std::string games_dir = rpcs3::utils::get_games_dir();
// Remove the specified and detected serials (title id) belonging to "games_dir" folder only from the game list in memory // Remove the specified and detected serials (title id) belonging to "games_dir" folder only from the game list in memory
@ -920,7 +984,7 @@ void game_list_frame::OnRefreshFinished()
if (!std::exchange(m_initial_refresh_done, true)) if (!std::exchange(m_initial_refresh_done, true))
{ {
m_game_list->restore_layout(m_gui_settings->GetValue(gui::gl_state).toByteArray()); m_game_list->restore_layout(m_gui_settings->GetValue(gui::gl_state).toByteArray());
m_game_list->sync_header_actions(m_columnActs, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast<gui::game_list_columns>(col)); }); m_game_list->sync_header_actions(m_column_acts, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast<gui::game_list_columns>(col)); });
} }
// Emit signal and remove slots // Emit signal and remove slots
@ -959,9 +1023,9 @@ void game_list_frame::ToggleCategoryFilter(const QStringList& categories, bool s
void game_list_frame::SaveSettings() void game_list_frame::SaveSettings()
{ {
for (int col = 0; col < m_columnActs.count(); ++col) for (const auto& [col, action] : m_column_acts)
{ {
m_gui_settings->SetGamelistColVisibility(static_cast<gui::game_list_columns>(col), m_columnActs[col]->isChecked()); m_gui_settings->SetGamelistColVisibility(static_cast<gui::game_list_columns>(col), action->isChecked());
} }
m_gui_settings->SetValue(gui::gl_sortCol, m_sort_column, false); m_gui_settings->SetValue(gui::gl_sortCol, m_sort_column, false);
m_gui_settings->SetValue(gui::gl_sortAsc, m_col_sort_order == Qt::AscendingOrder, false); m_gui_settings->SetValue(gui::gl_sortAsc, m_col_sort_order == Qt::AscendingOrder, false);
@ -2704,7 +2768,7 @@ void game_list_frame::ShowCustomConfigIcon(const game_info& game)
RepaintIcons(); RepaintIcons();
} }
void game_list_frame::ResizeIcons(const int& slider_pos) void game_list_frame::ResizeIcons(int slider_pos)
{ {
m_icon_size_index = slider_pos; m_icon_size_index = slider_pos;
m_icon_size = gui_settings::SizeFromSlider(slider_pos); m_icon_size = gui_settings::SizeFromSlider(slider_pos);
@ -2712,7 +2776,7 @@ void game_list_frame::ResizeIcons(const int& slider_pos)
RepaintIcons(); RepaintIcons();
} }
void game_list_frame::RepaintIcons(const bool& from_settings) void game_list_frame::RepaintIcons(bool from_settings)
{ {
gui::utils::stop_future_watcher(m_parsing_watcher, false); gui::utils::stop_future_watcher(m_parsing_watcher, false);
gui::utils::stop_future_watcher(m_refresh_watcher, false); gui::utils::stop_future_watcher(m_refresh_watcher, false);
@ -2746,7 +2810,7 @@ void game_list_frame::SetShowHidden(bool show)
m_show_hidden = show; m_show_hidden = show;
} }
void game_list_frame::SetListMode(const bool& is_list) void game_list_frame::SetListMode(bool is_list)
{ {
m_old_layout_is_list = m_is_list_layout; m_old_layout_is_list = m_is_list_layout;
m_is_list_layout = is_list; m_is_list_layout = is_list;

View file

@ -48,10 +48,10 @@ public:
void SaveSettings(); void SaveSettings();
/** Resize Gamelist Icons to size given by slider position */ /** Resize Gamelist Icons to size given by slider position */
void ResizeIcons(const int& slider_pos); void ResizeIcons(int slider_pos);
/** Repaint Gamelist Icons with new background color */ /** Repaint Gamelist Icons with new background color */
void RepaintIcons(const bool& from_settings = false); void RepaintIcons(bool from_settings = false);
void SetShowHidden(bool show); void SetShowHidden(bool show);
@ -70,7 +70,7 @@ public Q_SLOTS:
void BatchRemoveCustomConfigurations(); void BatchRemoveCustomConfigurations();
void BatchRemoveCustomPadConfigurations(); void BatchRemoveCustomPadConfigurations();
void BatchRemoveShaderCaches(); void BatchRemoveShaderCaches();
void SetListMode(const bool& is_list); void SetListMode(bool is_list);
void SetSearchText(const QString& text); void SetSearchText(const QString& text);
void SetShowCompatibilityInGrid(bool show); void SetShowCompatibilityInGrid(bool show);
void SetPreferGameDataIcons(bool enabled); void SetPreferGameDataIcons(bool enabled);
@ -91,7 +91,7 @@ Q_SIGNALS:
void GameListFrameClosed(); void GameListFrameClosed();
void NotifyGameSelection(const game_info& game); void NotifyGameSelection(const game_info& game);
void RequestBoot(const game_info& game, cfg_mode config_mode = cfg_mode::custom, const std::string& config_path = "", const std::string& savestate = ""); void RequestBoot(const game_info& game, cfg_mode config_mode = cfg_mode::custom, const std::string& config_path = "", const std::string& savestate = "");
void RequestIconSizeChange(const int& val); void RequestIconSizeChange(int val);
void NotifyEmuSettingsChange(); void NotifyEmuSettingsChange();
void FocusToSearchBar(); void FocusToSearchBar();
void Refreshed(); void Refreshed();
@ -127,6 +127,9 @@ protected:
private: private:
void push_path(const std::string& path, std::vector<std::string>& legit_paths); void push_path(const std::string& path, std::vector<std::string>& legit_paths);
QString get_header_text(int col) const;
QString get_action_text(int col) const;
void ShowCustomConfigIcon(const game_info& game); void ShowCustomConfigIcon(const game_info& game);
bool SearchMatchesApp(const QString& name, const QString& serial, bool fallback = false) const; bool SearchMatchesApp(const QString& name, const QString& serial, bool fallback = false) const;
@ -165,7 +168,7 @@ private:
game_list_table* m_game_list = nullptr; game_list_table* m_game_list = nullptr;
game_compatibility* m_game_compat = nullptr; game_compatibility* m_game_compat = nullptr;
progress_dialog* m_progress_dialog = nullptr; progress_dialog* m_progress_dialog = nullptr;
QList<QAction*> m_columnActs; std::map<int, QAction*> m_column_acts;
Qt::SortOrder m_col_sort_order{}; Qt::SortOrder m_col_sort_order{};
int m_sort_column{}; int m_sort_column{};
bool m_initial_refresh_done = false; bool m_initial_refresh_done = false;

View file

@ -2344,6 +2344,8 @@ void main_window::RetranslateUI(const QStringList& language_codes, const QString
{ {
m_game_list_frame->Refresh(true); m_game_list_frame->Refresh(true);
} }
Q_EMIT RequestDialogRepaint();
} }
void main_window::ShowTitleBars(bool show) const void main_window::ShowTitleBars(bool show) const
@ -3343,7 +3345,7 @@ void main_window::CreateConnects()
connect(ui->showCustomIconsAct, &QAction::triggered, m_game_list_frame, &game_list_frame::SetShowCustomIcons); connect(ui->showCustomIconsAct, &QAction::triggered, m_game_list_frame, &game_list_frame::SetShowCustomIcons);
connect(ui->playHoverGifsAct, &QAction::triggered, m_game_list_frame, &game_list_frame::SetPlayHoverGifs); connect(ui->playHoverGifsAct, &QAction::triggered, m_game_list_frame, &game_list_frame::SetPlayHoverGifs);
connect(m_game_list_frame, &game_list_frame::RequestIconSizeChange, this, [this](const int& val) connect(m_game_list_frame, &game_list_frame::RequestIconSizeChange, this, [this](int val)
{ {
const int idx = ui->sizeSlider->value() + val; const int idx = ui->sizeSlider->value() + val;
m_save_slider_pos = true; m_save_slider_pos = true;

View file

@ -432,9 +432,9 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, std::shared_ptr<CPUDis
scroll(0); // Refresh scroll(0); // Refresh
}); });
connect(sb_words, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [=, this]() connect(sb_words, &QSpinBox::valueChanged, this, [this](int value)
{ {
m_colcount = 1 << sb_words->value(); m_colcount = 1 << value;
ShowMemory(); ShowMemory();
}); });

View file

@ -105,7 +105,7 @@ pad_motion_settings_dialog::pad_motion_settings_dialog(QDialog* parent, std::sha
m_config_entries[i]->mirrored.set(state != Qt::Unchecked); m_config_entries[i]->mirrored.set(state != Qt::Unchecked);
}); });
connect(m_shifts[i], QOverload<int>::of(&QSpinBox::valueChanged), this, [this, i](int value) connect(m_shifts[i], &QSpinBox::valueChanged, this, [this, i](int value)
{ {
std::lock_guard lock(m_config_mutex); std::lock_guard lock(m_config_mutex);
m_config_entries[i]->shift.set(value); m_config_entries[i]->shift.set(value);

View file

@ -123,8 +123,8 @@ patch_manager_dialog::patch_manager_dialog(std::shared_ptr<gui_settings> gui_set
handle_config_value_changed(ui->configurable_combo_box->itemData(index).toDouble()); handle_config_value_changed(ui->configurable_combo_box->itemData(index).toDouble());
} }
}); });
connect(ui->configurable_spin_box, QOverload<int>::of(&QSpinBox::valueChanged), this, &patch_manager_dialog::handle_config_value_changed); connect(ui->configurable_spin_box, &QSpinBox::valueChanged, this, &patch_manager_dialog::handle_config_value_changed);
connect(ui->configurable_double_spin_box, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &patch_manager_dialog::handle_config_value_changed); connect(ui->configurable_double_spin_box, &QDoubleSpinBox::valueChanged, this, &patch_manager_dialog::handle_config_value_changed);
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close);
connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button)
{ {

View file

@ -281,7 +281,7 @@ void raw_mouse_settings_dialog::add_tabs(QTabWidget* tabs)
m_accel_spin_boxes.push_back(mouse_acceleration_spin_box); m_accel_spin_boxes.push_back(mouse_acceleration_spin_box);
mouse_acceleration_spin_box->setRange(0.1, 10.0); mouse_acceleration_spin_box->setRange(0.1, 10.0);
mouse_acceleration_spin_box->setValue(config->mouse_acceleration.get() / 100.0); mouse_acceleration_spin_box->setValue(config->mouse_acceleration.get() / 100.0);
connect(mouse_acceleration_spin_box, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, [player](double value) connect(mouse_acceleration_spin_box, &QDoubleSpinBox::valueChanged, this, [player](double value)
{ {
auto& config = ::at32(g_cfg_raw_mouse.players, player)->mouse_acceleration; auto& config = ::at32(g_cfg_raw_mouse.players, player)->mouse_acceleration;
config.set(std::clamp(value * 100.0, config.min, config.max)); config.set(std::clamp(value * 100.0, config.min, config.max));

View file

@ -32,7 +32,7 @@
LOG_CHANNEL(gui_log, "GUI"); LOG_CHANNEL(gui_log, "GUI");
enum SaveColumns enum class SaveColumns
{ {
Icon = 0, Icon = 0,
Name = 1, Name = 1,
@ -60,30 +60,26 @@ save_manager_dialog::save_manager_dialog(std::shared_ptr<gui_settings> gui_setti
setMinimumSize(QSize(400, 400)); setMinimumSize(QSize(400, 400));
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
Init();
}
/*
* Future proofing. Makes it easier in future if I add ability to change directories
*/
void save_manager_dialog::Init()
{
// Table // Table
m_list = new game_list(); m_list = new game_list();
m_list->setItemDelegate(new game_list_delegate(m_list)); m_list->setItemDelegate(new game_list_delegate(m_list));
m_list->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection); m_list->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
m_list->setSelectionBehavior(QAbstractItemView::SelectRows); m_list->setSelectionBehavior(QAbstractItemView::SelectRows);
m_list->setContextMenuPolicy(Qt::CustomContextMenu); m_list->setContextMenuPolicy(Qt::CustomContextMenu);
m_list->setColumnCount(SaveColumns::Count); m_list->setColumnCount(static_cast<int>(SaveColumns::Count));
m_list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); m_list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
m_list->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); m_list->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
m_list->verticalScrollBar()->setSingleStep(20); m_list->verticalScrollBar()->setSingleStep(20);
m_list->horizontalScrollBar()->setSingleStep(10); m_list->horizontalScrollBar()->setSingleStep(10);
m_list->setHorizontalHeaderLabels(QStringList() << tr("Icon") << tr("Title & Subtitle") << tr("Last Modified") << tr("Save ID") << tr("Notes"));
m_list->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed); m_list->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
m_list->horizontalHeader()->setStretchLastSection(true); m_list->horizontalHeader()->setStretchLastSection(true);
m_list->setMouseTracking(true); m_list->setMouseTracking(true);
for (int column = 0; column < static_cast<int>(SaveColumns::Count); column++)
{
m_list->setHorizontalHeaderItem(column, new QTableWidgetItem(get_header_text(column)));
}
// Bottom bar // Bottom bar
const int icon_size = m_gui_settings->GetValue(gui::sd_icon_size).toInt(); const int icon_size = m_gui_settings->GetValue(gui::sd_icon_size).toInt();
m_icon_size = QSize(icon_size, icon_size * 176 / 320); m_icon_size = QSize(icon_size, icon_size * 176 / 320);
@ -162,7 +158,7 @@ void save_manager_dialog::Init()
connect(m_button_folder, &QAbstractButton::clicked, [this]() connect(m_button_folder, &QAbstractButton::clicked, [this]()
{ {
const int idx = m_list->currentRow(); const int idx = m_list->currentRow();
QTableWidgetItem* item = m_list->item(idx, SaveColumns::Name); QTableWidgetItem* item = m_list->item(idx, static_cast<int>(SaveColumns::Name));
if (!item) if (!item)
{ {
return; return;
@ -176,12 +172,12 @@ void save_manager_dialog::Init()
connect(m_list, &QTableWidget::customContextMenuRequested, this, &save_manager_dialog::ShowContextMenu); connect(m_list, &QTableWidget::customContextMenuRequested, this, &save_manager_dialog::ShowContextMenu);
connect(m_list, &QTableWidget::cellChanged, [&](int row, int col) connect(m_list, &QTableWidget::cellChanged, [&](int row, int col)
{ {
if (col != SaveColumns::Note) if (col != static_cast<int>(SaveColumns::Note))
{ {
return; return;
} }
QTableWidgetItem* user_item = m_list->item(row, SaveColumns::Name); QTableWidgetItem* user_item = m_list->item(row, static_cast<int>(SaveColumns::Name));
QTableWidgetItem* text_item = m_list->item(row, SaveColumns::Note); QTableWidgetItem* text_item = m_list->item(row, static_cast<int>(SaveColumns::Note));
if (!user_item || !text_item) if (!user_item || !text_item)
{ {
return; return;
@ -196,7 +192,7 @@ void save_manager_dialog::Init()
connect(m_list, &QTableWidget::itemSelectionChanged, this, &save_manager_dialog::UpdateDetails); connect(m_list, &QTableWidget::itemSelectionChanged, this, &save_manager_dialog::UpdateDetails);
connect(this, &save_manager_dialog::IconReady, this, [this](int index, const QPixmap& pixmap) connect(this, &save_manager_dialog::IconReady, this, [this](int index, const QPixmap& pixmap)
{ {
if (movie_item* item = static_cast<movie_item*>(m_list->item(index, SaveColumns::Icon))) if (movie_item* item = static_cast<movie_item*>(m_list->item(index, static_cast<int>(SaveColumns::Icon))))
{ {
item->setData(SaveUserRole::PixmapScaled, pixmap); item->setData(SaveUserRole::PixmapScaled, pixmap);
item->image_change_callback(); item->image_change_callback();
@ -205,6 +201,20 @@ void save_manager_dialog::Init()
connect(search_bar, &QLineEdit::textChanged, this, &save_manager_dialog::text_changed); connect(search_bar, &QLineEdit::textChanged, this, &save_manager_dialog::text_changed);
} }
QString save_manager_dialog::get_header_text(int col) const
{
switch (static_cast<SaveColumns>(col))
{
case SaveColumns::Icon: return tr("Icon");
case SaveColumns::Name: return tr("Title & Subtitle");
case SaveColumns::Time: return tr("Last Modified");
case SaveColumns::Dir: return tr("Save ID");
case SaveColumns::Note: return tr("Notes");
case SaveColumns::Count: break;
}
return {};
}
/** /**
* This certainly isn't ideal for this code, as it essentially copies cellSaveData. But, I have no other choice without adding public methods to cellSaveData. * This certainly isn't ideal for this code, as it essentially copies cellSaveData. But, I have no other choice without adding public methods to cellSaveData.
*/ */
@ -300,6 +310,15 @@ void save_manager_dialog::UpdateList()
m_list->clearContents(); m_list->clearContents();
m_list->setRowCount(static_cast<int>(m_save_entries.size())); m_list->setRowCount(static_cast<int>(m_save_entries.size()));
// Update headers
for (int col = 0; col < m_list->horizontalHeader()->count(); col++)
{
if (auto item = m_list->horizontalHeaderItem(col))
{
item->setText(get_header_text(col));
}
}
const QVariantMap notes = m_persistent_settings->GetValue(gui::persistent::save_notes).toMap(); const QVariantMap notes = m_persistent_settings->GetValue(gui::persistent::save_notes).toMap();
if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool()) if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool())
@ -358,20 +377,20 @@ void save_manager_dialog::UpdateList()
icon_item->stop_movie(); icon_item->stop_movie();
} }
}); });
m_list->setItem(i, SaveColumns::Icon, icon_item); m_list->setItem(i, static_cast<int>(SaveColumns::Icon), icon_item);
custom_table_widget_item* titleItem = new custom_table_widget_item(title); custom_table_widget_item* titleItem = new custom_table_widget_item(title);
titleItem->setData(Qt::UserRole, i); // For sorting to work properly titleItem->setData(Qt::UserRole, i); // For sorting to work properly
titleItem->setFlags(titleItem->flags() & ~Qt::ItemIsEditable); titleItem->setFlags(titleItem->flags() & ~Qt::ItemIsEditable);
m_list->setItem(i, SaveColumns::Name, titleItem); m_list->setItem(i, static_cast<int>(SaveColumns::Name), titleItem);
custom_table_widget_item* timeItem = new custom_table_widget_item(gui::utils::format_timestamp(entry.mtime)); custom_table_widget_item* timeItem = new custom_table_widget_item(gui::utils::format_timestamp(entry.mtime));
timeItem->setFlags(timeItem->flags() & ~Qt::ItemIsEditable); timeItem->setFlags(timeItem->flags() & ~Qt::ItemIsEditable);
m_list->setItem(i, SaveColumns::Time, timeItem); m_list->setItem(i, static_cast<int>(SaveColumns::Time), timeItem);
custom_table_widget_item* dirNameItem = new custom_table_widget_item(dir_name); custom_table_widget_item* dirNameItem = new custom_table_widget_item(dir_name);
dirNameItem->setFlags(dirNameItem->flags() & ~Qt::ItemIsEditable); dirNameItem->setFlags(dirNameItem->flags() & ~Qt::ItemIsEditable);
m_list->setItem(i, SaveColumns::Dir, dirNameItem); m_list->setItem(i, static_cast<int>(SaveColumns::Dir), dirNameItem);
custom_table_widget_item* noteItem = new custom_table_widget_item(); custom_table_widget_item* noteItem = new custom_table_widget_item();
noteItem->setFlags(noteItem->flags() | Qt::ItemIsEditable); noteItem->setFlags(noteItem->flags() | Qt::ItemIsEditable);
@ -379,7 +398,7 @@ void save_manager_dialog::UpdateList()
{ {
noteItem->setText(notes[dir_name].toString()); noteItem->setText(notes[dir_name].toString());
} }
m_list->setItem(i, SaveColumns::Note, noteItem); m_list->setItem(i, static_cast<int>(SaveColumns::Note), noteItem);
} }
m_list->setSortingEnabled(true); // Enable sorting only after using setItem calls m_list->setSortingEnabled(true); // Enable sorting only after using setItem calls
@ -422,7 +441,7 @@ void save_manager_dialog::UpdateIcons()
for (int i = 0; i < m_list->rowCount(); ++i) for (int i = 0; i < m_list->rowCount(); ++i)
{ {
if (movie_item* icon_item = static_cast<movie_item*>(m_list->item(i, SaveColumns::Icon))) if (movie_item* icon_item = static_cast<movie_item*>(m_list->item(i, static_cast<int>(SaveColumns::Icon))))
{ {
icon_item->setData(SaveUserRole::PixmapScaled, placeholder); icon_item->setData(SaveUserRole::PixmapScaled, placeholder);
icon_item->setData(Qt::DecorationRole, placeholder); icon_item->setData(Qt::DecorationRole, placeholder);
@ -430,14 +449,14 @@ void save_manager_dialog::UpdateIcons()
} }
m_list->resizeRowsToContents(); m_list->resizeRowsToContents();
m_list->resizeColumnToContents(SaveColumns::Icon); m_list->resizeColumnToContents(static_cast<int>(SaveColumns::Icon));
const s32 language_index = gui_application::get_language_id(); const s32 language_index = gui_application::get_language_id();
const std::string localized_icon = fmt::format("ICON0_%02d.PNG", language_index); const std::string localized_icon = fmt::format("ICON0_%02d.PNG", language_index);
for (int i = 0; i < m_list->rowCount(); ++i) for (int i = 0; i < m_list->rowCount(); ++i)
{ {
if (movie_item* icon_item = static_cast<movie_item*>(m_list->item(i, SaveColumns::Icon))) if (movie_item* icon_item = static_cast<movie_item*>(m_list->item(i, static_cast<int>(SaveColumns::Icon))))
{ {
icon_item->set_icon_load_func([this, cancel = icon_item->icon_loading_aborted(), dpr, localized_icon](int index) icon_item->set_icon_load_func([this, cancel = icon_item->icon_loading_aborted(), dpr, localized_icon](int index)
{ {
@ -448,12 +467,12 @@ void save_manager_dialog::UpdateIcons()
QPixmap icon; QPixmap icon;
if (movie_item* item = static_cast<movie_item*>(m_list->item(index, SaveColumns::Icon))) if (movie_item* item = static_cast<movie_item*>(m_list->item(index, static_cast<int>(SaveColumns::Icon))))
{ {
if (!item->data(SaveUserRole::PixmapLoaded).toBool()) if (!item->data(SaveUserRole::PixmapLoaded).toBool())
{ {
// Load game icon // Load game icon
if (QTableWidgetItem* user_item = m_list->item(index, SaveColumns::Name)) if (QTableWidgetItem* user_item = m_list->item(index, static_cast<int>(SaveColumns::Name)))
{ {
const int idx_real = user_item->data(Qt::UserRole).toInt(); const int idx_real = user_item->data(Qt::UserRole).toInt();
const SaveDataEntry& entry = ::at32(m_save_entries, idx_real); const SaveDataEntry& entry = ::at32(m_save_entries, idx_real);
@ -534,7 +553,7 @@ void save_manager_dialog::OnSort(int logicalIndex)
// Remove a save file, need to be confirmed. // Remove a save file, need to be confirmed.
void save_manager_dialog::OnEntryRemove(int row, bool user_interaction) void save_manager_dialog::OnEntryRemove(int row, bool user_interaction)
{ {
if (QTableWidgetItem* item = m_list->item(row, SaveColumns::Name)) if (QTableWidgetItem* item = m_list->item(row, static_cast<int>(SaveColumns::Name)))
{ {
const int idx_real = item->data(Qt::UserRole).toInt(); const int idx_real = item->data(Qt::UserRole).toInt();
const SaveDataEntry& entry = ::at32(m_save_entries, idx_real); const SaveDataEntry& entry = ::at32(m_save_entries, idx_real);
@ -599,7 +618,7 @@ void save_manager_dialog::ShowContextMenu(const QPoint& pos)
connect(removeAct, &QAction::triggered, this, &save_manager_dialog::OnEntriesRemove); // entriesremove handles case of one as well connect(removeAct, &QAction::triggered, this, &save_manager_dialog::OnEntriesRemove); // entriesremove handles case of one as well
connect(showDirAct, &QAction::triggered, [this, idx]() connect(showDirAct, &QAction::triggered, [this, idx]()
{ {
QTableWidgetItem* item = m_list->item(idx, SaveColumns::Name); QTableWidgetItem* item = m_list->item(idx, static_cast<int>(SaveColumns::Name));
if (!item) if (!item)
{ {
return; return;
@ -655,8 +674,8 @@ void save_manager_dialog::UpdateDetails()
WaitForRepaintThreads(false); WaitForRepaintThreads(false);
const int row = m_list->currentRow(); const int row = m_list->currentRow();
QTableWidgetItem* item = m_list->item(row, SaveColumns::Name); QTableWidgetItem* item = m_list->item(row, static_cast<int>(SaveColumns::Name));
movie_item* icon_item = static_cast<movie_item*>(m_list->item(row, SaveColumns::Icon)); movie_item* icon_item = static_cast<movie_item*>(m_list->item(row, static_cast<int>(SaveColumns::Icon)));
if (!item || !icon_item) if (!item || !icon_item)
{ {
@ -692,7 +711,7 @@ void save_manager_dialog::WaitForRepaintThreads(bool abort)
{ {
for (int i = 0; i < m_list->rowCount(); i++) for (int i = 0; i < m_list->rowCount(); i++)
{ {
if (movie_item* item = static_cast<movie_item*>(m_list->item(i, SaveColumns::Icon))) if (movie_item* item = static_cast<movie_item*>(m_list->item(i, static_cast<int>(SaveColumns::Icon))))
{ {
item->wait_for_icon_loading(abort); item->wait_for_icon_loading(abort);
} }
@ -706,7 +725,7 @@ void save_manager_dialog::text_changed(const QString& text)
if (text.isEmpty()) if (text.isEmpty())
return true; return true;
for (int col = SaveColumns::Name; col < SaveColumns::Count; col++) for (int col = static_cast<int>(SaveColumns::Name); col < static_cast<int>(SaveColumns::Count); col++)
{ {
const QTableWidgetItem* item = m_list->item(row, col); const QTableWidgetItem* item = m_list->item(row, col);

View file

@ -39,7 +39,6 @@ Q_SIGNALS:
void IconReady(int index, const QPixmap& new_icon); void IconReady(int index, const QPixmap& new_icon);
private: private:
void Init();
void UpdateList(); void UpdateList();
void UpdateIcons(); void UpdateIcons();
void ShowContextMenu(const QPoint& pos); void ShowContextMenu(const QPoint& pos);
@ -49,6 +48,8 @@ private:
std::vector<SaveDataEntry> GetSaveEntries(const std::string& base_dir); std::vector<SaveDataEntry> GetSaveEntries(const std::string& base_dir);
QString get_header_text(int col) const;
game_list* m_list = nullptr; game_list* m_list = nullptr;
std::string m_dir; std::string m_dir;
std::vector<SaveDataEntry> m_save_entries; std::vector<SaveDataEntry> m_save_entries;

View file

@ -63,15 +63,16 @@ savestate_manager_dialog::savestate_manager_dialog(std::shared_ptr<gui_settings>
m_game_table->setAlternatingRowColors(true); m_game_table->setAlternatingRowColors(true);
m_game_table->installEventFilter(this); m_game_table->installEventFilter(this);
auto add_game_column = [this](gui::savestate_game_list_columns col, const QString& header_text, const QString& action_text) const auto add_game_column = [this](gui::savestate_game_list_columns col)
{ {
m_game_table->setHorizontalHeaderItem(static_cast<int>(col), new QTableWidgetItem(header_text)); const int column = static_cast<int>(col);
m_game_column_acts.append(new QAction(action_text, this)); m_game_table->setHorizontalHeaderItem(column, new QTableWidgetItem(get_gamelist_header_text(column)));
m_game_column_acts[column] = new QAction(get_gamelist_action_text(column), this);
}; };
add_game_column(gui::savestate_game_list_columns::icon, tr("Icon"), tr("Show Icons")); add_game_column(gui::savestate_game_list_columns::icon);
add_game_column(gui::savestate_game_list_columns::name, tr("Game"), tr("Show Games")); add_game_column(gui::savestate_game_list_columns::name);
add_game_column(gui::savestate_game_list_columns::savestates, tr("Savestates"), tr("Show Savestates")); add_game_column(gui::savestate_game_list_columns::savestates);
// Savestate Table // Savestate Table
m_savestate_table = new game_list(); m_savestate_table = new game_list();
@ -94,16 +95,17 @@ savestate_manager_dialog::savestate_manager_dialog(std::shared_ptr<gui_settings>
m_savestate_table->setAlternatingRowColors(true); m_savestate_table->setAlternatingRowColors(true);
m_savestate_table->installEventFilter(this); m_savestate_table->installEventFilter(this);
auto add_savestate_column = [this](gui::savestate_list_columns col, const QString& header_text, const QString& action_text) const auto add_savestate_column = [this](gui::savestate_list_columns col)
{ {
m_savestate_table->setHorizontalHeaderItem(static_cast<int>(col), new QTableWidgetItem(header_text)); const int column = static_cast<int>(col);
m_savestate_column_acts.append(new QAction(action_text, this)); m_savestate_table->setHorizontalHeaderItem(column, new QTableWidgetItem(get_savestate_header_text(column)));
m_savestate_column_acts[column] = new QAction(get_savestate_action_text(column), this);
}; };
add_savestate_column(gui::savestate_list_columns::name, tr("Name"), tr("Show Names")); add_savestate_column(gui::savestate_list_columns::name);
add_savestate_column(gui::savestate_list_columns::compatible, tr("Compatible"), tr("Show Compatible")); add_savestate_column(gui::savestate_list_columns::compatible);
add_savestate_column(gui::savestate_list_columns::date, tr("Created"), tr("Show Created")); add_savestate_column(gui::savestate_list_columns::date);
add_savestate_column(gui::savestate_list_columns::path, tr("Path"), tr("Show Paths")); add_savestate_column(gui::savestate_list_columns::path);
m_splitter = new QSplitter(); m_splitter = new QSplitter();
m_splitter->addWidget(m_game_table); m_splitter->addWidget(m_game_table);
@ -220,6 +222,56 @@ savestate_manager_dialog::~savestate_manager_dialog()
WaitAndAbortGameRepaintThreads(); WaitAndAbortGameRepaintThreads();
} }
QString savestate_manager_dialog::get_savestate_header_text(int col) const
{
switch (static_cast<gui::savestate_list_columns>(col))
{
case gui::savestate_list_columns::name: return tr("Name");
case gui::savestate_list_columns::compatible: return tr("Compatible");
case gui::savestate_list_columns::date: return tr("Created");
case gui::savestate_list_columns::path: return tr("Path");
case gui::savestate_list_columns::count: break;
}
return {};
}
QString savestate_manager_dialog::get_savestate_action_text(int col) const
{
switch (static_cast<gui::savestate_list_columns>(col))
{
case gui::savestate_list_columns::name: return tr("Show Names");
case gui::savestate_list_columns::compatible: return tr("Show Compatible");
case gui::savestate_list_columns::date: return tr("Show Created");
case gui::savestate_list_columns::path: return tr("Show Paths");
case gui::savestate_list_columns::count: break;
}
return {};
}
QString savestate_manager_dialog::get_gamelist_header_text(int col) const
{
switch (static_cast<gui::savestate_game_list_columns>(col))
{
case gui::savestate_game_list_columns::icon: return tr("Icon");
case gui::savestate_game_list_columns::name: return tr("Game");
case gui::savestate_game_list_columns::savestates: return tr("Savestates");
case gui::savestate_game_list_columns::count: break;
}
return {};
}
QString savestate_manager_dialog::get_gamelist_action_text(int col) const
{
switch (static_cast<gui::savestate_game_list_columns>(col))
{
case gui::savestate_game_list_columns::icon: return tr("Show Icons");
case gui::savestate_game_list_columns::name: return tr("Show Games");
case gui::savestate_game_list_columns::savestates: return tr("Show Savestates");
case gui::savestate_game_list_columns::count: break;
}
return {};
}
bool savestate_manager_dialog::LoadSavestateFolderToDB(std::unique_ptr<game_savestates_data>&& game_savestates) bool savestate_manager_dialog::LoadSavestateFolderToDB(std::unique_ptr<game_savestates_data>&& game_savestates)
{ {
ensure(!!game_savestates); ensure(!!game_savestates);
@ -625,6 +677,21 @@ void savestate_manager_dialog::PopulateGameTable()
m_game_table->clearContents(); m_game_table->clearContents();
m_game_table->setRowCount(static_cast<int>(m_savestate_db.size())); m_game_table->setRowCount(static_cast<int>(m_savestate_db.size()));
// Update headers
for (int col = 0; col < m_game_table->horizontalHeader()->count(); col++)
{
if (auto item = m_game_table->horizontalHeaderItem(col))
{
item->setText(get_gamelist_header_text(col));
}
}
// Update actions
for (auto& [col, action] : m_game_column_acts)
{
action->setText(get_gamelist_action_text(col));
}
m_game_combo->clear(); m_game_combo->clear();
m_game_combo->blockSignals(true); m_game_combo->blockSignals(true);
@ -681,6 +748,21 @@ void savestate_manager_dialog::PopulateSavestateTable()
m_savestate_table->setRowCount(static_cast<int>(savestates.size())); m_savestate_table->setRowCount(static_cast<int>(savestates.size()));
m_savestate_table->setSortingEnabled(false); // Disable sorting before using setItem calls m_savestate_table->setSortingEnabled(false); // Disable sorting before using setItem calls
// Update headers
for (int col = 0; col < m_savestate_table->horizontalHeader()->count(); col++)
{
if (auto item = m_savestate_table->horizontalHeaderItem(col))
{
item->setText(get_savestate_header_text(col));
}
}
// Update actions
for (auto& [col, action] : m_savestate_column_acts)
{
action->setText(get_savestate_action_text(col));
}
for (int i = 0; i < static_cast<int>(savestates.size()); i++) for (int i = 0; i < static_cast<int>(savestates.size()); i++)
{ {
const savestate_data& savestate = savestates[i]; const savestate_data& savestate = savestates[i];

View file

@ -64,6 +64,12 @@ private:
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
bool eventFilter(QObject *object, QEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override;
QString get_savestate_header_text(int col) const;
QString get_savestate_action_text(int col) const;
QString get_gamelist_header_text(int col) const;
QString get_gamelist_action_text(int col) const;
std::shared_ptr<gui_settings> m_gui_settings; std::shared_ptr<gui_settings> m_gui_settings;
std::vector<game_info> m_game_info; std::vector<game_info> m_game_info;
@ -74,8 +80,8 @@ private:
game_list* m_savestate_table; //! UI element to display savestate stuff. game_list* m_savestate_table; //! UI element to display savestate stuff.
game_list* m_game_table; //! UI element to display games. game_list* m_game_table; //! UI element to display games.
QList<QAction*> m_savestate_column_acts; std::map<int, QAction*> m_savestate_column_acts;
QList<QAction*> m_game_column_acts; std::map<int, QAction*> m_game_column_acts;
int m_game_icon_size_index = 25; int m_game_icon_size_index = 25;
QSize m_game_icon_size = QSize(m_game_icon_size_index, m_game_icon_size_index); QSize m_game_icon_size = QSize(m_game_icon_size_index, m_game_icon_size_index);

View file

@ -100,16 +100,17 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
m_game_table->setAlternatingRowColors(true); m_game_table->setAlternatingRowColors(true);
m_game_table->installEventFilter(this); m_game_table->installEventFilter(this);
auto add_game_column = [this](gui::trophy_game_list_columns col, const QString& header_text, const QString& action_text) const auto add_game_column = [this](gui::trophy_game_list_columns col)
{ {
m_game_table->setHorizontalHeaderItem(static_cast<int>(col), new QTableWidgetItem(header_text)); const int column = static_cast<int>(col);
m_game_column_acts.append(new QAction(action_text, this)); m_game_table->setHorizontalHeaderItem(column, new QTableWidgetItem(get_gamelist_header_text(column)));
m_game_column_acts[column] = new QAction(get_gamelist_action_text(column), this);
}; };
add_game_column(gui::trophy_game_list_columns::icon, tr("Icon"), tr("Show Icons")); add_game_column(gui::trophy_game_list_columns::icon);
add_game_column(gui::trophy_game_list_columns::name, tr("Game"), tr("Show Games")); add_game_column(gui::trophy_game_list_columns::name);
add_game_column(gui::trophy_game_list_columns::progress, tr("Progress"), tr("Show Progress")); add_game_column(gui::trophy_game_list_columns::progress);
add_game_column(gui::trophy_game_list_columns::trophies, tr("Trophies"), tr("Show Trophies")); add_game_column(gui::trophy_game_list_columns::trophies);
// Trophy Table // Trophy Table
m_trophy_table = new game_list(); m_trophy_table = new game_list();
@ -133,20 +134,21 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
m_trophy_table->setAlternatingRowColors(true); m_trophy_table->setAlternatingRowColors(true);
m_trophy_table->installEventFilter(this); m_trophy_table->installEventFilter(this);
auto add_trophy_column = [this](gui::trophy_list_columns col, const QString& header_text, const QString& action_text) const auto add_trophy_column = [this](gui::trophy_list_columns col)
{ {
m_trophy_table->setHorizontalHeaderItem(static_cast<int>(col), new QTableWidgetItem(header_text)); const int column = static_cast<int>(col);
m_trophy_column_acts.append(new QAction(action_text, this)); m_trophy_table->setHorizontalHeaderItem(column, new QTableWidgetItem(get_trophy_header_text(column)));
m_trophy_column_acts[column] = new QAction(get_trophy_action_text(column), this);
}; };
add_trophy_column(gui::trophy_list_columns::icon, tr("Icon"), tr("Show Icons")); add_trophy_column(gui::trophy_list_columns::icon);
add_trophy_column(gui::trophy_list_columns::name, tr("Name"), tr("Show Names")); add_trophy_column(gui::trophy_list_columns::name);
add_trophy_column(gui::trophy_list_columns::description, tr("Description"), tr("Show Descriptions")); add_trophy_column(gui::trophy_list_columns::description);
add_trophy_column(gui::trophy_list_columns::type, tr("Type"), tr("Show Types")); add_trophy_column(gui::trophy_list_columns::type);
add_trophy_column(gui::trophy_list_columns::is_unlocked, tr("Status"), tr("Show Status")); add_trophy_column(gui::trophy_list_columns::is_unlocked);
add_trophy_column(gui::trophy_list_columns::id, tr("ID"), tr("Show IDs")); add_trophy_column(gui::trophy_list_columns::id);
add_trophy_column(gui::trophy_list_columns::platinum_link, tr("Platinum Relevant"), tr("Show Platinum Relevant")); add_trophy_column(gui::trophy_list_columns::platinum_link);
add_trophy_column(gui::trophy_list_columns::time_unlocked, tr("Time Unlocked"), tr("Show Time Unlocked")); add_trophy_column(gui::trophy_list_columns::time_unlocked);
m_splitter = new QSplitter(); m_splitter = new QSplitter();
m_splitter->addWidget(m_game_table); m_splitter->addWidget(m_game_table);
@ -406,6 +408,66 @@ trophy_manager_dialog::~trophy_manager_dialog()
WaitAndAbortTrophyRepaintThreads(); WaitAndAbortTrophyRepaintThreads();
} }
QString trophy_manager_dialog::get_trophy_header_text(int col) const
{
switch (static_cast<gui::trophy_list_columns>(col))
{
case gui::trophy_list_columns::icon: return tr("Icon");
case gui::trophy_list_columns::name: return tr("Name");
case gui::trophy_list_columns::description: return tr("Description");
case gui::trophy_list_columns::type: return tr("Type");
case gui::trophy_list_columns::is_unlocked: return tr("Status");
case gui::trophy_list_columns::id: return tr("ID");
case gui::trophy_list_columns::platinum_link: return tr("Platinum Relevant");
case gui::trophy_list_columns::time_unlocked: return tr("Time Unlocked");
case gui::trophy_list_columns::count: break;
}
return {};
}
QString trophy_manager_dialog::get_trophy_action_text(int col) const
{
switch (static_cast<gui::trophy_list_columns>(col))
{
case gui::trophy_list_columns::icon: return tr("Show Icons");
case gui::trophy_list_columns::name: return tr("Show Names");
case gui::trophy_list_columns::description: return tr("Show Descriptions");
case gui::trophy_list_columns::type: return tr("Show Types");
case gui::trophy_list_columns::is_unlocked: return tr("Show Status");
case gui::trophy_list_columns::id: return tr("Show IDs");
case gui::trophy_list_columns::platinum_link: return tr("Show Platinum Relevant");
case gui::trophy_list_columns::time_unlocked: return tr("Show Time Unlocked");
case gui::trophy_list_columns::count: break;
}
return {};
}
QString trophy_manager_dialog::get_gamelist_header_text(int col) const
{
switch (static_cast<gui::trophy_game_list_columns>(col))
{
case gui::trophy_game_list_columns::icon: return tr("Icon");
case gui::trophy_game_list_columns::name: return tr("Game");
case gui::trophy_game_list_columns::progress: return tr("Progress");
case gui::trophy_game_list_columns::trophies: return tr("Trophies");
case gui::trophy_game_list_columns::count: break;
}
return {};
}
QString trophy_manager_dialog::get_gamelist_action_text(int col) const
{
switch (static_cast<gui::trophy_game_list_columns>(col))
{
case gui::trophy_game_list_columns::icon: return tr("Show Icons");
case gui::trophy_game_list_columns::name: return tr("Show Games");
case gui::trophy_game_list_columns::progress: return tr("Show Progress");
case gui::trophy_game_list_columns::trophies: return tr("Show Trophies");
case gui::trophy_game_list_columns::count: break;
}
return {};
}
bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name) bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name)
{ {
const std::string trophy_path = m_trophy_dir + trop_name; const std::string trophy_path = m_trophy_dir + trop_name;
@ -1062,6 +1124,21 @@ void trophy_manager_dialog::PopulateGameTable()
m_game_table->clearContents(); m_game_table->clearContents();
m_game_table->setRowCount(static_cast<int>(m_trophies_db.size())); m_game_table->setRowCount(static_cast<int>(m_trophies_db.size()));
// Update headers
for (int col = 0; col < m_game_table->horizontalHeader()->count(); col++)
{
if (auto item = m_game_table->horizontalHeaderItem(col))
{
item->setText(get_gamelist_header_text(col));
}
}
// Update actions
for (auto& [col, action] : m_game_column_acts)
{
action->setText(get_gamelist_action_text(col));
}
m_game_combo->clear(); m_game_combo->clear();
m_game_combo->blockSignals(true); m_game_combo->blockSignals(true);
@ -1126,6 +1203,21 @@ void trophy_manager_dialog::PopulateTrophyTable()
m_trophy_table->setRowCount(all_trophies); m_trophy_table->setRowCount(all_trophies);
m_trophy_table->setSortingEnabled(false); // Disable sorting before using setItem calls m_trophy_table->setSortingEnabled(false); // Disable sorting before using setItem calls
// Update headers
for (int col = 0; col < m_trophy_table->horizontalHeader()->count(); col++)
{
if (auto item = m_trophy_table->horizontalHeaderItem(col))
{
item->setText(get_trophy_header_text(col));
}
}
// Update actions
for (auto& [col, action] : m_trophy_column_acts)
{
action->setText(get_trophy_action_text(col));
}
QPixmap placeholder(m_icon_height, m_icon_height); QPixmap placeholder(m_icon_height, m_icon_height);
placeholder.fill(Qt::transparent); placeholder.fill(Qt::transparent);

View file

@ -83,6 +83,12 @@ private:
static QDateTime TickToDateTime(u64 tick); static QDateTime TickToDateTime(u64 tick);
static u64 DateTimeToTick(QDateTime date_time); static u64 DateTimeToTick(QDateTime date_time);
QString get_trophy_header_text(int col) const;
QString get_trophy_action_text(int col) const;
QString get_gamelist_header_text(int col) const;
QString get_gamelist_action_text(int col) const;
std::shared_ptr<gui_settings> m_gui_settings; std::shared_ptr<gui_settings> m_gui_settings;
std::vector<std::unique_ptr<GameTrophiesData>> m_trophies_db; //! Holds all the trophy information. std::vector<std::unique_ptr<GameTrophiesData>> m_trophies_db; //! Holds all the trophy information.
@ -93,8 +99,8 @@ private:
game_list* m_trophy_table; //! UI element to display trophy stuff. game_list* m_trophy_table; //! UI element to display trophy stuff.
game_list* m_game_table; //! UI element to display games. game_list* m_game_table; //! UI element to display games.
QList<QAction*> m_trophy_column_acts; std::map<int, QAction*> m_trophy_column_acts;
QList<QAction*> m_game_column_acts; std::map<int, QAction*> m_game_column_acts;
bool m_show_hidden_trophies = false; bool m_show_hidden_trophies = false;
bool m_show_unlocked_trophies = true; bool m_show_unlocked_trophies = true;