Use QFileSystemWatcher to reload the game list when a change is detected. (#2555)
* Added a refresh game directory option to the file menu * Make the game list watcher recursive and have it start watching from the initial load * Rework game list watcher to be thread safe * Fix code style issues
This commit is contained in:
parent
4dee08b343
commit
26823cd38b
|
@ -39,6 +39,7 @@ GameList::GameList(QWidget* parent) : QWidget{parent} {
|
||||||
|
|
||||||
connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
|
connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
|
||||||
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
|
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
|
||||||
|
connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
|
||||||
|
|
||||||
// We must register all custom types with the Qt Automoc system so that we are able to use it
|
// We must register all custom types with the Qt Automoc system so that we are able to use it
|
||||||
// with signals/slots. In this case, QList falls under the umbrells of custom types.
|
// with signals/slots. In this case, QList falls under the umbrells of custom types.
|
||||||
|
@ -103,6 +104,12 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
|
||||||
item_model->removeRows(0, item_model->rowCount());
|
item_model->removeRows(0, item_model->rowCount());
|
||||||
|
|
||||||
emit ShouldCancelWorker();
|
emit ShouldCancelWorker();
|
||||||
|
|
||||||
|
auto watch_dirs = watcher.directories();
|
||||||
|
if (!watch_dirs.isEmpty()) {
|
||||||
|
watcher.removePaths(watch_dirs);
|
||||||
|
}
|
||||||
|
UpdateWatcherList(dir_path.toStdString(), deep_scan ? 256 : 0);
|
||||||
GameListWorker* worker = new GameListWorker(dir_path, deep_scan);
|
GameListWorker* worker = new GameListWorker(dir_path, deep_scan);
|
||||||
|
|
||||||
connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
|
connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
|
||||||
|
@ -140,6 +147,45 @@ static bool HasSupportedFileExtension(const std::string& file_name) {
|
||||||
return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
|
return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameList::RefreshGameDirectory() {
|
||||||
|
if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) {
|
||||||
|
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
|
||||||
|
PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the game list folder to the QFileSystemWatcher to check for updates.
|
||||||
|
*
|
||||||
|
* The file watcher will fire off an update to the game list when a change is detected in the game
|
||||||
|
* list folder.
|
||||||
|
*
|
||||||
|
* Notice: This method is run on the UI thread because QFileSystemWatcher is not thread safe and
|
||||||
|
* this function is fast enough to not stall the UI thread. If performance is an issue, it should
|
||||||
|
* be moved to another thread and properly locked to prevent concurrency issues.
|
||||||
|
*
|
||||||
|
* @param dir folder to check for changes in
|
||||||
|
* @param recursion 0 if recursion is disabled. Any positive number passed to this will add each
|
||||||
|
* directory recursively to the watcher and will update the file list if any of the folders
|
||||||
|
* change. The number determines how deep the recursion should traverse.
|
||||||
|
*/
|
||||||
|
void GameList::UpdateWatcherList(const std::string& dir, unsigned int recursion) {
|
||||||
|
const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
|
||||||
|
const std::string& virtual_name) -> bool {
|
||||||
|
std::string physical_name = directory + DIR_SEP + virtual_name;
|
||||||
|
|
||||||
|
if (FileUtil::IsDirectory(physical_name)) {
|
||||||
|
UpdateWatcherList(physical_name, recursion - 1);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
watcher.addPath(QString::fromStdString(dir));
|
||||||
|
if (recursion > 0) {
|
||||||
|
FileUtil::ForeachDirectoryEntry(nullptr, dir, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
|
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
|
||||||
const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
|
const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
|
||||||
const std::string& virtual_name) -> bool {
|
const std::string& virtual_name) -> bool {
|
||||||
|
@ -182,6 +228,6 @@ void GameListWorker::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameListWorker::Cancel() {
|
void GameListWorker::Cancel() {
|
||||||
disconnect(this, nullptr, nullptr, nullptr);
|
this->disconnect();
|
||||||
stop_processing = true;
|
stop_processing = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
#include <QModelIndex>
|
#include <QModelIndex>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QStandardItem>
|
#include <QStandardItem>
|
||||||
|
@ -46,8 +47,11 @@ private:
|
||||||
void DonePopulating();
|
void DonePopulating();
|
||||||
|
|
||||||
void PopupContextMenu(const QPoint& menu_location);
|
void PopupContextMenu(const QPoint& menu_location);
|
||||||
|
void UpdateWatcherList(const std::string& path, unsigned int recursion);
|
||||||
|
void RefreshGameDirectory();
|
||||||
|
|
||||||
QTreeView* tree_view = nullptr;
|
QTreeView* tree_view = nullptr;
|
||||||
QStandardItemModel* item_model = nullptr;
|
QStandardItemModel* item_model = nullptr;
|
||||||
GameListWorker* current_worker = nullptr;
|
GameListWorker* current_worker = nullptr;
|
||||||
|
QFileSystemWatcher watcher;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue