mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	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
					
				
					 2 changed files with 51 additions and 1 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue