|
|
|
@ -1,7 +1,7 @@ |
|
|
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
|
|
#include "yuzu/game_list.h"
|
|
|
|
#include <regex>
|
|
|
|
#include <QApplication>
|
|
|
|
#include <QDir>
|
|
|
|
#include <QFileInfo>
|
|
|
|
@ -13,20 +13,21 @@ |
|
|
|
#include <QMenu>
|
|
|
|
#include <QThreadPool>
|
|
|
|
#include <QToolButton>
|
|
|
|
#include <fmt/ranges.h>
|
|
|
|
#include "common/common_types.h"
|
|
|
|
#include "common/logging/log.h"
|
|
|
|
#include "core/core.h"
|
|
|
|
#include "core/file_sys/patch_manager.h"
|
|
|
|
#include "core/file_sys/registered_cache.h"
|
|
|
|
#include "qt_common/util/game.h"
|
|
|
|
#include "core/hle/service/filesystem/filesystem.h"
|
|
|
|
#include "qt_common/config/uisettings.h"
|
|
|
|
#include "qt_common/util/game.h"
|
|
|
|
#include "yuzu/compatibility_list.h"
|
|
|
|
#include "yuzu/game_list.h"
|
|
|
|
#include "yuzu/game_list_p.h"
|
|
|
|
#include "yuzu/game_list_worker.h"
|
|
|
|
#include "yuzu/main.h"
|
|
|
|
#include "yuzu/util/controller_navigation.h"
|
|
|
|
#include <fmt/ranges.h>
|
|
|
|
#include <regex>
|
|
|
|
|
|
|
|
GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist_, QObject* parent) |
|
|
|
: QObject(parent), gamelist{gamelist_} {} |
|
|
|
@ -318,7 +319,8 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid |
|
|
|
: QWidget{parent}, vfs{std::move(vfs_)}, provider{provider_}, |
|
|
|
play_time_manager{play_time_manager_}, system{system_} { |
|
|
|
watcher = new QFileSystemWatcher(this); |
|
|
|
connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); |
|
|
|
connect(watcher, &QFileSystemWatcher::directoryChanged, this, |
|
|
|
&GameList::OnWatchedDirectoryChanged); |
|
|
|
|
|
|
|
this->main_window = parent; |
|
|
|
layout = new QVBoxLayout; |
|
|
|
@ -486,14 +488,21 @@ void GameList::DonePopulating(const QStringList& watch_list) { |
|
|
|
if (!watch_dirs.isEmpty()) { |
|
|
|
watcher->removePaths(watch_dirs); |
|
|
|
} |
|
|
|
|
|
|
|
QStringList all_watch_paths = watch_list; |
|
|
|
for (const auto& dir : Settings::values.external_dirs) { |
|
|
|
all_watch_paths.append(QString::fromStdString(dir)); |
|
|
|
} |
|
|
|
all_watch_paths.removeDuplicates(); |
|
|
|
|
|
|
|
// Workaround: Add the watch paths in chunks to allow the gui to refresh
|
|
|
|
// This prevents the UI from stalling when a large number of watch paths are added
|
|
|
|
// Also artificially caps the watcher to a certain number of directories
|
|
|
|
constexpr int LIMIT_WATCH_DIRECTORIES = 5000; |
|
|
|
constexpr int SLICE_SIZE = 25; |
|
|
|
int len = (std::min)(static_cast<int>(watch_list.size()), LIMIT_WATCH_DIRECTORIES); |
|
|
|
int len = (std::min)(static_cast<int>(all_watch_paths.size()), LIMIT_WATCH_DIRECTORIES); |
|
|
|
for (int i = 0; i < len; i += SLICE_SIZE) { |
|
|
|
watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE)); |
|
|
|
watcher->addPaths(all_watch_paths.mid(i, i + SLICE_SIZE)); |
|
|
|
QCoreApplication::processEvents(); |
|
|
|
} |
|
|
|
tree_view->setEnabled(true); |
|
|
|
@ -619,26 +628,32 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri |
|
|
|
emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::Update); |
|
|
|
}); |
|
|
|
connect(remove_dlc, &QAction::triggered, [this, program_id]() { |
|
|
|
emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::AddOnContent); |
|
|
|
emit RemoveInstalledEntryRequested(program_id, |
|
|
|
QtCommon::Game::InstalledEntryType::AddOnContent); |
|
|
|
}); |
|
|
|
connect(remove_gl_shader_cache, &QAction::triggered, [this, program_id, path]() { |
|
|
|
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::GlShaderCache, path); |
|
|
|
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::GlShaderCache, |
|
|
|
path); |
|
|
|
}); |
|
|
|
connect(remove_vk_shader_cache, &QAction::triggered, [this, program_id, path]() { |
|
|
|
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::VkShaderCache, path); |
|
|
|
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::VkShaderCache, |
|
|
|
path); |
|
|
|
}); |
|
|
|
connect(remove_shader_cache, &QAction::triggered, [this, program_id, path]() { |
|
|
|
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::AllShaderCache, path); |
|
|
|
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::AllShaderCache, |
|
|
|
path); |
|
|
|
}); |
|
|
|
connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() { |
|
|
|
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CustomConfiguration, path); |
|
|
|
emit RemoveFileRequested(program_id, |
|
|
|
QtCommon::Game::GameListRemoveTarget::CustomConfiguration, path); |
|
|
|
}); |
|
|
|
connect(set_play_time, &QAction::triggered, |
|
|
|
[this, program_id]() { emit SetPlayTimeRequested(program_id); }); |
|
|
|
connect(remove_play_time_data, &QAction::triggered, |
|
|
|
[this, program_id]() { emit RemovePlayTimeRequested(program_id); }); |
|
|
|
connect(remove_cache_storage, &QAction::triggered, [this, program_id, path] { |
|
|
|
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CacheStorage, path); |
|
|
|
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CacheStorage, |
|
|
|
path); |
|
|
|
}); |
|
|
|
connect(dump_romfs, &QAction::triggered, [this, program_id, path]() { |
|
|
|
emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::Normal); |
|
|
|
@ -665,8 +680,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri |
|
|
|
connect(properties, &QAction::triggered, |
|
|
|
[this, path]() { emit OpenPerGameGeneralRequested(path); }); |
|
|
|
|
|
|
|
connect(ryujinx, &QAction::triggered, [this, program_id]() { emit LinkToRyujinxRequested(program_id); |
|
|
|
}); |
|
|
|
connect(ryujinx, &QAction::triggered, |
|
|
|
[this, program_id]() { emit LinkToRyujinxRequested(program_id); }); |
|
|
|
}; |
|
|
|
|
|
|
|
void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) { |
|
|
|
@ -830,8 +845,7 @@ QStandardItemModel* GameList::GetModel() const { |
|
|
|
return item_model; |
|
|
|
} |
|
|
|
|
|
|
|
void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) |
|
|
|
{ |
|
|
|
void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) { |
|
|
|
tree_view->setEnabled(false); |
|
|
|
|
|
|
|
// Update the columns in case UISettings has changed
|
|
|
|
@ -848,12 +862,8 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) |
|
|
|
item_model->removeRows(0, item_model->rowCount()); |
|
|
|
search_field->clear(); |
|
|
|
|
|
|
|
current_worker = std::make_unique<GameListWorker>(vfs, |
|
|
|
provider, |
|
|
|
game_dirs, |
|
|
|
compatibility_list, |
|
|
|
play_time_manager, |
|
|
|
system); |
|
|
|
current_worker = std::make_unique<GameListWorker>(vfs, provider, game_dirs, compatibility_list, |
|
|
|
play_time_manager, system); |
|
|
|
|
|
|
|
// Get events from the worker as data becomes available
|
|
|
|
connect(current_worker.get(), &GameListWorker::DataAvailable, this, &GameList::WorkerEvent, |
|
|
|
@ -882,8 +892,7 @@ const QStringList GameList::supported_file_extensions = { |
|
|
|
QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"), |
|
|
|
QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; |
|
|
|
|
|
|
|
void GameList::RefreshGameDirectory() |
|
|
|
{ |
|
|
|
void GameList::RefreshGameDirectory() { |
|
|
|
if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { |
|
|
|
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); |
|
|
|
PopulateAsync(UISettings::values.game_dirs); |
|
|
|
@ -967,6 +976,17 @@ GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} |
|
|
|
|
|
|
|
GameListPlaceholder::~GameListPlaceholder() = default; |
|
|
|
|
|
|
|
void GameList::OnWatchedDirectoryChanged(const QString& path) { |
|
|
|
LOG_INFO(Frontend, "Change detected in watched directory {}. Reloading content.", |
|
|
|
path.toStdString()); |
|
|
|
|
|
|
|
system.GetFileSystemController().RebuildExternalContentIndex(); |
|
|
|
|
|
|
|
QtCommon::Game::ResetMetadata(false); |
|
|
|
|
|
|
|
RefreshGameDirectory(); |
|
|
|
} |
|
|
|
|
|
|
|
void GameListPlaceholder::onUpdateThemedIcons() { |
|
|
|
image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); |
|
|
|
} |
|
|
|
|