feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,13 @@ void ProjectDialog::_validate_path() {
|
|||
ERR_FAIL_COND_MSG(ret != UNZ_OK, "Failed to get current file info.");
|
||||
|
||||
String name = String::utf8(fname);
|
||||
|
||||
// Skip the __MACOSX directory created by macOS's built-in file zipper.
|
||||
if (name.begins_with("__MACOSX")) {
|
||||
ret = unzGoToNextFile(pkg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name.get_file() == "project.godot") {
|
||||
break; // ret == UNZ_OK.
|
||||
}
|
||||
|
|
@ -162,7 +169,7 @@ void ProjectDialog::_validate_path() {
|
|||
}
|
||||
}
|
||||
|
||||
if (target_path.is_empty() || target_path.is_relative_path()) {
|
||||
if (target_path.is_relative_path()) {
|
||||
_set_message(TTR("The path specified is invalid."), MESSAGE_ERROR, target_path_input_type);
|
||||
return;
|
||||
}
|
||||
|
|
@ -352,7 +359,7 @@ void ProjectDialog::_install_path_changed() {
|
|||
|
||||
void ProjectDialog::_browse_project_path() {
|
||||
String path = project_path->get_text();
|
||||
if (path.is_empty()) {
|
||||
if (path.is_relative_path()) {
|
||||
path = EDITOR_GET("filesystem/directories/default_project_path");
|
||||
}
|
||||
if (mode == MODE_IMPORT && install_path->is_visible_in_tree()) {
|
||||
|
|
@ -382,12 +389,16 @@ void ProjectDialog::_browse_project_path() {
|
|||
void ProjectDialog::_browse_install_path() {
|
||||
ERR_FAIL_COND_MSG(mode != MODE_IMPORT, "Install path is only used for MODE_IMPORT.");
|
||||
|
||||
String path = install_path->get_text();
|
||||
if (path.is_relative_path() || !DirAccess::dir_exists_absolute(path)) {
|
||||
path = EDITOR_GET("filesystem/directories/default_project_path");
|
||||
}
|
||||
if (create_dir->is_pressed()) {
|
||||
// Select parent directory of install path.
|
||||
fdialog_install->set_current_dir(install_path->get_text().get_base_dir());
|
||||
fdialog_install->set_current_dir(path.get_base_dir());
|
||||
} else {
|
||||
// Select install path.
|
||||
fdialog_install->set_current_dir(install_path->get_text());
|
||||
fdialog_install->set_current_dir(path);
|
||||
}
|
||||
|
||||
fdialog_install->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
|
||||
|
|
@ -438,6 +449,8 @@ void ProjectDialog::_renderer_selected() {
|
|||
|
||||
String renderer_type = renderer_button_group->get_pressed_button()->get_meta(SNAME("rendering_method"));
|
||||
|
||||
bool rd_error = false;
|
||||
|
||||
if (renderer_type == "forward_plus") {
|
||||
renderer_info->set_text(
|
||||
String::utf8("• ") + TTR("Supports desktop platforms only.") +
|
||||
|
|
@ -445,6 +458,7 @@ void ProjectDialog::_renderer_selected() {
|
|||
String::utf8("\n• ") + TTR("Can scale to large complex scenes.") +
|
||||
String::utf8("\n• ") + TTR("Uses RenderingDevice backend.") +
|
||||
String::utf8("\n• ") + TTR("Slower rendering of simple scenes."));
|
||||
rd_error = !rendering_device_supported;
|
||||
} else if (renderer_type == "mobile") {
|
||||
renderer_info->set_text(
|
||||
String::utf8("• ") + TTR("Supports desktop + mobile platforms.") +
|
||||
|
|
@ -452,16 +466,24 @@ void ProjectDialog::_renderer_selected() {
|
|||
String::utf8("\n• ") + TTR("Less scalable for complex scenes.") +
|
||||
String::utf8("\n• ") + TTR("Uses RenderingDevice backend.") +
|
||||
String::utf8("\n• ") + TTR("Fast rendering of simple scenes."));
|
||||
rd_error = !rendering_device_supported;
|
||||
} else if (renderer_type == "gl_compatibility") {
|
||||
renderer_info->set_text(
|
||||
String::utf8("• ") + TTR("Supports desktop, mobile + web platforms.") +
|
||||
String::utf8("\n• ") + TTR("Least advanced 3D graphics (currently work-in-progress).") +
|
||||
String::utf8("\n• ") + TTR("Least advanced 3D graphics.") +
|
||||
String::utf8("\n• ") + TTR("Intended for low-end/older devices.") +
|
||||
String::utf8("\n• ") + TTR("Uses OpenGL 3 backend (OpenGL 3.3/ES 3.0/WebGL2).") +
|
||||
String::utf8("\n• ") + TTR("Fastest rendering of simple scenes."));
|
||||
} else {
|
||||
WARN_PRINT("Unknown renderer type. Please report this as a bug on GitHub.");
|
||||
}
|
||||
|
||||
rd_not_supported->set_visible(rd_error);
|
||||
get_ok_button()->set_disabled(rd_error);
|
||||
if (rd_error) {
|
||||
// Needs to be set here since theme colors aren't available at startup.
|
||||
rd_not_supported->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectDialog::_nonempty_confirmation_ok_pressed() {
|
||||
|
|
@ -473,12 +495,14 @@ void ProjectDialog::ok_pressed() {
|
|||
// Before we create a project, check that the target folder is empty.
|
||||
// If not, we need to ask the user if they're sure they want to do this.
|
||||
if (!is_folder_empty) {
|
||||
ConfirmationDialog *cd = memnew(ConfirmationDialog);
|
||||
cd->set_title(TTR("Warning: This folder is not empty"));
|
||||
cd->set_text(TTR("You are about to create a Godot project in a non-empty folder.\nThe entire contents of this folder will be imported as project resources!\n\nAre you sure you wish to continue?"));
|
||||
cd->get_ok_button()->connect(SceneStringName(pressed), callable_mp(this, &ProjectDialog::_nonempty_confirmation_ok_pressed));
|
||||
get_parent()->add_child(cd);
|
||||
cd->popup_centered();
|
||||
if (!nonempty_confirmation) {
|
||||
nonempty_confirmation = memnew(ConfirmationDialog);
|
||||
nonempty_confirmation->set_title(TTR("Warning: This folder is not empty"));
|
||||
nonempty_confirmation->set_text(TTR("You are about to create a Godot project in a non-empty folder.\nThe entire contents of this folder will be imported as project resources!\n\nAre you sure you wish to continue?"));
|
||||
nonempty_confirmation->get_ok_button()->connect(SceneStringName(pressed), callable_mp(this, &ProjectDialog::_nonempty_confirmation_ok_pressed));
|
||||
add_child(nonempty_confirmation);
|
||||
}
|
||||
nonempty_confirmation->popup_centered();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -537,6 +561,21 @@ void ProjectDialog::ok_pressed() {
|
|||
fa_icon->store_string(get_default_project_icon());
|
||||
|
||||
EditorVCSInterface::create_vcs_metadata_files(EditorVCSInterface::VCSMetadata(vcs_metadata_selection->get_selected()), path);
|
||||
|
||||
// Ensures external editors and IDEs use UTF-8 encoding.
|
||||
const String editor_config_path = path.path_join(".editorconfig");
|
||||
Ref<FileAccess> f = FileAccess::open(editor_config_path, FileAccess::WRITE);
|
||||
if (f.is_null()) {
|
||||
// .editorconfig isn't so critical.
|
||||
ERR_PRINT("Couldn't create .editorconfig in project path.");
|
||||
} else {
|
||||
f->store_line("root = true");
|
||||
f->store_line("");
|
||||
f->store_line("[*]");
|
||||
f->store_line("charset = utf-8");
|
||||
f->close();
|
||||
FileAccess::set_hidden_attribute(editor_config_path, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Two cases for importing a ZIP.
|
||||
|
|
@ -572,6 +611,13 @@ void ProjectDialog::ok_pressed() {
|
|||
ERR_FAIL_COND_MSG(ret != UNZ_OK, "Failed to get current file info.");
|
||||
|
||||
String name = String::utf8(fname);
|
||||
|
||||
// Skip the __MACOSX directory created by macOS's built-in file zipper.
|
||||
if (name.begins_with("__MACOSX")) {
|
||||
ret = unzGoToNextFile(pkg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name.get_file() == "project.godot") {
|
||||
zip_root = name.get_base_dir();
|
||||
break;
|
||||
|
|
@ -604,7 +650,15 @@ void ProjectDialog::ok_pressed() {
|
|||
ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
|
||||
ERR_FAIL_COND_MSG(ret != UNZ_OK, "Failed to get current file info.");
|
||||
|
||||
String rel_path = String::utf8(fname).trim_prefix(zip_root);
|
||||
String name = String::utf8(fname);
|
||||
|
||||
// Skip the __MACOSX directory created by macOS's built-in file zipper.
|
||||
if (name.begins_with("__MACOSX")) {
|
||||
ret = unzGoToNextFile(pkg);
|
||||
continue;
|
||||
}
|
||||
|
||||
String rel_path = name.trim_prefix(zip_root);
|
||||
if (rel_path.is_empty()) { // Root.
|
||||
} else if (rel_path.ends_with("/")) { // Directory.
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
|
|
@ -671,7 +725,7 @@ void ProjectDialog::ok_pressed() {
|
|||
|
||||
hide();
|
||||
if (mode == MODE_NEW || mode == MODE_IMPORT || mode == MODE_INSTALL) {
|
||||
emit_signal(SNAME("project_created"), path);
|
||||
emit_signal(SNAME("project_created"), path, edit_check_box->is_pressed());
|
||||
} else if (mode == MODE_RENAME) {
|
||||
emit_signal(SNAME("projects_updated"));
|
||||
}
|
||||
|
|
@ -713,6 +767,7 @@ void ProjectDialog::show_dialog(bool p_reset_name) {
|
|||
create_dir->hide();
|
||||
project_status_rect->hide();
|
||||
project_browse->hide();
|
||||
edit_check_box->hide();
|
||||
|
||||
name_container->show();
|
||||
install_path_container->hide();
|
||||
|
|
@ -728,6 +783,7 @@ void ProjectDialog::show_dialog(bool p_reset_name) {
|
|||
project_path->set_editable(true);
|
||||
|
||||
String fav_dir = EDITOR_GET("filesystem/directories/default_project_path");
|
||||
fav_dir = fav_dir.simplify_path();
|
||||
if (!fav_dir.is_empty()) {
|
||||
project_path->set_text(fav_dir);
|
||||
install_path->set_text(fav_dir);
|
||||
|
|
@ -742,10 +798,11 @@ void ProjectDialog::show_dialog(bool p_reset_name) {
|
|||
create_dir->show();
|
||||
project_status_rect->show();
|
||||
project_browse->show();
|
||||
edit_check_box->show();
|
||||
|
||||
if (mode == MODE_IMPORT) {
|
||||
set_title(TTR("Import Existing Project"));
|
||||
set_ok_button_text(TTR("Import & Edit"));
|
||||
set_ok_button_text(TTR("Import"));
|
||||
|
||||
name_container->hide();
|
||||
install_path_container->hide();
|
||||
|
|
@ -755,7 +812,7 @@ void ProjectDialog::show_dialog(bool p_reset_name) {
|
|||
// Project path dialog is also opened; no need to change focus.
|
||||
} else if (mode == MODE_NEW) {
|
||||
set_title(TTR("Create New Project"));
|
||||
set_ok_button_text(TTR("Create & Edit"));
|
||||
set_ok_button_text(TTR("Create"));
|
||||
|
||||
name_container->show();
|
||||
install_path_container->hide();
|
||||
|
|
@ -766,7 +823,7 @@ void ProjectDialog::show_dialog(bool p_reset_name) {
|
|||
callable_mp(project_name, &LineEdit::select_all).call_deferred();
|
||||
} else if (mode == MODE_INSTALL) {
|
||||
set_title(TTR("Install Project:") + " " + zip_title);
|
||||
set_ok_button_text(TTR("Install & Edit"));
|
||||
set_ok_button_text(TTR("Install"));
|
||||
|
||||
project_name->set_text(zip_title);
|
||||
|
||||
|
|
@ -795,9 +852,9 @@ void ProjectDialog::show_dialog(bool p_reset_name) {
|
|||
void ProjectDialog::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
create_dir->set_icon(get_editor_theme_icon(SNAME("FolderCreate")));
|
||||
project_browse->set_icon(get_editor_theme_icon(SNAME("FolderBrowse")));
|
||||
install_browse->set_icon(get_editor_theme_icon(SNAME("FolderBrowse")));
|
||||
create_dir->set_button_icon(get_editor_theme_icon(SNAME("FolderCreate")));
|
||||
project_browse->set_button_icon(get_editor_theme_icon(SNAME("FolderBrowse")));
|
||||
install_browse->set_button_icon(get_editor_theme_icon(SNAME("FolderBrowse")));
|
||||
} break;
|
||||
case NOTIFICATION_READY: {
|
||||
fdialog_project = memnew(EditorFileDialog);
|
||||
|
|
@ -846,7 +903,7 @@ ProjectDialog::ProjectDialog() {
|
|||
create_dir->set_text(TTR("Create Folder"));
|
||||
create_dir->set_pressed(true);
|
||||
pphb_label->add_child(create_dir);
|
||||
create_dir->connect("toggled", callable_mp(this, &ProjectDialog::_create_dir_toggled));
|
||||
create_dir->connect(SceneStringName(toggled), callable_mp(this, &ProjectDialog::_create_dir_toggled));
|
||||
|
||||
HBoxContainer *pphb = memnew(HBoxContainer);
|
||||
project_path_container->add_child(pphb);
|
||||
|
|
@ -916,10 +973,16 @@ ProjectDialog::ProjectDialog() {
|
|||
default_renderer_type = EditorSettings::get_singleton()->get_setting("project_manager/default_renderer");
|
||||
}
|
||||
|
||||
rendering_device_supported = DisplayServer::can_create_rendering_device();
|
||||
|
||||
if (!rendering_device_supported) {
|
||||
default_renderer_type = "gl_compatibility";
|
||||
}
|
||||
|
||||
Button *rs_button = memnew(CheckBox);
|
||||
rs_button->set_button_group(renderer_button_group);
|
||||
rs_button->set_text(TTR("Forward+"));
|
||||
#if defined(WEB_ENABLED)
|
||||
#ifndef RD_ENABLED
|
||||
rs_button->set_disabled(true);
|
||||
#endif
|
||||
rs_button->set_meta(SNAME("rendering_method"), "forward_plus");
|
||||
|
|
@ -931,7 +994,7 @@ ProjectDialog::ProjectDialog() {
|
|||
rs_button = memnew(CheckBox);
|
||||
rs_button->set_button_group(renderer_button_group);
|
||||
rs_button->set_text(TTR("Mobile"));
|
||||
#if defined(WEB_ENABLED)
|
||||
#ifndef RD_ENABLED
|
||||
rs_button->set_disabled(true);
|
||||
#endif
|
||||
rs_button->set_meta(SNAME("rendering_method"), "mobile");
|
||||
|
|
@ -963,6 +1026,15 @@ ProjectDialog::ProjectDialog() {
|
|||
renderer_info = memnew(Label);
|
||||
renderer_info->set_modulate(Color(1, 1, 1, 0.7));
|
||||
rvb->add_child(renderer_info);
|
||||
|
||||
rd_not_supported = memnew(Label);
|
||||
rd_not_supported->set_text(vformat(TTR("RenderingDevice-based methods not available on this GPU:\n%s\nPlease use the Compatibility renderer."), RenderingServer::get_singleton()->get_video_adapter_name()));
|
||||
rd_not_supported->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
rd_not_supported->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
|
||||
rd_not_supported->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
|
||||
rd_not_supported->set_visible(false);
|
||||
renderer_container->add_child(rd_not_supported);
|
||||
|
||||
_renderer_selected();
|
||||
|
||||
l = memnew(Label);
|
||||
|
|
@ -993,9 +1065,25 @@ ProjectDialog::ProjectDialog() {
|
|||
fdialog_install->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
|
||||
add_child(fdialog_install);
|
||||
|
||||
Control *spacer2 = memnew(Control);
|
||||
spacer2->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
vb->add_child(spacer2);
|
||||
|
||||
edit_check_box = memnew(CheckBox);
|
||||
edit_check_box->set_text(TTR("Edit Now"));
|
||||
edit_check_box->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
edit_check_box->set_pressed(true);
|
||||
vb->add_child(edit_check_box);
|
||||
|
||||
project_name->connect(SceneStringName(text_changed), callable_mp(this, &ProjectDialog::_project_name_changed).unbind(1));
|
||||
project_name->connect(SceneStringName(text_submitted), callable_mp(this, &ProjectDialog::ok_pressed).unbind(1));
|
||||
|
||||
project_path->connect(SceneStringName(text_changed), callable_mp(this, &ProjectDialog::_project_path_changed).unbind(1));
|
||||
project_path->connect(SceneStringName(text_submitted), callable_mp(this, &ProjectDialog::ok_pressed).unbind(1));
|
||||
|
||||
install_path->connect(SceneStringName(text_changed), callable_mp(this, &ProjectDialog::_install_path_changed).unbind(1));
|
||||
install_path->connect(SceneStringName(text_submitted), callable_mp(this, &ProjectDialog::ok_pressed).unbind(1));
|
||||
|
||||
fdialog_install->connect("dir_selected", callable_mp(this, &ProjectDialog::_install_path_selected));
|
||||
fdialog_install->connect("file_selected", callable_mp(this, &ProjectDialog::_install_path_selected));
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "scene/gui/dialogs.h"
|
||||
|
||||
class Button;
|
||||
class CheckBox;
|
||||
class CheckButton;
|
||||
class EditorFileDialog;
|
||||
class LineEdit;
|
||||
|
|
@ -65,6 +66,7 @@ private:
|
|||
|
||||
Mode mode = MODE_NEW;
|
||||
bool is_folder_empty = true;
|
||||
ConfirmationDialog *nonempty_confirmation = nullptr;
|
||||
|
||||
CheckButton *create_dir = nullptr;
|
||||
Button *project_browse = nullptr;
|
||||
|
|
@ -77,6 +79,8 @@ private:
|
|||
Label *renderer_info = nullptr;
|
||||
HBoxContainer *default_files_container = nullptr;
|
||||
Ref<ButtonGroup> renderer_button_group;
|
||||
bool rendering_device_supported = false;
|
||||
Label *rd_not_supported = nullptr;
|
||||
|
||||
Label *msg = nullptr;
|
||||
LineEdit *project_name = nullptr;
|
||||
|
|
@ -87,6 +91,8 @@ private:
|
|||
|
||||
OptionButton *vcs_metadata_selection = nullptr;
|
||||
|
||||
CheckBox *edit_check_box = nullptr;
|
||||
|
||||
EditorFileDialog *fdialog_project = nullptr;
|
||||
EditorFileDialog *fdialog_install = nullptr;
|
||||
AcceptDialog *dialog_error = nullptr;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -67,9 +69,9 @@ void ProjectListItemControl::_notification(int p_what) {
|
|||
|
||||
favorite_button->set_texture_normal(get_editor_theme_icon(SNAME("Favorites")));
|
||||
if (project_is_missing) {
|
||||
explore_button->set_icon(get_editor_theme_icon(SNAME("FileBroken")));
|
||||
explore_button->set_button_icon(get_editor_theme_icon(SNAME("FileBroken")));
|
||||
} else {
|
||||
explore_button->set_icon(get_editor_theme_icon(SNAME("Load")));
|
||||
explore_button->set_button_icon(get_editor_theme_icon(SNAME("Load")));
|
||||
}
|
||||
} break;
|
||||
|
||||
|
|
@ -88,7 +90,7 @@ void ProjectListItemControl::_notification(int p_what) {
|
|||
draw_style_box(get_theme_stylebox(SNAME("selected"), SNAME("Tree")), Rect2(Point2(), get_size()));
|
||||
}
|
||||
if (is_hovering) {
|
||||
draw_style_box(get_theme_stylebox(SNAME("hover"), SNAME("Tree")), Rect2(Point2(), get_size()));
|
||||
draw_style_box(get_theme_stylebox(SNAME("hovered"), SNAME("Tree")), Rect2(Point2(), get_size()));
|
||||
}
|
||||
|
||||
draw_line(Point2(0, get_size().y + 1), Point2(get_size().x, get_size().y + 1), get_theme_color(SNAME("guide_color"), SNAME("Tree")));
|
||||
|
|
@ -196,12 +198,12 @@ void ProjectListItemControl::set_is_missing(bool p_missing) {
|
|||
if (project_is_missing) {
|
||||
project_icon->set_modulate(Color(1, 1, 1, 0.5));
|
||||
|
||||
explore_button->set_icon(get_editor_theme_icon(SNAME("FileBroken")));
|
||||
explore_button->set_button_icon(get_editor_theme_icon(SNAME("FileBroken")));
|
||||
explore_button->set_tooltip_text(TTR("Error: Project is missing on the filesystem."));
|
||||
} else {
|
||||
project_icon->set_modulate(Color(1, 1, 1, 1.0));
|
||||
|
||||
explore_button->set_icon(get_editor_theme_icon(SNAME("Load")));
|
||||
explore_button->set_button_icon(get_editor_theme_icon(SNAME("Load")));
|
||||
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
|
||||
explore_button->set_tooltip_text(TTR("Show in File Manager"));
|
||||
#else
|
||||
|
|
@ -350,7 +352,7 @@ const char *ProjectList::SIGNAL_PROJECT_ASK_OPEN = "project_ask_open";
|
|||
// Helpers.
|
||||
|
||||
bool ProjectList::project_feature_looks_like_version(const String &p_feature) {
|
||||
return p_feature.contains(".") && p_feature.substr(0, 3).is_numeric();
|
||||
return p_feature.contains_char('.') && p_feature.substr(0, 3).is_numeric();
|
||||
}
|
||||
|
||||
// Notifications.
|
||||
|
|
@ -358,7 +360,7 @@ 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()) {
|
||||
|
|
@ -366,13 +368,62 @@ void ProjectList::_notification(int p_what) {
|
|||
}
|
||||
_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() {
|
||||
|
|
@ -417,14 +468,16 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa
|
|||
String conf = p_path.path_join("project.godot");
|
||||
bool grayed = false;
|
||||
bool missing = false;
|
||||
bool recovery_mode = false;
|
||||
|
||||
Ref<ConfigFile> cf = memnew(ConfigFile);
|
||||
Error cf_err = cf->load(conf);
|
||||
|
||||
int config_version = 0;
|
||||
String cf_project_name;
|
||||
String project_name = TTR("Unnamed Project");
|
||||
if (cf_err == OK) {
|
||||
String cf_project_name = cf->get_value("application", "config/name", "");
|
||||
cf_project_name = cf->get_value("application", "config/name", "");
|
||||
if (!cf_project_name.is_empty()) {
|
||||
project_name = cf_project_name.xml_unescape();
|
||||
}
|
||||
|
|
@ -438,9 +491,23 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa
|
|||
|
||||
const String description = cf->get_value("application", "config/description", "");
|
||||
const PackedStringArray tags = cf->get_value("application", "config/tags", PackedStringArray());
|
||||
const String icon = cf->get_value("application", "config/icon", "");
|
||||
const String main_scene = cf->get_value("application", "run/main_scene", "");
|
||||
|
||||
String icon = cf->get_value("application", "config/icon", "");
|
||||
if (icon.begins_with("uid://")) {
|
||||
Error err;
|
||||
Ref<FileAccess> file = FileAccess::open(p_path.path_join(".godot/uid_cache.bin"), FileAccess::READ, &err);
|
||||
if (err == OK) {
|
||||
icon = ResourceUID::get_path_from_cache(file, icon);
|
||||
if (icon.is_empty()) {
|
||||
WARN_PRINT(vformat("Could not load icon from UID for project at path \"%s\". Make sure UID cache exists.", p_path));
|
||||
}
|
||||
} else {
|
||||
// Cache does not exist yet, so ignore and fallback to default icon.
|
||||
icon = "";
|
||||
}
|
||||
}
|
||||
|
||||
PackedStringArray project_features = cf->get_value("application", "config/features", PackedStringArray());
|
||||
PackedStringArray unsupported_features = ProjectSettings::get_unsupported_features(project_features);
|
||||
|
||||
|
|
@ -486,7 +553,29 @@ ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_fa
|
|||
ProjectManager::get_singleton()->add_new_tag(tag);
|
||||
}
|
||||
|
||||
return Item(project_name, description, project_version, tags, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version);
|
||||
// We can't use OS::get_user_dir() because it attempts to load paths from the current loaded project through ProjectSettings,
|
||||
// while here we're parsing project files externally. Therefore, we have to replicate its behavior.
|
||||
String user_dir;
|
||||
if (!cf_project_name.is_empty()) {
|
||||
String appname = OS::get_singleton()->get_safe_dir_name(cf_project_name);
|
||||
bool use_custom_dir = cf->get_value("application", "config/use_custom_user_dir", false);
|
||||
if (use_custom_dir) {
|
||||
String custom_dir = OS::get_singleton()->get_safe_dir_name(cf->get_value("application", "config/custom_user_dir_name", ""), true);
|
||||
if (custom_dir.is_empty()) {
|
||||
custom_dir = appname;
|
||||
}
|
||||
user_dir = custom_dir;
|
||||
} else {
|
||||
user_dir = OS::get_singleton()->get_godot_dir_name().path_join("app_userdata").path_join(appname);
|
||||
}
|
||||
} else {
|
||||
user_dir = OS::get_singleton()->get_godot_dir_name().path_join("app_userdata").path_join("[unnamed project]");
|
||||
}
|
||||
|
||||
String recovery_mode_lock_file = OS::get_singleton()->get_user_data_dir(user_dir).path_join(".recovery_mode_lock");
|
||||
recovery_mode = FileAccess::exists(recovery_mode_lock_file);
|
||||
|
||||
return Item(project_name, description, project_version, tags, p_path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, recovery_mode, config_version);
|
||||
}
|
||||
|
||||
void ProjectList::_update_icons_async() {
|
||||
|
|
@ -581,7 +670,7 @@ void ProjectList::sort_projects() {
|
|||
bool item_visible = true;
|
||||
if (!_search_term.is_empty()) {
|
||||
String search_path;
|
||||
if (search_term.contains("/")) {
|
||||
if (search_term.contains_char('/')) {
|
||||
// Search path will match the whole path
|
||||
search_path = item.path;
|
||||
} else {
|
||||
|
|
@ -624,25 +713,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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
for (const String &E : projects) {
|
||||
add_project(E, false);
|
||||
}
|
||||
scan_data = memnew(ScanData);
|
||||
scan_data->paths_to_scan = p_paths;
|
||||
scan_data->scan_in_progress.set();
|
||||
|
||||
save_config();
|
||||
scan_data->thread = memnew(Thread);
|
||||
scan_data->thread->start(_scan_thread, scan_data);
|
||||
|
||||
if (ProjectManager::get_singleton()->is_initialized()) {
|
||||
update_project_list();
|
||||
if (scan_progress) {
|
||||
scan_progress->reset_size();
|
||||
scan_progress->popup_centered();
|
||||
}
|
||||
set_process(true);
|
||||
}
|
||||
|
||||
void ProjectList::load_project_list() {
|
||||
|
|
@ -656,7 +759,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);
|
||||
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));
|
||||
|
|
@ -664,8 +771,12 @@ void ProjectList::_scan_folder_recursive(const String &p_path, List<String> *r_p
|
|||
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());
|
||||
}
|
||||
|
|
@ -760,7 +871,7 @@ void ProjectList::_create_project_item_control(int p_index) {
|
|||
hb->set_tags(item.tags, this);
|
||||
hb->set_unsupported_features(item.unsupported_features.duplicate());
|
||||
hb->set_project_version(item.project_version);
|
||||
hb->set_last_edited_info(Time::get_singleton()->get_datetime_string_from_unix_time(item.last_edited, true));
|
||||
hb->set_last_edited_info(!item.missing ? Time::get_singleton()->get_datetime_string_from_unix_time(item.last_edited, true) : TTR("Missing Date"));
|
||||
|
||||
hb->set_is_favorite(item.favorite);
|
||||
hb->set_is_missing(item.missing);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/scroll_container.h"
|
||||
|
||||
class AcceptDialog;
|
||||
class Button;
|
||||
class Label;
|
||||
class ProjectList;
|
||||
|
|
@ -115,6 +116,7 @@ public:
|
|||
bool favorite = false;
|
||||
bool grayed = false;
|
||||
bool missing = false;
|
||||
bool recovery_mode = false;
|
||||
int version = 0;
|
||||
|
||||
ProjectListItemControl *control = nullptr;
|
||||
|
|
@ -133,6 +135,7 @@ public:
|
|||
bool p_favorite,
|
||||
bool p_grayed,
|
||||
bool p_missing,
|
||||
bool p_recovery_mode,
|
||||
int p_version) {
|
||||
project_name = p_name;
|
||||
description = p_description;
|
||||
|
|
@ -146,6 +149,7 @@ public:
|
|||
favorite = p_favorite;
|
||||
grayed = p_grayed;
|
||||
missing = p_missing;
|
||||
recovery_mode = p_recovery_mode;
|
||||
version = p_version;
|
||||
|
||||
control = nullptr;
|
||||
|
|
@ -176,6 +180,20 @@ private:
|
|||
|
||||
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();
|
||||
|
|
@ -186,7 +204,7 @@ private:
|
|||
|
||||
// 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
void ProjectTag::_notification(int p_what) {
|
||||
if (display_close && p_what == NOTIFICATION_THEME_CHANGED) {
|
||||
button->set_icon(get_theme_icon(SNAME("close"), SNAME("TabBar")));
|
||||
button->set_button_icon(get_theme_icon(SNAME("close"), SNAME("TabBar")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -70,5 +70,5 @@ ProjectTag::ProjectTag(const String &p_text, bool p_display_close) {
|
|||
button->set_text(p_text.capitalize());
|
||||
button->set_focus_mode(FOCUS_NONE);
|
||||
button->set_icon_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
|
||||
button->set_theme_type_variation(SNAME("ProjectTag"));
|
||||
button->set_theme_type_variation(SNAME("ProjectTagButton"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,14 +31,13 @@
|
|||
#include "quick_settings_dialog.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/string/translation.h"
|
||||
#include "core/string/translation_server.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
|
||||
|
|
@ -98,6 +97,7 @@ void QuickSettingsDialog::_update_current_values() {
|
|||
if (current_theme == theme_value) {
|
||||
theme_option_button->set_text(current_theme);
|
||||
theme_option_button->select(i);
|
||||
theme_option_button->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
|
||||
custom_theme_label->set_visible(current_theme == "Custom");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue