Skip to content

Commit

Permalink
Make scan for projects threaded
Browse files Browse the repository at this point in the history
  • Loading branch information
KoBeWi committed Dec 12, 2024
1 parent 19e003b commit cb4ceb8
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 19 deletions.
103 changes: 88 additions & 15 deletions editor/project_manager/project_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@
#include "editor/project_manager/project_tag.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/button.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/progress_bar.h"
#include "scene/gui/texture_button.h"
#include "scene/gui/texture_rect.h"
#include "scene/resources/image_texture.h"
Expand Down Expand Up @@ -358,21 +360,70 @@ bool ProjectList::project_feature_looks_like_version(const String &p_feature) {
void ProjectList::_notification(int p_what) {
switch (p_what) {
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()) {
Item &item = _projects.write[_icon_load_index];
if (item.control->should_load_project_icon()) {
_load_project_icon(_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 {
set_process(false);
if (scan_data) {
_scan_finished();
}
}
} 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.

void ProjectList::_migrate_config() {
Expand Down Expand Up @@ -624,25 +675,39 @@ void ProjectList::find_projects(const String &p_path) {
}

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() {
Expand All @@ -656,16 +721,24 @@ 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);
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));

da->list_dir_begin();
String n = da->get_next();
while (!n.is_empty()) {
if (!p_scan_active.is_set()) {
return;
}

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") {
r_projects->push_back(da->get_current_dir());
}
Expand Down
17 changes: 16 additions & 1 deletion editor/project_manager/project_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "scene/gui/box_container.h"
#include "scene/gui/scroll_container.h"

class AcceptDialog;
class Button;
class Label;
class ProjectList;
Expand Down Expand Up @@ -176,6 +177,20 @@ class ProjectList : public ScrollContainer {

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.

void _migrate_config();
Expand All @@ -186,7 +201,7 @@ class ProjectList : public ScrollContainer {

// 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.

Expand Down
6 changes: 3 additions & 3 deletions scene/gui/progress_bar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void ProgressBar::_notification(int p_what) {
Size2 size = get_size();
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.
_inderminate_fill_progress = (MAX(size.width, size.height) / 2) + (fill_size / 2);
}
Expand Down Expand Up @@ -217,7 +217,7 @@ void ProgressBar::set_indeterminate(bool p_indeterminate) {
indeterminate = p_indeterminate;
_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);

notify_property_list_changed();
Expand All @@ -235,7 +235,7 @@ void ProgressBar::set_editor_preview_indeterminate(bool 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;
set_process_internal(indeterminate && editor_preview_indeterminate);
queue_redraw();
Expand Down

0 comments on commit cb4ceb8

Please sign in to comment.