Browse Source

Merge pull request #91064 from KoBeWi/scan_everything

Make scan for projects threaded
Thaddeus Crews 8 months ago
parent
commit
e0968eee8c

+ 88 - 15
editor/project_manager/project_list.cpp

@@ -41,8 +41,10 @@
 #include "editor/project_manager/project_tag.h"
 #include "editor/project_manager/project_tag.h"
 #include "editor/themes/editor_scale.h"
 #include "editor/themes/editor_scale.h"
 #include "scene/gui/button.h"
 #include "scene/gui/button.h"
+#include "scene/gui/dialogs.h"
 #include "scene/gui/label.h"
 #include "scene/gui/label.h"
 #include "scene/gui/line_edit.h"
 #include "scene/gui/line_edit.h"
+#include "scene/gui/progress_bar.h"
 #include "scene/gui/texture_button.h"
 #include "scene/gui/texture_button.h"
 #include "scene/gui/texture_rect.h"
 #include "scene/gui/texture_rect.h"
 #include "scene/resources/image_texture.h"
 #include "scene/resources/image_texture.h"
@@ -358,7 +360,7 @@ bool ProjectList::project_feature_looks_like_version(const String &p_feature) {
 void ProjectList::_notification(int p_what) {
 void ProjectList::_notification(int p_what) {
 	switch (p_what) {
 	switch (p_what) {
 		case NOTIFICATION_PROCESS: {
 		case NOTIFICATION_PROCESS: {
-			// Load icons as a coroutine to speed up launch when you have hundreds of projects
+			// Load icons as a coroutine to speed up launch when you have hundreds of projects.
 			if (_icon_load_index < _projects.size()) {
 			if (_icon_load_index < _projects.size()) {
 				Item &item = _projects.write[_icon_load_index];
 				Item &item = _projects.write[_icon_load_index];
 				if (item.control->should_load_project_icon()) {
 				if (item.control->should_load_project_icon()) {
@@ -366,13 +368,62 @@ void ProjectList::_notification(int p_what) {
 				}
 				}
 				_icon_load_index++;
 				_icon_load_index++;
 
 
+				// Scan directories in thread to avoid blocking the window.
+			} else if (scan_data && scan_data->scan_in_progress.is_set()) {
+				// Wait for the thread.
 			} else {
 			} else {
 				set_process(false);
 				set_process(false);
+				if (scan_data) {
+					_scan_finished();
+				}
 			}
 			}
 		} break;
 		} break;
 	}
 	}
 }
 }
 
 
+// Projects scan.
+
+void ProjectList::_scan_thread(void *p_scan_data) {
+	ScanData *scan_data = static_cast<ScanData *>(p_scan_data);
+
+	for (const String &base_path : scan_data->paths_to_scan) {
+		print_verbose(vformat("Scanning for projects in \"%s\".", base_path));
+		_scan_folder_recursive(base_path, &scan_data->found_projects, scan_data->scan_in_progress);
+
+		if (!scan_data->scan_in_progress.is_set()) {
+			print_verbose("Scan aborted.");
+			break;
+		}
+	}
+	print_verbose(vformat("Found %d project(s).", scan_data->found_projects.size()));
+	scan_data->scan_in_progress.clear();
+}
+
+void ProjectList::_scan_finished() {
+	if (scan_data->scan_in_progress.is_set()) {
+		// Abort scanning.
+		scan_data->scan_in_progress.clear();
+	}
+
+	scan_data->thread->wait_to_finish();
+	memdelete(scan_data->thread);
+	if (scan_progress) {
+		scan_progress->hide();
+	}
+
+	for (const String &E : scan_data->found_projects) {
+		add_project(E, false);
+	}
+	memdelete(scan_data);
+	scan_data = nullptr;
+
+	save_config();
+
+	if (ProjectManager::get_singleton()->is_initialized()) {
+		update_project_list();
+	}
+}
+
 // Initialization & loading.
 // Initialization & loading.
 
 
 void ProjectList::_migrate_config() {
 void ProjectList::_migrate_config() {
@@ -624,25 +675,39 @@ void ProjectList::find_projects(const String &p_path) {
 }
 }
 
 
 void ProjectList::find_projects_multiple(const PackedStringArray &p_paths) {
 void ProjectList::find_projects_multiple(const PackedStringArray &p_paths) {
-	List<String> projects;
+	if (!scan_progress && is_inside_tree()) {
+		scan_progress = memnew(AcceptDialog);
+		scan_progress->set_title(TTR("Scanning"));
+		scan_progress->set_ok_button_text(TTR("Cancel"));
 
 
-	for (int i = 0; i < p_paths.size(); i++) {
-		const String &base_path = p_paths.get(i);
-		print_verbose(vformat("Scanning for projects in \"%s\".", base_path));
+		VBoxContainer *vb = memnew(VBoxContainer);
+		scan_progress->add_child(vb);
 
 
-		_scan_folder_recursive(base_path, &projects);
-		print_verbose(vformat("Found %d project(s).", projects.size()));
-	}
+		Label *label = memnew(Label);
+		label->set_text(TTR("Scanning for projects..."));
+		vb->add_child(label);
 
 
-	for (const String &E : projects) {
-		add_project(E, false);
+		ProgressBar *progress = memnew(ProgressBar);
+		progress->set_indeterminate(true);
+		vb->add_child(progress);
+
+		add_child(scan_progress);
+		scan_progress->connect(SceneStringName(confirmed), callable_mp(this, &ProjectList::_scan_finished));
+		scan_progress->connect("canceled", callable_mp(this, &ProjectList::_scan_finished));
 	}
 	}
 
 
-	save_config();
+	scan_data = memnew(ScanData);
+	scan_data->paths_to_scan = p_paths;
+	scan_data->scan_in_progress.set();
 
 
-	if (ProjectManager::get_singleton()->is_initialized()) {
-		update_project_list();
+	scan_data->thread = memnew(Thread);
+	scan_data->thread->start(_scan_thread, scan_data);
+
+	if (scan_progress) {
+		scan_progress->reset_size();
+		scan_progress->popup_centered();
 	}
 	}
+	set_process(true);
 }
 }
 
 
 void ProjectList::load_project_list() {
 void ProjectList::load_project_list() {
@@ -656,7 +721,11 @@ void ProjectList::load_project_list() {
 	}
 	}
 }
 }
 
 
-void ProjectList::_scan_folder_recursive(const String &p_path, List<String> *r_projects) {
+void ProjectList::_scan_folder_recursive(const String &p_path, List<String> *r_projects, const SafeFlag &p_scan_active) {
+	if (!p_scan_active.is_set()) {
+		return;
+	}
+
 	Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 	Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 	Error error = da->change_dir(p_path);
 	Error error = da->change_dir(p_path);
 	ERR_FAIL_COND_MSG(error != OK, vformat("Failed to open the path \"%s\" for scanning (code %d).", p_path, error));
 	ERR_FAIL_COND_MSG(error != OK, vformat("Failed to open the path \"%s\" for scanning (code %d).", p_path, error));
@@ -664,8 +733,12 @@ void ProjectList::_scan_folder_recursive(const String &p_path, List<String> *r_p
 	da->list_dir_begin();
 	da->list_dir_begin();
 	String n = da->get_next();
 	String n = da->get_next();
 	while (!n.is_empty()) {
 	while (!n.is_empty()) {
+		if (!p_scan_active.is_set()) {
+			return;
+		}
+
 		if (da->current_is_dir() && n[0] != '.') {
 		if (da->current_is_dir() && n[0] != '.') {
-			_scan_folder_recursive(da->get_current_dir().path_join(n), r_projects);
+			_scan_folder_recursive(da->get_current_dir().path_join(n), r_projects, p_scan_active);
 		} else if (n == "project.godot") {
 		} else if (n == "project.godot") {
 			r_projects->push_back(da->get_current_dir());
 			r_projects->push_back(da->get_current_dir());
 		}
 		}

+ 16 - 1
editor/project_manager/project_list.h

@@ -35,6 +35,7 @@
 #include "scene/gui/box_container.h"
 #include "scene/gui/box_container.h"
 #include "scene/gui/scroll_container.h"
 #include "scene/gui/scroll_container.h"
 
 
+class AcceptDialog;
 class Button;
 class Button;
 class Label;
 class Label;
 class ProjectList;
 class ProjectList;
@@ -176,6 +177,20 @@ private:
 
 
 	VBoxContainer *project_list_vbox = nullptr;
 	VBoxContainer *project_list_vbox = nullptr;
 
 
+	// Projects scan.
+
+	struct ScanData {
+		Thread *thread = nullptr;
+		PackedStringArray paths_to_scan;
+		List<String> found_projects;
+		SafeFlag scan_in_progress;
+	};
+	ScanData *scan_data = nullptr;
+	AcceptDialog *scan_progress = nullptr;
+
+	static void _scan_thread(void *p_scan_data);
+	void _scan_finished();
+
 	// Initialization & loading.
 	// Initialization & loading.
 
 
 	void _migrate_config();
 	void _migrate_config();
@@ -186,7 +201,7 @@ private:
 
 
 	// Project list updates.
 	// Project list updates.
 
 
-	void _scan_folder_recursive(const String &p_path, List<String> *r_projects);
+	static void _scan_folder_recursive(const String &p_path, List<String> *r_projects, const SafeFlag &p_scan_active);
 
 
 	// Project list items.
 	// Project list items.
 
 

+ 3 - 3
scene/gui/progress_bar.cpp

@@ -61,7 +61,7 @@ void ProgressBar::_notification(int p_what) {
 				Size2 size = get_size();
 				Size2 size = get_size();
 				real_t fill_size = MIN(size.width, size.height) * 2;
 				real_t fill_size = MIN(size.width, size.height) * 2;
 
 
-				if (Engine::get_singleton()->is_editor_hint() && !editor_preview_indeterminate) {
+				if (is_part_of_edited_scene() && !editor_preview_indeterminate) {
 					// Center the filled bar when we're not previewing the animation.
 					// Center the filled bar when we're not previewing the animation.
 					_inderminate_fill_progress = (MAX(size.width, size.height) / 2) + (fill_size / 2);
 					_inderminate_fill_progress = (MAX(size.width, size.height) / 2) + (fill_size / 2);
 				}
 				}
@@ -217,7 +217,7 @@ void ProgressBar::set_indeterminate(bool p_indeterminate) {
 	indeterminate = p_indeterminate;
 	indeterminate = p_indeterminate;
 	_inderminate_fill_progress = 0;
 	_inderminate_fill_progress = 0;
 
 
-	bool should_process = !Engine::get_singleton()->is_editor_hint() || editor_preview_indeterminate;
+	bool should_process = !is_part_of_edited_scene() || editor_preview_indeterminate;
 	set_process_internal(indeterminate && should_process);
 	set_process_internal(indeterminate && should_process);
 
 
 	notify_property_list_changed();
 	notify_property_list_changed();
@@ -235,7 +235,7 @@ void ProgressBar::set_editor_preview_indeterminate(bool p_preview_indeterminate)
 	}
 	}
 	editor_preview_indeterminate = p_preview_indeterminate;
 	editor_preview_indeterminate = p_preview_indeterminate;
 
 
-	if (Engine::get_singleton()->is_editor_hint()) {
+	if (is_part_of_edited_scene()) {
 		_inderminate_fill_progress = 0;
 		_inderminate_fill_progress = 0;
 		set_process_internal(indeterminate && editor_preview_indeterminate);
 		set_process_internal(indeterminate && editor_preview_indeterminate);
 		queue_redraw();
 		queue_redraw();