summaryrefslogtreecommitdiff
path: root/editor/project_manager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'editor/project_manager.cpp')
-rw-r--r--editor/project_manager.cpp201
1 files changed, 121 insertions, 80 deletions
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 08b968edb6..2e7b6f7476 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -186,7 +186,8 @@ private:
if (mode == MODE_IMPORT || mode == MODE_RENAME) {
if (!valid_path.is_empty() && !d->file_exists("project.godot")) {
if (valid_path.ends_with(".zip")) {
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
unzFile pkg = unzOpen2(valid_path.utf8().get_data(), &io);
if (!pkg) {
@@ -499,7 +500,8 @@ private:
zip_path = project_path->get_text();
}
- zlib_filefunc_def io = zipio_create_io();
+ Ref<FileAccess> io_fa;
+ zlib_filefunc_def io = zipio_create_io(&io_fa);
unzFile pkg = unzOpen2(zip_path.utf8().get_data(), &io);
if (!pkg) {
@@ -557,9 +559,7 @@ private:
//read
unzOpenCurrentFile(pkg);
ret = unzReadCurrentFile(pkg, data.ptrw(), data.size());
- if (ret != UNZ_OK) {
- break;
- }
+ ERR_BREAK_MSG(ret < 0, vformat("An error occurred while attempting to read from file: %s. This file will not be used.", rel_path));
unzCloseCurrentFile(pkg);
Ref<FileAccess> f = FileAccess::open(dir.plus_file(rel_path), FileAccess::WRITE);
@@ -577,16 +577,16 @@ private:
unzClose(pkg);
if (failed_files.size()) {
- String msg = TTR("The following files failed extraction from package:") + "\n\n";
+ String err_msg = TTR("The following files failed extraction from package:") + "\n\n";
for (int i = 0; i < failed_files.size(); i++) {
if (i > 15) {
- msg += "\nAnd " + itos(failed_files.size() - i) + " more files.";
+ err_msg += "\nAnd " + itos(failed_files.size() - i) + " more files.";
break;
}
- msg += failed_files[i] + "\n";
+ err_msg += failed_files[i] + "\n";
}
- dialog_error->set_text(msg);
+ dialog_error->set_text(err_msg);
dialog_error->popup_centered();
} else if (!project_path->get_text().ends_with(".zip")) {
@@ -804,7 +804,7 @@ public:
project_path = memnew(LineEdit);
project_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- project_path->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE);
+ project_path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
pphb->add_child(project_path);
install_path_container = memnew(VBoxContainer);
@@ -819,7 +819,7 @@ public:
install_path = memnew(LineEdit);
install_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- install_path->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE);
+ install_path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
iphb->add_child(install_path);
// status icon
@@ -1064,7 +1064,7 @@ public:
void select_first_visible_project();
void erase_selected_projects(bool p_delete_project_contents);
Vector<Item> get_selected_projects() const;
- const Set<String> &get_selected_project_keys() const;
+ const HashSet<String> &get_selected_project_keys() const;
void ensure_project_visible(int p_index);
int get_single_selected_index() const;
bool is_any_project_missing() const;
@@ -1090,7 +1090,7 @@ private:
String _search_term;
FilterOption _order_option;
- Set<String> _selected_project_keys;
+ HashSet<String> _selected_project_keys;
String _last_clicked; // Project key
VBoxContainer *_scroll_children;
int _icon_load_index;
@@ -1258,7 +1258,7 @@ void ProjectList::load_projects() {
List<PropertyInfo> properties;
EditorSettings::get_singleton()->get_property_list(&properties);
- Set<String> favorites;
+ HashSet<String> favorites;
// Find favourites...
for (const PropertyInfo &E : properties) {
String property_key = E.name;
@@ -1403,7 +1403,7 @@ void ProjectList::create_project_item_control(int p_index) {
title->set_clip_text(true);
title_hb->add_child(title);
- String unsupported_features_str = Variant(item.unsupported_features).operator String().trim_prefix("[").trim_suffix("]");
+ String unsupported_features_str = String(", ").join(item.unsupported_features);
int length = unsupported_features_str.length();
if (length > 0) {
Label *unsupported_label = memnew(Label(unsupported_features_str));
@@ -1442,7 +1442,7 @@ void ProjectList::create_project_item_control(int p_index) {
}
Label *fpath = memnew(Label(item.path));
- fpath->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE);
+ fpath->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
path_hb->add_child(fpath);
fpath->set_h_size_flags(Control::SIZE_EXPAND_FILL);
fpath->set_modulate(Color(1, 1, 1, 0.5));
@@ -1504,7 +1504,7 @@ void ProjectList::sort_projects() {
update_dock_menu();
}
-const Set<String> &ProjectList::get_selected_project_keys() const {
+const HashSet<String> &ProjectList::get_selected_project_keys() const {
// Faster if that's all you need
return _selected_project_keys;
}
@@ -1539,7 +1539,7 @@ int ProjectList::get_single_selected_index() const {
String key;
if (_selected_project_keys.size() == 1) {
// Only one selected
- key = _selected_project_keys.front()->get();
+ key = *_selected_project_keys.begin();
} else {
// Multiple selected, consider the last clicked one as "main"
key = _last_clicked;
@@ -1877,13 +1877,34 @@ void ProjectManager::_notification(int p_what) {
search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
search_box->set_clear_button_enabled(true);
+ create_btn->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ import_btn->set_icon(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")));
+ scan_btn->set_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+ open_btn->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
+ run_btn->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons")));
+ rename_btn->set_icon(get_theme_icon(SNAME("Rename"), SNAME("EditorIcons")));
+ erase_btn->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
+ erase_missing_btn->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons")));
+
Engine::get_singleton()->set_editor_hint(false);
} break;
case NOTIFICATION_RESIZED: {
- if (open_templates->is_visible()) {
+ if (open_templates && open_templates->is_visible()) {
open_templates->popup_centered();
}
+ if (asset_library) {
+ real_t size = get_size().x / EDSCALE;
+ asset_library->set_columns(size < 1000 ? 1 : 2);
+ // Adjust names of tabs to fit the new size.
+ if (size < 650) {
+ local_projects_hb->set_name(TTR("Local"));
+ asset_library->set_name(TTR("Asset Library"));
+ } else {
+ local_projects_hb->set_name(TTR("Local Projects"));
+ asset_library->set_name(TTR("Asset Library Projects"));
+ }
+ }
} break;
case NOTIFICATION_READY: {
@@ -1891,10 +1912,6 @@ void ProjectManager::_notification(int p_what) {
filter_option->select(default_sorting);
_project_list->set_order_option(default_sorting);
- if (_project_list->get_project_count() == 0 && StreamPeerSSL::is_available()) {
- open_templates->popup_centered();
- }
-
if (_project_list->get_project_count() >= 1) {
// Focus on the search box immediately to allow the user
// to search without having to reach for their mouse
@@ -1904,6 +1921,10 @@ void ProjectManager::_notification(int p_what) {
if (asset_library) {
// Removes extra border margins.
asset_library->add_theme_style_override("panel", memnew(StyleBoxEmpty));
+ // Suggest browsing asset library to get templates/demos.
+ if (open_templates && _project_list->get_project_count() == 0) {
+ open_templates->popup_centered();
+ }
}
} break;
@@ -2095,12 +2116,12 @@ void ProjectManager::_confirm_update_settings() {
void ProjectManager::_open_selected_projects() {
// Show loading text to tell the user that the project manager is busy loading.
// This is especially important for the HTML5 project manager.
- loading_label->set_modulate(Color(1, 1, 1));
+ loading_label->show();
- const Set<String> &selected_list = _project_list->get_selected_project_keys();
+ const HashSet<String> &selected_list = _project_list->get_selected_project_keys();
- for (const Set<String>::Element *E = selected_list.front(); E; E = E->next()) {
- const String &selected = E->get();
+ for (const String &E : selected_list) {
+ const String &selected = E;
String path = EditorSettings::get_singleton()->get("projects/" + selected);
String conf = path.plus_file("project.godot");
@@ -2146,7 +2167,7 @@ void ProjectManager::_open_selected_projects() {
}
void ProjectManager::_open_selected_projects_ask() {
- const Set<String> &selected_list = _project_list->get_selected_project_keys();
+ const HashSet<String> &selected_list = _project_list->get_selected_project_keys();
if (selected_list.size() < 1) {
return;
@@ -2209,7 +2230,7 @@ void ProjectManager::_open_selected_projects_ask() {
}
}
if (!unsupported_features.is_empty()) {
- String unsupported_features_str = Variant(unsupported_features).operator String().trim_prefix("[").trim_suffix("]");
+ String unsupported_features_str = String(", ").join(unsupported_features);
warning_message += vformat(TTR("Warning: This project uses the following features not supported by this build of Godot:\n\n%s\n\n"), unsupported_features_str);
}
warning_message += TTR("Open anyway? Project will be modified.");
@@ -2261,7 +2282,7 @@ void ProjectManager::_run_project_confirm() {
}
void ProjectManager::_run_project() {
- const Set<String> &selected_list = _project_list->get_selected_project_keys();
+ const HashSet<String> &selected_list = _project_list->get_selected_project_keys();
if (selected_list.size() < 1) {
return;
@@ -2321,14 +2342,14 @@ void ProjectManager::_import_project() {
}
void ProjectManager::_rename_project() {
- const Set<String> &selected_list = _project_list->get_selected_project_keys();
+ const HashSet<String> &selected_list = _project_list->get_selected_project_keys();
if (selected_list.size() == 0) {
return;
}
- for (Set<String>::Element *E = selected_list.front(); E; E = E->next()) {
- const String &selected = E->get();
+ for (const String &E : selected_list) {
+ const String &selected = E;
String path = EditorSettings::get_singleton()->get("projects/" + selected);
npdialog->set_project_path(path);
npdialog->set_mode(ProjectDialog::MODE_RENAME);
@@ -2347,7 +2368,7 @@ void ProjectManager::_erase_missing_projects_confirm() {
}
void ProjectManager::_erase_project() {
- const Set<String> &selected_list = _project_list->get_selected_project_keys();
+ const HashSet<String> &selected_list = _project_list->get_selected_project_keys();
if (selected_list.size() == 0) {
return;
@@ -2404,7 +2425,7 @@ void ProjectManager::_files_dropped(PackedStringArray p_files) {
_install_project(p_files[0], file.substr(0, file.length() - 4).capitalize());
return;
}
- Set<String> folders_set;
+ HashSet<String> folders_set;
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < p_files.size(); i++) {
String file = p_files[i];
@@ -2412,8 +2433,8 @@ void ProjectManager::_files_dropped(PackedStringArray p_files) {
}
if (folders_set.size() > 0) {
PackedStringArray folders;
- for (Set<String>::Element *E = folders_set.front(); E; E = E->next()) {
- folders.push_back(E->get());
+ for (const String &E : folders_set) {
+ folders.push_back(E);
}
bool confirm = true;
@@ -2567,14 +2588,14 @@ ProjectManager::ProjectManager() {
tabs->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
tabs->connect("tab_changed", callable_mp(this, &ProjectManager::_on_tab_changed));
- HBoxContainer *projects_hb = memnew(HBoxContainer);
- projects_hb->set_name(TTR("Local Projects"));
- tabs->add_child(projects_hb);
+ local_projects_hb = memnew(HBoxContainer);
+ local_projects_hb->set_name(TTR("Local Projects"));
+ tabs->add_child(local_projects_hb);
{
// Projects + search bar
VBoxContainer *search_tree_vb = memnew(VBoxContainer);
- projects_hb->add_child(search_tree_vb);
+ local_projects_hb->add_child(search_tree_vb);
search_tree_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
HBoxContainer *hb = memnew(HBoxContainer);
@@ -2582,7 +2603,7 @@ ProjectManager::ProjectManager() {
search_tree_vb->add_child(hb);
search_box = memnew(LineEdit);
- search_box->set_placeholder(TTR("Filter projects"));
+ search_box->set_placeholder(TTR("Filter Projects"));
search_box->set_tooltip(TTR("This field filters projects by name and last path component.\nTo filter projects by name and full path, the query must contain at least one `/` character."));
search_box->connect("text_changed", callable_mp(this, &ProjectManager::_on_search_term_changed));
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -2592,8 +2613,8 @@ ProjectManager::ProjectManager() {
loading_label->add_theme_font_override("font", get_theme_font(SNAME("bold"), SNAME("EditorFonts")));
loading_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
hb->add_child(loading_label);
- // Hide the label but make it still take up space. This prevents reflows when showing the label.
- loading_label->set_modulate(Color(0, 0, 0, 0));
+ // The loading label is shown later.
+ loading_label->hide();
Label *sort_label = memnew(Label);
sort_label->set_text(TTR("Sort:"));
@@ -2601,7 +2622,7 @@ ProjectManager::ProjectManager() {
filter_option = memnew(OptionButton);
filter_option->set_clip_text(true);
- filter_option->set_custom_minimum_size(Size2(150 * EDSCALE, 10 * EDSCALE));
+ filter_option->set_h_size_flags(Control::SIZE_EXPAND_FILL);
filter_option->connect("item_selected", callable_mp(this, &ProjectManager::_on_order_option_changed));
hb->add_child(filter_option);
@@ -2630,42 +2651,50 @@ ProjectManager::ProjectManager() {
// Project tab side bar
VBoxContainer *tree_vb = memnew(VBoxContainer);
tree_vb->set_custom_minimum_size(Size2(120, 120));
- projects_hb->add_child(tree_vb);
-
- Button *create = memnew(Button);
- create->set_text(TTR("New Project"));
- create->set_shortcut(ED_SHORTCUT("project_manager/new_project", TTR("New Project"), KeyModifierMask::CMD | Key::N));
- create->connect("pressed", callable_mp(this, &ProjectManager::_new_project));
- tree_vb->add_child(create);
-
- Button *import = memnew(Button);
- import->set_text(TTR("Import"));
- import->set_shortcut(ED_SHORTCUT("project_manager/import_project", TTR("Import Project"), KeyModifierMask::CMD | Key::I));
- import->connect("pressed", callable_mp(this, &ProjectManager::_import_project));
- tree_vb->add_child(import);
-
- Button *scan = memnew(Button);
- scan->set_text(TTR("Scan"));
- scan->set_shortcut(ED_SHORTCUT("project_manager/scan_projects", TTR("Scan Projects"), KeyModifierMask::CMD | Key::S));
- scan->connect("pressed", callable_mp(this, &ProjectManager::_scan_projects));
- tree_vb->add_child(scan);
+ local_projects_hb->add_child(tree_vb);
+
+ const int btn_h_separation = int(6 * EDSCALE);
+
+ create_btn = memnew(Button);
+ create_btn->set_text(TTR("New Project"));
+ create_btn->add_theme_constant_override("h_separation", btn_h_separation);
+ create_btn->set_shortcut(ED_SHORTCUT("project_manager/new_project", TTR("New Project"), KeyModifierMask::CMD | Key::N));
+ create_btn->connect("pressed", callable_mp(this, &ProjectManager::_new_project));
+ tree_vb->add_child(create_btn);
+
+ import_btn = memnew(Button);
+ import_btn->set_text(TTR("Import"));
+ import_btn->add_theme_constant_override("h_separation", btn_h_separation);
+ import_btn->set_shortcut(ED_SHORTCUT("project_manager/import_project", TTR("Import Project"), KeyModifierMask::CMD | Key::I));
+ import_btn->connect("pressed", callable_mp(this, &ProjectManager::_import_project));
+ tree_vb->add_child(import_btn);
+
+ scan_btn = memnew(Button);
+ scan_btn->set_text(TTR("Scan"));
+ scan_btn->add_theme_constant_override("h_separation", btn_h_separation);
+ scan_btn->set_shortcut(ED_SHORTCUT("project_manager/scan_projects", TTR("Scan Projects"), KeyModifierMask::CMD | Key::S));
+ scan_btn->connect("pressed", callable_mp(this, &ProjectManager::_scan_projects));
+ tree_vb->add_child(scan_btn);
tree_vb->add_child(memnew(HSeparator));
open_btn = memnew(Button);
open_btn->set_text(TTR("Edit"));
+ open_btn->add_theme_constant_override("h_separation", btn_h_separation);
open_btn->set_shortcut(ED_SHORTCUT("project_manager/edit_project", TTR("Edit Project"), KeyModifierMask::CMD | Key::E));
open_btn->connect("pressed", callable_mp(this, &ProjectManager::_open_selected_projects_ask));
tree_vb->add_child(open_btn);
run_btn = memnew(Button);
run_btn->set_text(TTR("Run"));
+ run_btn->add_theme_constant_override("h_separation", btn_h_separation);
run_btn->set_shortcut(ED_SHORTCUT("project_manager/run_project", TTR("Run Project"), KeyModifierMask::CMD | Key::R));
run_btn->connect("pressed", callable_mp(this, &ProjectManager::_run_project));
tree_vb->add_child(run_btn);
rename_btn = memnew(Button);
rename_btn->set_text(TTR("Rename"));
+ rename_btn->add_theme_constant_override("h_separation", btn_h_separation);
// The F2 shortcut isn't overridden with Enter on macOS as Enter is already used to edit a project.
rename_btn->set_shortcut(ED_SHORTCUT("project_manager/rename_project", TTR("Rename Project"), Key::F2));
rename_btn->connect("pressed", callable_mp(this, &ProjectManager::_rename_project));
@@ -2673,12 +2702,14 @@ ProjectManager::ProjectManager() {
erase_btn = memnew(Button);
erase_btn->set_text(TTR("Remove"));
+ erase_btn->add_theme_constant_override("h_separation", btn_h_separation);
erase_btn->set_shortcut(ED_SHORTCUT("project_manager/remove_project", TTR("Remove Project"), Key::KEY_DELETE));
erase_btn->connect("pressed", callable_mp(this, &ProjectManager::_erase_project));
tree_vb->add_child(erase_btn);
erase_missing_btn = memnew(Button);
erase_missing_btn->set_text(TTR("Remove Missing"));
+ erase_missing_btn->add_theme_constant_override("h_separation", btn_h_separation);
erase_missing_btn->connect("pressed", callable_mp(this, &ProjectManager::_erase_missing_projects));
tree_vb->add_child(erase_missing_btn);
@@ -2727,6 +2758,12 @@ ProjectManager::ProjectManager() {
language_btn->set_icon(get_theme_icon(SNAME("Environment"), SNAME("EditorIcons")));
language_btn->set_focus_mode(Control::FOCUS_NONE);
language_btn->connect("item_selected", callable_mp(this, &ProjectManager::_language_selected));
+#ifdef ANDROID_ENABLED
+ // The language selection dropdown doesn't work on Android (as the setting isn't saved), see GH-60353.
+ // Also, the dropdown it spawns is very tall and can't be scrolled without a hardware mouse.
+ // Hiding the language selection dropdown also leaves more space for the version label to display.
+ language_btn->hide();
+#endif
Vector<String> editor_languages;
List<PropertyInfo> editor_settings_properties;
@@ -2755,6 +2792,9 @@ ProjectManager::ProjectManager() {
center_box->add_child(settings_hb);
}
+ // Asset Library can't work on Web editor for now as most assets are sourced
+ // directly from GitHub which does not set CORS.
+#ifndef JAVASCRIPT_ENABLED
if (StreamPeerSSL::is_available()) {
asset_library = memnew(EditorAssetLibrary(true));
asset_library->set_name(TTR("Asset Library Projects"));
@@ -2763,6 +2803,7 @@ ProjectManager::ProjectManager() {
} else {
WARN_PRINT("Asset Library not available, as it requires SSL to work.");
}
+#endif
{
// Dialogs
@@ -2831,11 +2872,13 @@ ProjectManager::ProjectManager() {
dialog_error = memnew(AcceptDialog);
add_child(dialog_error);
- open_templates = memnew(ConfirmationDialog);
- open_templates->set_text(TTR("You currently don't have any projects.\nWould you like to explore official example projects in the Asset Library?"));
- open_templates->get_ok_button()->set_text(TTR("Open Asset Library"));
- open_templates->connect("confirmed", callable_mp(this, &ProjectManager::_open_asset_library));
- add_child(open_templates);
+ if (asset_library) {
+ open_templates = memnew(ConfirmationDialog);
+ open_templates->set_text(TTR("You currently don't have any projects.\nWould you like to explore official example projects in the Asset Library?"));
+ open_templates->get_ok_button()->set_text(TTR("Open Asset Library"));
+ open_templates->connect("confirmed", callable_mp(this, &ProjectManager::_open_asset_library));
+ add_child(open_templates);
+ }
about = memnew(EditorAbout);
add_child(about);
@@ -2869,8 +2912,8 @@ ProjectManager::ProjectManager() {
SceneTree::get_singleton()->get_root()->connect("files_dropped", callable_mp(this, &ProjectManager::_files_dropped));
- // Define a minimum window size to prevent UI elements from overlapping or being cut off
- DisplayServer::get_singleton()->window_set_min_size(Size2(750, 420) * EDSCALE);
+ // Define a minimum window size to prevent UI elements from overlapping or being cut off.
+ DisplayServer::get_singleton()->window_set_min_size(Size2(520, 350) * EDSCALE);
// Resize the bootsplash window based on Editor display scale EDSCALE.
float scale_factor = MAX(1, EDSCALE);
@@ -2879,17 +2922,15 @@ ProjectManager::ProjectManager() {
Vector2i screen_size = DisplayServer::get_singleton()->screen_get_size();
Vector2i screen_position = DisplayServer::get_singleton()->screen_get_position();
- // Consider the editor display scale.
- window_size.x = round((float)window_size.x * scale_factor);
- window_size.y = round((float)window_size.y * scale_factor);
-
- // Make the window centered on the screen.
- Vector2i window_position;
- window_position.x = screen_position.x + (screen_size.x - window_size.x) / 2;
- window_position.y = screen_position.y + (screen_size.y - window_size.y) / 2;
+ window_size *= scale_factor;
DisplayServer::get_singleton()->window_set_size(window_size);
- DisplayServer::get_singleton()->window_set_position(window_position);
+ if (screen_size != Vector2i()) {
+ Vector2i window_position;
+ window_position.x = screen_position.x + (screen_size.x - window_size.x) / 2;
+ window_position.y = screen_position.y + (screen_size.y - window_size.y) / 2;
+ DisplayServer::get_singleton()->window_set_position(window_position);
+ }
}
OS::get_singleton()->set_low_processor_usage_mode(true);