diff --git a/BUILDING.md b/BUILDING.md index 04a01364d7..60b7046cb2 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -8,11 +8,12 @@ Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Bu ### 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) **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) 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 diff --git a/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp b/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp index c9d784d3ac..d23903997f 100644 --- a/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp @@ -720,9 +720,19 @@ void spu_cache::initialize(bool build_existing_cache) } // 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) { @@ -4963,6 +4973,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s u32 lsa_last_pc = SPU_LS_SIZE; // PC of first LSA write u32 get_pc = SPU_LS_SIZE; // PC of GETLLAR 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_offs = reg_state_t::from_value(0); // Added value to ls reg_state_t lsa{}; // state of LSA register on GETLLAR @@ -5008,7 +5019,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s ls_invalid = true; ls_write |= write; - if (write) + if (ls_write) { return discard(); } @@ -6323,6 +6334,8 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s break; } + atomic16->rdatomic_pc = pos; + const auto it = atomic16_all.find(pos); if (it == atomic16_all.end()) @@ -7263,7 +7276,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s for (const auto& [pc_commited, pattern] : atomic16_all) { - if (!pattern.active) + if (!pattern.active || pattern.lsa_pc >= pattern.rdatomic_pc) { continue; } @@ -7273,6 +7286,17 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s continue; } + std::string pattern_hash; + { + sha1_context ctx; + u8 output[20]{}; + + sha1_starts(&ctx); + sha1_update(&ctx, reinterpret_cast(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 { u64 data; @@ -7295,8 +7319,8 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s 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); - add_pattern(false, inst_attr::putllc0, pattern.put_pc - lsa, value.data); + // 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); continue; } @@ -7363,16 +7387,35 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s value.reg2 = pattern.reg2; } + bool allow_pattern = true; + if (g_cfg.core.spu_accurate_reservations) { - // Because enabling it is a hack, as it turns out - // continue; + // The problem with PUTLLC16 optimization, that it is in theory correct at the bounds of the spu function. + // 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 allowed_patterns = + { + "620oYSe8uQqq9eTkhWfMqoEXX0us"sv, // CellSpurs JobChain acquire pattern + }; + + allow_pattern = std::any_of(allowed_patterns.begin(), allowed_patterns.end(), FN(pattern_hash == x)); } - add_pattern(false, inst_attr::putllc16, pattern.put_pc - result.entry_point, value.data); + if (allow_pattern) + { + 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)" - , 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); + 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, pattern_hash, +stats.nowrite, ++stats.single, +stats.all); } for (const auto& [read_pc, pattern] : rchcnt_loop_all) diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index bba3b0b235..e9aca513a7 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -668,10 +668,9 @@ void emu_settings::EnhanceSpinBox(QSpinBox* spinbox, emu_settings_type type, con spinbox->setRange(min, max); 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, spinbox->cleanText().toStdString()); + SetSetting(type, fmt::format("%d", value)); }); 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->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, spinbox->cleanText().toStdString()); + SetSetting(type, fmt::format("%f", value)); }); connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]() diff --git a/rpcs3/rpcs3qt/game_list.cpp b/rpcs3/rpcs3qt/game_list.cpp index 62ea8f73ba..c5eba4efd3 100644 --- a/rpcs3/rpcs3qt/game_list.cpp +++ b/rpcs3/rpcs3qt/game_list.cpp @@ -14,16 +14,16 @@ game_list::game_list() : QTableWidget(), game_list_base() }; } -void game_list::sync_header_actions(QList& actions, std::function get_visibility) +void game_list::sync_header_actions(std::map& actions, std::function get_visibility) { ensure(get_visibility); bool is_dirty = false; - for (int col = 0; col < actions.count(); ++col) + for (auto& [col, action] : actions) { const bool is_hidden = !get_visibility(col); - actions[col]->setChecked(!is_hidden); + action->setChecked(!is_hidden); if (isColumnHidden(col) != is_hidden) { @@ -38,7 +38,7 @@ void game_list::sync_header_actions(QList& actions, std::function& actions, std::function get_visibility, std::function set_visibility) +void game_list::create_header_actions(std::map& actions, std::function get_visibility, std::function set_visibility) { ensure(get_visibility); ensure(set_visibility); @@ -48,27 +48,30 @@ void game_list::create_header_actions(QList& actions, std::functionaddActions(actions); + for (auto& [col, action] : actions) + { + configure->addAction(action); + } 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 { 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; } 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; } } diff --git a/rpcs3/rpcs3qt/game_list.h b/rpcs3/rpcs3qt/game_list.h index db59b07913..6d5a3f5a3e 100644 --- a/rpcs3/rpcs3qt/game_list.h +++ b/rpcs3/rpcs3qt/game_list.h @@ -23,8 +23,8 @@ class game_list : public QTableWidget, public game_list_base public: game_list(); - void sync_header_actions(QList& actions, std::function get_visibility); - void create_header_actions(QList& actions, std::function get_visibility, std::function set_visibility); + void sync_header_actions(std::map& actions, std::function get_visibility); + void create_header_actions(std::map& actions, std::function get_visibility, std::function set_visibility); void clear_list() override; // Use this instead of clearContents diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 162b8cb0f6..7bf27c4695 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -100,27 +100,28 @@ game_list_frame::game_list_frame(std::shared_ptr gui_settings, std m_game_dock->setCentralWidget(m_central_widget); // 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(col), new QTableWidgetItem(header_text)); - m_columnActs.append(new QAction(action_text, this)); + const int column = static_cast(col); + 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::name, tr("Name"), tr("Show Names")); - add_column(gui::game_list_columns::serial, tr("Serial"), tr("Show Serials")); - add_column(gui::game_list_columns::firmware, tr("Firmware"), tr("Show Firmwares")); - add_column(gui::game_list_columns::version, tr("Version"), tr("Show Versions")); - add_column(gui::game_list_columns::category, tr("Category"), tr("Show Categories")); - add_column(gui::game_list_columns::path, tr("Path"), tr("Show Paths")); - add_column(gui::game_list_columns::move, tr("PlayStation Move"), tr("Show PlayStation Move")); - add_column(gui::game_list_columns::resolution, tr("Supported Resolutions"), tr("Show Supported Resolutions")); - add_column(gui::game_list_columns::sound, tr("Sound Formats"), tr("Show Sound Formats")); - add_column(gui::game_list_columns::parental, tr("Parental Level"), tr("Show Parental Levels")); - add_column(gui::game_list_columns::last_play, tr("Last Played"), tr("Show Last Played")); - add_column(gui::game_list_columns::playtime, tr("Time Played"), tr("Show Time Played")); - add_column(gui::game_list_columns::compat, tr("Compatibility"), tr("Show Compatibility")); - add_column(gui::game_list_columns::dir_size, tr("Space On Disk"), tr("Show Space On Disk")); + add_column(gui::game_list_columns::icon); + add_column(gui::game_list_columns::name); + add_column(gui::game_list_columns::serial); + add_column(gui::game_list_columns::firmware); + add_column(gui::game_list_columns::version); + add_column(gui::game_list_columns::category); + add_column(gui::game_list_columns::path); + add_column(gui::game_list_columns::move); + add_column(gui::game_list_columns::resolution); + add_column(gui::game_list_columns::sound); + add_column(gui::game_list_columns::parental); + add_column(gui::game_list_columns::last_play); + add_column(gui::game_list_columns::playtime); + add_column(gui::game_list_columns::compat); + 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->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, std 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); - 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(col)); }, [this](int col, bool visible) { m_gui_settings->SetGamelistColVisibility(static_cast(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_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(col)); }); + m_game_list->sync_header_actions(m_column_acts, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast(col)); }); } 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); } +QString game_list_frame::get_header_text(int col) const +{ + switch (static_cast(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(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) { if (col == static_cast(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::vectorSetValue(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(); // 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)) { 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(col)); }); + m_game_list->sync_header_actions(m_column_acts, [this](int col) { return m_gui_settings->GetGamelistColVisibility(static_cast(col)); }); } // Emit signal and remove slots @@ -959,9 +1023,9 @@ void game_list_frame::ToggleCategoryFilter(const QStringList& categories, bool s 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(col), m_columnActs[col]->isChecked()); + m_gui_settings->SetGamelistColVisibility(static_cast(col), action->isChecked()); } 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); @@ -1121,7 +1185,7 @@ void game_list_frame::CreateShortcuts(const std::vector& games, const } } -void game_list_frame::ShowContextMenu(const QPoint &pos) +void game_list_frame::ShowContextMenu(const QPoint& pos) { QPoint global_pos; game_info gameinfo; @@ -2704,7 +2768,7 @@ void game_list_frame::ShowCustomConfigIcon(const game_info& game) 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 = gui_settings::SizeFromSlider(slider_pos); @@ -2712,7 +2776,7 @@ void game_list_frame::ResizeIcons(const int& slider_pos) 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_refresh_watcher, false); @@ -2746,7 +2810,7 @@ void game_list_frame::SetShowHidden(bool 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_is_list_layout = is_list; diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index fb366c933c..c31e6aa66a 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -48,10 +48,10 @@ public: void SaveSettings(); /** 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 */ - void RepaintIcons(const bool& from_settings = false); + void RepaintIcons(bool from_settings = false); void SetShowHidden(bool show); @@ -70,7 +70,7 @@ public Q_SLOTS: void BatchRemoveCustomConfigurations(); void BatchRemoveCustomPadConfigurations(); void BatchRemoveShaderCaches(); - void SetListMode(const bool& is_list); + void SetListMode(bool is_list); void SetSearchText(const QString& text); void SetShowCompatibilityInGrid(bool show); void SetPreferGameDataIcons(bool enabled); @@ -83,7 +83,7 @@ private Q_SLOTS: void OnRefreshFinished(); void OnCompatFinished(); void OnColClicked(int col); - void ShowContextMenu(const QPoint &pos); + void ShowContextMenu(const QPoint& pos); void doubleClickedSlot(QTableWidgetItem* item); void doubleClickedSlot(const game_info& game); void ItemSelectionChangedSlot(); @@ -91,7 +91,7 @@ Q_SIGNALS: void GameListFrameClosed(); 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 RequestIconSizeChange(const int& val); + void RequestIconSizeChange(int val); void NotifyEmuSettingsChange(); void FocusToSearchBar(); void Refreshed(); @@ -127,6 +127,9 @@ protected: private: void push_path(const std::string& path, std::vector& legit_paths); + QString get_header_text(int col) const; + QString get_action_text(int col) const; + void ShowCustomConfigIcon(const game_info& game); 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_compatibility* m_game_compat = nullptr; progress_dialog* m_progress_dialog = nullptr; - QList m_columnActs; + std::map m_column_acts; Qt::SortOrder m_col_sort_order{}; int m_sort_column{}; bool m_initial_refresh_done = false; diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 3efbae75d2..c0dcb9bfcd 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2344,6 +2344,8 @@ void main_window::RetranslateUI(const QStringList& language_codes, const QString { m_game_list_frame->Refresh(true); } + + Q_EMIT RequestDialogRepaint(); } 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->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; m_save_slider_pos = true; diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.cpp b/rpcs3/rpcs3qt/memory_viewer_panel.cpp index 951ff41087..2a8b6c29b1 100644 --- a/rpcs3/rpcs3qt/memory_viewer_panel.cpp +++ b/rpcs3/rpcs3qt/memory_viewer_panel.cpp @@ -432,9 +432,9 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, std::shared_ptr(&QSpinBox::valueChanged), this, [=, this]() + connect(sb_words, &QSpinBox::valueChanged, this, [this](int value) { - m_colcount = 1 << sb_words->value(); + m_colcount = 1 << value; ShowMemory(); }); diff --git a/rpcs3/rpcs3qt/pad_motion_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_motion_settings_dialog.cpp index 433f9adb5a..0899096c95 100644 --- a/rpcs3/rpcs3qt/pad_motion_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_motion_settings_dialog.cpp @@ -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); }); - connect(m_shifts[i], QOverload::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); m_config_entries[i]->shift.set(value); diff --git a/rpcs3/rpcs3qt/patch_manager_dialog.cpp b/rpcs3/rpcs3qt/patch_manager_dialog.cpp index a58c4b8662..0df58fb35e 100644 --- a/rpcs3/rpcs3qt/patch_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/patch_manager_dialog.cpp @@ -123,8 +123,8 @@ patch_manager_dialog::patch_manager_dialog(std::shared_ptr gui_set handle_config_value_changed(ui->configurable_combo_box->itemData(index).toDouble()); } }); - connect(ui->configurable_spin_box, QOverload::of(&QSpinBox::valueChanged), this, &patch_manager_dialog::handle_config_value_changed); - connect(ui->configurable_double_spin_box, QOverload::of(&QDoubleSpinBox::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, &QDoubleSpinBox::valueChanged, this, &patch_manager_dialog::handle_config_value_changed); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close); connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) { diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp index eaacc9c524..57b4f70d02 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp @@ -281,7 +281,7 @@ void raw_mouse_settings_dialog::add_tabs(QTabWidget* tabs) m_accel_spin_boxes.push_back(mouse_acceleration_spin_box); mouse_acceleration_spin_box->setRange(0.1, 10.0); mouse_acceleration_spin_box->setValue(config->mouse_acceleration.get() / 100.0); - connect(mouse_acceleration_spin_box, QOverload::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; config.set(std::clamp(value * 100.0, config.min, config.max)); diff --git a/rpcs3/rpcs3qt/save_manager_dialog.cpp b/rpcs3/rpcs3qt/save_manager_dialog.cpp index 006e0d9043..2dd2a14e86 100644 --- a/rpcs3/rpcs3qt/save_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/save_manager_dialog.cpp @@ -32,7 +32,7 @@ LOG_CHANNEL(gui_log, "GUI"); -enum SaveColumns +enum class SaveColumns { Icon = 0, Name = 1, @@ -60,30 +60,26 @@ save_manager_dialog::save_manager_dialog(std::shared_ptr gui_setti setMinimumSize(QSize(400, 400)); 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 m_list = new game_list(); m_list->setItemDelegate(new game_list_delegate(m_list)); m_list->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection); m_list->setSelectionBehavior(QAbstractItemView::SelectRows); m_list->setContextMenuPolicy(Qt::CustomContextMenu); - m_list->setColumnCount(SaveColumns::Count); + m_list->setColumnCount(static_cast(SaveColumns::Count)); m_list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); m_list->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); m_list->verticalScrollBar()->setSingleStep(20); 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()->setStretchLastSection(true); m_list->setMouseTracking(true); + for (int column = 0; column < static_cast(SaveColumns::Count); column++) + { + m_list->setHorizontalHeaderItem(column, new QTableWidgetItem(get_header_text(column))); + } + // Bottom bar const int icon_size = m_gui_settings->GetValue(gui::sd_icon_size).toInt(); 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]() { const int idx = m_list->currentRow(); - QTableWidgetItem* item = m_list->item(idx, SaveColumns::Name); + QTableWidgetItem* item = m_list->item(idx, static_cast(SaveColumns::Name)); if (!item) { return; @@ -176,12 +172,12 @@ void save_manager_dialog::Init() connect(m_list, &QTableWidget::customContextMenuRequested, this, &save_manager_dialog::ShowContextMenu); connect(m_list, &QTableWidget::cellChanged, [&](int row, int col) { - if (col != SaveColumns::Note) + if (col != static_cast(SaveColumns::Note)) { return; } - QTableWidgetItem* user_item = m_list->item(row, SaveColumns::Name); - QTableWidgetItem* text_item = m_list->item(row, SaveColumns::Note); + QTableWidgetItem* user_item = m_list->item(row, static_cast(SaveColumns::Name)); + QTableWidgetItem* text_item = m_list->item(row, static_cast(SaveColumns::Note)); if (!user_item || !text_item) { return; @@ -196,7 +192,7 @@ void save_manager_dialog::Init() connect(m_list, &QTableWidget::itemSelectionChanged, this, &save_manager_dialog::UpdateDetails); connect(this, &save_manager_dialog::IconReady, this, [this](int index, const QPixmap& pixmap) { - if (movie_item* item = static_cast(m_list->item(index, SaveColumns::Icon))) + if (movie_item* item = static_cast(m_list->item(index, static_cast(SaveColumns::Icon)))) { item->setData(SaveUserRole::PixmapScaled, pixmap); item->image_change_callback(); @@ -205,6 +201,20 @@ void save_manager_dialog::Init() connect(search_bar, &QLineEdit::textChanged, this, &save_manager_dialog::text_changed); } +QString save_manager_dialog::get_header_text(int col) const +{ + switch (static_cast(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. */ @@ -300,6 +310,15 @@ void save_manager_dialog::UpdateList() m_list->clearContents(); m_list->setRowCount(static_cast(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(); if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool()) @@ -358,20 +377,20 @@ void save_manager_dialog::UpdateList() icon_item->stop_movie(); } }); - m_list->setItem(i, SaveColumns::Icon, icon_item); + m_list->setItem(i, static_cast(SaveColumns::Icon), icon_item); custom_table_widget_item* titleItem = new custom_table_widget_item(title); titleItem->setData(Qt::UserRole, i); // For sorting to work properly titleItem->setFlags(titleItem->flags() & ~Qt::ItemIsEditable); - m_list->setItem(i, SaveColumns::Name, titleItem); + m_list->setItem(i, static_cast(SaveColumns::Name), titleItem); custom_table_widget_item* timeItem = new custom_table_widget_item(gui::utils::format_timestamp(entry.mtime)); timeItem->setFlags(timeItem->flags() & ~Qt::ItemIsEditable); - m_list->setItem(i, SaveColumns::Time, timeItem); + m_list->setItem(i, static_cast(SaveColumns::Time), timeItem); custom_table_widget_item* dirNameItem = new custom_table_widget_item(dir_name); dirNameItem->setFlags(dirNameItem->flags() & ~Qt::ItemIsEditable); - m_list->setItem(i, SaveColumns::Dir, dirNameItem); + m_list->setItem(i, static_cast(SaveColumns::Dir), dirNameItem); custom_table_widget_item* noteItem = new custom_table_widget_item(); noteItem->setFlags(noteItem->flags() | Qt::ItemIsEditable); @@ -379,7 +398,7 @@ void save_manager_dialog::UpdateList() { noteItem->setText(notes[dir_name].toString()); } - m_list->setItem(i, SaveColumns::Note, noteItem); + m_list->setItem(i, static_cast(SaveColumns::Note), noteItem); } 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) { - if (movie_item* icon_item = static_cast(m_list->item(i, SaveColumns::Icon))) + if (movie_item* icon_item = static_cast(m_list->item(i, static_cast(SaveColumns::Icon)))) { icon_item->setData(SaveUserRole::PixmapScaled, placeholder); icon_item->setData(Qt::DecorationRole, placeholder); @@ -430,14 +449,14 @@ void save_manager_dialog::UpdateIcons() } m_list->resizeRowsToContents(); - m_list->resizeColumnToContents(SaveColumns::Icon); + m_list->resizeColumnToContents(static_cast(SaveColumns::Icon)); const s32 language_index = gui_application::get_language_id(); const std::string localized_icon = fmt::format("ICON0_%02d.PNG", language_index); for (int i = 0; i < m_list->rowCount(); ++i) { - if (movie_item* icon_item = static_cast(m_list->item(i, SaveColumns::Icon))) + if (movie_item* icon_item = static_cast(m_list->item(i, static_cast(SaveColumns::Icon)))) { 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; - if (movie_item* item = static_cast(m_list->item(index, SaveColumns::Icon))) + if (movie_item* item = static_cast(m_list->item(index, static_cast(SaveColumns::Icon)))) { if (!item->data(SaveUserRole::PixmapLoaded).toBool()) { // Load game icon - if (QTableWidgetItem* user_item = m_list->item(index, SaveColumns::Name)) + if (QTableWidgetItem* user_item = m_list->item(index, static_cast(SaveColumns::Name))) { const int idx_real = user_item->data(Qt::UserRole).toInt(); 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. 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(SaveColumns::Name))) { const int idx_real = item->data(Qt::UserRole).toInt(); 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(showDirAct, &QAction::triggered, [this, idx]() { - QTableWidgetItem* item = m_list->item(idx, SaveColumns::Name); + QTableWidgetItem* item = m_list->item(idx, static_cast(SaveColumns::Name)); if (!item) { return; @@ -655,8 +674,8 @@ void save_manager_dialog::UpdateDetails() WaitForRepaintThreads(false); const int row = m_list->currentRow(); - QTableWidgetItem* item = m_list->item(row, SaveColumns::Name); - movie_item* icon_item = static_cast(m_list->item(row, SaveColumns::Icon)); + QTableWidgetItem* item = m_list->item(row, static_cast(SaveColumns::Name)); + movie_item* icon_item = static_cast(m_list->item(row, static_cast(SaveColumns::Icon))); if (!item || !icon_item) { @@ -692,7 +711,7 @@ void save_manager_dialog::WaitForRepaintThreads(bool abort) { for (int i = 0; i < m_list->rowCount(); i++) { - if (movie_item* item = static_cast(m_list->item(i, SaveColumns::Icon))) + if (movie_item* item = static_cast(m_list->item(i, static_cast(SaveColumns::Icon)))) { item->wait_for_icon_loading(abort); } @@ -706,7 +725,7 @@ void save_manager_dialog::text_changed(const QString& text) if (text.isEmpty()) return true; - for (int col = SaveColumns::Name; col < SaveColumns::Count; col++) + for (int col = static_cast(SaveColumns::Name); col < static_cast(SaveColumns::Count); col++) { const QTableWidgetItem* item = m_list->item(row, col); diff --git a/rpcs3/rpcs3qt/save_manager_dialog.h b/rpcs3/rpcs3qt/save_manager_dialog.h index 791ee9a814..819f9cf43d 100644 --- a/rpcs3/rpcs3qt/save_manager_dialog.h +++ b/rpcs3/rpcs3qt/save_manager_dialog.h @@ -39,7 +39,6 @@ Q_SIGNALS: void IconReady(int index, const QPixmap& new_icon); private: - void Init(); void UpdateList(); void UpdateIcons(); void ShowContextMenu(const QPoint& pos); @@ -49,6 +48,8 @@ private: std::vector GetSaveEntries(const std::string& base_dir); + QString get_header_text(int col) const; + game_list* m_list = nullptr; std::string m_dir; std::vector m_save_entries; diff --git a/rpcs3/rpcs3qt/savestate_manager_dialog.cpp b/rpcs3/rpcs3qt/savestate_manager_dialog.cpp index 93c87c01d8..44620bd24c 100644 --- a/rpcs3/rpcs3qt/savestate_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/savestate_manager_dialog.cpp @@ -63,15 +63,16 @@ savestate_manager_dialog::savestate_manager_dialog(std::shared_ptr m_game_table->setAlternatingRowColors(true); 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(col), new QTableWidgetItem(header_text)); - m_game_column_acts.append(new QAction(action_text, this)); + const int column = static_cast(col); + 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::name, tr("Game"), tr("Show Games")); - add_game_column(gui::savestate_game_list_columns::savestates, tr("Savestates"), tr("Show Savestates")); + add_game_column(gui::savestate_game_list_columns::icon); + add_game_column(gui::savestate_game_list_columns::name); + add_game_column(gui::savestate_game_list_columns::savestates); // Savestate Table m_savestate_table = new game_list(); @@ -94,16 +95,17 @@ savestate_manager_dialog::savestate_manager_dialog(std::shared_ptr m_savestate_table->setAlternatingRowColors(true); 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(col), new QTableWidgetItem(header_text)); - m_savestate_column_acts.append(new QAction(action_text, this)); + const int column = static_cast(col); + 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::compatible, tr("Compatible"), tr("Show Compatible")); - add_savestate_column(gui::savestate_list_columns::date, tr("Created"), tr("Show Created")); - add_savestate_column(gui::savestate_list_columns::path, tr("Path"), tr("Show Paths")); + add_savestate_column(gui::savestate_list_columns::name); + add_savestate_column(gui::savestate_list_columns::compatible); + add_savestate_column(gui::savestate_list_columns::date); + add_savestate_column(gui::savestate_list_columns::path); m_splitter = new QSplitter(); m_splitter->addWidget(m_game_table); @@ -220,6 +222,56 @@ savestate_manager_dialog::~savestate_manager_dialog() WaitAndAbortGameRepaintThreads(); } +QString savestate_manager_dialog::get_savestate_header_text(int col) const +{ + switch (static_cast(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(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(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(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) { ensure(!!game_savestates); @@ -625,6 +677,21 @@ void savestate_manager_dialog::PopulateGameTable() m_game_table->clearContents(); m_game_table->setRowCount(static_cast(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->blockSignals(true); @@ -681,6 +748,21 @@ void savestate_manager_dialog::PopulateSavestateTable() m_savestate_table->setRowCount(static_cast(savestates.size())); 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(savestates.size()); i++) { const savestate_data& savestate = savestates[i]; diff --git a/rpcs3/rpcs3qt/savestate_manager_dialog.h b/rpcs3/rpcs3qt/savestate_manager_dialog.h index 950fede559..28c3eddc8c 100644 --- a/rpcs3/rpcs3qt/savestate_manager_dialog.h +++ b/rpcs3/rpcs3qt/savestate_manager_dialog.h @@ -64,6 +64,12 @@ private: void closeEvent(QCloseEvent *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 m_gui_settings; std::vector m_game_info; @@ -74,8 +80,8 @@ private: game_list* m_savestate_table; //! UI element to display savestate stuff. game_list* m_game_table; //! UI element to display games. - QList m_savestate_column_acts; - QList m_game_column_acts; + std::map m_savestate_column_acts; + std::map m_game_column_acts; int m_game_icon_size_index = 25; QSize m_game_icon_size = QSize(m_game_icon_size_index, m_game_icon_size_index); diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp index bae7aae25d..d784f16f6e 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp @@ -100,16 +100,17 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr gui_s m_game_table->setAlternatingRowColors(true); 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(col), new QTableWidgetItem(header_text)); - m_game_column_acts.append(new QAction(action_text, this)); + const int column = static_cast(col); + 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::name, tr("Game"), tr("Show Games")); - add_game_column(gui::trophy_game_list_columns::progress, tr("Progress"), tr("Show Progress")); - add_game_column(gui::trophy_game_list_columns::trophies, tr("Trophies"), tr("Show Trophies")); + add_game_column(gui::trophy_game_list_columns::icon); + add_game_column(gui::trophy_game_list_columns::name); + add_game_column(gui::trophy_game_list_columns::progress); + add_game_column(gui::trophy_game_list_columns::trophies); // Trophy Table m_trophy_table = new game_list(); @@ -133,20 +134,21 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr gui_s m_trophy_table->setAlternatingRowColors(true); 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(col), new QTableWidgetItem(header_text)); - m_trophy_column_acts.append(new QAction(action_text, this)); + const int column = static_cast(col); + 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::name, tr("Name"), tr("Show Names")); - add_trophy_column(gui::trophy_list_columns::description, tr("Description"), tr("Show Descriptions")); - add_trophy_column(gui::trophy_list_columns::type, tr("Type"), tr("Show Types")); - add_trophy_column(gui::trophy_list_columns::is_unlocked, tr("Status"), tr("Show Status")); - add_trophy_column(gui::trophy_list_columns::id, tr("ID"), tr("Show IDs")); - add_trophy_column(gui::trophy_list_columns::platinum_link, tr("Platinum Relevant"), tr("Show Platinum Relevant")); - add_trophy_column(gui::trophy_list_columns::time_unlocked, tr("Time Unlocked"), tr("Show Time Unlocked")); + add_trophy_column(gui::trophy_list_columns::icon); + add_trophy_column(gui::trophy_list_columns::name); + add_trophy_column(gui::trophy_list_columns::description); + add_trophy_column(gui::trophy_list_columns::type); + add_trophy_column(gui::trophy_list_columns::is_unlocked); + add_trophy_column(gui::trophy_list_columns::id); + add_trophy_column(gui::trophy_list_columns::platinum_link); + add_trophy_column(gui::trophy_list_columns::time_unlocked); m_splitter = new QSplitter(); m_splitter->addWidget(m_game_table); @@ -406,6 +408,66 @@ trophy_manager_dialog::~trophy_manager_dialog() WaitAndAbortTrophyRepaintThreads(); } +QString trophy_manager_dialog::get_trophy_header_text(int col) const +{ + switch (static_cast(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(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(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(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) { 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->setRowCount(static_cast(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->blockSignals(true); @@ -1126,6 +1203,21 @@ void trophy_manager_dialog::PopulateTrophyTable() m_trophy_table->setRowCount(all_trophies); 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); placeholder.fill(Qt::transparent); diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.h b/rpcs3/rpcs3qt/trophy_manager_dialog.h index ead945114b..736f099b0e 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.h +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.h @@ -83,6 +83,12 @@ private: static QDateTime TickToDateTime(u64 tick); 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 m_gui_settings; std::vector> 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_game_table; //! UI element to display games. - QList m_trophy_column_acts; - QList m_game_column_acts; + std::map m_trophy_column_acts; + std::map m_game_column_acts; bool m_show_hidden_trophies = false; bool m_show_unlocked_trophies = true;