diff options
Diffstat (limited to 'editor/project_manager.cpp')
-rw-r--r-- | editor/project_manager.cpp | 277 |
1 files changed, 180 insertions, 97 deletions
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index e8fd3070c2..7ae03b3072 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -41,6 +41,7 @@ #include "core/string/translation.h" #include "core/version.h" #include "core/version_hash.gen.h" +#include "editor/editor_vcs_interface.h" #include "editor_scale.h" #include "editor_settings.h" #include "editor_themes.h" @@ -52,6 +53,7 @@ #include "scene/gui/texture_rect.h" #include "scene/main/window.h" #include "servers/display_server.h" +#include "servers/navigation_server_3d.h" static inline String get_project_key_from_path(const String &dir) { return dir.replace("/", "::"); @@ -66,19 +68,19 @@ public: MODE_NEW, MODE_IMPORT, MODE_INSTALL, - MODE_RENAME + MODE_RENAME, }; private: enum MessageType { MESSAGE_ERROR, MESSAGE_WARNING, - MESSAGE_SUCCESS + MESSAGE_SUCCESS, }; enum InputType { PROJECT_PATH, - INSTALL_PATH + INSTALL_PATH, }; Mode mode; @@ -89,6 +91,7 @@ private: Container *path_container; Container *install_path_container; Container *rasterizer_container; + HBoxContainer *default_files_container; Ref<ButtonGroup> rasterizer_button_group; Label *msg; LineEdit *project_path; @@ -98,6 +101,7 @@ private: TextureRect *install_status_rect; FileDialog *fdialog; FileDialog *fdialog_install; + OptionButton *vcs_metadata_selection; String zip_path; String zip_title; AcceptDialog *dialog_error; @@ -473,32 +477,29 @@ private: cd->grab_focus(); return; } + PackedStringArray project_features = ProjectSettings::get_required_features(); ProjectSettings::CustomMap initial_settings; - initial_settings["rendering/vulkan/rendering/back_end"] = rasterizer_button_group->get_pressed_button()->get_meta(SNAME("driver_name")); + // Be sure to change this code if/when renderers are changed. + int renderer_type = rasterizer_button_group->get_pressed_button()->get_meta(SNAME("driver_name")); + initial_settings["rendering/vulkan/rendering/back_end"] = renderer_type; + if (renderer_type == 0) { + project_features.push_back("Vulkan Clustered"); + } else if (renderer_type == 1) { + project_features.push_back("Vulkan Mobile"); + } else { + WARN_PRINT("Unknown renderer type. Please report this as a bug on GitHub."); + } + project_features.sort(); + initial_settings["application/config/features"] = project_features; initial_settings["application/config/name"] = project_name->get_text().strip_edges(); initial_settings["application/config/icon"] = "res://icon.png"; - initial_settings["rendering/environment/defaults/default_environment"] = "res://default_env.tres"; if (ProjectSettings::get_singleton()->save_custom(dir.plus_file("project.godot"), initial_settings, Vector<String>(), false) != OK) { set_message(TTR("Couldn't create project.godot in project path."), MESSAGE_ERROR); } else { ResourceSaver::save(dir.plus_file("icon.png"), create_unscaled_default_project_icon()); - - FileAccess *f = FileAccess::open(dir.plus_file("default_env.tres"), FileAccess::WRITE); - if (!f) { - set_message(TTR("Couldn't create project.godot in project path."), MESSAGE_ERROR); - } else { - f->store_line("[gd_resource type=\"Environment\" load_steps=2 format=3]"); - f->store_line(""); - f->store_line("[sub_resource type=\"Sky\" id=\"1\"]"); - f->store_line(""); - f->store_line("[resource]"); - f->store_line("background_mode = 2"); - f->store_line("sky = SubResource( \"1\" )"); - memdelete(f); - } + EditorVCSInterface::create_vcs_metadata_files(EditorVCSInterface::VCSMetadata(vcs_metadata_selection->get_selected()), dir); } - } else if (mode == MODE_INSTALL) { if (project_path->get_text().ends_with(".zip")) { dir = install_path->get_text(); @@ -693,6 +694,7 @@ public: install_path_container->hide(); install_status_rect->hide(); rasterizer_container->hide(); + default_files_container->hide(); get_ok_button()->set_disabled(false); ProjectSettings *current = memnew(ProjectSettings); @@ -744,6 +746,7 @@ public: name_container->hide(); install_path_container->hide(); rasterizer_container->hide(); + default_files_container->hide(); project_path->grab_focus(); } else if (mode == MODE_NEW) { @@ -752,6 +755,7 @@ public: name_container->show(); install_path_container->hide(); rasterizer_container->show(); + default_files_container->show(); project_name->call_deferred(SNAME("grab_focus")); project_name->call_deferred(SNAME("select_all")); @@ -762,6 +766,7 @@ public: name_container->show(); install_path_container->hide(); rasterizer_container->hide(); + default_files_container->hide(); project_path->grab_focus(); } @@ -904,6 +909,21 @@ public: l->set_modulate(Color(1, 1, 1, 0.7)); rasterizer_container->add_child(l); + default_files_container = memnew(HBoxContainer); + vb->add_child(default_files_container); + l = memnew(Label); + l->set_text(TTR("Version Control Metadata:")); + default_files_container->add_child(l); + vcs_metadata_selection = memnew(OptionButton); + vcs_metadata_selection->set_custom_minimum_size(Size2(100, 20)); + vcs_metadata_selection->add_item("None", (int)EditorVCSInterface::VCSMetadata::NONE); + vcs_metadata_selection->add_item("Git", (int)EditorVCSInterface::VCSMetadata::GIT); + vcs_metadata_selection->select((int)EditorVCSInterface::VCSMetadata::GIT); + default_files_container->add_child(vcs_metadata_selection); + Control *spacer = memnew(Control); + spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL); + default_files_container->add_child(spacer); + fdialog = memnew(FileDialog); fdialog->set_access(FileDialog::ACCESS_FILESYSTEM); fdialog_install = memnew(FileDialog); @@ -985,6 +1005,7 @@ public: String path; String icon; String main_scene; + PackedStringArray unsupported_features; uint64_t last_edited = 0; bool favorite = false; bool grayed = false; @@ -1001,6 +1022,7 @@ public: const String &p_path, const String &p_icon, const String &p_main_scene, + const PackedStringArray &p_unsupported_features, uint64_t p_last_edited, bool p_favorite, bool p_grayed, @@ -1012,6 +1034,7 @@ public: path = p_path; icon = p_icon; main_scene = p_main_scene; + unsupported_features = p_unsupported_features; last_edited = p_last_edited; favorite = p_favorite; grayed = p_grayed; @@ -1063,8 +1086,7 @@ private: void remove_project(int p_index, bool p_update_settings); void update_icons_async(); void load_project_icon(int p_index); - - static void load_project_data(const String &p_property_key, Item &p_item, bool p_favorite); + static Item load_project_data(const String &p_property_key, bool p_favorite); String _search_term; FilterOption _order_option; @@ -1155,7 +1177,8 @@ void ProjectList::load_project_icon(int p_index) { item.control->icon_needs_reload = false; } -void ProjectList::load_project_data(const String &p_property_key, Item &p_item, bool p_favorite) { +// Load project data from p_property_key and return it in a ProjectList::Item. p_favorite is passed directly into the Item. +ProjectList::Item ProjectList::load_project_data(const String &p_property_key, bool p_favorite) { String path = EditorSettings::get_singleton()->get(p_property_key); String conf = path.plus_file("project.godot"); bool grayed = false; @@ -1175,13 +1198,16 @@ void ProjectList::load_project_data(const String &p_property_key, Item &p_item, } if (config_version > ProjectSettings::CONFIG_VERSION) { - // Comes from an incompatible (more recent) Godot version, grey it out + // Comes from an incompatible (more recent) Godot version, gray it out. grayed = true; } - String description = cf->get_value("application", "config/description", ""); - String icon = cf->get_value("application", "config/icon", ""); - String main_scene = cf->get_value("application", "run/main_scene", ""); + const String description = cf->get_value("application", "config/description", ""); + const String icon = cf->get_value("application", "config/icon", ""); + const String main_scene = cf->get_value("application", "run/main_scene", ""); + + PackedStringArray project_features = cf->get_value("application", "config/features", PackedStringArray()); + PackedStringArray unsupported_features = ProjectSettings::get_unsupported_features(project_features); uint64_t last_edited = 0; if (FileAccess::exists(conf)) { @@ -1203,9 +1229,9 @@ void ProjectList::load_project_data(const String &p_property_key, Item &p_item, print_line("Project is missing: " + conf); } - String project_key = p_property_key.get_slice("/", 1); + const String project_key = p_property_key.get_slice("/", 1); - p_item = Item(project_key, project_name, description, path, icon, main_scene, last_edited, p_favorite, grayed, missing, config_version); + return Item(project_key, project_name, description, path, icon, main_scene, unsupported_features, last_edited, p_favorite, grayed, missing, config_version); } void ProjectList::load_projects() { @@ -1248,8 +1274,7 @@ void ProjectList::load_projects() { String project_key = property_key.get_slice("/", 1); bool favorite = favorites.has("favorite_projects/" + project_key); - Item item; - load_project_data(property_key, item, favorite); + Item item = load_project_data(property_key, favorite); _projects.push_back(item); } @@ -1297,8 +1322,7 @@ void ProjectList::update_dock_menu() { void ProjectList::_global_menu_new_window(const Variant &p_tag) { List<String> args; args.push_back("-p"); - String exec = OS::get_singleton()->get_executable_path(); - OS::get_singleton()->create_process(exec, args); + OS::get_singleton()->create_instance(args); } void ProjectList::_global_menu_open_project(const Variant &p_tag) { @@ -1308,8 +1332,7 @@ void ProjectList::_global_menu_open_project(const Variant &p_tag) { String conf = _projects[idx].path.plus_file("project.godot"); List<String> args; args.push_back(conf); - String exec = OS::get_singleton()->get_executable_path(); - OS::get_singleton()->create_process(exec, args); + OS::get_singleton()->create_instance(args); } } @@ -1334,7 +1357,7 @@ void ProjectList::create_project_item_control(int p_index) { TextureButton *favorite = memnew(TextureButton); favorite->set_name("FavoriteButton"); favorite->set_normal_texture(favorite_icon); - // This makes the project's "hover" style display correctly when hovering the favorite icon + // This makes the project's "hover" style display correctly when hovering the favorite icon. favorite->set_mouse_filter(MOUSE_FILTER_PASS); favorite->connect("pressed", callable_mp(this, &ProjectList::_favorite_pressed), varray(hb)); favorite_box->add_child(favorite); @@ -1364,40 +1387,65 @@ void ProjectList::create_project_item_control(int p_index) { ec->set_custom_minimum_size(Size2(0, 1)); ec->set_mouse_filter(MOUSE_FILTER_PASS); vb->add_child(ec); - Label *title = memnew(Label(!item.missing ? item.project_name : TTR("Missing Project"))); - title->add_theme_font_override("font", get_theme_font(SNAME("title"), SNAME("EditorFonts"))); - title->add_theme_font_size_override("font_size", get_theme_font_size(SNAME("title_size"), SNAME("EditorFonts"))); - title->add_theme_color_override("font_color", font_color); - title->set_clip_text(true); - vb->add_child(title); - - HBoxContainer *path_hb = memnew(HBoxContainer); - path_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); - vb->add_child(path_hb); - - Button *show = memnew(Button); - // Display a folder icon if the project directory can be opened, or a "broken file" icon if it can't. - show->set_icon(get_theme_icon(!item.missing ? "Load" : "FileBroken", "EditorIcons")); - if (!item.grayed) { - // Don't make the icon less prominent if the parent is already grayed out. - show->set_modulate(Color(1, 1, 1, 0.5)); - } - path_hb->add_child(show); - - if (!item.missing) { - show->connect("pressed", callable_mp(this, &ProjectList::_show_project), varray(item.path)); - show->set_tooltip(TTR("Show in File Manager")); - } else { - show->set_tooltip(TTR("Error: Project is missing on the filesystem.")); - } - Label *fpath = memnew(Label(item.path)); - fpath->set_structured_text_bidi_override(Control::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)); - fpath->add_theme_color_override("font_color", font_color); - fpath->set_clip_text(true); + { // Top half, title and unsupported features labels. + HBoxContainer *title_hb = memnew(HBoxContainer); + vb->add_child(title_hb); + + Label *title = memnew(Label(!item.missing ? item.project_name : TTR("Missing Project"))); + title->set_h_size_flags(Control::SIZE_EXPAND_FILL); + title->add_theme_font_override("font", get_theme_font(SNAME("title"), SNAME("EditorFonts"))); + title->add_theme_font_size_override("font_size", get_theme_font_size(SNAME("title_size"), SNAME("EditorFonts"))); + title->add_theme_color_override("font_color", font_color); + title->set_clip_text(true); + title_hb->add_child(title); + + String unsupported_features_str = Variant(item.unsupported_features).operator String().trim_prefix("[").trim_suffix("]"); + int length = unsupported_features_str.length(); + if (length > 0) { + Label *unsupported_label = memnew(Label(unsupported_features_str)); + unsupported_label->set_custom_minimum_size(Size2(length * 15, 10) * EDSCALE); + unsupported_label->add_theme_font_override("font", get_theme_font(SNAME("title"), SNAME("EditorFonts"))); + unsupported_label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + unsupported_label->set_clip_text(true); + unsupported_label->set_align(Label::ALIGN_RIGHT); + title_hb->add_child(unsupported_label); + Control *spacer = memnew(Control()); + spacer->set_custom_minimum_size(Size2(10, 10)); + title_hb->add_child(spacer); + } + } + + { // Bottom half, containing the path and view folder button. + HBoxContainer *path_hb = memnew(HBoxContainer); + path_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + vb->add_child(path_hb); + + Button *show = memnew(Button); + // Display a folder icon if the project directory can be opened, or a "broken file" icon if it can't. + show->set_icon(get_theme_icon(!item.missing ? "Load" : "FileBroken", "EditorIcons")); + show->set_flat(true); + if (!item.grayed) { + // Don't make the icon less prominent if the parent is already grayed out. + show->set_modulate(Color(1, 1, 1, 0.5)); + } + path_hb->add_child(show); + + if (!item.missing) { + show->connect("pressed", callable_mp(this, &ProjectList::_show_project), varray(item.path)); + show->set_tooltip(TTR("Show in File Manager")); + } else { + show->set_tooltip(TTR("Error: Project is missing on the filesystem.")); + } + + Label *fpath = memnew(Label(item.path)); + fpath->set_structured_text_bidi_override(Control::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)); + fpath->add_theme_color_override("font_color", font_color); + fpath->set_clip_text(true); + } _scroll_children->add_child(hb); item.control = hb; @@ -1511,7 +1559,7 @@ void ProjectList::remove_project(int p_index, bool p_update_settings) { } memdelete(item.control); - _projects.remove(p_index); + _projects.remove_at(p_index); if (p_update_settings) { EditorSettings::get_singleton()->erase("projects/" + item.project_key); @@ -1602,8 +1650,7 @@ int ProjectList::refresh_project(const String &dir_path) { if (should_be_in_list) { // Recreate it with updated info - Item item; - load_project_data(property_key, item, is_favourite); + Item item = load_project_data(property_key, is_favourite); _projects.push_back(item); create_project_item_control(_projects.size() - 1); @@ -1701,7 +1748,7 @@ void ProjectList::erase_selected_projects(bool p_delete_project_contents) { } memdelete(item.control); - _projects.remove(i); + _projects.remove_at(i); --i; } } @@ -1737,7 +1784,7 @@ void ProjectList::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) { int clicked_index = p_hb->get_index(); const Item &clicked_project = _projects[clicked_index]; - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { if (mb->is_shift_pressed() && _selected_project_keys.size() > 0 && _last_clicked != "" && clicked_project.project_key != _last_clicked) { int anchor_index = -1; for (int i = 0; i < _projects.size(); ++i) { @@ -1901,7 +1948,7 @@ void ProjectManager::unhandled_key_input(const Ref<InputEvent> &p_ev) { // This is handled by the platform implementation on macOS, // so only define the shortcut on other platforms #ifndef OSX_ENABLED - if (k->get_keycode_with_modifiers() == (KEY_MASK_CMD | KEY_Q)) { + if (k->get_keycode_with_modifiers() == (KeyModifierMask::CMD | Key::Q)) { _dim_window(); get_tree()->quit(); } @@ -1914,24 +1961,24 @@ void ProjectManager::unhandled_key_input(const Ref<InputEvent> &p_ev) { bool keycode_handled = true; switch (k->get_keycode()) { - case KEY_ENTER: { + case Key::ENTER: { _open_selected_projects_ask(); } break; - case KEY_HOME: { + case Key::HOME: { if (_project_list->get_project_count() > 0) { _project_list->select_project(0); _update_project_buttons(); } } break; - case KEY_END: { + case Key::END: { if (_project_list->get_project_count() > 0) { _project_list->select_project(_project_list->get_project_count() - 1); _update_project_buttons(); } } break; - case KEY_UP: { + case Key::UP: { if (k->is_shift_pressed()) { break; } @@ -1945,7 +1992,7 @@ void ProjectManager::unhandled_key_input(const Ref<InputEvent> &p_ev) { break; } - case KEY_DOWN: { + case Key::DOWN: { if (k->is_shift_pressed()) { break; } @@ -1958,7 +2005,7 @@ void ProjectManager::unhandled_key_input(const Ref<InputEvent> &p_ev) { } } break; - case KEY_F: { + case Key::F: { if (k->is_command_pressed()) { this->search_box->grab_focus(); } else { @@ -2055,8 +2102,7 @@ void ProjectManager::_open_selected_projects() { args.push_back("--single-window"); } - String exec = OS::get_singleton()->get_executable_path(); - Error err = OS::get_singleton()->create_process(exec, args); + Error err = OS::get_singleton()->create_instance(args); ERR_FAIL_COND(err); } @@ -2083,8 +2129,12 @@ void ProjectManager::_open_selected_projects_ask() { } // Update the project settings or don't open - String conf = project.path.plus_file("project.godot"); - int config_version = project.version; + const String conf = project.path.plus_file("project.godot"); + const int config_version = project.version; + PackedStringArray unsupported_features = project.unsupported_features; + + Label *ask_update_label = ask_update_settings->get_label(); + ask_update_label->set_align(Label::ALIGN_LEFT); // Reset in case of previous center align. // Check if the config_version property was empty or 0 if (config_version == 0) { @@ -2104,6 +2154,35 @@ void ProjectManager::_open_selected_projects_ask() { dialog_error->popup_centered(); return; } + // Check if the project is using features not supported by this build of Godot. + if (!unsupported_features.is_empty()) { + String warning_message = ""; + for (int i = 0; i < unsupported_features.size(); i++) { + String feature = unsupported_features[i]; + if (feature == "Double Precision") { + warning_message += TTR("Warning: This project uses double precision floats, but this version of\nGodot uses single precision floats. Opening this project may cause data loss.\n\n"); + unsupported_features.remove_at(i); + i--; + } else if (feature == "C#") { + warning_message += TTR("Warning: This project uses C#, but this build of Godot does not have\nthe Mono module. If you proceed you will not be able to use any C# scripts.\n\n"); + unsupported_features.remove_at(i); + i--; + } else if (feature.substr(0, 3).is_numeric()) { + warning_message += vformat(TTR("Warning: This project was built in Godot %s.\nOpening will upgrade or downgrade the project to Godot %s.\n\n"), Variant(feature), Variant(VERSION_BRANCH)); + unsupported_features.remove_at(i); + i--; + } + } + if (!unsupported_features.is_empty()) { + String unsupported_features_str = Variant(unsupported_features).operator String().trim_prefix("[").trim_suffix("]"); + 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."); + ask_update_label->set_align(Label::ALIGN_CENTER); + ask_update_settings->set_text(warning_message); + ask_update_settings->popup_centered(); + return; + } // Open if the project is up-to-date _open_selected_projects(); @@ -2141,8 +2220,7 @@ void ProjectManager::_run_project_confirm() { args.push_back("--disable-crash-handler"); } - String exec = OS::get_singleton()->get_executable_path(); - Error err = OS::get_singleton()->create_process(exec, args); + Error err = OS::get_singleton()->create_instance(args); ERR_FAIL_COND(err); } } @@ -2271,8 +2349,7 @@ void ProjectManager::_language_selected(int p_id) { void ProjectManager::_restart_confirm() { List<String> args = OS::get_singleton()->get_cmdline_args(); - String exec = OS::get_singleton()->get_executable_path(); - Error err = OS::get_singleton()->create_process(exec, args); + Error err = OS::get_singleton()->create_instance(args); ERR_FAIL_COND(err); _dim_window(); @@ -2387,6 +2464,11 @@ ProjectManager::ProjectManager() { EditorSettings::create(); } + // Turn off some servers we aren't going to be using in the Project Manager. + NavigationServer3D::get_singleton()->set_active(false); + PhysicsServer3D::get_singleton()->set_active(false); + PhysicsServer2D::get_singleton()->set_active(false); + EditorSettings::get_singleton()->set_optimize_save(false); //just write settings as they came { @@ -2525,19 +2607,19 @@ ProjectManager::ProjectManager() { Button *create = memnew(Button); create->set_text(TTR("New Project")); - create->set_shortcut(ED_SHORTCUT("project_manager/new_project", TTR("New Project"), KEY_MASK_CMD | KEY_N)); + 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"), KEY_MASK_CMD | KEY_I)); + 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"), KEY_MASK_CMD | KEY_S)); + 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); @@ -2545,25 +2627,26 @@ ProjectManager::ProjectManager() { open_btn = memnew(Button); open_btn->set_text(TTR("Edit")); - open_btn->set_shortcut(ED_SHORTCUT("project_manager/edit_project", TTR("Edit Project"), KEY_MASK_CMD | KEY_E)); + 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->set_shortcut(ED_SHORTCUT("project_manager/run_project", TTR("Run Project"), KEY_MASK_CMD | KEY_R)); + 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->set_shortcut(ED_SHORTCUT("project_manager/rename_project", TTR("Rename Project"), KEY_F2)); + // 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)); tree_vb->add_child(rename_btn); erase_btn = memnew(Button); erase_btn->set_text(TTR("Remove")); - erase_btn->set_shortcut(ED_SHORTCUT("project_manager/remove_project", TTR("Remove Project"), KEY_DELETE)); + 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); @@ -2634,7 +2717,7 @@ ProjectManager::ProjectManager() { for (int i = 0; i < editor_languages.size(); i++) { String lang = editor_languages[i]; String lang_name = TranslationServer::get_singleton()->get_locale_name(lang); - language_btn->add_item(lang_name + " [" + lang + "]", i); + language_btn->add_item(vformat("[%s] %s", lang, lang_name), i); language_btn->set_item_metadata(i, lang); if (current_lang == lang) { language_btn->select(i); |