From bba1cf4b6e7237227e65b9eb1740ced639882603 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 7 Dec 2025 12:17:07 +0100 Subject: [PATCH] Qt: ask user for install options before starting the installation --- rpcs3/rpcs3qt/main_window.cpp | 220 +++++++++------------------ rpcs3/rpcs3qt/main_window.h | 1 + rpcs3/rpcs3qt/pkg_install_dialog.cpp | 90 +++++++---- rpcs3/rpcs3qt/pkg_install_dialog.h | 12 +- 4 files changed, 142 insertions(+), 181 deletions(-) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index d4e52b8025..3ba64487e6 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -801,7 +801,7 @@ bool main_window::InstallPackages(QStringList file_paths, bool from_boot) if (file_paths.count() == 1) { - const QString file_path = file_paths.front(); + const QString& file_path = file_paths.front(); const QFileInfo file_info(file_path); if (file_info.isDir()) @@ -819,92 +819,6 @@ bool main_window::InstallPackages(QStringList file_paths, bool from_boot) return InstallPackages(dir_file_paths, from_boot); } - - if (file_info.suffix().compare("pkg", Qt::CaseInsensitive) == 0) - { - compat::package_info info = game_compatibility::GetPkgInfo(file_path, m_game_list_frame ? m_game_list_frame->GetGameCompatibility() : nullptr); - - if (!info.is_valid) - { - QMessageBox::warning(this, tr("Invalid package!"), tr("The selected package is invalid!\n\nPath:\n%0").arg(file_path)); - return false; - } - - if (info.type != compat::package_type::other) - { - if (info.type == compat::package_type::dlc) - { - info.local_cat = tr("\nDLC", "Block for package type (DLC)"); - } - else - { - info.local_cat = tr("\nUpdate", "Block for package type (Update)"); - } - } - else if (!info.local_cat.isEmpty()) - { - info.local_cat = tr("\n%0", "Block for package type").arg(info.local_cat); - } - - if (!info.title_id.isEmpty()) - { - info.title_id = tr("\n%0", "Block for Title ID").arg(info.title_id); - } - - if (!info.version.isEmpty()) - { - info.version = tr("\nVersion %0", "Block for Version").arg(info.version); - } - - if (!info.changelog.isEmpty()) - { - info.changelog = tr("Changelog:\n%0", "Block for Changelog").arg(info.changelog); - } - - u64 free_space = 0; - - // Retrieve disk space info on data path's drive - if (fs::device_stat stat{}; fs::statfs(rpcs3::utils::get_hdd0_dir(), stat)) - { - free_space = stat.avail_free; - } - - const QString installation_info = - tr("Installation path: %0\nAvailable disk space: %1%2\nRequired disk space: %3") - .arg(QString::fromStdString(rpcs3::utils::get_hdd0_game_dir())) - .arg(gui::utils::format_byte_size(free_space)) - .arg(info.data_size <= free_space ? QString() : tr(" - NOT ENOUGH SPACE")) - .arg(gui::utils::format_byte_size(info.data_size)); - - const QString info_string = QStringLiteral("%0\n\n%1%2%3%4").arg(file_info.fileName()).arg(info.title).arg(info.local_cat).arg(info.title_id).arg(info.version); - QString message = tr("Do you want to install this package?\n\n%0\n\n%1").arg(info_string).arg(installation_info); - - QMessageBox mb(QMessageBox::Icon::Question, tr("PKG Decrypter / Installer"), gui::utils::make_paragraph(message), QMessageBox::Yes | QMessageBox::No, this); - mb.setDefaultButton(QMessageBox::No); - mb.setTextFormat(Qt::RichText); // Support HTML tags - mb.button(QMessageBox::Yes)->setEnabled(info.data_size <= free_space); - - if (!info.changelog.isEmpty()) - { - mb.setInformativeText(tr("To see the changelog, please click \"Show Details\".")); - mb.setDetailedText(info.changelog); - - // Smartass hack to make the unresizeable message box wide enough for the changelog - const int log_width = QLabel(info.changelog).sizeHint().width(); - while (QLabel(message).sizeHint().width() < log_width) - { - message += " "; - } - - mb.setText(message); - } - - if (mb.exec() != QMessageBox::Yes) - { - gui_log.notice("PKG: Cancelled installation from drop.\n%s\n%s", info_string, info.changelog); - return true; - } - } } // Install rap files if available @@ -983,28 +897,22 @@ bool main_window::HandlePackageInstallation(QStringList file_paths, bool from_bo } std::vector packages; + bool precompile_caches = false; + bool create_desktop_shortcuts = false; + bool create_app_shortcut = false; game_compatibility* compat = m_game_list_frame ? m_game_list_frame->GetGameCompatibility() : nullptr; - if (file_paths.size() > 1) + // Let the user choose the packages to install and select the order in which they shall be installed. + pkg_install_dialog dlg(file_paths, compat, this); + connect(&dlg, &QDialog::accepted, this, [&]() { - // Let the user choose the packages to install and select the order in which they shall be installed. - pkg_install_dialog dlg(file_paths, compat, this); - connect(&dlg, &QDialog::accepted, this, [&packages, &dlg]() - { - packages = dlg.GetPathsToInstall(); - }); - dlg.exec(); - } - else - { - packages.push_back(game_compatibility::GetPkgInfo(file_paths.front(), compat)); - } - - if (packages.empty()) - { - return true; - } + packages = dlg.get_paths_to_install(); + precompile_caches = dlg.precompile_caches(); + create_desktop_shortcuts = dlg.create_desktop_shortcuts(); + create_app_shortcut = dlg.create_app_shortcut(); + }); + dlg.exec(); if (!from_boot) { @@ -1178,7 +1086,7 @@ bool main_window::HandlePackageInstallation(QStringList file_paths, bool from_bo if (!bootable_paths_installed.empty()) { - m_game_list_frame->AddRefreshedSlot([this, paths = std::move(bootable_paths_installed)](std::set& claimed_paths) mutable + m_game_list_frame->AddRefreshedSlot([this, create_desktop_shortcuts, precompile_caches, create_app_shortcut, paths = std::move(bootable_paths_installed)](std::set& claimed_paths) mutable { // Try to claim operations on ID for (auto it = paths.begin(); it != paths.end();) @@ -1196,7 +1104,12 @@ bool main_window::HandlePackageInstallation(QStringList file_paths, bool from_bo } } - ShowOptionalGamePreparations(tr("Success!"), tr("Successfully installed software from package(s)!"), std::move(paths)); + CreateShortCuts(paths, create_desktop_shortcuts, create_app_shortcut); + + if (precompile_caches) + { + PrecompileCachesFromInstalledPackages(paths); + } }); } @@ -2431,49 +2344,7 @@ void main_window::ShowOptionalGamePreparations(const QString& title, const QStri dlg->hide(); dlg->accept(); - std::set locations; - -#ifdef _WIN32 - locations.insert(gui::utils::shortcut_location::rpcs3_shortcuts); -#endif - if (create_desktop_shortcuts) - { - locations.insert(gui::utils::shortcut_location::desktop); - } - - if (create_app_shortcut) - { - locations.insert(gui::utils::shortcut_location::applications); - } - - if (!locations.empty()) - { - std::vector game_data_shortcuts; - - for (const auto& [boot_path, title_id] : paths) - { - for (const game_info& gameinfo : m_game_list_frame->GetGameInfo()) - { - if (gameinfo && gameinfo->info.serial == title_id.toStdString()) - { - if (Emu.IsPathInsideDir(boot_path, gameinfo->info.path)) - { - if (!locations.empty()) - { - game_data_shortcuts.push_back(gameinfo); - } - } - - break; - } - } - } - - if (!game_data_shortcuts.empty() && !locations.empty()) - { - m_game_list_frame->CreateShortcuts(game_data_shortcuts, locations); - } - } + CreateShortCuts(paths, create_desktop_shortcuts, create_app_shortcut); if (precompile_caches) { @@ -2485,6 +2356,55 @@ void main_window::ShowOptionalGamePreparations(const QString& title, const QStri dlg->open(); } +void main_window::CreateShortCuts(const std::map& paths, bool create_desktop_shortcuts, bool create_app_shortcut) +{ + if (paths.empty()) return; + + std::set locations; + +#ifdef _WIN32 + locations.insert(gui::utils::shortcut_location::rpcs3_shortcuts); +#endif + if (create_desktop_shortcuts) + { + locations.insert(gui::utils::shortcut_location::desktop); + } + + if (create_app_shortcut) + { + locations.insert(gui::utils::shortcut_location::applications); + } + + if (!locations.empty()) + { + std::vector game_data_shortcuts; + + for (const auto& [boot_path, title_id] : paths) + { + for (const game_info& gameinfo : m_game_list_frame->GetGameInfo()) + { + if (gameinfo && gameinfo->info.serial == title_id.toStdString()) + { + if (Emu.IsPathInsideDir(boot_path, gameinfo->info.path)) + { + if (!locations.empty()) + { + game_data_shortcuts.push_back(gameinfo); + } + } + + break; + } + } + } + + if (!game_data_shortcuts.empty() && !locations.empty()) + { + m_game_list_frame->CreateShortcuts(game_data_shortcuts, locations); + } + } +} + void main_window::PrecompileCachesFromInstalledPackages(const std::map& bootable_paths) { std::vector game_data; diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index 960a70c722..40d874e11c 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -149,6 +149,7 @@ private: static bool InstallFileInExData(const std::string& extension, const QString& path, const std::string& filename); bool HandlePackageInstallation(QStringList file_paths, bool from_boot); + void CreateShortCuts(const std::map& paths, bool create_desktop_shortcuts, bool create_app_shortcut); void HandlePupInstallation(const QString& file_path, const QString& dir_path = ""); void ExtractPup(); diff --git a/rpcs3/rpcs3qt/pkg_install_dialog.cpp b/rpcs3/rpcs3qt/pkg_install_dialog.cpp index 370f4d053c..9b0f926484 100644 --- a/rpcs3/rpcs3qt/pkg_install_dialog.cpp +++ b/rpcs3/rpcs3qt/pkg_install_dialog.cpp @@ -7,12 +7,11 @@ #include "Emu/system_utils.hpp" #include "Utilities/File.h" -#include #include #include #include -#include #include +#include enum Roles { @@ -105,8 +104,6 @@ pkg_install_dialog::pkg_install_dialog(const QStringList& paths, game_compatibil item->setData(Roles::VersionRole, info.version); item->setData(Roles::DataSizeRole, static_cast(info.data_size)); item->setToolTip(tooltip); - item->setFlags(item->flags() | Qt::ItemIsUserCheckable); - item->setCheckState(Qt::Checked); } m_dir_list->sortItems(); @@ -134,42 +131,79 @@ pkg_install_dialog::pkg_install_dialog(const QStringList& paths, game_compatibil } }); - connect(m_dir_list, &QListWidget::itemChanged, this, [this, installation_info, buttons](QListWidgetItem*) + QHBoxLayout* hbox = nullptr; + if (m_dir_list->count() > 1) { - UpdateInfo(installation_info, buttons); - }); + for (int i = 0; i < m_dir_list->count(); i++) + { + if (QListWidgetItem* item = m_dir_list->item(i)) + { + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Checked); + } + } + + connect(m_dir_list, &QListWidget::itemChanged, this, [this, installation_info, buttons](QListWidgetItem*) + { + update_info(installation_info, buttons); + }); - QToolButton* move_up = new QToolButton; - move_up->setArrowType(Qt::UpArrow); - move_up->setToolTip(tr("Move selected item up")); - connect(move_up, &QToolButton::clicked, this, [this]() { MoveItem(-1); }); + QToolButton* move_up = new QToolButton; + move_up->setArrowType(Qt::UpArrow); + move_up->setToolTip(tr("Move selected item up")); + connect(move_up, &QToolButton::clicked, this, [this]() { move_item(-1); }); - QToolButton* move_down = new QToolButton; - move_down->setArrowType(Qt::DownArrow); - move_down->setToolTip(tr("Move selected item down")); - connect(move_down, &QToolButton::clicked, this, [this]() { MoveItem(1); }); + QToolButton* move_down = new QToolButton; + move_down->setArrowType(Qt::DownArrow); + move_down->setToolTip(tr("Move selected item down")); + connect(move_down, &QToolButton::clicked, this, [this]() { move_item(1); }); - QHBoxLayout* hbox = new QHBoxLayout; - hbox->addStretch(); - hbox->addWidget(move_up); - hbox->addWidget(move_down); + hbox = new QHBoxLayout; + hbox->addStretch(); + hbox->addWidget(move_up); + hbox->addWidget(move_down); + } - QLabel* description = new QLabel(tr("You are about to install multiple packages.\nReorder and/or exclude them if needed, then click \"Install\" to proceed.")); + QLabel* description = new QLabel(m_dir_list->count() == 1 + ? tr("Do you want to install this package?") + : tr("You are about to install multiple packages.\nReorder and/or exclude them if needed, then click \"Install\" to proceed.") + ); + QLabel* label = new QLabel(tr("Would you like to precompile caches and install shortcuts to the installed software?")); + + QCheckBox* precompile_check = new QCheckBox(tr("Precompile caches")); + connect(precompile_check, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state){ m_precompile_caches = state != Qt::CheckState::Unchecked; }); + precompile_check->setChecked(true); + + QCheckBox* desk_check = new QCheckBox(tr("Add desktop shortcut(s)")); + connect(desk_check, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state){ m_create_desktop_shortcuts = state != Qt::CheckState::Unchecked; }); + +#ifdef _WIN32 + QCheckBox* quick_check = new QCheckBox(tr("Add Start menu shortcut(s)")); +#elif defined(__APPLE__) + QCheckBox* quick_check = new QCheckBox(tr("Add dock shortcut(s)")); +#else + QCheckBox* quick_check = new QCheckBox(tr("Add launcher shortcut(s)")); +#endif + connect(quick_check, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state){ m_create_app_shortcut = state != Qt::CheckState::Unchecked; }); QVBoxLayout* vbox = new QVBoxLayout; vbox->addWidget(description); - vbox->addLayout(hbox); + if (hbox) vbox->addLayout(hbox); vbox->addWidget(m_dir_list); + vbox->addWidget(label); + vbox->addWidget(precompile_check); + vbox->addWidget(desk_check); + vbox->addWidget(quick_check); vbox->addWidget(installation_info); vbox->addWidget(buttons); setLayout(vbox); - setWindowTitle(tr("Batch PKG Installation")); + setWindowTitle(tr("PKG Installation")); setObjectName("pkg_install_dialog"); - UpdateInfo(installation_info, buttons); // Just to show and check available and required size + update_info(installation_info, buttons); // Just to show and check available and required size } -void pkg_install_dialog::UpdateInfo(QLabel* installation_info, QDialogButtonBox* buttons) const +void pkg_install_dialog::update_info(QLabel* installation_info, QDialogButtonBox* buttons) const { u64 data_size = 0; u64 free_space = 0; @@ -182,7 +216,7 @@ void pkg_install_dialog::UpdateInfo(QLabel* installation_info, QDialogButtonBox* for (int i = 0; i < m_dir_list->count(); i++) { - if (m_dir_list->item(i)->checkState() == Qt::Checked) + if (m_dir_list->count() == 1 || m_dir_list->item(i)->checkState() == Qt::Checked) { data_size += m_dir_list->item(i)->data(Roles::DataSizeRole).toULongLong(); } @@ -197,7 +231,7 @@ void pkg_install_dialog::UpdateInfo(QLabel* installation_info, QDialogButtonBox* buttons->button(QDialogButtonBox::Ok)->setEnabled(data_size && (data_size <= free_space)); } -void pkg_install_dialog::MoveItem(int offset) const +void pkg_install_dialog::move_item(int offset) const { const int src_index = m_dir_list->currentRow(); const int dest_index = src_index + offset; @@ -211,14 +245,14 @@ void pkg_install_dialog::MoveItem(int offset) const } } -std::vector pkg_install_dialog::GetPathsToInstall() const +std::vector pkg_install_dialog::get_paths_to_install() const { std::vector result; for (int i = 0; i < m_dir_list->count(); i++) { const QListWidgetItem* item = m_dir_list->item(i); - if (item && item->checkState() == Qt::Checked) + if (item && (m_dir_list->count() == 1 || item->checkState() == Qt::Checked)) { compat::package_info info; info.path = item->data(Roles::FullPathRole).toString(); diff --git a/rpcs3/rpcs3qt/pkg_install_dialog.h b/rpcs3/rpcs3qt/pkg_install_dialog.h index 2122259a1d..82593ab6b5 100644 --- a/rpcs3/rpcs3qt/pkg_install_dialog.h +++ b/rpcs3/rpcs3qt/pkg_install_dialog.h @@ -18,11 +18,17 @@ class pkg_install_dialog : public QDialog public: explicit pkg_install_dialog(const QStringList& paths, game_compatibility* compat, QWidget* parent = nullptr); - std::vector GetPathsToInstall() const; + std::vector get_paths_to_install() const; + bool precompile_caches() const { return m_precompile_caches; } + bool create_desktop_shortcuts() const { return m_create_desktop_shortcuts; } + bool create_app_shortcut() const { return m_create_app_shortcut; } private: - void UpdateInfo(QLabel* installation_info, QDialogButtonBox* buttons) const; - void MoveItem(int offset) const; + void update_info(QLabel* installation_info, QDialogButtonBox* buttons) const; + void move_item(int offset) const; QListWidget* m_dir_list; + bool m_precompile_caches = false; + bool m_create_desktop_shortcuts = false; + bool m_create_app_shortcut = false; };