/*************************************************************************/ /* editor_node.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "editor_node.h" #include "core/bind/core_bind.h" #include "core/class_db.h" #include "core/io/config_file.h" #include "core/io/image_loader.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/io/stream_peer_ssl.h" #include "core/message_queue.h" #include "core/os/file_access.h" #include "core/os/input.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/path_remap.h" #include "core/print_string.h" #include "core/project_settings.h" #include "core/translation.h" #include "core/version.h" #include "core/version_hash.gen.h" #include "main/input_default.h" #include "main/main.h" #include "scene/gui/center_container.h" #include "scene/gui/control.h" #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" #include "scene/gui/link_button.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" #include "scene/gui/panel_container.h" #include "scene/gui/split_container.h" #include "scene/gui/tab_container.h" #include "scene/gui/tabs.h" #include "scene/gui/texture_progress.h" #include "scene/gui/tool_button.h" #include "scene/resources/packed_scene.h" #include "servers/physics_2d_server.h" #include "editor/audio_stream_preview.h" #include "editor/dependency_editor.h" #include "editor/editor_about.h" #include "editor/editor_audio_buses.h" #include "editor/editor_export.h" #include "editor/editor_feature_profile.h" #include "editor/editor_file_system.h" #include "editor/editor_help.h" #include "editor/editor_inspector.h" #include "editor/editor_layouts_dialog.h" #include "editor/editor_log.h" #include "editor/editor_plugin.h" #include "editor/editor_properties.h" #include "editor/editor_resource_picker.h" #include "editor/editor_resource_preview.h" #include "editor/editor_run_native.h" #include "editor/editor_run_script.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/editor_spin_slider.h" #include "editor/editor_themes.h" #include "editor/export_template_manager.h" #include "editor/fileserver/editor_file_server.h" #include "editor/filesystem_dock.h" #include "editor/import/editor_import_collada.h" #include "editor/import/resource_importer_bitmask.h" #include "editor/import/resource_importer_csv_translation.h" #include "editor/import/resource_importer_image.h" #include "editor/import/resource_importer_layered_texture.h" #include "editor/import/resource_importer_obj.h" #include "editor/import/resource_importer_scene.h" #include "editor/import/resource_importer_texture.h" #include "editor/import/resource_importer_texture_atlas.h" #include "editor/import/resource_importer_wav.h" #include "editor/import_dock.h" #include "editor/multi_node_edit.h" #include "editor/node_dock.h" #include "editor/pane_drag.h" #include "editor/plugin_config_dialog.h" #include "editor/plugins/animation_blend_space_1d_editor.h" #include "editor/plugins/animation_blend_space_2d_editor.h" #include "editor/plugins/animation_blend_tree_editor_plugin.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/animation_state_machine_editor.h" #include "editor/plugins/animation_tree_editor_plugin.h" #include "editor/plugins/animation_tree_player_editor_plugin.h" #include "editor/plugins/asset_library_editor_plugin.h" #include "editor/plugins/audio_stream_editor_plugin.h" #include "editor/plugins/baked_lightmap_editor_plugin.h" #include "editor/plugins/camera_editor_plugin.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "editor/plugins/collision_polygon_2d_editor_plugin.h" #include "editor/plugins/collision_polygon_editor_plugin.h" #include "editor/plugins/collision_shape_2d_editor_plugin.h" #include "editor/plugins/cpu_particles_2d_editor_plugin.h" #include "editor/plugins/cpu_particles_editor_plugin.h" #include "editor/plugins/curve_editor_plugin.h" #include "editor/plugins/editor_preview_plugins.h" #include "editor/plugins/gi_probe_editor_plugin.h" #include "editor/plugins/gradient_editor_plugin.h" #include "editor/plugins/item_list_editor_plugin.h" #include "editor/plugins/light_occluder_2d_editor_plugin.h" #include "editor/plugins/line_2d_editor_plugin.h" #include "editor/plugins/material_editor_plugin.h" #include "editor/plugins/mesh_editor_plugin.h" #include "editor/plugins/mesh_instance_editor_plugin.h" #include "editor/plugins/mesh_library_editor_plugin.h" #include "editor/plugins/multimesh_editor_plugin.h" #include "editor/plugins/navigation_polygon_editor_plugin.h" #include "editor/plugins/particles_2d_editor_plugin.h" #include "editor/plugins/particles_editor_plugin.h" #include "editor/plugins/path_2d_editor_plugin.h" #include "editor/plugins/path_editor_plugin.h" #include "editor/plugins/physical_bone_plugin.h" #include "editor/plugins/polygon_2d_editor_plugin.h" #include "editor/plugins/resource_preloader_editor_plugin.h" #include "editor/plugins/room_manager_editor_plugin.h" #include "editor/plugins/root_motion_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/plugins/script_text_editor.h" #include "editor/plugins/shader_editor_plugin.h" #include "editor/plugins/skeleton_2d_editor_plugin.h" #include "editor/plugins/skeleton_editor_plugin.h" #include "editor/plugins/skeleton_ik_editor_plugin.h" #include "editor/plugins/spatial_editor_plugin.h" #include "editor/plugins/sprite_editor_plugin.h" #include "editor/plugins/sprite_frames_editor_plugin.h" #include "editor/plugins/style_box_editor_plugin.h" #include "editor/plugins/text_editor.h" #include "editor/plugins/texture_editor_plugin.h" #include "editor/plugins/texture_region_editor_plugin.h" #include "editor/plugins/theme_editor_plugin.h" #include "editor/plugins/tile_map_editor_plugin.h" #include "editor/plugins/tile_set_editor_plugin.h" #include "editor/plugins/version_control_editor_plugin.h" #include "editor/plugins/viewport_preview_editor_plugin.h" #include "editor/plugins/visual_shader_editor_plugin.h" #include "editor/progress_dialog.h" #include "editor/project_export.h" #include "editor/project_settings_editor.h" #include "editor/quick_open.h" #include "editor/register_exporters.h" #include "editor/run_settings_dialog.h" #include "editor/script_editor_debugger.h" #include "editor/settings_config_dialog.h" #include #include 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"; void EditorNode::disambiguate_filenames(const Vector p_full_paths, Vector &r_filenames) { // Keep track of a list of "index sets," i.e. sets of indices // within disambiguated_scene_names which contain the same name. Vector> index_sets; Map scene_name_to_set_index; for (int i = 0; i < r_filenames.size(); i++) { String scene_name = r_filenames[i]; if (!scene_name_to_set_index.has(scene_name)) { index_sets.push_back(Set()); scene_name_to_set_index.insert(r_filenames[i], index_sets.size() - 1); } index_sets.write[scene_name_to_set_index[scene_name]].insert(i); } // For each index set with a size > 1, we need to disambiguate for (int i = 0; i < index_sets.size(); i++) { Set iset = index_sets[i]; while (iset.size() > 1) { // Append the parent folder to each scene name for (Set::Element *E = iset.front(); E; E = E->next()) { int set_idx = E->get(); String scene_name = r_filenames[set_idx]; String full_path = p_full_paths[set_idx]; // Get rid of file extensions and res:// prefixes if (scene_name.rfind(".") >= 0) { scene_name = scene_name.substr(0, scene_name.rfind(".")); } if (full_path.begins_with("res://")) { full_path = full_path.substr(6); } if (full_path.rfind(".") >= 0) { full_path = full_path.substr(0, full_path.rfind(".")); } int scene_name_size = scene_name.size(); int full_path_size = full_path.size(); int difference = full_path_size - scene_name_size; // Find just the parent folder of the current path and append it. // If the current name is foo.tscn, and the full path is /some/folder/foo.tscn // then slash_idx is the second '/', so that we select just "folder", and // append that to yield "folder/foo.tscn". if (difference > 0) { String parent = full_path.substr(0, difference); int slash_idx = parent.rfind("/"); slash_idx = parent.rfind("/", slash_idx - 1); parent = slash_idx >= 0 ? parent.substr(slash_idx + 1) : parent; r_filenames.write[set_idx] = parent + r_filenames[set_idx]; } } // Loop back through scene names and remove non-ambiguous names bool can_proceed = false; Set::Element *E = iset.front(); while (E) { String scene_name = r_filenames[E->get()]; bool duplicate_found = false; for (Set::Element *F = iset.front(); F; F = F->next()) { if (E->get() == F->get()) { continue; } String other_scene_name = r_filenames[F->get()]; if (other_scene_name == scene_name) { duplicate_found = true; break; } } Set::Element *to_erase = duplicate_found ? nullptr : E; // We need to check that we could actually append anymore names // if we wanted to for disambiguation. If we can't, then we have // to abort even with ambiguous names. We clean the full path // and the scene name first to remove extensions so that this // comparison actually works. String path = p_full_paths[E->get()]; if (path.begins_with("res://")) { path = path.substr(6); } if (path.rfind(".") >= 0) { path = path.substr(0, path.rfind(".")); } if (scene_name.rfind(".") >= 0) { scene_name = scene_name.substr(0, scene_name.rfind(".")); } // We can proceed iff the full path is longer than the scene name, // meaning that there is at least one more parent folder we can // tack onto the name. can_proceed = can_proceed || (path.size() - scene_name.size()) >= 1; E = E->next(); if (to_erase) { iset.erase(to_erase); } } if (!can_proceed) { break; } } } } void EditorNode::_update_scene_tabs() { bool show_rb = EditorSettings::get_singleton()->get("interface/scene_tabs/show_script_button"); OS::get_singleton()->global_menu_clear("_dock"); // Get all scene names, which may be ambiguous Vector disambiguated_scene_names; Vector full_path_names; for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { disambiguated_scene_names.push_back(editor_data.get_scene_title(i)); full_path_names.push_back(editor_data.get_scene_path(i)); } disambiguate_filenames(full_path_names, disambiguated_scene_names); scene_tabs->clear_tabs(); Ref script_icon = gui_base->get_icon("Script", "EditorIcons"); for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { Node *type_node = editor_data.get_edited_scene_root(i); Ref icon; if (type_node) { icon = EditorNode::get_singleton()->get_object_icon(type_node, "Node"); } int current = editor_data.get_edited_scene(); bool unsaved = (i == current) ? saved_version != editor_data.get_undo_redo().get_version() : editor_data.get_scene_version(i) != 0; scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : ""), icon); OS::get_singleton()->global_menu_add_item("_dock", editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), GLOBAL_SCENE, i); if (show_rb && editor_data.get_scene_root_script(i).is_valid()) { scene_tabs->set_tab_right_button(i, script_icon); } } OS::get_singleton()->global_menu_add_separator("_dock"); OS::get_singleton()->global_menu_add_item("_dock", TTR("New Window"), GLOBAL_NEW_WINDOW, Variant()); scene_tabs->set_current_tab(editor_data.get_edited_scene()); if (scene_tabs->get_offset_buttons_visible()) { // move add button to fixed position on the tabbar if (scene_tab_add->get_parent() == scene_tabs) { scene_tab_add->set_position(Point2(0, 0)); scene_tabs->remove_child(scene_tab_add); tabbar_container->add_child(scene_tab_add); tabbar_container->move_child(scene_tab_add, 1); } } else { // move add button to after last tab if (scene_tab_add->get_parent() == tabbar_container) { tabbar_container->remove_child(scene_tab_add); scene_tabs->add_child(scene_tab_add); } Rect2 last_tab = Rect2(); if (scene_tabs->get_tab_count() != 0) { last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1); } scene_tab_add->set_position(Point2(last_tab.get_position().x + last_tab.get_size().x + 3, last_tab.get_position().y)); } } void EditorNode::_version_control_menu_option(int p_idx) { switch (vcs_actions_menu->get_item_id(p_idx)) { case RUN_VCS_SETTINGS: { VersionControlEditorPlugin::get_singleton()->popup_vcs_set_up_dialog(gui_base); } break; case RUN_VCS_SHUT_DOWN: { VersionControlEditorPlugin::get_singleton()->shut_down(); } break; } } void EditorNode::_update_title() { const String appname = ProjectSettings::get_singleton()->get("application/config/name"); String title = (appname.empty() ? "Unnamed Project" : appname) + String(" - ") + VERSION_NAME; const String edited = editor_data.get_edited_scene_root() ? editor_data.get_edited_scene_root()->get_filename() : String(); if (!edited.empty()) { // Display the edited scene name before the program name so that it can be seen in the OS task bar. title = vformat("%s - %s", edited.get_file(), title); } if (unsaved_cache) { // Display the "modified" mark before anything else so that it can always be seen in the OS task bar. title = vformat("(*) %s", title); } OS::get_singleton()->set_window_title(title); } void EditorNode::_unhandled_input(const Ref &p_event) { if (Node::get_viewport()->get_modal_stack_top()) { return; //ignore because of modal window } Ref k = p_event; if (k.is_valid() && k->is_pressed() && !k->is_echo() && !gui_base->get_viewport()->gui_has_modal_stack()) { EditorPlugin *old_editor = editor_plugin_screen; if (ED_IS_SHORTCUT("editor/next_tab", p_event)) { int next_tab = editor_data.get_edited_scene() + 1; next_tab %= editor_data.get_edited_scene_count(); _scene_tab_changed(next_tab); } if (ED_IS_SHORTCUT("editor/prev_tab", p_event)) { int next_tab = editor_data.get_edited_scene() - 1; next_tab = next_tab >= 0 ? next_tab : editor_data.get_edited_scene_count() - 1; _scene_tab_changed(next_tab); } if (ED_IS_SHORTCUT("editor/filter_files", p_event)) { filesystem_dock->focus_on_filter(); } if (ED_IS_SHORTCUT("editor/editor_2d", p_event)) { _editor_select(EDITOR_2D); } else if (ED_IS_SHORTCUT("editor/editor_3d", p_event)) { _editor_select(EDITOR_3D); } else if (ED_IS_SHORTCUT("editor/editor_script", p_event)) { _editor_select(EDITOR_SCRIPT); } else if (ED_IS_SHORTCUT("editor/editor_help", p_event)) { emit_signal("request_help_search", ""); } else if (ED_IS_SHORTCUT("editor/editor_assetlib", p_event) && StreamPeerSSL::is_available()) { _editor_select(EDITOR_ASSETLIB); } else if (ED_IS_SHORTCUT("editor/editor_next", p_event)) { _editor_select_next(); } else if (ED_IS_SHORTCUT("editor/editor_prev", p_event)) { _editor_select_prev(); } if (old_editor != editor_plugin_screen) { get_tree()->set_input_as_handled(); } } } void EditorNode::_notification(int p_what) { switch (p_what) { case NOTIFICATION_PROCESS: { if (opening_prev && !confirmation->is_visible()) { opening_prev = false; } if (unsaved_cache != (saved_version != editor_data.get_undo_redo().get_version())) { unsaved_cache = (saved_version != editor_data.get_undo_redo().get_version()); _update_title(); } if (last_checked_version != editor_data.get_undo_redo().get_version()) { _update_scene_tabs(); last_checked_version = editor_data.get_undo_redo().get_version(); } // update the animation frame of the update spinner uint64_t frame = Engine::get_singleton()->get_frames_drawn(); uint32_t tick = OS::get_singleton()->get_ticks_msec(); if (frame != update_spinner_step_frame && (tick - update_spinner_step_msec) > (1000 / 8)) { update_spinner_step++; if (update_spinner_step >= 8) { update_spinner_step = 0; } update_spinner_step_msec = tick; update_spinner_step_frame = frame + 1; // update the icon itself only when the spinner is visible if (EditorSettings::get_singleton()->get("interface/editor/show_update_spinner")) { update_spinner->set_icon(gui_base->get_icon("Progress" + itos(update_spinner_step + 1), "EditorIcons")); } } editor_selection->update(); scene_root->set_size_override(true, Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"))); ResourceImporterTexture::get_singleton()->update_imports(); } break; case NOTIFICATION_ENTER_TREE: { Engine::get_singleton()->set_editor_hint(true); OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/low_processor_mode_sleep_usec"))); get_tree()->get_root()->set_usage(Viewport::USAGE_2D_NO_SAMPLING); //reduce memory usage get_tree()->get_root()->set_disable_3d(true); get_tree()->get_root()->set_as_audio_listener(false); get_tree()->get_root()->set_as_audio_listener_2d(false); get_tree()->set_auto_accept_quit(false); get_tree()->connect("files_dropped", this, "_dropped_files"); get_tree()->connect("global_menu_action", this, "_global_menu_action"); /* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */ } break; case NOTIFICATION_EXIT_TREE: { 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 NOTIFICATION_READY: { VisualServer::get_singleton()->viewport_set_hide_scenario(get_scene_root()->get_viewport_rid(), true); VisualServer::get_singleton()->viewport_set_hide_canvas(get_scene_root()->get_viewport_rid(), true); VisualServer::get_singleton()->viewport_set_disable_environment(get_viewport()->get_viewport_rid(), true); feature_profile_manager->notify_changed(); if (!main_editor_buttons[EDITOR_3D]->is_visible()) { //may be hidden due to feature profile _editor_select(EDITOR_2D); } else { _editor_select(EDITOR_3D); } _update_debug_options(); // Save the project after opening to mark it as last modified, except in headless mode. if (OS::get_singleton()->can_draw() && !OS::get_singleton()->is_no_window_mode_enabled()) { ProjectSettings::get_singleton()->save(); } /* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */ } break; case MainLoop::NOTIFICATION_WM_FOCUS_IN: { // Restore the original FPS cap after focusing back on the editor. OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/low_processor_mode_sleep_usec"))); EditorFileSystem::get_singleton()->scan_changes(); _scan_external_changes(); } break; case MainLoop::NOTIFICATION_WM_FOCUS_OUT: { // Save on focus loss before applying the FPS limit to avoid slowing down the saving process. if (EDITOR_GET("interface/editor/save_on_focus_loss")) { _menu_option_confirm(FILE_SAVE_SCENE, false); } // Set a low FPS cap to decrease CPU/GPU usage while the editor is unfocused. OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/unfocused_low_processor_mode_sleep_usec"))); } break; case MainLoop::NOTIFICATION_WM_ABOUT: { show_about(); } break; case MainLoop::NOTIFICATION_WM_QUIT_REQUEST: { _menu_option_confirm(FILE_QUIT, false); } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { scene_tabs->set_tab_close_display_policy((bool(EDITOR_GET("interface/scene_tabs/always_show_close_button")) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY)); theme = create_custom_theme(theme_base->get_theme()); theme_base->set_theme(theme); gui_base->set_theme(theme); gui_base->add_style_override("panel", gui_base->get_stylebox("Background", "EditorStyles")); scene_root_parent->add_style_override("panel", gui_base->get_stylebox("Content", "EditorStyles")); bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer")); scene_tabs->add_style_override("tab_fg", gui_base->get_stylebox("SceneTabFG", "EditorStyles")); scene_tabs->add_style_override("tab_bg", gui_base->get_stylebox("SceneTabBG", "EditorStyles")); file_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles")); project_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles")); debug_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles")); settings_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles")); help_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles")); if (EDITOR_GET("interface/scene_tabs/resize_if_many_tabs")) { scene_tabs->set_min_width(int(EDITOR_GET("interface/scene_tabs/minimum_width")) * EDSCALE); } else { scene_tabs->set_min_width(0); } _update_scene_tabs(); recent_scenes->set_as_minsize(); // debugger area if (ScriptEditor::get_singleton()->get_debugger()->is_visible()) { bottom_panel->add_style_override("panel", gui_base->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles")); } // update_icons for (int i = 0; i < singleton->main_editor_buttons.size(); i++) { ToolButton *tb = singleton->main_editor_buttons[i]; EditorPlugin *p_editor = singleton->editor_table[i]; Ref icon = p_editor->get_icon(); if (icon.is_valid()) { tb->set_icon(icon); } else if (singleton->gui_base->has_icon(p_editor->get_name(), "EditorIcons")) { tb->set_icon(singleton->gui_base->get_icon(p_editor->get_name(), "EditorIcons")); } } _build_icon_type_cache(); play_button->set_icon(gui_base->get_icon("MainPlay", "EditorIcons")); play_scene_button->set_icon(gui_base->get_icon("PlayScene", "EditorIcons")); play_custom_scene_button->set_icon(gui_base->get_icon("PlayCustom", "EditorIcons")); pause_button->set_icon(gui_base->get_icon("Pause", "EditorIcons")); stop_button->set_icon(gui_base->get_icon("Stop", "EditorIcons")); prev_scene->set_icon(gui_base->get_icon("PrevScene", "EditorIcons")); distraction_free->set_icon(gui_base->get_icon("DistractionFree", "EditorIcons")); scene_tab_add->set_icon(gui_base->get_icon("Add", "EditorIcons")); bottom_panel_raise->set_icon(gui_base->get_icon("ExpandBottomDock", "EditorIcons")); // clear_button->set_icon(gui_base->get_icon("Close", "EditorIcons")); don't have access to that node. needs to become a class property dock_tab_move_left->set_icon(theme->get_icon("Back", "EditorIcons")); dock_tab_move_right->set_icon(theme->get_icon("Forward", "EditorIcons")); PopupMenu *p = help_menu->get_popup(); p->set_item_icon(p->get_item_index(HELP_SEARCH), gui_base->get_icon("HelpSearch", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_REPORT_A_BUG), gui_base->get_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_SUGGEST_A_FEATURE), gui_base->get_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_COMMUNITY), gui_base->get_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_icon("Godot", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), gui_base->get_icon("Heart", "EditorIcons")); _update_update_spinner(); } break; case Control::NOTIFICATION_RESIZED: { _update_scene_tabs(); } break; } } void EditorNode::_update_update_spinner() { update_spinner->set_visible(EditorSettings::get_singleton()->get("interface/editor/show_update_spinner")); const bool update_continuously = EditorSettings::get_singleton()->get("interface/editor/update_continuously"); PopupMenu *update_popup = update_spinner->get_popup(); update_popup->set_item_checked(update_popup->get_item_index(SETTINGS_UPDATE_CONTINUOUSLY), update_continuously); update_popup->set_item_checked(update_popup->get_item_index(SETTINGS_UPDATE_WHEN_CHANGED), !update_continuously); if (update_continuously) { update_spinner->set_tooltip(TTR("Spins when the editor window redraws.\nUpdate Continuously is enabled, which can increase power usage. Click to disable it.")); // Use a different color for the update spinner when Update Continuously is enabled, // as this feature should only be enabled for troubleshooting purposes. // Make the icon modulate color overbright because icons are not completely white on a dark theme. // On a light theme, icons are dark, so we need to modulate them with an even brighter color. const bool dark_theme = EditorSettings::get_singleton()->is_dark_theme(); update_spinner->set_self_modulate( gui_base->get_color("error_color", "Editor") * (dark_theme ? Color(1.1, 1.1, 1.1) : Color(4.25, 4.25, 4.25))); } else { update_spinner->set_tooltip(TTR("Spins when the editor window redraws.")); update_spinner->set_self_modulate(Color(1, 1, 1)); } OS::get_singleton()->set_low_processor_usage_mode(!update_continuously); } void EditorNode::_on_plugin_ready(Object *p_script, const String &p_activate_name) { Ref