mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-10-10 05:51:13 -04:00
7f85c6e282
we have no use for it it's dead code most of the time users just complain about how the list isn't updated ok so what if we update it? great now its more noise added to the git, awesome even if we constantly update it, we introduce new breakage that makes keeping track of things extremely difficult either we integrate with emuready (would violate self containment principles) or we integrate with emuready in a similar fashion to eden news outlet (where we cache the list with some TTL of like 60 mins or whatever) that would be better respecting of "self containment" but would probably require having a quick way to update the list additively, probably filtered to the user's device instead of a global thing, and i dont think emuready has that granularity nor we have the "connect this to that" for the api stuffs another (which is what i propose here) is throw everything out of the window until someone can come up with something better Signed-off-by: lizzie <lizzie@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4201 Reviewed-by: Maufeat <sahyno1996@gmail.com> Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
209 lines
6.7 KiB
C++
209 lines
6.7 KiB
C++
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#include <QDir>
|
|
#include <QIcon>
|
|
#include <QJsonArray>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QThreadPool>
|
|
|
|
#include "common/logging.h"
|
|
#include "common/settings.h"
|
|
#include "core/core.h"
|
|
#include "core/file_sys/patch_manager.h"
|
|
#include "core/file_sys/registered_cache.h"
|
|
#include "core/hle/service/filesystem/filesystem.h"
|
|
#include "qt_common/config/uisettings.h"
|
|
#include "qt_common/qt_common.h"
|
|
#include "qt_common/util/game.h"
|
|
|
|
#include "qt_common/game_list/game_list_p.h"
|
|
#include "qt_common/game_list/model.h"
|
|
#include "qt_common/game_list/worker.h"
|
|
|
|
GameListModel::GameListModel(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
|
|
FileSys::ManualContentProvider* provider_,
|
|
const PlayTime::PlayTimeManager& play_time_manager_,
|
|
Core::System& system_, QObject* parent)
|
|
: QStandardItemModel{parent}, vfs{std::move(vfs_)}, provider{provider_},
|
|
play_time_manager{play_time_manager_}, system{system_} {
|
|
watcher = new QFileSystemWatcher(this);
|
|
external_watcher = new QFileSystemWatcher(this);
|
|
|
|
connect(watcher, &QFileSystemWatcher::directoryChanged, this,
|
|
&GameListModel::RefreshGameDirectory);
|
|
connect(external_watcher, &QFileSystemWatcher::directoryChanged, this,
|
|
&GameListModel::RefreshExternalContent);
|
|
|
|
ResetExternalWatcher();
|
|
|
|
insertColumns(0, COLUMN_COUNT);
|
|
RetranslateUI();
|
|
|
|
setSortRole(GameListItemPath::SortRole);
|
|
}
|
|
|
|
GameListModel::~GameListModel() = default;
|
|
|
|
void GameListModel::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
|
|
emit PopulatingStarted();
|
|
|
|
current_worker.reset();
|
|
removeRows(0, rowCount());
|
|
|
|
current_worker = std::make_unique<GameListWorker>(vfs, provider, game_dirs,
|
|
play_time_manager, system);
|
|
|
|
connect(current_worker.get(), &GameListWorker::DataAvailable, this, &GameListModel::WorkerEvent,
|
|
Qt::QueuedConnection);
|
|
|
|
QThreadPool::globalInstance()->start(current_worker.get());
|
|
}
|
|
|
|
void GameListModel::WorkerEvent() {
|
|
current_worker->ProcessEvents(this);
|
|
}
|
|
|
|
void GameListModel::AddDirEntry(GameListDir* entry_items) {
|
|
if (m_flat) {
|
|
return;
|
|
}
|
|
invisibleRootItem()->appendRow(entry_items);
|
|
}
|
|
|
|
void GameListModel::AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent) {
|
|
if (m_flat) {
|
|
invisibleRootItem()->appendRow(entry_items);
|
|
} else {
|
|
parent->appendRow(entry_items);
|
|
}
|
|
}
|
|
|
|
void GameListModel::DonePopulating(const QStringList& watch_list) {
|
|
emit ShowList(!IsEmpty());
|
|
|
|
if (!m_flat) {
|
|
invisibleRootItem()->appendRow(new GameListAddDir());
|
|
invisibleRootItem()->insertRow(0, new GameListFavorites());
|
|
|
|
for (const auto id : std::as_const(UISettings::values.favorited_ids)) {
|
|
AddFavorite(id);
|
|
}
|
|
}
|
|
|
|
emit PopulatingCompleted(watch_list);
|
|
}
|
|
|
|
bool GameListModel::IsEmpty() const {
|
|
for (int i = 0; i < rowCount(); i++) {
|
|
const QStandardItem* child = invisibleRootItem()->child(i);
|
|
const auto type = static_cast<GameListItemType>(child->type());
|
|
|
|
if (!child->hasChildren() &&
|
|
(type == GameListItemType::SdmcDir || type == GameListItemType::UserNandDir ||
|
|
type == GameListItemType::SysNandDir)) {
|
|
invisibleRootItem()->removeRow(child->row());
|
|
i--;
|
|
}
|
|
}
|
|
|
|
return !invisibleRootItem()->hasChildren();
|
|
}
|
|
|
|
void GameListModel::ToggleFavorite(u64 program_id) {
|
|
if (!UISettings::values.favorited_ids.contains(program_id)) {
|
|
UISettings::values.favorited_ids.append(program_id);
|
|
AddFavorite(program_id);
|
|
} else {
|
|
UISettings::values.favorited_ids.removeOne(program_id);
|
|
RemoveFavorite(program_id);
|
|
}
|
|
emit SaveConfig();
|
|
}
|
|
|
|
void GameListModel::AddFavorite(u64 program_id) {
|
|
auto* favorites_row = item(0);
|
|
|
|
for (int i = 1; i < rowCount() - 1; i++) {
|
|
const auto* folder = item(i);
|
|
for (int j = 0; j < folder->rowCount(); j++) {
|
|
if (folder->child(j)->data(GameListItemPath::ProgramIdRole).toULongLong() ==
|
|
program_id) {
|
|
QList<QStandardItem*> list;
|
|
for (int k = 0; k < COLUMN_COUNT; k++) {
|
|
list.append(folder->child(j, k)->clone());
|
|
}
|
|
list[0]->setData(folder->child(j)->data(GameListItem::SortRole),
|
|
GameListItem::SortRole);
|
|
list[0]->setText(folder->child(j)->data(Qt::DisplayRole).toString());
|
|
|
|
favorites_row->appendRow(list);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameListModel::RemoveFavorite(u64 program_id) {
|
|
auto* favorites_row = item(0);
|
|
|
|
for (int i = 0; i < favorites_row->rowCount(); i++) {
|
|
const auto* game = favorites_row->child(i);
|
|
if (game->data(GameListItemPath::ProgramIdRole).toULongLong() == program_id) {
|
|
favorites_row->removeRow(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameListModel::Repopulate() {
|
|
current_worker.reset();
|
|
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
|
|
PopulateAsync(UISettings::values.game_dirs);
|
|
}
|
|
|
|
void GameListModel::RefreshGameDirectory() {
|
|
ResetExternalWatcher();
|
|
if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) {
|
|
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
|
|
Repopulate();
|
|
}
|
|
}
|
|
|
|
void GameListModel::RefreshExternalContent() {
|
|
if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) {
|
|
LOG_INFO(Frontend, "External content directory changed. Clearing metadata cache.");
|
|
QtCommon::Game::ResetMetadata(false);
|
|
Repopulate();
|
|
}
|
|
}
|
|
|
|
void GameListModel::ResetExternalWatcher() {
|
|
auto watch_dirs = external_watcher->directories();
|
|
if (!watch_dirs.isEmpty()) {
|
|
external_watcher->removePaths(watch_dirs);
|
|
}
|
|
|
|
for (const std::string& dir : Settings::values.external_content_dirs) {
|
|
external_watcher->addPath(QString::fromStdString(dir));
|
|
}
|
|
}
|
|
|
|
void GameListModel::RetranslateUI() {
|
|
setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
|
|
setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
|
|
setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
|
|
setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
|
|
setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
|
|
setHeaderData(COLUMN_PLAY_TIME, Qt::Horizontal, tr("Play time"));
|
|
}
|
|
|
|
QFileSystemWatcher* GameListModel::GetWatcher() const {
|
|
return watcher;
|
|
}
|
|
|
|
void GameListModel::SetFlat(bool flat) {
|
|
m_flat = flat;
|
|
}
|