41
41
#include " editor/project_manager/project_tag.h"
42
42
#include " editor/themes/editor_scale.h"
43
43
#include " scene/gui/button.h"
44
+ #include " scene/gui/dialogs.h"
44
45
#include " scene/gui/label.h"
45
46
#include " scene/gui/line_edit.h"
47
+ #include " scene/gui/progress_bar.h"
46
48
#include " scene/gui/texture_button.h"
47
49
#include " scene/gui/texture_rect.h"
48
50
#include " scene/resources/image_texture.h"
@@ -358,21 +360,70 @@ bool ProjectList::project_feature_looks_like_version(const String &p_feature) {
358
360
void ProjectList::_notification (int p_what) {
359
361
switch (p_what) {
360
362
case NOTIFICATION_PROCESS: {
361
- // Load icons as a coroutine to speed up launch when you have hundreds of projects
363
+ // Load icons as a coroutine to speed up launch when you have hundreds of projects.
362
364
if (_icon_load_index < _projects.size ()) {
363
365
Item &item = _projects.write [_icon_load_index];
364
366
if (item.control ->should_load_project_icon ()) {
365
367
_load_project_icon (_icon_load_index);
366
368
}
367
369
_icon_load_index++;
368
370
371
+ // Scan directories in thread to avoid blocking the window.
372
+ } else if (scan_data && scan_data->scan_in_progress .is_set ()) {
373
+ // Wait for the thread.
369
374
} else {
370
375
set_process (false );
376
+ if (scan_data) {
377
+ _scan_finished ();
378
+ }
371
379
}
372
380
} break ;
373
381
}
374
382
}
375
383
384
+ // Projects scan.
385
+
386
+ void ProjectList::_scan_thread (void *p_scan_data) {
387
+ ScanData *scan_data = static_cast <ScanData *>(p_scan_data);
388
+
389
+ for (const String &base_path : scan_data->paths_to_scan ) {
390
+ print_verbose (vformat (" Scanning for projects in \" %s\" ." , base_path));
391
+ _scan_folder_recursive (base_path, &scan_data->found_projects , scan_data->scan_in_progress );
392
+
393
+ if (!scan_data->scan_in_progress .is_set ()) {
394
+ print_verbose (" Scan aborted." );
395
+ break ;
396
+ }
397
+ }
398
+ print_verbose (vformat (" Found %d project(s)." , scan_data->found_projects .size ()));
399
+ scan_data->scan_in_progress .clear ();
400
+ }
401
+
402
+ void ProjectList::_scan_finished () {
403
+ if (scan_data->scan_in_progress .is_set ()) {
404
+ // Abort scanning.
405
+ scan_data->scan_in_progress .clear ();
406
+ }
407
+
408
+ scan_data->thread ->wait_to_finish ();
409
+ memdelete (scan_data->thread );
410
+ if (scan_progress) {
411
+ scan_progress->hide ();
412
+ }
413
+
414
+ for (const String &E : scan_data->found_projects ) {
415
+ add_project (E, false );
416
+ }
417
+ memdelete (scan_data);
418
+ scan_data = nullptr ;
419
+
420
+ save_config ();
421
+
422
+ if (ProjectManager::get_singleton ()->is_initialized ()) {
423
+ update_project_list ();
424
+ }
425
+ }
426
+
376
427
// Initialization & loading.
377
428
378
429
void ProjectList::_migrate_config () {
@@ -624,25 +675,39 @@ void ProjectList::find_projects(const String &p_path) {
624
675
}
625
676
626
677
void ProjectList::find_projects_multiple (const PackedStringArray &p_paths) {
627
- List<String> projects;
678
+ if (!scan_progress && is_inside_tree ()) {
679
+ scan_progress = memnew (AcceptDialog);
680
+ scan_progress->set_title (TTR (" Scanning" ));
681
+ scan_progress->set_ok_button_text (TTR (" Cancel" ));
628
682
629
- for (int i = 0 ; i < p_paths.size (); i++) {
630
- const String &base_path = p_paths.get (i);
631
- print_verbose (vformat (" Scanning for projects in \" %s\" ." , base_path));
683
+ VBoxContainer *vb = memnew (VBoxContainer);
684
+ scan_progress->add_child (vb);
632
685
633
- _scan_folder_recursive (base_path, &projects );
634
- print_verbose ( vformat ( " Found %d project(s). " , projects.size () ));
635
- }
686
+ Label *label = memnew (Label );
687
+ label-> set_text ( TTR ( " Scanning for projects... " ));
688
+ vb-> add_child (label);
636
689
637
- for (const String &E : projects) {
638
- add_project (E, false );
690
+ ProgressBar *progress = memnew (ProgressBar);
691
+ progress->set_indeterminate (true );
692
+ vb->add_child (progress);
693
+
694
+ add_child (scan_progress);
695
+ scan_progress->connect (SceneStringName (confirmed), callable_mp (this , &ProjectList::_scan_finished));
696
+ scan_progress->connect (" canceled" , callable_mp (this , &ProjectList::_scan_finished));
639
697
}
640
698
641
- save_config ();
699
+ scan_data = memnew (ScanData);
700
+ scan_data->paths_to_scan = p_paths;
701
+ scan_data->scan_in_progress .set ();
642
702
643
- if (ProjectManager::get_singleton ()->is_initialized ()) {
644
- update_project_list ();
703
+ scan_data->thread = memnew (Thread);
704
+ scan_data->thread ->start (_scan_thread, scan_data);
705
+
706
+ if (scan_progress) {
707
+ scan_progress->reset_size ();
708
+ scan_progress->popup_centered ();
645
709
}
710
+ set_process (true );
646
711
}
647
712
648
713
void ProjectList::load_project_list () {
@@ -656,16 +721,24 @@ void ProjectList::load_project_list() {
656
721
}
657
722
}
658
723
659
- void ProjectList::_scan_folder_recursive (const String &p_path, List<String> *r_projects) {
724
+ void ProjectList::_scan_folder_recursive (const String &p_path, List<String> *r_projects, const SafeFlag &p_scan_active) {
725
+ if (!p_scan_active.is_set ()) {
726
+ return ;
727
+ }
728
+
660
729
Ref<DirAccess> da = DirAccess::create (DirAccess::ACCESS_FILESYSTEM);
661
730
Error error = da->change_dir (p_path);
662
731
ERR_FAIL_COND_MSG (error != OK, vformat (" Failed to open the path \" %s\" for scanning (code %d)." , p_path, error));
663
732
664
733
da->list_dir_begin ();
665
734
String n = da->get_next ();
666
735
while (!n.is_empty ()) {
736
+ if (!p_scan_active.is_set ()) {
737
+ return ;
738
+ }
739
+
667
740
if (da->current_is_dir () && n[0 ] != ' .' ) {
668
- _scan_folder_recursive (da->get_current_dir ().path_join (n), r_projects);
741
+ _scan_folder_recursive (da->get_current_dir ().path_join (n), r_projects, p_scan_active );
669
742
} else if (n == " project.godot" ) {
670
743
r_projects->push_back (da->get_current_dir ());
671
744
}
0 commit comments