diff options
Diffstat (limited to 'editor/editor_node.cpp')
| -rw-r--r-- | editor/editor_node.cpp | 620 |
1 files changed, 344 insertions, 276 deletions
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 3555caac8a..d9318f6bde 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -34,7 +34,6 @@ #include "core/input/input.h" #include "core/io/config_file.h" #include "core/io/file_access.h" -#include "core/io/image_loader.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/object/class_db.h" @@ -151,6 +150,51 @@ EditorNode *EditorNode::singleton = nullptr; // The metadata key used to store and retrieve the version text to copy to the clipboard. static const String META_TEXT_TO_COPY = "text_to_copy"; +class AcceptDialogAutoReparent : public AcceptDialog { + GDCLASS(AcceptDialogAutoReparent, AcceptDialog); + +protected: + void _notification(int p_what) { + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + if (!is_visible()) { + Node *p = get_parent(); + if (p) { + p->remove_child(this); + } + } + } + } + +public: + void attach_and_popup_centered() { + EditorNode *ed = EditorNode::get_singleton(); + if (ed && !is_inside_tree()) { + Window *w = ed->get_window(); + while (w && w->get_exclusive_child()) { + w = w->get_exclusive_child(); + } + if (w && w != this) { + w->add_child(this); + popup_centered(); + } + } + } + + void attach_and_popup_centered_ratio(float p_ratio = 0.8) { + EditorNode *ed = EditorNode::get_singleton(); + if (ed && !is_inside_tree()) { + Window *w = ed->get_window(); + while (w && w->get_exclusive_child()) { + w = w->get_exclusive_child(); + } + if (w && w != this) { + w->add_child(this); + popup_centered_ratio(p_ratio); + } + } + } +}; + void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames) { ERR_FAIL_COND_MSG(p_full_paths.size() != r_filenames.size(), vformat("disambiguate_filenames requires two string vectors of same length (%d != %d).", p_full_paths.size(), r_filenames.size())); @@ -311,12 +355,13 @@ void EditorNode::_update_scene_tabs() { scene_tabs->set_current_tab(editor_data.get_edited_scene()); } + const Size2 add_button_size = Size2(0, scene_tabs->get_size().y); if (scene_tabs->get_offset_buttons_visible()) { // Move the add button to a fixed position. if (scene_tab_add->get_parent() == scene_tabs) { scene_tabs->remove_child(scene_tab_add); scene_tab_add_ph->add_child(scene_tab_add); - scene_tab_add->set_position(Point2()); + scene_tab_add->set_rect(Rect2(Point2(), add_button_size)); } } else { // Move the add button to be after the last tab. @@ -326,16 +371,16 @@ void EditorNode::_update_scene_tabs() { } if (scene_tabs->get_tab_count() == 0) { - scene_tab_add->set_position(Point2()); + scene_tab_add->set_rect(Rect2(Point2(), add_button_size)); return; } Rect2 last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1); int hsep = scene_tabs->get_theme_constant(SNAME("h_separation")); if (scene_tabs->is_layout_rtl()) { - scene_tab_add->set_position(Point2(last_tab.position.x - scene_tab_add->get_size().x - hsep, last_tab.position.y)); + scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x - scene_tab_add->get_size().x - hsep, last_tab.position.y), add_button_size)); } else { - scene_tab_add->set_position(Point2(last_tab.position.x + last_tab.size.width + hsep, last_tab.position.y)); + scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x + last_tab.size.width + hsep, last_tab.position.y), add_button_size)); } } @@ -615,16 +660,27 @@ void EditorNode::_notification(int p_what) { if (progress_dialog) { progress_dialog->queue_free(); } + if (load_error_dialog) { + load_error_dialog->queue_free(); + } + if (execute_output_dialog) { + execute_output_dialog->queue_free(); + } + if (warning) { + warning->queue_free(); + } + if (accept) { + accept->queue_free(); + } + if (save_accept) { + save_accept->queue_free(); + } editor_data.save_editor_external_data(); FileAccess::set_file_close_fail_notify_callback(nullptr); log->deinit(); // Do not get messages anymore. editor_data.clear_edited_scenes(); } break; - case Control::NOTIFICATION_THEME_CHANGED: { - scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size()); - } break; - case NOTIFICATION_READY: { { _initializing_plugins = true; @@ -696,7 +752,8 @@ void EditorNode::_notification(int p_what) { EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/code_font") || EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/theme") || EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/help/help") || - EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog/thumbnail_size"); + EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog/thumbnail_size") || + EditorSettings::get_singleton()->check_changed_settings_in_group("run/output/font_size"); if (theme_changed) { theme = create_custom_theme(theme_base->get_theme()); @@ -711,6 +768,9 @@ void EditorNode::_notification(int p_what) { bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("BottomPanel"), SNAME("EditorStyles"))); tabbar_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("tabbar_background"), SNAME("TabContainer"))); + scene_tabs->add_theme_constant_override("icon_max_width", gui_base->get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"))); + scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size()); + main_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); } @@ -1083,7 +1143,7 @@ void EditorNode::_scan_external_changes() { } if (need_reload) { - disk_changed->call_deferred(SNAME("popup_centered_ratio"), 0.5); + disk_changed->call_deferred(SNAME("popup_centered_ratio"), 0.3); } } @@ -1232,6 +1292,12 @@ void EditorNode::edit_resource(const Ref<Resource> &p_resource) { void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path) { editor_data.apply_changes_in_editors(); + + if (saving_resources_in_path.has(p_resource)) { + return; + } + saving_resources_in_path.insert(p_resource); + int flg = 0; if (EDITOR_GET("filesystem/on_save/compress_binary_resources")) { flg |= ResourceSaver::FLAG_COMPRESS; @@ -1246,10 +1312,16 @@ void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const St } else { show_accept(TTR("Error saving resource!"), TTR("OK")); } + + saving_resources_in_path.erase(p_resource); return; } ((Resource *)p_resource.ptr())->set_path(path); + saving_resources_in_path.erase(p_resource); + + _resource_saved(p_resource, path); + emit_signal(SNAME("resource_saved"), p_resource); editor_data.notify_resource_saved(p_resource); } @@ -1914,9 +1986,6 @@ void EditorNode::_dialog_action(String p_file) { } } break; case FILE_CLOSE: - case FILE_CLOSE_ALL_AND_QUIT: - case FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER: - case FILE_CLOSE_ALL_AND_RELOAD_CURRENT_PROJECT: case SCENE_TAB_CLOSE: case FILE_SAVE_SCENE: case FILE_SAVE_AS_SCENE: { @@ -2148,7 +2217,7 @@ void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) { } void EditorNode::push_node_item(Node *p_node) { - if (p_node || Object::cast_to<Node>(InspectorDock::get_inspector_singleton()->get_edited_object())) { + if (p_node || Object::cast_to<Node>(InspectorDock::get_inspector_singleton()->get_edited_object()) || Object::cast_to<MultiNodeEdit>(InspectorDock::get_inspector_singleton()->get_edited_object())) { // Don't push null if the currently edited object is not a Node. push_item(p_node); } @@ -2631,74 +2700,33 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { previous_scenes.pop_back(); } break; - case FILE_CLOSE_OTHERS: - case FILE_CLOSE_RIGHT: - case FILE_CLOSE_ALL: { - if (editor_data.get_edited_scene_count() > 1 && (current_menu_option != FILE_CLOSE_RIGHT || editor_data.get_edited_scene() < editor_data.get_edited_scene_count() - 1)) { - int next_tab = editor_data.get_edited_scene() + 1; - next_tab %= editor_data.get_edited_scene_count(); - _scene_tab_closed(next_tab, current_menu_option); - } else { - if (current_menu_option != FILE_CLOSE_ALL) { - current_menu_option = -1; - } else { - _scene_tab_closed(editor_data.get_edited_scene()); + case FILE_CLOSE_OTHERS: { + tab_closing_menu_option = -1; + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + if (i == editor_data.get_edited_scene()) { + continue; } + tabs_to_close.push_back(editor_data.get_scene_path(i)); } - - if (p_confirmed) { - _menu_option_confirm(SCENE_TAB_CLOSE, true); + _proceed_closing_scene_tabs(); + } break; + case FILE_CLOSE_RIGHT: { + tab_closing_menu_option = -1; + for (int i = editor_data.get_edited_scene() + 1; i < editor_data.get_edited_scene_count(); i++) { + tabs_to_close.push_back(editor_data.get_scene_path(i)); } - + _proceed_closing_scene_tabs(); + } break; + case FILE_CLOSE_ALL: { + tab_closing_menu_option = -1; + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + tabs_to_close.push_back(editor_data.get_scene_path(i)); + } + _proceed_closing_scene_tabs(); } break; case FILE_CLOSE: { _scene_tab_closed(editor_data.get_edited_scene()); } break; - case FILE_CLOSE_ALL_AND_QUIT: - case FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER: - case FILE_CLOSE_ALL_AND_RELOAD_CURRENT_PROJECT: { - if (!p_confirmed) { - tab_closing_idx = _next_unsaved_scene(false); - if (tab_closing_idx == -1) { - tab_closing_idx = -2; // Only external resources are unsaved. - } else { - _scene_tab_changed(tab_closing_idx); - } - - if (unsaved_cache || p_option == FILE_CLOSE_ALL_AND_QUIT || p_option == FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER || p_option == FILE_CLOSE_ALL_AND_RELOAD_CURRENT_PROJECT) { - if (tab_closing_idx == -2) { - if (p_option == FILE_CLOSE_ALL_AND_RELOAD_CURRENT_PROJECT) { - save_confirmation->set_ok_button_text(TTR("Save & Reload")); - save_confirmation->set_text(TTR("Save modified resources before reloading?")); - } else { - save_confirmation->set_ok_button_text(TTR("Save & Quit")); - save_confirmation->set_text(TTR("Save modified resources before closing?")); - } - } else { - Node *ed_scene_root = editor_data.get_edited_scene_root(tab_closing_idx); - if (ed_scene_root) { - String scene_filename = ed_scene_root->get_scene_file_path(); - if (p_option == FILE_CLOSE_ALL_AND_RELOAD_CURRENT_PROJECT) { - save_confirmation->set_ok_button_text(TTR("Save & Reload")); - save_confirmation->set_text(vformat(TTR("Save changes to '%s' before reloading?"), !scene_filename.is_empty() ? scene_filename : "unsaved scene")); - } else { - save_confirmation->set_ok_button_text(TTR("Save & Quit")); - save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), !scene_filename.is_empty() ? scene_filename : "unsaved scene")); - } - } - } - save_confirmation->popup_centered(); - break; - } - } - if (!editor_data.get_edited_scene_root(tab_closing_idx)) { - // Empty tab. - _scene_tab_closed(tab_closing_idx); - break; - } - - [[fallthrough]]; - } case SCENE_TAB_CLOSE: case FILE_SAVE_SCENE: { int scene_idx = (p_option == FILE_SAVE_SCENE) ? -1 : tab_closing_idx; @@ -2974,43 +3002,52 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { case RELOAD_CURRENT_PROJECT: { if (!p_confirmed) { bool save_each = EDITOR_GET("interface/editor/save_each_scene_on_quit"); - if (_next_unsaved_scene(!save_each) == -1 && !EditorUndoRedoManager::get_singleton()->is_history_unsaved(EditorUndoRedoManager::GLOBAL_HISTORY)) { - _discard_changes(); - break; - } else { - if (save_each) { - if (p_option == RELOAD_CURRENT_PROJECT) { - _menu_option_confirm(FILE_CLOSE_ALL_AND_RELOAD_CURRENT_PROJECT, false); - } else if (p_option == FILE_QUIT) { - _menu_option_confirm(FILE_CLOSE_ALL_AND_QUIT, false); - } else { - _menu_option_confirm(FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER, false); - } - } else { - String unsaved_scenes; - int i = _next_unsaved_scene(true, 0); - while (i != -1) { - unsaved_scenes += "\n " + editor_data.get_edited_scene_root(i)->get_scene_file_path(); - i = _next_unsaved_scene(true, ++i); - } + if (_next_unsaved_scene(!save_each) == -1) { + if (EditorUndoRedoManager::get_singleton()->is_history_unsaved(EditorUndoRedoManager::GLOBAL_HISTORY)) { if (p_option == RELOAD_CURRENT_PROJECT) { save_confirmation->set_ok_button_text(TTR("Save & Reload")); - save_confirmation->set_text(TTR("Save changes to the following scene(s) before reloading?") + unsaved_scenes); + save_confirmation->set_text(TTR("Save modified resources before reloading?")); } else { save_confirmation->set_ok_button_text(TTR("Save & Quit")); - save_confirmation->set_text((p_option == FILE_QUIT ? TTR("Save changes to the following scene(s) before quitting?") : TTR("Save changes to the following scene(s) before opening Project Manager?")) + unsaved_scenes); + save_confirmation->set_text(TTR("Save modified resources before closing?")); } + save_confirmation->reset_size(); save_confirmation->popup_centered(); + break; + } + + _discard_changes(); + break; + } + + if (save_each) { + tab_closing_menu_option = current_menu_option; + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + tabs_to_close.push_back(editor_data.get_scene_path(i)); + } + _proceed_closing_scene_tabs(); + } else { + String unsaved_scenes; + int i = _next_unsaved_scene(true, 0); + while (i != -1) { + unsaved_scenes += "\n " + editor_data.get_edited_scene_root(i)->get_scene_file_path(); + i = _next_unsaved_scene(true, ++i); + } + if (p_option == RELOAD_CURRENT_PROJECT) { + save_confirmation->set_ok_button_text(TTR("Save & Reload")); + save_confirmation->set_text(TTR("Save changes to the following scene(s) before reloading?") + unsaved_scenes); + } else { + save_confirmation->set_ok_button_text(TTR("Save & Quit")); + save_confirmation->set_text((p_option == FILE_QUIT ? TTR("Save changes to the following scene(s) before quitting?") : TTR("Save changes to the following scene(s) before opening Project Manager?")) + unsaved_scenes); } + save_confirmation->reset_size(); + save_confirmation->popup_centered(); } DisplayServer::get_singleton()->window_request_attention(); break; } - - if (_next_unsaved_scene(true) != -1) { - _save_all_scenes(); - } + _save_external_resources(); _discard_changes(); } break; case SETTINGS_UPDATE_CONTINUOUSLY: { @@ -3216,7 +3253,7 @@ int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) { bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(i)); if (unsaved) { String scene_filename = editor_data.get_edited_scene_root(i)->get_scene_file_path(); - if (p_valid_filename && scene_filename.length() == 0) { + if (p_valid_filename && scene_filename.is_empty()) { continue; } return i; @@ -3238,13 +3275,7 @@ void EditorNode::_exit_editor(int p_exit_code) { void EditorNode::_discard_changes(const String &p_str) { switch (current_menu_option) { - case FILE_CLOSE_ALL_AND_QUIT: - case FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER: - case FILE_CLOSE_ALL_AND_RELOAD_CURRENT_PROJECT: case FILE_CLOSE: - case FILE_CLOSE_OTHERS: - case FILE_CLOSE_RIGHT: - case FILE_CLOSE_ALL: case SCENE_TAB_CLOSE: { Node *scene = editor_data.get_edited_scene_root(tab_closing_idx); if (scene != nullptr) { @@ -3254,39 +3285,12 @@ void EditorNode::_discard_changes(const String &p_str) { } } - _remove_scene(tab_closing_idx); - _update_scene_tabs(); - - if (current_menu_option == FILE_CLOSE_ALL_AND_QUIT || current_menu_option == FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER || current_menu_option == FILE_CLOSE_ALL_AND_RELOAD_CURRENT_PROJECT) { - // If restore tabs is enabled, reopen the scene that has just been closed, so it's remembered properly. - if (bool(EDITOR_GET("interface/scene_tabs/restore_scenes_on_load"))) { - _menu_option_confirm(FILE_OPEN_PREV, true); - } - if (_next_unsaved_scene(false) == -1) { - if (current_menu_option == FILE_CLOSE_ALL_AND_RELOAD_CURRENT_PROJECT) { - current_menu_option = RELOAD_CURRENT_PROJECT; - } else if (current_menu_option == FILE_CLOSE_ALL_AND_QUIT) { - current_menu_option = FILE_QUIT; - } else { - current_menu_option = RUN_PROJECT_MANAGER; - } - _discard_changes(); - } else { - _menu_option_confirm(current_menu_option, false); - } - } else if (current_menu_option == FILE_CLOSE_OTHERS || current_menu_option == FILE_CLOSE_RIGHT) { - if (editor_data.get_edited_scene_count() == 1 || (current_menu_option == FILE_CLOSE_RIGHT && editor_data.get_edited_scene_count() <= editor_data.get_edited_scene() + 1)) { - current_menu_option = -1; - save_confirmation->hide(); - } else { - _menu_option_confirm(current_menu_option, false); - } - } else if (current_menu_option == FILE_CLOSE_ALL && editor_data.get_edited_scene_count() > 0) { - _menu_option_confirm(current_menu_option, false); - } else { - current_menu_option = -1; - save_confirmation->hide(); + // Don't close tabs when exiting the editor (required for "restore_scenes_on_load" setting). + if (!_is_closing_editor()) { + _remove_scene(tab_closing_idx); + _update_scene_tabs(); } + _proceed_closing_scene_tabs(); } break; case FILE_QUIT: { _menu_option_confirm(RUN_STOP, true); @@ -3460,6 +3464,10 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor, bool p_config_chan singleton->editor_plugins_force_input_forwarding->remove_plugin(p_editor); singleton->remove_child(p_editor); singleton->editor_data.remove_editor_plugin(p_editor); + + for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : singleton->active_plugins) { + kv.value.erase(p_editor); + } } void EditorNode::_update_addon_config() { @@ -3525,7 +3533,7 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, // Only try to load the script if it has a name. Else, the plugin has no init script. if (script_path.length() > 0) { script_path = addon_path.get_base_dir().path_join(script_path); - scr = ResourceLoader::load(script_path); + scr = ResourceLoader::load(script_path, "Script", ResourceFormatLoader::CACHE_MODE_IGNORE); if (scr.is_null()) { show_warning(vformat(TTR("Unable to load addon script from path: '%s'."), script_path)); @@ -3604,7 +3612,7 @@ void EditorNode::_remove_edited_scene(bool p_change_tab) { void EditorNode::_remove_scene(int index, bool p_change_tab) { // Clear icon cache in case some scripts are no longer needed. - script_icon_cache.clear(); + editor_data.clear_script_icon_cache(); if (editor_data.get_edited_scene() == index) { // Scene to remove is current scene. @@ -3983,6 +3991,15 @@ HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node * return modified_property_map; } +void EditorNode::update_ownership_table_for_addition_node_ancestors(Node *p_current_node, HashMap<Node *, Node *> &p_ownership_table) { + p_ownership_table.insert(p_current_node, p_current_node->get_owner()); + + for (int i = 0; i < p_current_node->get_child_count(); i++) { + Node *child = p_current_node->get_child(i); + update_ownership_table_for_addition_node_ancestors(child, p_ownership_table); + } +} + void EditorNode::update_diff_data_for_node( Node *p_edited_scene, Node *p_root, @@ -4078,6 +4095,16 @@ void EditorNode::update_diff_data_for_node( if (node_3d) { new_additive_node_entry.transform_3d = node_3d->get_relative_transform(node_3d->get_parent()); } + + // Gathers the ownership of all ancestor nodes for later use. + HashMap<Node *, Node *> ownership_table; + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + update_ownership_table_for_addition_node_ancestors(child, ownership_table); + } + + new_additive_node_entry.ownership_table = ownership_table; + p_addition_list.push_back(new_additive_node_entry); return; @@ -4246,9 +4273,11 @@ void EditorNode::add_io_error(const String &p_error) { void EditorNode::_load_error_notify(void *p_ud, const String &p_text) { EditorNode *en = static_cast<EditorNode *>(p_ud); - en->load_errors->add_image(en->gui_base->get_theme_icon(SNAME("Error"), SNAME("EditorIcons"))); - en->load_errors->add_text(p_text + "\n"); - en->load_error_dialog->popup_centered_ratio(0.5); + if (en && en->load_error_dialog) { + en->load_errors->add_image(en->gui_base->get_theme_icon(SNAME("Error"), SNAME("EditorIcons"))); + en->load_errors->add_text(p_text + "\n"); + en->load_error_dialog->attach_and_popup_centered_ratio(0.5); + } } bool EditorNode::_find_scene_in_use(Node *p_node, const String &p_path) const { @@ -4347,18 +4376,6 @@ StringName EditorNode::get_object_custom_type_name(const Object *p_object) const return StringName(); } -Ref<ImageTexture> EditorNode::_load_custom_class_icon(const String &p_path) const { - if (p_path.length()) { - Ref<Image> img = memnew(Image); - Error err = ImageLoader::load_image(p_path, img); - if (err == OK) { - img->resize(16 * EDSCALE, 16 * EDSCALE, Image::INTERPOLATE_LANCZOS); - return ImageTexture::create_from_image(img); - } - } - return nullptr; -} - void EditorNode::_pick_main_scene_custom_action(const String &p_custom_action_name) { if (p_custom_action_name == "select_current") { Node *scene = editor_data.get_edited_scene_root(); @@ -4381,106 +4398,86 @@ void EditorNode::_pick_main_scene_custom_action(const String &p_custom_action_na } } -Ref<Texture2D> EditorNode::get_object_icon(const Object *p_object, const String &p_fallback) { - ERR_FAIL_COND_V(!p_object || !gui_base, nullptr); +Ref<Texture2D> EditorNode::_get_class_or_script_icon(const String &p_class, const Ref<Script> &p_script, const String &p_fallback) { + ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty."); + EditorData &ed = EditorNode::get_editor_data(); - Ref<Script> scr = p_object->get_script(); - if (scr.is_null() && p_object->is_class("Script")) { - scr = p_object; - } + // Check for a script icon first. + if (p_script.is_valid()) { + Ref<Texture2D> script_icon = ed.get_script_icon(p_script); + if (script_icon.is_valid()) { + return script_icon; + } - if (scr.is_valid() && !script_icon_cache.has(scr)) { - Ref<Script> base_scr = scr; - while (base_scr.is_valid()) { - StringName name = EditorNode::get_editor_data().script_class_get_name(base_scr->get_path()); - String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name); - Ref<ImageTexture> icon = _load_custom_class_icon(icon_path); - if (icon.is_valid()) { - script_icon_cache[scr] = icon; - return icon; - } + // No custom icon was found in the inheritance chain, so check the base + // class of the script instead. + String base_type; + p_script->get_language()->get_global_class_name(p_script->get_path(), &base_type); - // TODO: should probably be deprecated in 4.x - StringName base = base_scr->get_instance_base_type(); - if (base != StringName() && EditorNode::get_editor_data().get_custom_types().has(base)) { - const Vector<EditorData::CustomType> &types = EditorNode::get_editor_data().get_custom_types()[base]; - for (int i = 0; i < types.size(); ++i) { - if (types[i].script == base_scr && types[i].icon.is_valid()) { - script_icon_cache[scr] = types[i].icon; - return types[i].icon; - } - } - } - base_scr = base_scr->get_base_script(); + // Check if the base type is an extension-defined type. + Ref<Texture2D> ext_icon = ed.extension_class_get_icon(base_type); + if (ext_icon.is_valid()) { + return ext_icon; } - // If no icon found, cache it as null. - script_icon_cache[scr] = Ref<Texture>(); - } else if (scr.is_valid() && script_icon_cache.has(scr) && script_icon_cache[scr].is_valid()) { - return script_icon_cache[scr]; + // Look for the base type in the editor theme. + // This is only relevant for built-in classes. + if (gui_base && gui_base->has_theme_icon(base_type, "EditorIcons")) { + return gui_base->get_theme_icon(base_type, "EditorIcons"); + } } - // TODO: Should probably be deprecated in 4.x. - if (p_object->has_meta("_editor_icon")) { - return p_object->get_meta("_editor_icon"); + // Script was not valid or didn't yield any useful values, try the class name + // directly. + + // Check if the class name is an extension-defined type. + Ref<Texture2D> ext_icon = ed.extension_class_get_icon(p_class); + if (ext_icon.is_valid()) { + return ext_icon; } - if (gui_base->has_theme_icon(p_object->get_class(), SNAME("EditorIcons"))) { - return gui_base->get_theme_icon(p_object->get_class(), SNAME("EditorIcons")); + // Check if the class name is a custom type. + // TODO: Should probably be deprecated in 4.x + const EditorData::CustomType *ctype = ed.get_custom_type_by_name(p_class); + if (ctype && ctype->icon.is_valid()) { + return ctype->icon; } - if (p_fallback.length()) { - return gui_base->get_theme_icon(p_fallback, SNAME("EditorIcons")); + // Look up the class name or the fallback name in the editor theme. + // This is only relevant for built-in classes. + if (gui_base) { + if (gui_base->has_theme_icon(p_class, SNAME("EditorIcons"))) { + return gui_base->get_theme_icon(p_class, SNAME("EditorIcons")); + } + + if (p_fallback.length() && gui_base->has_theme_icon(p_fallback, SNAME("EditorIcons"))) { + return gui_base->get_theme_icon(p_fallback, SNAME("EditorIcons")); + } } return nullptr; } -Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) const { - ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty."); +Ref<Texture2D> EditorNode::get_object_icon(const Object *p_object, const String &p_fallback) { + ERR_FAIL_NULL_V_MSG(p_object, nullptr, "Object cannot be null."); - if (ScriptServer::is_global_class(p_class)) { - String class_name = p_class; - Ref<Script> scr = EditorNode::get_editor_data().script_class_load_script(class_name); - - while (true) { - String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(class_name); - Ref<Texture> icon = _load_custom_class_icon(icon_path); - if (icon.is_valid()) { - return icon; // Current global class has icon. - } - - // Find next global class along the inheritance chain. - do { - Ref<Script> base_scr = scr->get_base_script(); - if (base_scr.is_null()) { - // We've reached a native class, use its icon. - String base_type; - scr->get_language()->get_global_class_name(scr->get_path(), &base_type); - if (gui_base->has_theme_icon(base_type, "EditorIcons")) { - return gui_base->get_theme_icon(base_type, "EditorIcons"); - } - return gui_base->get_theme_icon(p_fallback, "EditorIcons"); - } - scr = base_scr; - class_name = EditorNode::get_editor_data().script_class_get_name(scr->get_path()); - } while (class_name.is_empty()); - } + Ref<Script> scr = p_object->get_script(); + if (scr.is_null() && p_object->is_class("Script")) { + scr = p_object; } - if (const EditorData::CustomType *ctype = EditorNode::get_editor_data().get_custom_type_by_name(p_class)) { - return ctype->icon; - } + return _get_class_or_script_icon(p_object->get_class(), scr, p_fallback); +} - if (gui_base->has_theme_icon(p_class, SNAME("EditorIcons"))) { - return gui_base->get_theme_icon(p_class, SNAME("EditorIcons")); - } +Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) { + ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty."); - if (p_fallback.length() && gui_base->has_theme_icon(p_fallback, SNAME("EditorIcons"))) { - return gui_base->get_theme_icon(p_fallback, SNAME("EditorIcons")); + Ref<Script> scr; + if (ScriptServer::is_global_class(p_class)) { + scr = EditorNode::get_editor_data().script_class_load_script(p_class); } - return nullptr; + return _get_class_or_script_icon(p_class, scr, p_fallback); } bool EditorNode::is_object_of_custom_type(const Object *p_object, const StringName &p_class) { @@ -4619,23 +4616,27 @@ Error EditorNode::export_preset(const String &p_preset, const String &p_path, bo void EditorNode::show_accept(const String &p_text, const String &p_title) { current_menu_option = -1; - accept->set_ok_button_text(p_title); - accept->set_text(p_text); - accept->popup_centered(); + if (accept) { + accept->set_ok_button_text(p_title); + accept->set_text(p_text); + accept->attach_and_popup_centered(); + } } void EditorNode::show_save_accept(const String &p_text, const String &p_title) { current_menu_option = -1; - save_accept->set_ok_button_text(p_title); - save_accept->set_text(p_text); - save_accept->popup_centered(); + if (save_accept) { + save_accept->set_ok_button_text(p_title); + save_accept->set_text(p_text); + save_accept->attach_and_popup_centered(); + } } void EditorNode::show_warning(const String &p_text, const String &p_title) { - if (warning->is_inside_tree()) { + if (warning) { warning->set_text(p_text); warning->set_title(p_title); - warning->popup_centered(); + warning->attach_and_popup_centered(); } else { WARN_PRINT(p_title + " " + p_text); } @@ -4652,8 +4653,8 @@ void EditorNode::_dock_floating_close_request(Control *p_control) { p_control->get_parent()->remove_child(p_control); dock_slot[window_slot]->add_child(p_control); - dock_slot[window_slot]->move_child(p_control, MIN((int)window->get_meta("dock_index"), dock_slot[window_slot]->get_tab_count())); - dock_slot[window_slot]->set_current_tab(window->get_meta("dock_index")); + dock_slot[window_slot]->move_child(p_control, MIN((int)window->get_meta("dock_index"), dock_slot[window_slot]->get_tab_count() - 1)); + dock_slot[window_slot]->set_current_tab(dock_slot[window_slot]->get_tab_idx_from_control(p_control)); dock_slot[window_slot]->set_tab_title(dock_slot[window_slot]->get_tab_idx_from_control(p_control), TTRGET(p_control->get_name())); window->queue_free(); @@ -4674,7 +4675,7 @@ void EditorNode::_dock_make_float() { Size2 dock_size = dock->get_size() + borders * 2; Point2 dock_screen_pos = dock->get_global_position() + get_tree()->get_root()->get_position() - borders; - int dock_index = dock->get_index(); + int dock_index = dock->get_index(false); dock_slot[dock_popup_selected_idx]->remove_child(dock); Window *window = memnew(Window); @@ -4775,6 +4776,7 @@ void EditorNode::_dock_select_input(const Ref<InputEvent> &p_input) { dock_slot[nrect]->add_child(dock); dock_popup_selected_idx = nrect; dock_slot[nrect]->set_current_tab(dock_slot[nrect]->get_tab_count() - 1); + dock_slot[nrect]->set_tab_title(dock_slot[nrect]->get_tab_count() - 1, TTRGET(dock->get_name())); dock_slot[nrect]->show(); dock_select->queue_redraw(); @@ -5132,6 +5134,7 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String } dock_slot[i]->add_child(node); dock_slot[i]->move_child(node, 0); + dock_slot[i]->set_tab_title(0, TTRGET(node->get_name())); dock_slot[i]->show(); } } @@ -5416,8 +5419,39 @@ void EditorNode::_scene_tab_script_edited(int p_tab) { } } -void EditorNode::_scene_tab_closed(int p_tab, int option) { - current_menu_option = option; +void EditorNode::_proceed_closing_scene_tabs() { + List<String>::Element *E = tabs_to_close.front(); + if (!E) { + if (_is_closing_editor()) { + current_menu_option = tab_closing_menu_option; + _menu_option_confirm(tab_closing_menu_option, true); + } else { + current_menu_option = -1; + save_confirmation->hide(); + } + return; + } + String scene_to_close = E->get(); + tabs_to_close.pop_front(); + + int tab_idx = -1; + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + if (editor_data.get_scene_path(i) == scene_to_close) { + tab_idx = i; + break; + } + } + ERR_FAIL_COND(tab_idx < 0); + + _scene_tab_closed(tab_idx); +} + +bool EditorNode::_is_closing_editor() const { + return tab_closing_menu_option == FILE_QUIT || tab_closing_menu_option == RUN_PROJECT_MANAGER || tab_closing_menu_option == RELOAD_CURRENT_PROJECT; +} + +void EditorNode::_scene_tab_closed(int p_tab, int p_option) { + current_menu_option = p_option; tab_closing_idx = p_tab; Node *scene = editor_data.get_edited_scene_root(p_tab); if (!scene) { @@ -5427,8 +5461,19 @@ void EditorNode::_scene_tab_closed(int p_tab, int option) { bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(p_tab)); if (unsaved) { - save_confirmation->set_ok_button_text(TTR("Save & Close")); - save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), !scene->get_scene_file_path().is_empty() ? scene->get_scene_file_path() : "unsaved scene")); + if (get_current_tab() != p_tab) { + set_current_scene(p_tab); + } + + String scene_filename = scene->get_scene_file_path(); + if (current_menu_option == RELOAD_CURRENT_PROJECT) { + save_confirmation->set_ok_button_text(TTR("Save & Reload")); + save_confirmation->set_text(vformat(TTR("Save changes to '%s' before reloading?"), !scene_filename.is_empty() ? scene_filename : "unsaved scene")); + } else { + save_confirmation->set_ok_button_text(TTR("Save & Close")); + save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), !scene_filename.is_empty() ? scene_filename : "unsaved scene")); + } + save_confirmation->reset_size(); save_confirmation->popup_centered(); } else { _discard_changes(); @@ -6202,6 +6247,18 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins node_3d->set_transform(additive_node_entry.transform_3d); } } + + // Restore the ownership of its ancestors + for (KeyValue<Node *, Node *> &E : additive_node_entry.ownership_table) { + Node *current_ancestor = E.key; + Node *ancestor_owner = E.value; + + if (ancestor_owner == original_node) { + ancestor_owner = instantiated_node; + } + + current_ancestor->set_owner(ancestor_owner); + } } // Restore the selection. @@ -6382,13 +6439,15 @@ void EditorNode::_bottom_panel_raise_toggled(bool p_pressed) { } void EditorNode::_update_renderer_color() { - if (renderer->get_text() == "Forward+") { + String rendering_method = renderer->get_selected_metadata(); + + if (rendering_method == "forward_plus") { renderer->add_theme_color_override("font_color", Color::hex(0x5d8c3fff)); } - if (renderer->get_text() == "Mobile") { + if (rendering_method == "mobile") { renderer->add_theme_color_override("font_color", Color::hex(0xa5557dff)); } - if (renderer->get_text() == "Compatibility") { + if (rendering_method == "gl_compatibility") { renderer->add_theme_color_override("font_color", Color::hex(0x5586a4ff)); } } @@ -6409,6 +6468,11 @@ void EditorNode::_renderer_selected(int p_which) { } void EditorNode::_resource_saved(Ref<Resource> p_resource, const String &p_path) { + if (singleton->saving_resources_in_path.has(p_resource)) { + // This is going to be handled by save_resource_in_path when the time is right. + return; + } + if (EditorFileSystem::get_singleton()) { EditorFileSystem::get_singleton()->update_file(p_path); } @@ -6460,7 +6524,6 @@ void EditorNode::_feature_profile_changed() { } void EditorNode::_bind_methods() { - GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/scene_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case"), SCENE_NAME_CASING_SNAKE_CASE); ClassDB::bind_method("edit_current", &EditorNode::edit_current); ClassDB::bind_method("edit_node", &EditorNode::edit_node); @@ -6517,11 +6580,13 @@ static void _execute_thread(void *p_ud) { } int EditorNode::execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok, bool p_close_on_errors) { - execute_output_dialog->set_title(p_title); - execute_output_dialog->get_ok_button()->set_disabled(true); - execute_outputs->clear(); - execute_outputs->set_scroll_follow(true); - execute_output_dialog->popup_centered_ratio(); + if (execute_output_dialog) { + execute_output_dialog->set_title(p_title); + execute_output_dialog->get_ok_button()->set_disabled(true); + execute_outputs->clear(); + execute_outputs->set_scroll_follow(true); + execute_output_dialog->attach_and_popup_centered_ratio(); + } ExecuteThreadArgs eta; eta.path = p_path; @@ -6539,6 +6604,7 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p String to_add = eta.output.substr(prev_len, eta.output.length()); prev_len = eta.output.length(); execute_outputs->add_text(to_add); + DisplayServer::get_singleton()->process_events(); // Get rid of pending events. Main::iteration(); } } @@ -6548,14 +6614,16 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p eta.execute_output_thread.wait_to_finish(); execute_outputs->add_text("\nExit Code: " + itos(eta.exitcode)); - if (p_close_on_errors && eta.exitcode != 0) { - execute_output_dialog->hide(); - } - if (p_close_on_ok && eta.exitcode == 0) { - execute_output_dialog->hide(); - } + if (execute_output_dialog) { + if (p_close_on_errors && eta.exitcode != 0) { + execute_output_dialog->hide(); + } + if (p_close_on_ok && eta.exitcode == 0) { + execute_output_dialog->hide(); + } - execute_output_dialog->get_ok_button()->set_disabled(false); + execute_output_dialog->get_ok_button()->set_disabled(false); + } return eta.exitcode; } @@ -7031,8 +7099,10 @@ EditorNode::EditorNode() { scene_tabs->set_select_with_rmb(true); scene_tabs->add_tab("unsaved"); scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button").operator int()); + scene_tabs->add_theme_constant_override("icon_max_width", gui_base->get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"))); scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width")) * EDSCALE); scene_tabs->set_drag_to_rearrange_enabled(true); + scene_tabs->set_auto_translate(false); scene_tabs->connect("tab_changed", callable_mp(this, &EditorNode::_scene_tab_changed)); scene_tabs->connect("tab_button_pressed", callable_mp(this, &EditorNode::_scene_tab_script_edited)); scene_tabs->connect("tab_close_pressed", callable_mp(this, &EditorNode::_scene_tab_closed).bind(SCENE_TAB_CLOSE)); @@ -7126,12 +7196,10 @@ EditorNode::EditorNode() { prev_scene->set_position(Point2(3, 24)); prev_scene->hide(); - accept = memnew(AcceptDialog); - gui_base->add_child(accept); + accept = memnew(AcceptDialogAutoReparent); accept->connect("confirmed", callable_mp(this, &EditorNode::_menu_confirm_current)); - save_accept = memnew(AcceptDialog); - gui_base->add_child(save_accept); + save_accept = memnew(AcceptDialogAutoReparent); save_accept->connect("confirmed", callable_mp(this, &EditorNode::_menu_option).bind((int)MenuOptions::FILE_SAVE_AS_SCENE)); project_export = memnew(ProjectExportDialog); @@ -7176,9 +7244,8 @@ EditorNode::EditorNode() { gui_base->add_child(fbx_importer_manager); #endif - warning = memnew(AcceptDialog); + warning = memnew(AcceptDialogAutoReparent); warning->add_button(TTR("Copy Text"), true, "copy"); - gui_base->add_child(warning); warning->connect("custom_action", callable_mp(this, &EditorNode::_copy_warning)); ED_SHORTCUT("editor/next_tab", TTR("Next Scene Tab"), KeyModifierMask::CMD_OR_CTRL + Key::TAB); @@ -7316,6 +7383,7 @@ EditorNode::EditorNode() { editor_layouts = memnew(PopupMenu); editor_layouts->set_name("Layouts"); + editor_layouts->set_auto_translate(false); settings_menu->add_child(editor_layouts); editor_layouts->connect("id_pressed", callable_mp(this, &EditorNode::_layout_menu_option)); settings_menu->add_submenu_item(TTR("Editor Layout"), "Layouts"); @@ -7336,7 +7404,7 @@ EditorNode::EditorNode() { #ifndef ANDROID_ENABLED if (OS::get_singleton()->get_data_path() == OS::get_singleton()->get_config_path()) { - // Configuration and data folders are located in the same place (Windows/MacOS). + // Configuration and data folders are located in the same place (Windows/macOS). settings_menu->add_item(TTR("Open Editor Data/Settings Folder"), SETTINGS_EDITOR_DATA_FOLDER); } else { // Separate configuration and data folders (Linux). @@ -7705,6 +7773,7 @@ EditorNode::EditorNode() { save_confirmation = memnew(ConfirmationDialog); save_confirmation->add_button(TTR("Don't Save"), DisplayServer::get_singleton()->get_swap_cancel_ok(), "discard"); gui_base->add_child(save_confirmation); + save_confirmation->set_min_size(Vector2(450.0 * EDSCALE, 0)); save_confirmation->connect("confirmed", callable_mp(this, &EditorNode::_menu_confirm_current)); save_confirmation->connect("custom_action", callable_mp(this, &EditorNode::_discard_changes)); @@ -7946,17 +8015,16 @@ EditorNode::EditorNode() { set_process_shortcut_input(true); load_errors = memnew(RichTextLabel); - load_error_dialog = memnew(AcceptDialog); + load_error_dialog = memnew(AcceptDialogAutoReparent); load_error_dialog->add_child(load_errors); load_error_dialog->set_title(TTR("Load Errors")); - gui_base->add_child(load_error_dialog); execute_outputs = memnew(RichTextLabel); execute_outputs->set_selection_enabled(true); - execute_output_dialog = memnew(AcceptDialog); + execute_outputs->set_context_menu_enabled(true); + execute_output_dialog = memnew(AcceptDialogAutoReparent); execute_output_dialog->add_child(execute_outputs); execute_output_dialog->set_title(""); - gui_base->add_child(execute_output_dialog); EditorFileSystem::get_singleton()->connect("sources_changed", callable_mp(this, &EditorNode::_sources_changed)); EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &EditorNode::_fs_changed)); |
