1 /*************************************************************************/
2 /* editor_node.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30
31 #include "editor_node.h"
32
33 #include "core/bind/core_bind.h"
34 #include "core/class_db.h"
35 #include "core/io/config_file.h"
36 #include "core/io/image_loader.h"
37 #include "core/io/resource_loader.h"
38 #include "core/io/resource_saver.h"
39 #include "core/io/stream_peer_ssl.h"
40 #include "core/message_queue.h"
41 #include "core/os/file_access.h"
42 #include "core/os/input.h"
43 #include "core/os/keyboard.h"
44 #include "core/os/os.h"
45 #include "core/path_remap.h"
46 #include "core/print_string.h"
47 #include "core/project_settings.h"
48 #include "core/translation.h"
49 #include "core/version.h"
50 #include "main/input_default.h"
51 #include "main/main.h"
52 #include "scene/gui/center_container.h"
53 #include "scene/gui/control.h"
54 #include "scene/gui/dialogs.h"
55 #include "scene/gui/file_dialog.h"
56 #include "scene/gui/menu_button.h"
57 #include "scene/gui/panel.h"
58 #include "scene/gui/panel_container.h"
59 #include "scene/gui/split_container.h"
60 #include "scene/gui/tab_container.h"
61 #include "scene/gui/tabs.h"
62 #include "scene/gui/texture_progress.h"
63 #include "scene/gui/tool_button.h"
64 #include "scene/resources/packed_scene.h"
65 #include "servers/physics_2d_server.h"
66
67 #include "editor/audio_stream_preview.h"
68 #include "editor/dependency_editor.h"
69 #include "editor/editor_about.h"
70 #include "editor/editor_audio_buses.h"
71 #include "editor/editor_export.h"
72 #include "editor/editor_feature_profile.h"
73 #include "editor/editor_file_system.h"
74 #include "editor/editor_help.h"
75 #include "editor/editor_inspector.h"
76 #include "editor/editor_layouts_dialog.h"
77 #include "editor/editor_log.h"
78 #include "editor/editor_plugin.h"
79 #include "editor/editor_properties.h"
80 #include "editor/editor_resource_preview.h"
81 #include "editor/editor_run_native.h"
82 #include "editor/editor_run_script.h"
83 #include "editor/editor_scale.h"
84 #include "editor/editor_settings.h"
85 #include "editor/editor_spin_slider.h"
86 #include "editor/editor_themes.h"
87 #include "editor/export_template_manager.h"
88 #include "editor/fileserver/editor_file_server.h"
89 #include "editor/filesystem_dock.h"
90 #include "editor/import/editor_import_collada.h"
91 #include "editor/import/editor_scene_importer_gltf.h"
92 #include "editor/import/resource_importer_bitmask.h"
93 #include "editor/import/resource_importer_csv.h"
94 #include "editor/import/resource_importer_csv_translation.h"
95 #include "editor/import/resource_importer_image.h"
96 #include "editor/import/resource_importer_layered_texture.h"
97 #include "editor/import/resource_importer_obj.h"
98 #include "editor/import/resource_importer_scene.h"
99 #include "editor/import/resource_importer_texture.h"
100 #include "editor/import/resource_importer_texture_atlas.h"
101 #include "editor/import/resource_importer_wav.h"
102 #include "editor/import_dock.h"
103 #include "editor/multi_node_edit.h"
104 #include "editor/node_dock.h"
105 #include "editor/pane_drag.h"
106 #include "editor/plugin_config_dialog.h"
107 #include "editor/plugins/animation_blend_space_1d_editor.h"
108 #include "editor/plugins/animation_blend_space_2d_editor.h"
109 #include "editor/plugins/animation_blend_tree_editor_plugin.h"
110 #include "editor/plugins/animation_player_editor_plugin.h"
111 #include "editor/plugins/animation_state_machine_editor.h"
112 #include "editor/plugins/animation_tree_editor_plugin.h"
113 #include "editor/plugins/animation_tree_player_editor_plugin.h"
114 #include "editor/plugins/asset_library_editor_plugin.h"
115 #include "editor/plugins/audio_stream_editor_plugin.h"
116 #include "editor/plugins/baked_lightmap_editor_plugin.h"
117 #include "editor/plugins/camera_editor_plugin.h"
118 #include "editor/plugins/canvas_item_editor_plugin.h"
119 #include "editor/plugins/collision_polygon_2d_editor_plugin.h"
120 #include "editor/plugins/collision_polygon_editor_plugin.h"
121 #include "editor/plugins/collision_shape_2d_editor_plugin.h"
122 #include "editor/plugins/cpu_particles_2d_editor_plugin.h"
123 #include "editor/plugins/cpu_particles_editor_plugin.h"
124 #include "editor/plugins/curve_editor_plugin.h"
125 #include "editor/plugins/editor_preview_plugins.h"
126 #include "editor/plugins/gi_probe_editor_plugin.h"
127 #include "editor/plugins/gradient_editor_plugin.h"
128 #include "editor/plugins/item_list_editor_plugin.h"
129 #include "editor/plugins/light_occluder_2d_editor_plugin.h"
130 #include "editor/plugins/line_2d_editor_plugin.h"
131 #include "editor/plugins/material_editor_plugin.h"
132 #include "editor/plugins/mesh_editor_plugin.h"
133 #include "editor/plugins/mesh_instance_editor_plugin.h"
134 #include "editor/plugins/mesh_library_editor_plugin.h"
135 #include "editor/plugins/multimesh_editor_plugin.h"
136 #include "editor/plugins/navigation_polygon_editor_plugin.h"
137 #include "editor/plugins/particles_2d_editor_plugin.h"
138 #include "editor/plugins/particles_editor_plugin.h"
139 #include "editor/plugins/path_2d_editor_plugin.h"
140 #include "editor/plugins/path_editor_plugin.h"
141 #include "editor/plugins/physical_bone_plugin.h"
142 #include "editor/plugins/polygon_2d_editor_plugin.h"
143 #include "editor/plugins/resource_preloader_editor_plugin.h"
144 #include "editor/plugins/root_motion_editor_plugin.h"
145 #include "editor/plugins/script_editor_plugin.h"
146 #include "editor/plugins/script_text_editor.h"
147 #include "editor/plugins/shader_editor_plugin.h"
148 #include "editor/plugins/skeleton_2d_editor_plugin.h"
149 #include "editor/plugins/skeleton_editor_plugin.h"
150 #include "editor/plugins/skeleton_ik_editor_plugin.h"
151 #include "editor/plugins/spatial_editor_plugin.h"
152 #include "editor/plugins/sprite_editor_plugin.h"
153 #include "editor/plugins/sprite_frames_editor_plugin.h"
154 #include "editor/plugins/style_box_editor_plugin.h"
155 #include "editor/plugins/text_editor.h"
156 #include "editor/plugins/texture_editor_plugin.h"
157 #include "editor/plugins/texture_region_editor_plugin.h"
158 #include "editor/plugins/theme_editor_plugin.h"
159 #include "editor/plugins/tile_map_editor_plugin.h"
160 #include "editor/plugins/tile_set_editor_plugin.h"
161 #include "editor/plugins/version_control_editor_plugin.h"
162 #include "editor/plugins/visual_shader_editor_plugin.h"
163 #include "editor/progress_dialog.h"
164 #include "editor/project_export.h"
165 #include "editor/project_settings_editor.h"
166 #include "editor/pvrtc_compress.h"
167 #include "editor/quick_open.h"
168 #include "editor/register_exporters.h"
169 #include "editor/run_settings_dialog.h"
170 #include "editor/script_editor_debugger.h"
171 #include "editor/settings_config_dialog.h"
172
173 #include <stdio.h>
174 #include <stdlib.h>
175
176 EditorNode *EditorNode::singleton = NULL;
177
disambiguate_filenames(const Vector<String> p_full_paths,Vector<String> & r_filenames)178 void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames) {
179 // Keep track of a list of "index sets," i.e. sets of indices
180 // within disambiguated_scene_names which contain the same name.
181 Vector<Set<int> > index_sets;
182 Map<String, int> scene_name_to_set_index;
183 for (int i = 0; i < r_filenames.size(); i++) {
184 String scene_name = r_filenames[i];
185 if (!scene_name_to_set_index.has(scene_name)) {
186 index_sets.push_back(Set<int>());
187 scene_name_to_set_index.insert(r_filenames[i], index_sets.size() - 1);
188 }
189 index_sets.write[scene_name_to_set_index[scene_name]].insert(i);
190 }
191
192 // For each index set with a size > 1, we need to disambiguate
193 for (int i = 0; i < index_sets.size(); i++) {
194 Set<int> iset = index_sets[i];
195 while (iset.size() > 1) {
196 // Append the parent folder to each scene name
197 for (Set<int>::Element *E = iset.front(); E; E = E->next()) {
198 int set_idx = E->get();
199 String scene_name = r_filenames[set_idx];
200 String full_path = p_full_paths[set_idx];
201
202 // Get rid of file extensions and res:// prefixes
203 if (scene_name.rfind(".") >= 0) {
204 scene_name = scene_name.substr(0, scene_name.rfind("."));
205 }
206 if (full_path.begins_with("res://")) {
207 full_path = full_path.substr(6);
208 }
209 if (full_path.rfind(".") >= 0) {
210 full_path = full_path.substr(0, full_path.rfind("."));
211 }
212
213 int scene_name_size = scene_name.size();
214 int full_path_size = full_path.size();
215 int difference = full_path_size - scene_name_size;
216
217 // Find just the parent folder of the current path and append it.
218 // If the current name is foo.tscn, and the full path is /some/folder/foo.tscn
219 // then slash_idx is the second '/', so that we select just "folder", and
220 // append that to yield "folder/foo.tscn".
221 if (difference > 0) {
222 String parent = full_path.substr(0, difference);
223 int slash_idx = parent.rfind("/");
224 slash_idx = parent.rfind("/", slash_idx - 1);
225 parent = slash_idx >= 0 ? parent.substr(slash_idx + 1) : parent;
226 r_filenames.write[set_idx] = parent + r_filenames[set_idx];
227 }
228 }
229
230 // Loop back through scene names and remove non-ambiguous names
231 bool can_proceed = false;
232 Set<int>::Element *E = iset.front();
233 while (E) {
234 String scene_name = r_filenames[E->get()];
235 bool duplicate_found = false;
236 for (Set<int>::Element *F = iset.front(); F; F = F->next()) {
237 if (E->get() == F->get()) {
238 continue;
239 }
240 String other_scene_name = r_filenames[F->get()];
241 if (other_scene_name == scene_name) {
242 duplicate_found = true;
243 break;
244 }
245 }
246
247 Set<int>::Element *to_erase = duplicate_found ? nullptr : E;
248
249 // We need to check that we could actually append anymore names
250 // if we wanted to for disambiguation. If we can't, then we have
251 // to abort even with ambiguous names. We clean the full path
252 // and the scene name first to remove extensions so that this
253 // comparison actually works.
254 String path = p_full_paths[E->get()];
255 if (path.begins_with("res://")) {
256 path = path.substr(6);
257 }
258 if (path.rfind(".") >= 0) {
259 path = path.substr(0, path.rfind("."));
260 }
261 if (scene_name.rfind(".") >= 0) {
262 scene_name = scene_name.substr(0, scene_name.rfind("."));
263 }
264
265 // We can proceed iff the full path is longer than the scene name,
266 // meaning that there is at least one more parent folder we can
267 // tack onto the name.
268 can_proceed = can_proceed || (path.size() - scene_name.size()) >= 1;
269
270 E = E->next();
271 if (to_erase) {
272 iset.erase(to_erase);
273 }
274 }
275
276 if (!can_proceed) {
277 break;
278 }
279 }
280 }
281 }
282
_update_scene_tabs()283 void EditorNode::_update_scene_tabs() {
284
285 bool show_rb = EditorSettings::get_singleton()->get("interface/scene_tabs/show_script_button");
286
287 OS::get_singleton()->global_menu_clear("_dock");
288
289 // Get all scene names, which may be ambiguous
290 Vector<String> disambiguated_scene_names;
291 Vector<String> full_path_names;
292 for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
293 disambiguated_scene_names.push_back(editor_data.get_scene_title(i));
294 full_path_names.push_back(editor_data.get_scene_path(i));
295 }
296
297 disambiguate_filenames(full_path_names, disambiguated_scene_names);
298
299 scene_tabs->clear_tabs();
300 Ref<Texture> script_icon = gui_base->get_icon("Script", "EditorIcons");
301 for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
302
303 Node *type_node = editor_data.get_edited_scene_root(i);
304 Ref<Texture> icon;
305 if (type_node) {
306 icon = EditorNode::get_singleton()->get_object_icon(type_node, "Node");
307 }
308
309 int current = editor_data.get_edited_scene();
310 bool unsaved = (i == current) ? saved_version != editor_data.get_undo_redo().get_version() : editor_data.get_scene_version(i) != 0;
311 scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : ""), icon);
312
313 OS::get_singleton()->global_menu_add_item("_dock", editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), GLOBAL_SCENE, i);
314
315 if (show_rb && editor_data.get_scene_root_script(i).is_valid()) {
316 scene_tabs->set_tab_right_button(i, script_icon);
317 }
318 }
319
320 OS::get_singleton()->global_menu_add_separator("_dock");
321 OS::get_singleton()->global_menu_add_item("_dock", TTR("New Window"), GLOBAL_NEW_WINDOW, Variant());
322
323 scene_tabs->set_current_tab(editor_data.get_edited_scene());
324
325 if (scene_tabs->get_offset_buttons_visible()) {
326 // move add button to fixed position on the tabbar
327 if (scene_tab_add->get_parent() == scene_tabs) {
328 scene_tab_add->set_position(Point2(0, 0));
329 scene_tabs->remove_child(scene_tab_add);
330 tabbar_container->add_child(scene_tab_add);
331 tabbar_container->move_child(scene_tab_add, 1);
332 }
333 } else {
334 // move add button to after last tab
335 if (scene_tab_add->get_parent() == tabbar_container) {
336 tabbar_container->remove_child(scene_tab_add);
337 scene_tabs->add_child(scene_tab_add);
338 }
339 Rect2 last_tab = Rect2();
340 if (scene_tabs->get_tab_count() != 0)
341 last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1);
342 scene_tab_add->set_position(Point2(last_tab.get_position().x + last_tab.get_size().x + 3, last_tab.get_position().y));
343 }
344 }
345
_version_control_menu_option(int p_idx)346 void EditorNode::_version_control_menu_option(int p_idx) {
347
348 switch (vcs_actions_menu->get_item_id(p_idx)) {
349 case RUN_VCS_SETTINGS: {
350
351 VersionControlEditorPlugin::get_singleton()->popup_vcs_set_up_dialog(gui_base);
352 } break;
353 case RUN_VCS_SHUT_DOWN: {
354
355 VersionControlEditorPlugin::get_singleton()->shut_down();
356 } break;
357 }
358 }
359
_update_title()360 void EditorNode::_update_title() {
361
362 String appname = ProjectSettings::get_singleton()->get("application/config/name");
363 String title = appname.empty() ? String(VERSION_FULL_NAME) : String(VERSION_NAME + String(" - ") + appname);
364 String edited = editor_data.get_edited_scene_root() ? editor_data.get_edited_scene_root()->get_filename() : String();
365 if (!edited.empty())
366 title += " - " + String(edited.get_file());
367 if (unsaved_cache)
368 title += " (*)";
369
370 OS::get_singleton()->set_window_title(title);
371 }
372
_unhandled_input(const Ref<InputEvent> & p_event)373 void EditorNode::_unhandled_input(const Ref<InputEvent> &p_event) {
374
375 if (Node::get_viewport()->get_modal_stack_top())
376 return; //ignore because of modal window
377
378 Ref<InputEventKey> k = p_event;
379 if (k.is_valid() && k->is_pressed() && !k->is_echo() && !gui_base->get_viewport()->gui_has_modal_stack()) {
380
381 EditorPlugin *old_editor = editor_plugin_screen;
382
383 if (ED_IS_SHORTCUT("editor/next_tab", p_event)) {
384 int next_tab = editor_data.get_edited_scene() + 1;
385 next_tab %= editor_data.get_edited_scene_count();
386 _scene_tab_changed(next_tab);
387 }
388 if (ED_IS_SHORTCUT("editor/prev_tab", p_event)) {
389 int next_tab = editor_data.get_edited_scene() - 1;
390 next_tab = next_tab >= 0 ? next_tab : editor_data.get_edited_scene_count() - 1;
391 _scene_tab_changed(next_tab);
392 }
393 if (ED_IS_SHORTCUT("editor/filter_files", p_event)) {
394 filesystem_dock->focus_on_filter();
395 }
396
397 if (ED_IS_SHORTCUT("editor/editor_2d", p_event)) {
398 _editor_select(EDITOR_2D);
399 } else if (ED_IS_SHORTCUT("editor/editor_3d", p_event)) {
400 _editor_select(EDITOR_3D);
401 } else if (ED_IS_SHORTCUT("editor/editor_script", p_event)) {
402 _editor_select(EDITOR_SCRIPT);
403 } else if (ED_IS_SHORTCUT("editor/editor_help", p_event)) {
404 emit_signal("request_help_search", "");
405 } else if (ED_IS_SHORTCUT("editor/editor_assetlib", p_event) && StreamPeerSSL::is_available()) {
406 _editor_select(EDITOR_ASSETLIB);
407 } else if (ED_IS_SHORTCUT("editor/editor_next", p_event)) {
408 _editor_select_next();
409 } else if (ED_IS_SHORTCUT("editor/editor_prev", p_event)) {
410 _editor_select_prev();
411 }
412
413 if (old_editor != editor_plugin_screen) {
414 get_tree()->set_input_as_handled();
415 }
416 }
417 }
418
_notification(int p_what)419 void EditorNode::_notification(int p_what) {
420
421 switch (p_what) {
422 case NOTIFICATION_PROCESS: {
423 if (opening_prev && !confirmation->is_visible())
424 opening_prev = false;
425
426 if (unsaved_cache != (saved_version != editor_data.get_undo_redo().get_version())) {
427
428 unsaved_cache = (saved_version != editor_data.get_undo_redo().get_version());
429 _update_title();
430 }
431
432 if (last_checked_version != editor_data.get_undo_redo().get_version()) {
433 _update_scene_tabs();
434 last_checked_version = editor_data.get_undo_redo().get_version();
435 }
436
437 // update the animation frame of the update spinner
438 uint64_t frame = Engine::get_singleton()->get_frames_drawn();
439 uint32_t tick = OS::get_singleton()->get_ticks_msec();
440
441 if (frame != update_spinner_step_frame && (tick - update_spinner_step_msec) > (1000 / 8)) {
442
443 update_spinner_step++;
444 if (update_spinner_step >= 8)
445 update_spinner_step = 0;
446
447 update_spinner_step_msec = tick;
448 update_spinner_step_frame = frame + 1;
449
450 // update the icon itself only when the spinner is visible
451 if (EditorSettings::get_singleton()->get("interface/editor/show_update_spinner")) {
452 update_spinner->set_icon(gui_base->get_icon("Progress" + itos(update_spinner_step + 1), "EditorIcons"));
453 }
454 }
455
456 editor_selection->update();
457
458 scene_root->set_size_override(true, Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")));
459
460 ResourceImporterTexture::get_singleton()->update_imports();
461 } break;
462
463 case NOTIFICATION_ENTER_TREE: {
464 Engine::get_singleton()->set_editor_hint(true);
465
466 OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/low_processor_mode_sleep_usec")));
467 get_tree()->get_root()->set_usage(Viewport::USAGE_2D_NO_SAMPLING); //reduce memory usage
468 get_tree()->get_root()->set_disable_3d(true);
469 get_tree()->get_root()->set_as_audio_listener(false);
470 get_tree()->get_root()->set_as_audio_listener_2d(false);
471 get_tree()->set_auto_accept_quit(false);
472 get_tree()->connect("files_dropped", this, "_dropped_files");
473 get_tree()->connect("global_menu_action", this, "_global_menu_action");
474
475 /* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */
476 } break;
477
478 case NOTIFICATION_EXIT_TREE: {
479 editor_data.save_editor_external_data();
480 FileAccess::set_file_close_fail_notify_callback(NULL);
481 log->deinit(); // do not get messages anymore
482 editor_data.clear_edited_scenes();
483 } break;
484
485 case NOTIFICATION_READY: {
486
487 {
488 _initializing_addons = true;
489 Vector<String> addons;
490 if (ProjectSettings::get_singleton()->has_setting("editor_plugins/enabled")) {
491 addons = ProjectSettings::get_singleton()->get("editor_plugins/enabled");
492 }
493
494 for (int i = 0; i < addons.size(); i++) {
495 set_addon_plugin_enabled(addons[i], true);
496 }
497 _initializing_addons = false;
498 }
499
500 VisualServer::get_singleton()->viewport_set_hide_scenario(get_scene_root()->get_viewport_rid(), true);
501 VisualServer::get_singleton()->viewport_set_hide_canvas(get_scene_root()->get_viewport_rid(), true);
502 VisualServer::get_singleton()->viewport_set_disable_environment(get_viewport()->get_viewport_rid(), true);
503
504 feature_profile_manager->notify_changed();
505
506 if (!main_editor_buttons[EDITOR_3D]->is_visible()) { //may be hidden due to feature profile
507 _editor_select(EDITOR_2D);
508 } else {
509 _editor_select(EDITOR_3D);
510 }
511
512 _update_debug_options();
513
514 /* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */
515 } break;
516
517 case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
518
519 // Restore the original FPS cap after focusing back on the editor
520 OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/low_processor_mode_sleep_usec")));
521
522 EditorFileSystem::get_singleton()->scan_changes();
523 } break;
524
525 case MainLoop::NOTIFICATION_WM_FOCUS_OUT: {
526
527 // Set a low FPS cap to decrease CPU/GPU usage while the editor is unfocused
528 OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/unfocused_low_processor_mode_sleep_usec")));
529 } break;
530
531 case MainLoop::NOTIFICATION_WM_ABOUT: {
532
533 show_about();
534 } break;
535
536 case MainLoop::NOTIFICATION_WM_QUIT_REQUEST: {
537
538 _menu_option_confirm(FILE_QUIT, false);
539 } break;
540
541 case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
542 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));
543 theme = create_editor_theme(theme_base->get_theme());
544
545 theme_base->set_theme(theme);
546 gui_base->set_theme(theme);
547
548 gui_base->add_style_override("panel", gui_base->get_stylebox("Background", "EditorStyles"));
549 scene_root_parent->add_style_override("panel", gui_base->get_stylebox("Content", "EditorStyles"));
550 bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer"));
551 scene_tabs->add_style_override("tab_fg", gui_base->get_stylebox("SceneTabFG", "EditorStyles"));
552 scene_tabs->add_style_override("tab_bg", gui_base->get_stylebox("SceneTabBG", "EditorStyles"));
553
554 file_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
555 project_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
556 debug_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
557 settings_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
558 help_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
559
560 if (EDITOR_GET("interface/scene_tabs/resize_if_many_tabs")) {
561 scene_tabs->set_min_width(int(EDITOR_GET("interface/scene_tabs/minimum_width")) * EDSCALE);
562 } else {
563 scene_tabs->set_min_width(0);
564 }
565 _update_scene_tabs();
566
567 recent_scenes->set_as_minsize();
568
569 // debugger area
570 if (ScriptEditor::get_singleton()->get_debugger()->is_visible())
571 bottom_panel->add_style_override("panel", gui_base->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles"));
572
573 // update_icons
574 for (int i = 0; i < singleton->main_editor_buttons.size(); i++) {
575
576 ToolButton *tb = singleton->main_editor_buttons[i];
577 EditorPlugin *p_editor = singleton->editor_table[i];
578 Ref<Texture> icon = p_editor->get_icon();
579
580 if (icon.is_valid()) {
581 tb->set_icon(icon);
582 } else if (singleton->gui_base->has_icon(p_editor->get_name(), "EditorIcons")) {
583 tb->set_icon(singleton->gui_base->get_icon(p_editor->get_name(), "EditorIcons"));
584 }
585 }
586
587 _build_icon_type_cache();
588
589 play_button->set_icon(gui_base->get_icon("MainPlay", "EditorIcons"));
590 play_scene_button->set_icon(gui_base->get_icon("PlayScene", "EditorIcons"));
591 play_custom_scene_button->set_icon(gui_base->get_icon("PlayCustom", "EditorIcons"));
592 pause_button->set_icon(gui_base->get_icon("Pause", "EditorIcons"));
593 stop_button->set_icon(gui_base->get_icon("Stop", "EditorIcons"));
594
595 prev_scene->set_icon(gui_base->get_icon("PrevScene", "EditorIcons"));
596 distraction_free->set_icon(gui_base->get_icon("DistractionFree", "EditorIcons"));
597 scene_tab_add->set_icon(gui_base->get_icon("Add", "EditorIcons"));
598
599 bottom_panel_raise->set_icon(gui_base->get_icon("ExpandBottomDock", "EditorIcons"));
600
601 // clear_button->set_icon(gui_base->get_icon("Close", "EditorIcons")); don't have access to that node. needs to become a class property
602 dock_tab_move_left->set_icon(theme->get_icon("Back", "EditorIcons"));
603 dock_tab_move_right->set_icon(theme->get_icon("Forward", "EditorIcons"));
604
605 PopupMenu *p = help_menu->get_popup();
606 p->set_item_icon(p->get_item_index(HELP_SEARCH), gui_base->get_icon("HelpSearch", "EditorIcons"));
607 p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_icon("Instance", "EditorIcons"));
608 p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_icon("Instance", "EditorIcons"));
609 p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_icon("Godot", "EditorIcons"));
610 p->set_item_icon(p->get_item_index(HELP_REPORT_A_BUG), gui_base->get_icon("Instance", "EditorIcons"));
611 p->set_item_icon(p->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_icon("Instance", "EditorIcons"));
612 p->set_item_icon(p->get_item_index(HELP_COMMUNITY), gui_base->get_icon("Instance", "EditorIcons"));
613 p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_icon("Godot", "EditorIcons"));
614
615 _update_update_spinner();
616 } break;
617
618 case Control::NOTIFICATION_RESIZED: {
619 _update_scene_tabs();
620 } break;
621 }
622 }
623
_update_update_spinner()624 void EditorNode::_update_update_spinner() {
625 update_spinner->set_visible(EditorSettings::get_singleton()->get("interface/editor/show_update_spinner"));
626
627 bool update_continuously = EditorSettings::get_singleton()->get("interface/editor/update_continuously");
628 PopupMenu *update_popup = update_spinner->get_popup();
629 update_popup->set_item_checked(update_popup->get_item_index(SETTINGS_UPDATE_CONTINUOUSLY), update_continuously);
630 update_popup->set_item_checked(update_popup->get_item_index(SETTINGS_UPDATE_WHEN_CHANGED), !update_continuously);
631
632 OS::get_singleton()->set_low_processor_usage_mode(!update_continuously);
633 }
634
_on_plugin_ready(Object * p_script,const String & p_activate_name)635 void EditorNode::_on_plugin_ready(Object *p_script, const String &p_activate_name) {
636 Ref<Script> script = Object::cast_to<Script>(p_script);
637 if (script.is_null())
638 return;
639 if (p_activate_name.length()) {
640 set_addon_plugin_enabled(p_activate_name, true);
641 }
642 project_settings->update_plugins();
643 project_settings->hide();
644 push_item(script.operator->());
645 }
646
_resources_changed(const PoolVector<String> & p_resources)647 void EditorNode::_resources_changed(const PoolVector<String> &p_resources) {
648
649 List<Ref<Resource> > changed;
650
651 int rc = p_resources.size();
652 for (int i = 0; i < rc; i++) {
653
654 Ref<Resource> res(ResourceCache::get(p_resources.get(i)));
655 if (res.is_null()) {
656 continue;
657 }
658
659 if (!res->editor_can_reload_from_file())
660 continue;
661 if (!res->get_path().is_resource_file() && !res->get_path().is_abs_path())
662 continue;
663 if (!FileAccess::exists(res->get_path()))
664 continue;
665
666 if (res->get_import_path() != String()) {
667 //this is an imported resource, will be reloaded if reimported via the _resources_reimported() callback
668 continue;
669 }
670
671 changed.push_back(res);
672 }
673
674 if (changed.size()) {
675 for (List<Ref<Resource> >::Element *E = changed.front(); E; E = E->next()) {
676 E->get()->reload_from_file();
677 }
678 }
679 }
680
_fs_changed()681 void EditorNode::_fs_changed() {
682
683 for (Set<FileDialog *>::Element *E = file_dialogs.front(); E; E = E->next()) {
684
685 E->get()->invalidate();
686 }
687
688 for (Set<EditorFileDialog *>::Element *E = editor_file_dialogs.front(); E; E = E->next()) {
689
690 E->get()->invalidate();
691 }
692
693 _mark_unsaved_scenes();
694
695 // FIXME: Move this to a cleaner location, it's hacky to do this is _fs_changed.
696 String export_error;
697 if (export_defer.preset != "" && !EditorFileSystem::get_singleton()->is_scanning()) {
698 String preset_name = export_defer.preset;
699 // Ensures export_project does not loop infinitely, because notifications may
700 // come during the export.
701 export_defer.preset = "";
702 Ref<EditorExportPreset> preset;
703 for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); ++i) {
704 preset = EditorExport::get_singleton()->get_export_preset(i);
705 if (preset->get_name() == preset_name) {
706 break;
707 }
708 preset.unref();
709 }
710 if (preset.is_null()) {
711 export_error = vformat(
712 "Invalid export preset name: %s. Make sure `export_presets.cfg` is present in the current directory.",
713 preset_name);
714 } else {
715 Ref<EditorExportPlatform> platform = preset->get_platform();
716 if (platform.is_null()) {
717 export_error = vformat("Export preset '%s' doesn't have a matching platform.", preset_name);
718 } else {
719 Error err = OK;
720 if (export_defer.pack_only) { // Only export .pck or .zip data pack.
721 if (export_defer.path.ends_with(".zip")) {
722 err = platform->export_zip(preset, export_defer.debug, export_defer.path);
723 } else if (export_defer.path.ends_with(".pck")) {
724 err = platform->export_pack(preset, export_defer.debug, export_defer.path);
725 }
726 } else { // Normal project export.
727 String config_error;
728 bool missing_templates;
729 if (!platform->can_export(preset, config_error, missing_templates)) {
730 ERR_PRINT(vformat("Cannot export project with preset '%s' due to configuration errors:\n%s", preset_name, config_error));
731 err = missing_templates ? ERR_FILE_NOT_FOUND : ERR_UNCONFIGURED;
732 } else {
733 err = platform->export_project(preset, export_defer.debug, export_defer.path);
734 }
735 }
736 switch (err) {
737 case OK:
738 break;
739 case ERR_FILE_NOT_FOUND:
740 export_error = vformat("Project export failed for preset '%s', the export template appears to be missing.", preset_name);
741 break;
742 case ERR_FILE_BAD_PATH:
743 export_error = vformat("Project export failed for preset '%s', the target path '%s' appears to be invalid.", preset_name, export_defer.path);
744 break;
745 default:
746 export_error = vformat("Project export failed with error code %d for preset '%s'.", (int)err, preset_name);
747 break;
748 }
749 }
750 }
751
752 if (!export_error.empty()) {
753 ERR_PRINT(export_error);
754 OS::get_singleton()->set_exit_code(EXIT_FAILURE);
755 }
756 _exit_editor();
757 }
758 }
759
_resources_reimported(const Vector<String> & p_resources)760 void EditorNode::_resources_reimported(const Vector<String> &p_resources) {
761
762 List<String> scenes; //will load later
763 int current_tab = scene_tabs->get_current_tab();
764
765 for (int i = 0; i < p_resources.size(); i++) {
766 String file_type = ResourceLoader::get_resource_type(p_resources[i]);
767 if (file_type == "PackedScene") {
768 scenes.push_back(p_resources[i]);
769 //reload later if needed, first go with normal resources
770 continue;
771 }
772
773 if (!ResourceCache::has(p_resources[i])) {
774 continue; //not loaded, no need to reload
775 }
776 //reload normally
777 Resource *resource = ResourceCache::get(p_resources[i]);
778 if (resource) {
779 resource->reload_from_file();
780 }
781 }
782
783 for (List<String>::Element *E = scenes.front(); E; E = E->next()) {
784 reload_scene(E->get());
785 }
786
787 scene_tabs->set_current_tab(current_tab);
788 }
789
_sources_changed(bool p_exist)790 void EditorNode::_sources_changed(bool p_exist) {
791
792 if (waiting_for_first_scan) {
793 waiting_for_first_scan = false;
794
795 // Start preview thread now that it's safe.
796 if (!singleton->cmdline_export_mode) {
797 EditorResourcePreview::get_singleton()->start();
798 }
799
800 _load_docks();
801
802 if (defer_load_scene != "") {
803 load_scene(defer_load_scene);
804 defer_load_scene = "";
805 }
806 }
807 }
808
_vp_resized()809 void EditorNode::_vp_resized() {
810 }
811
_node_renamed()812 void EditorNode::_node_renamed() {
813
814 if (get_inspector())
815 get_inspector()->update_tree();
816 }
817
_editor_select_next()818 void EditorNode::_editor_select_next() {
819
820 int editor = _get_current_main_editor();
821
822 do {
823 if (editor == editor_table.size() - 1) {
824 editor = 0;
825 } else {
826 editor++;
827 }
828 } while (!main_editor_buttons[editor]->is_visible());
829
830 _editor_select(editor);
831 }
832
_editor_select_prev()833 void EditorNode::_editor_select_prev() {
834
835 int editor = _get_current_main_editor();
836
837 do {
838 if (editor == 0) {
839 editor = editor_table.size() - 1;
840 } else {
841 editor--;
842 }
843 } while (!main_editor_buttons[editor]->is_visible());
844
845 _editor_select(editor);
846 }
847
load_resource(const String & p_resource,bool p_ignore_broken_deps)848 Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_deps) {
849
850 dependency_errors.clear();
851
852 Error err;
853 RES res = ResourceLoader::load(p_resource, "", false, &err);
854 ERR_FAIL_COND_V(!res.is_valid(), ERR_CANT_OPEN);
855
856 if (!p_ignore_broken_deps && dependency_errors.has(p_resource)) {
857
858 //current_option = -1;
859 Vector<String> errors;
860 for (Set<String>::Element *E = dependency_errors[p_resource].front(); E; E = E->next()) {
861
862 errors.push_back(E->get());
863 }
864 dependency_error->show(DependencyErrorDialog::MODE_RESOURCE, p_resource, errors);
865 dependency_errors.erase(p_resource);
866
867 return ERR_FILE_MISSING_DEPENDENCIES;
868 }
869
870 inspector_dock->edit_resource(res);
871 return OK;
872 }
873
edit_node(Node * p_node)874 void EditorNode::edit_node(Node *p_node) {
875
876 push_item(p_node);
877 }
878
save_resource_in_path(const Ref<Resource> & p_resource,const String & p_path)879 void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path) {
880
881 editor_data.apply_changes_in_editors();
882 int flg = 0;
883 if (EditorSettings::get_singleton()->get("filesystem/on_save/compress_binary_resources"))
884 flg |= ResourceSaver::FLAG_COMPRESS;
885
886 String path = ProjectSettings::get_singleton()->localize_path(p_path);
887 Error err = ResourceSaver::save(path, p_resource, flg | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS);
888
889 if (err != OK) {
890 if (ResourceLoader::is_imported(p_resource->get_path())) {
891 show_accept(TTR("Imported resources can't be saved."), TTR("OK"));
892 } else {
893 show_accept(TTR("Error saving resource!"), TTR("OK"));
894 }
895 return;
896 }
897
898 ((Resource *)p_resource.ptr())->set_path(path);
899 emit_signal("resource_saved", p_resource);
900 editor_data.notify_resource_saved(p_resource);
901 }
902
save_resource(const Ref<Resource> & p_resource)903 void EditorNode::save_resource(const Ref<Resource> &p_resource) {
904
905 if (p_resource->get_path().is_resource_file()) {
906 save_resource_in_path(p_resource, p_resource->get_path());
907 } else {
908 save_resource_as(p_resource);
909 }
910 }
911
save_resource_as(const Ref<Resource> & p_resource,const String & p_at_path)912 void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String &p_at_path) {
913
914 {
915 String path = p_resource->get_path();
916 int srpos = path.find("::");
917 if (srpos != -1) {
918 String base = path.substr(0, srpos);
919 if (!get_edited_scene() || get_edited_scene()->get_filename() != base) {
920 show_warning(TTR("This resource can't be saved because it does not belong to the edited scene. Make it unique first."));
921 return;
922 }
923 }
924 }
925
926 file->set_mode(EditorFileDialog::MODE_SAVE_FILE);
927 saving_resource = p_resource;
928
929 current_option = RESOURCE_SAVE_AS;
930 List<String> extensions;
931 Ref<PackedScene> sd = memnew(PackedScene);
932 ResourceSaver::get_recognized_extensions(p_resource, &extensions);
933 file->clear_filters();
934
935 List<String> preferred;
936 for (int i = 0; i < extensions.size(); i++) {
937
938 if (p_resource->is_class("Script") && (extensions[i] == "tres" || extensions[i] == "res" || extensions[i] == "xml")) {
939 //this serves no purpose and confused people
940 continue;
941 }
942 file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
943 preferred.push_back(extensions[i]);
944 }
945
946 if (p_at_path != String()) {
947
948 file->set_current_dir(p_at_path);
949 if (p_resource->get_path().is_resource_file()) {
950 file->set_current_file(p_resource->get_path().get_file());
951 } else {
952 if (extensions.size()) {
953 file->set_current_file("new_" + p_resource->get_class().to_lower() + "." + preferred.front()->get().to_lower());
954 } else {
955 file->set_current_file(String());
956 }
957 }
958 } else if (p_resource->get_path() != "") {
959
960 file->set_current_path(p_resource->get_path());
961 if (extensions.size()) {
962 String ext = p_resource->get_path().get_extension().to_lower();
963 if (extensions.find(ext) == NULL) {
964 file->set_current_path(p_resource->get_path().replacen("." + ext, "." + extensions.front()->get()));
965 }
966 }
967 } else if (preferred.size()) {
968
969 String existing;
970 if (extensions.size()) {
971 existing = "new_" + p_resource->get_class().to_lower() + "." + preferred.front()->get().to_lower();
972 }
973 file->set_current_path(existing);
974 }
975 file->popup_centered_ratio();
976 file->set_title(TTR("Save Resource As..."));
977 }
978
_menu_option(int p_option)979 void EditorNode::_menu_option(int p_option) {
980
981 _menu_option_confirm(p_option, false);
982 }
983
_menu_confirm_current()984 void EditorNode::_menu_confirm_current() {
985
986 _menu_option_confirm(current_option, true);
987 }
988
_dialog_display_save_error(String p_file,Error p_error)989 void EditorNode::_dialog_display_save_error(String p_file, Error p_error) {
990
991 if (p_error) {
992
993 switch (p_error) {
994
995 case ERR_FILE_CANT_WRITE: {
996
997 show_accept(TTR("Can't open file for writing:") + " " + p_file.get_extension(), TTR("OK"));
998 } break;
999 case ERR_FILE_UNRECOGNIZED: {
1000
1001 show_accept(TTR("Requested file format unknown:") + " " + p_file.get_extension(), TTR("OK"));
1002 } break;
1003 default: {
1004
1005 show_accept(TTR("Error while saving."), TTR("OK"));
1006 } break;
1007 }
1008 }
1009 }
1010
_dialog_display_load_error(String p_file,Error p_error)1011 void EditorNode::_dialog_display_load_error(String p_file, Error p_error) {
1012
1013 if (p_error) {
1014
1015 switch (p_error) {
1016
1017 case ERR_CANT_OPEN: {
1018
1019 show_accept(vformat(TTR("Can't open '%s'. The file could have been moved or deleted."), p_file.get_file()), TTR("OK"));
1020 } break;
1021 case ERR_PARSE_ERROR: {
1022
1023 show_accept(vformat(TTR("Error while parsing '%s'."), p_file.get_file()), TTR("OK"));
1024 } break;
1025 case ERR_FILE_CORRUPT: {
1026
1027 show_accept(vformat(TTR("Unexpected end of file '%s'."), p_file.get_file()), TTR("OK"));
1028 } break;
1029 case ERR_FILE_NOT_FOUND: {
1030
1031 show_accept(vformat(TTR("Missing '%s' or its dependencies."), p_file.get_file()), TTR("OK"));
1032 } break;
1033 default: {
1034
1035 show_accept(vformat(TTR("Error while loading '%s'."), p_file.get_file()), TTR("OK"));
1036 } break;
1037 }
1038 }
1039 }
1040
_get_scene_metadata(const String & p_file)1041 void EditorNode::_get_scene_metadata(const String &p_file) {
1042
1043 Node *scene = editor_data.get_edited_scene_root();
1044
1045 if (!scene)
1046 return;
1047
1048 String path = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(p_file.get_file() + "-editstate-" + p_file.md5_text() + ".cfg");
1049
1050 Ref<ConfigFile> cf;
1051 cf.instance();
1052
1053 Error err = cf->load(path);
1054 if (err != OK || !cf->has_section("editor_states"))
1055 return; //must not exist
1056
1057 List<String> esl;
1058 cf->get_section_keys("editor_states", &esl);
1059
1060 Dictionary md;
1061 for (List<String>::Element *E = esl.front(); E; E = E->next()) {
1062
1063 Variant st = cf->get_value("editor_states", E->get());
1064 if (st.get_type() != Variant::NIL) {
1065 md[E->get()] = st;
1066 }
1067 }
1068
1069 editor_data.set_editor_states(md);
1070 }
1071
_set_scene_metadata(const String & p_file,int p_idx)1072 void EditorNode::_set_scene_metadata(const String &p_file, int p_idx) {
1073
1074 Node *scene = editor_data.get_edited_scene_root(p_idx);
1075
1076 if (!scene)
1077 return;
1078
1079 scene->set_meta("__editor_run_settings__", Variant()); //clear it (no point in keeping it)
1080 scene->set_meta("__editor_plugin_states__", Variant());
1081
1082 String path = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(p_file.get_file() + "-editstate-" + p_file.md5_text() + ".cfg");
1083
1084 Ref<ConfigFile> cf;
1085 cf.instance();
1086
1087 Dictionary md;
1088
1089 if (p_idx < 0 || editor_data.get_edited_scene() == p_idx) {
1090 md = editor_data.get_editor_states();
1091 } else {
1092 md = editor_data.get_scene_editor_states(p_idx);
1093 }
1094
1095 List<Variant> keys;
1096 md.get_key_list(&keys);
1097
1098 for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
1099
1100 cf->set_value("editor_states", E->get(), md[E->get()]);
1101 }
1102
1103 Error err = cf->save(path);
1104 ERR_FAIL_COND_MSG(err != OK, "Cannot save config file to '" + path + "'.");
1105 }
1106
_find_and_save_resource(RES p_res,Map<RES,bool> & processed,int32_t flags)1107 bool EditorNode::_find_and_save_resource(RES p_res, Map<RES, bool> &processed, int32_t flags) {
1108
1109 if (p_res.is_null())
1110 return false;
1111
1112 if (processed.has(p_res)) {
1113
1114 return processed[p_res];
1115 }
1116
1117 bool changed = p_res->is_edited();
1118 p_res->set_edited(false);
1119
1120 bool subchanged = _find_and_save_edited_subresources(p_res.ptr(), processed, flags);
1121
1122 if (p_res->get_path().is_resource_file()) {
1123 if (changed || subchanged) {
1124 //save
1125 ResourceSaver::save(p_res->get_path(), p_res, flags);
1126 }
1127 processed[p_res] = false; //because it's a file
1128 return false;
1129 } else {
1130
1131 processed[p_res] = changed;
1132 return changed;
1133 }
1134 }
1135
_find_and_save_edited_subresources(Object * obj,Map<RES,bool> & processed,int32_t flags)1136 bool EditorNode::_find_and_save_edited_subresources(Object *obj, Map<RES, bool> &processed, int32_t flags) {
1137
1138 bool ret_changed = false;
1139 List<PropertyInfo> pi;
1140 obj->get_property_list(&pi);
1141 for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) {
1142
1143 if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
1144 continue;
1145
1146 switch (E->get().type) {
1147 case Variant::OBJECT: {
1148
1149 RES res = obj->get(E->get().name);
1150
1151 if (_find_and_save_resource(res, processed, flags))
1152 ret_changed = true;
1153
1154 } break;
1155 case Variant::ARRAY: {
1156
1157 Array varray = obj->get(E->get().name);
1158 int len = varray.size();
1159 for (int i = 0; i < len; i++) {
1160
1161 const Variant &v = varray.get(i);
1162 RES res = v;
1163 if (_find_and_save_resource(res, processed, flags))
1164 ret_changed = true;
1165 }
1166
1167 } break;
1168 case Variant::DICTIONARY: {
1169
1170 Dictionary d = obj->get(E->get().name);
1171 List<Variant> keys;
1172 d.get_key_list(&keys);
1173 for (List<Variant>::Element *F = keys.front(); F; F = F->next()) {
1174
1175 Variant v = d[F->get()];
1176 RES res = v;
1177 if (_find_and_save_resource(res, processed, flags))
1178 ret_changed = true;
1179 }
1180 } break;
1181 default: {
1182 }
1183 }
1184 }
1185
1186 return ret_changed;
1187 }
1188
_save_edited_subresources(Node * scene,Map<RES,bool> & processed,int32_t flags)1189 void EditorNode::_save_edited_subresources(Node *scene, Map<RES, bool> &processed, int32_t flags) {
1190
1191 _find_and_save_edited_subresources(scene, processed, flags);
1192
1193 for (int i = 0; i < scene->get_child_count(); i++) {
1194
1195 Node *n = scene->get_child(i);
1196 if (n->get_owner() != editor_data.get_edited_scene_root())
1197 continue;
1198 _save_edited_subresources(n, processed, flags);
1199 }
1200 }
1201
_find_node_types(Node * p_node,int & count_2d,int & count_3d)1202 void EditorNode::_find_node_types(Node *p_node, int &count_2d, int &count_3d) {
1203
1204 if (p_node->is_class("Viewport") || (p_node != editor_data.get_edited_scene_root() && p_node->get_owner() != editor_data.get_edited_scene_root()))
1205 return;
1206
1207 if (p_node->is_class("CanvasItem"))
1208 count_2d++;
1209 else if (p_node->is_class("Spatial"))
1210 count_3d++;
1211
1212 for (int i = 0; i < p_node->get_child_count(); i++)
1213 _find_node_types(p_node->get_child(i), count_2d, count_3d);
1214 }
1215
_save_scene_with_preview(String p_file,int p_idx)1216 void EditorNode::_save_scene_with_preview(String p_file, int p_idx) {
1217
1218 EditorProgress save("save", TTR("Saving Scene"), 4);
1219
1220 if (editor_data.get_edited_scene_root() != NULL) {
1221 save.step(TTR("Analyzing"), 0);
1222
1223 int c2d = 0;
1224 int c3d = 0;
1225
1226 _find_node_types(editor_data.get_edited_scene_root(), c2d, c3d);
1227
1228 save.step(TTR("Creating Thumbnail"), 1);
1229 //current view?
1230
1231 Ref<Image> img;
1232 // If neither 3D or 2D nodes are present, make a 1x1 black texture.
1233 // We cannot fallback on the 2D editor, because it may not have been used yet,
1234 // which would result in an invalid texture.
1235 if (c3d == 0 && c2d == 0) {
1236 img.instance();
1237 img->create(1, 1, 0, Image::FORMAT_RGB8);
1238 } else if (c3d < c2d) {
1239 Ref<ViewportTexture> viewport_texture = scene_root->get_texture();
1240 if (viewport_texture->get_width() > 0 && viewport_texture->get_height() > 0) {
1241 img = viewport_texture->get_data();
1242 }
1243 } else {
1244 // The 3D editor may be disabled as a feature, but scenes can still be opened.
1245 // This check prevents the preview from regenerating in case those scenes are then saved.
1246 Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile();
1247 if (profile.is_valid() && !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)) {
1248 img = SpatialEditor::get_singleton()->get_editor_viewport(0)->get_viewport_node()->get_texture()->get_data();
1249 }
1250 }
1251
1252 if (img.is_valid() && img->get_width() > 0 && img->get_height() > 0) {
1253 img = img->duplicate();
1254
1255 save.step(TTR("Creating Thumbnail"), 2);
1256 save.step(TTR("Creating Thumbnail"), 3);
1257
1258 int preview_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
1259 preview_size *= EDSCALE;
1260
1261 // consider a square region
1262 int vp_size = MIN(img->get_width(), img->get_height());
1263 int x = (img->get_width() - vp_size) / 2;
1264 int y = (img->get_height() - vp_size) / 2;
1265
1266 if (vp_size < preview_size) {
1267 // just square it.
1268 img->crop_from_point(x, y, vp_size, vp_size);
1269 } else {
1270 int ratio = vp_size / preview_size;
1271 int size = preview_size * MAX(1, ratio / 2);
1272
1273 x = (img->get_width() - size) / 2;
1274 y = (img->get_height() - size) / 2;
1275
1276 img->crop_from_point(x, y, size, size);
1277 img->resize(preview_size, preview_size, Image::INTERPOLATE_LANCZOS);
1278 }
1279 img->convert(Image::FORMAT_RGB8);
1280
1281 img->flip_y();
1282
1283 //save thumbnail directly, as thumbnailer may not update due to actual scene not changing md5
1284 String temp_path = EditorSettings::get_singleton()->get_cache_dir();
1285 String cache_base = ProjectSettings::get_singleton()->globalize_path(p_file).md5_text();
1286 cache_base = temp_path.plus_file("resthumb-" + cache_base);
1287
1288 //does not have it, try to load a cached thumbnail
1289
1290 String file = cache_base + ".png";
1291
1292 post_process_preview(img);
1293 img->save_png(file);
1294 }
1295 }
1296
1297 save.step(TTR("Saving Scene"), 4);
1298 _save_scene(p_file, p_idx);
1299
1300 if (!singleton->cmdline_export_mode) {
1301 EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
1302 }
1303 }
1304
_validate_scene_recursive(const String & p_filename,Node * p_node)1305 bool EditorNode::_validate_scene_recursive(const String &p_filename, Node *p_node) {
1306
1307 for (int i = 0; i < p_node->get_child_count(); i++) {
1308 Node *child = p_node->get_child(i);
1309 if (child->get_filename() == p_filename) {
1310 return true;
1311 }
1312
1313 if (_validate_scene_recursive(p_filename, child)) {
1314 return true;
1315 }
1316 }
1317
1318 return false;
1319 }
1320
_find_edited_resources(const Ref<Resource> & p_resource,Set<Ref<Resource>> & edited_resources)1321 static bool _find_edited_resources(const Ref<Resource> &p_resource, Set<Ref<Resource> > &edited_resources) {
1322
1323 if (p_resource->is_edited()) {
1324 edited_resources.insert(p_resource);
1325 return true;
1326 }
1327
1328 List<PropertyInfo> plist;
1329
1330 p_resource->get_property_list(&plist);
1331
1332 for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
1333 if (E->get().type == Variant::OBJECT && E->get().usage & PROPERTY_USAGE_STORAGE && !(E->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)) {
1334 RES res = p_resource->get(E->get().name);
1335 if (res.is_null()) {
1336 continue;
1337 }
1338 if (res->get_path().is_resource_file()) { //not a subresource, continue
1339 continue;
1340 }
1341 if (_find_edited_resources(res, edited_resources)) {
1342 return true;
1343 }
1344 }
1345 }
1346
1347 return false;
1348 }
1349
_save_external_resources()1350 int EditorNode::_save_external_resources() {
1351 //save external resources and its subresources if any was modified
1352
1353 int flg = 0;
1354 if (EditorSettings::get_singleton()->get("filesystem/on_save/compress_binary_resources"))
1355 flg |= ResourceSaver::FLAG_COMPRESS;
1356 flg |= ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
1357
1358 Set<Ref<Resource> > edited_subresources;
1359 int saved = 0;
1360 List<Ref<Resource> > cached;
1361 ResourceCache::get_cached_resources(&cached);
1362 for (List<Ref<Resource> >::Element *E = cached.front(); E; E = E->next()) {
1363
1364 Ref<Resource> res = E->get();
1365 if (!res->get_path().is_resource_file())
1366 continue;
1367 //not only check if this resourec is edited, check contained subresources too
1368 if (_find_edited_resources(res, edited_subresources)) {
1369 ResourceSaver::save(res->get_path(), res, flg);
1370 saved++;
1371 }
1372 }
1373
1374 // clear later, because user may have put the same subresource in two different resources,
1375 // which will be shared until the next reload
1376
1377 for (Set<Ref<Resource> >::Element *E = edited_subresources.front(); E; E = E->next()) {
1378 Ref<Resource> res = E->get();
1379 res->set_edited(false);
1380 }
1381
1382 return saved;
1383 }
1384
_save_scene(String p_file,int idx)1385 void EditorNode::_save_scene(String p_file, int idx) {
1386
1387 Node *scene = editor_data.get_edited_scene_root(idx);
1388
1389 if (!scene) {
1390
1391 show_accept(TTR("This operation can't be done without a tree root."), TTR("OK"));
1392 return;
1393 }
1394
1395 if (scene->get_filename() != String() && _validate_scene_recursive(scene->get_filename(), scene)) {
1396 show_accept(TTR("This scene can't be saved because there is a cyclic instancing inclusion.\nPlease resolve it and then attempt to save again."), TTR("OK"));
1397 return;
1398 }
1399
1400 editor_data.apply_changes_in_editors();
1401 _save_default_environment();
1402
1403 _set_scene_metadata(p_file, idx);
1404
1405 Ref<PackedScene> sdata;
1406
1407 if (ResourceCache::has(p_file)) {
1408 // something may be referencing this resource and we are good with that.
1409 // we must update it, but also let the previous scene state go, as
1410 // old version still work for referencing changes in instanced or inherited scenes
1411
1412 sdata = Ref<PackedScene>(Object::cast_to<PackedScene>(ResourceCache::get(p_file)));
1413 if (sdata.is_valid())
1414 sdata->recreate_state();
1415 else
1416 sdata.instance();
1417 } else {
1418 sdata.instance();
1419 }
1420 Error err = sdata->pack(scene);
1421
1422 if (err != OK) {
1423
1424 show_accept(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."), TTR("OK"));
1425 return;
1426 }
1427
1428 // force creation of node path cache
1429 // (hacky but needed for the tree to update properly)
1430 Node *dummy_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE);
1431 if (!dummy_scene) {
1432 show_accept(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."), TTR("OK"));
1433 return;
1434 }
1435 memdelete(dummy_scene);
1436
1437 int flg = 0;
1438 if (EditorSettings::get_singleton()->get("filesystem/on_save/compress_binary_resources"))
1439 flg |= ResourceSaver::FLAG_COMPRESS;
1440 flg |= ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
1441
1442 err = ResourceSaver::save(p_file, sdata, flg);
1443
1444 _save_external_resources();
1445
1446 editor_data.save_editor_external_data();
1447 if (err == OK) {
1448 scene->set_filename(ProjectSettings::get_singleton()->localize_path(p_file));
1449 if (idx < 0 || idx == editor_data.get_edited_scene())
1450 set_current_version(editor_data.get_undo_redo().get_version());
1451 else
1452 editor_data.set_edited_scene_version(0, idx);
1453
1454 editor_folding.save_scene_folding(scene, p_file);
1455
1456 _update_title();
1457 _update_scene_tabs();
1458 } else {
1459
1460 _dialog_display_save_error(p_file, err);
1461 }
1462 }
1463
save_all_scenes()1464 void EditorNode::save_all_scenes() {
1465
1466 _menu_option_confirm(RUN_STOP, true);
1467 _save_all_scenes();
1468 }
1469
save_scene_list(Vector<String> p_scene_filenames)1470 void EditorNode::save_scene_list(Vector<String> p_scene_filenames) {
1471
1472 for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
1473 Node *scene = editor_data.get_edited_scene_root(i);
1474
1475 if (scene && (p_scene_filenames.find(scene->get_filename()) >= 0)) {
1476 _save_scene(scene->get_filename(), i);
1477 }
1478 }
1479 }
1480
restart_editor()1481 void EditorNode::restart_editor() {
1482
1483 exiting = true;
1484
1485 String to_reopen;
1486 if (get_tree()->get_edited_scene_root()) {
1487 to_reopen = get_tree()->get_edited_scene_root()->get_filename();
1488 }
1489
1490 _exit_editor();
1491
1492 List<String> args;
1493 args.push_back("--path");
1494 args.push_back(ProjectSettings::get_singleton()->get_resource_path());
1495 args.push_back("-e");
1496 if (to_reopen != String()) {
1497 args.push_back(to_reopen);
1498 }
1499
1500 OS::get_singleton()->set_restart_on_exit(true, args);
1501 }
1502
_save_all_scenes()1503 void EditorNode::_save_all_scenes() {
1504
1505 for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
1506 Node *scene = editor_data.get_edited_scene_root(i);
1507 if (scene && scene->get_filename() != "") {
1508 if (i != editor_data.get_edited_scene())
1509 _save_scene(scene->get_filename(), i);
1510 else
1511 _save_scene_with_preview(scene->get_filename());
1512 } // else: ignore new scenes
1513 }
1514
1515 _save_default_environment();
1516 }
1517
_mark_unsaved_scenes()1518 void EditorNode::_mark_unsaved_scenes() {
1519
1520 for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
1521
1522 Node *node = editor_data.get_edited_scene_root(i);
1523 if (!node)
1524 continue;
1525
1526 String path = node->get_filename();
1527 if (!(path == String() || FileAccess::exists(path))) {
1528
1529 if (i == editor_data.get_edited_scene())
1530 set_current_version(-1);
1531 else
1532 editor_data.set_edited_scene_version(-1, i);
1533 }
1534 }
1535
1536 _update_title();
1537 _update_scene_tabs();
1538 }
1539
_dialog_action(String p_file)1540 void EditorNode::_dialog_action(String p_file) {
1541
1542 switch (current_option) {
1543 case FILE_NEW_INHERITED_SCENE: {
1544
1545 Node *scene = editor_data.get_edited_scene_root();
1546 // If the previous scene is rootless, just close it in favor of the new one.
1547 if (!scene)
1548 _menu_option_confirm(FILE_CLOSE, true);
1549
1550 load_scene(p_file, false, true);
1551 } break;
1552 case FILE_OPEN_SCENE: {
1553
1554 load_scene(p_file);
1555 } break;
1556 case SETTINGS_PICK_MAIN_SCENE: {
1557
1558 ProjectSettings::get_singleton()->set("application/run/main_scene", p_file);
1559 ProjectSettings::get_singleton()->save();
1560 //would be nice to show the project manager opened with the highlighted field..
1561
1562 if (pick_main_scene->has_meta("from_native") && (bool)pick_main_scene->get_meta("from_native")) {
1563 run_native->resume_run_native();
1564 } else {
1565 _run(false, ""); // automatically run the project
1566 }
1567 } break;
1568 case FILE_CLOSE:
1569 case FILE_CLOSE_ALL_AND_QUIT:
1570 case FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER:
1571 case SCENE_TAB_CLOSE:
1572 case FILE_SAVE_SCENE:
1573 case FILE_SAVE_AS_SCENE: {
1574
1575 int scene_idx = (current_option == FILE_SAVE_SCENE || current_option == FILE_SAVE_AS_SCENE) ? -1 : tab_closing;
1576
1577 if (file->get_mode() == EditorFileDialog::MODE_SAVE_FILE) {
1578 bool same_open_scene = false;
1579 for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
1580 if (editor_data.get_scene_path(i) == p_file && i != scene_idx)
1581 same_open_scene = true;
1582 }
1583
1584 if (same_open_scene) {
1585 show_warning(TTR("Can't overwrite scene that is still open!"));
1586 return;
1587 }
1588
1589 _save_default_environment();
1590 _save_scene_with_preview(p_file, scene_idx);
1591 _add_to_recent_scenes(p_file);
1592 save_layout();
1593
1594 if (scene_idx != -1)
1595 _discard_changes();
1596 }
1597
1598 } break;
1599
1600 case FILE_SAVE_AND_RUN: {
1601 if (file->get_mode() == EditorFileDialog::MODE_SAVE_FILE) {
1602
1603 _save_default_environment();
1604 _save_scene_with_preview(p_file);
1605 _run(false, p_file);
1606 }
1607 } break;
1608
1609 case FILE_EXPORT_MESH_LIBRARY: {
1610
1611 Ref<MeshLibrary> ml;
1612 if (file_export_lib_merge->is_pressed() && FileAccess::exists(p_file)) {
1613 ml = ResourceLoader::load(p_file, "MeshLibrary");
1614
1615 if (ml.is_null()) {
1616 show_accept(TTR("Can't load MeshLibrary for merging!"), TTR("OK"));
1617 return;
1618 }
1619 }
1620
1621 if (ml.is_null()) {
1622 ml = Ref<MeshLibrary>(memnew(MeshLibrary));
1623 }
1624
1625 MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(), ml, true);
1626
1627 Error err = ResourceSaver::save(p_file, ml);
1628 if (err) {
1629 show_accept(TTR("Error saving MeshLibrary!"), TTR("OK"));
1630 return;
1631 }
1632
1633 } break;
1634 case FILE_EXPORT_TILESET: {
1635
1636 Ref<TileSet> tileset;
1637 if (FileAccess::exists(p_file) && file_export_lib_merge->is_pressed()) {
1638 tileset = ResourceLoader::load(p_file, "TileSet");
1639
1640 if (tileset.is_null()) {
1641 show_accept(TTR("Can't load TileSet for merging!"), TTR("OK"));
1642 return;
1643 }
1644
1645 } else {
1646 tileset = Ref<TileSet>(memnew(TileSet));
1647 }
1648
1649 TileSetEditor::update_library_file(editor_data.get_edited_scene_root(), tileset, true);
1650
1651 Error err = ResourceSaver::save(p_file, tileset);
1652 if (err) {
1653
1654 show_accept(TTR("Error saving TileSet!"), TTR("OK"));
1655 return;
1656 }
1657 } break;
1658
1659 case RESOURCE_SAVE:
1660 case RESOURCE_SAVE_AS: {
1661
1662 ERR_FAIL_COND(saving_resource.is_null());
1663 save_resource_in_path(saving_resource, p_file);
1664 saving_resource = Ref<Resource>();
1665 ObjectID current = editor_history.get_current();
1666 Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
1667 ERR_FAIL_COND(!current_obj);
1668 current_obj->_change_notify();
1669 } break;
1670 case SETTINGS_LAYOUT_SAVE: {
1671
1672 if (p_file.empty())
1673 return;
1674
1675 Ref<ConfigFile> config;
1676 config.instance();
1677 Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config());
1678
1679 if (err == ERR_FILE_CANT_OPEN || err == ERR_FILE_NOT_FOUND) {
1680 config.instance(); // new config
1681 } else if (err != OK) {
1682 show_warning(TTR("Error trying to save layout!"));
1683 return;
1684 }
1685
1686 _save_docks_to_config(config, p_file);
1687
1688 config->save(EditorSettings::get_singleton()->get_editor_layouts_config());
1689
1690 layout_dialog->hide();
1691 _update_layouts_menu();
1692
1693 if (p_file == "Default") {
1694 show_warning(TTR("Default editor layout overridden."));
1695 }
1696
1697 } break;
1698 case SETTINGS_LAYOUT_DELETE: {
1699
1700 if (p_file.empty())
1701 return;
1702
1703 Ref<ConfigFile> config;
1704 config.instance();
1705 Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config());
1706
1707 if (err != OK || !config->has_section(p_file)) {
1708 show_warning(TTR("Layout name not found!"));
1709 return;
1710 }
1711
1712 // erase
1713 List<String> keys;
1714 config->get_section_keys(p_file, &keys);
1715 for (List<String>::Element *E = keys.front(); E; E = E->next()) {
1716 config->set_value(p_file, E->get(), Variant());
1717 }
1718
1719 config->save(EditorSettings::get_singleton()->get_editor_layouts_config());
1720
1721 layout_dialog->hide();
1722 _update_layouts_menu();
1723
1724 if (p_file == "Default") {
1725 show_warning(TTR("Restored default layout to base settings."));
1726 }
1727
1728 } break;
1729 default: { //save scene?
1730
1731 if (file->get_mode() == EditorFileDialog::MODE_SAVE_FILE) {
1732 _save_scene_with_preview(p_file);
1733 }
1734
1735 } break;
1736 }
1737 }
1738
item_has_editor(Object * p_object)1739 bool EditorNode::item_has_editor(Object *p_object) {
1740
1741 if (_is_class_editor_disabled_by_feature_profile(p_object->get_class())) {
1742 return false;
1743 }
1744
1745 return editor_data.get_subeditors(p_object).size() > 0;
1746 }
1747
edit_item_resource(RES p_resource)1748 void EditorNode::edit_item_resource(RES p_resource) {
1749 edit_item(p_resource.ptr());
1750 }
1751
_is_class_editor_disabled_by_feature_profile(const StringName & p_class)1752 bool EditorNode::_is_class_editor_disabled_by_feature_profile(const StringName &p_class) {
1753
1754 Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile();
1755 if (profile.is_null()) {
1756 return false;
1757 }
1758
1759 StringName class_name = p_class;
1760
1761 while (class_name != StringName()) {
1762
1763 if (profile->is_class_disabled(class_name)) {
1764 return true;
1765 }
1766 if (profile->is_class_editor_disabled(class_name)) {
1767 return true;
1768 }
1769 class_name = ClassDB::get_parent_class(class_name);
1770 }
1771
1772 return false;
1773 }
1774
edit_item(Object * p_object)1775 void EditorNode::edit_item(Object *p_object) {
1776
1777 Vector<EditorPlugin *> sub_plugins;
1778
1779 if (p_object) {
1780 if (_is_class_editor_disabled_by_feature_profile(p_object->get_class())) {
1781 return;
1782 }
1783 sub_plugins = editor_data.get_subeditors(p_object);
1784 }
1785
1786 if (!sub_plugins.empty()) {
1787
1788 bool same = true;
1789 if (sub_plugins.size() == editor_plugins_over->get_plugins_list().size()) {
1790 for (int i = 0; i < sub_plugins.size(); i++) {
1791 if (sub_plugins[i] != editor_plugins_over->get_plugins_list()[i]) {
1792 same = false;
1793 }
1794 }
1795 } else {
1796 same = false;
1797 }
1798 if (!same) {
1799 _display_top_editors(false);
1800 _set_top_editors(sub_plugins);
1801 }
1802 _set_editing_top_editors(p_object);
1803 _display_top_editors(true);
1804 } else {
1805 hide_top_editors();
1806 }
1807 }
1808
push_item(Object * p_object,const String & p_property,bool p_inspector_only)1809 void EditorNode::push_item(Object *p_object, const String &p_property, bool p_inspector_only) {
1810
1811 if (!p_object) {
1812 get_inspector()->edit(NULL);
1813 node_dock->set_node(NULL);
1814 scene_tree_dock->set_selected(NULL);
1815 inspector_dock->update(NULL);
1816 return;
1817 }
1818
1819 uint32_t id = p_object->get_instance_id();
1820 if (id != editor_history.get_current()) {
1821
1822 if (p_inspector_only) {
1823 editor_history.add_object_inspector_only(id);
1824 } else if (p_property == "")
1825 editor_history.add_object(id);
1826 else
1827 editor_history.add_object(id, p_property);
1828 }
1829
1830 _edit_current();
1831 }
1832
_save_default_environment()1833 void EditorNode::_save_default_environment() {
1834
1835 Ref<Environment> fallback = get_tree()->get_root()->get_world()->get_fallback_environment();
1836
1837 if (fallback.is_valid() && fallback->get_path().is_resource_file()) {
1838 Map<RES, bool> processed;
1839 _find_and_save_edited_subresources(fallback.ptr(), processed, 0);
1840 save_resource_in_path(fallback, fallback->get_path());
1841 }
1842 }
1843
hide_top_editors()1844 void EditorNode::hide_top_editors() {
1845
1846 _display_top_editors(false);
1847
1848 editor_plugins_over->clear();
1849 }
1850
_display_top_editors(bool p_display)1851 void EditorNode::_display_top_editors(bool p_display) {
1852 editor_plugins_over->make_visible(p_display);
1853 }
1854
_set_top_editors(Vector<EditorPlugin * > p_editor_plugins_over)1855 void EditorNode::_set_top_editors(Vector<EditorPlugin *> p_editor_plugins_over) {
1856 editor_plugins_over->set_plugins_list(p_editor_plugins_over);
1857 }
1858
_set_editing_top_editors(Object * p_current_object)1859 void EditorNode::_set_editing_top_editors(Object *p_current_object) {
1860 editor_plugins_over->edit(p_current_object);
1861 }
1862
overrides_external_editor(Object * p_object)1863 static bool overrides_external_editor(Object *p_object) {
1864
1865 Script *script = Object::cast_to<Script>(p_object);
1866
1867 if (!script)
1868 return false;
1869
1870 return script->get_language()->overrides_external_editor();
1871 }
1872
_edit_current()1873 void EditorNode::_edit_current() {
1874
1875 uint32_t current = editor_history.get_current();
1876 Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
1877 bool inspector_only = editor_history.is_current_inspector_only();
1878
1879 this->current = current_obj;
1880
1881 if (!current_obj) {
1882
1883 scene_tree_dock->set_selected(NULL);
1884 get_inspector()->edit(NULL);
1885 node_dock->set_node(NULL);
1886 inspector_dock->update(NULL);
1887
1888 _display_top_editors(false);
1889
1890 return;
1891 }
1892
1893 Object *prev_inspected_object = get_inspector()->get_edited_object();
1894
1895 bool capitalize = bool(EDITOR_GET("interface/inspector/capitalize_properties"));
1896 bool disable_folding = bool(EDITOR_GET("interface/inspector/disable_folding"));
1897 bool is_resource = current_obj->is_class("Resource");
1898 bool is_node = current_obj->is_class("Node");
1899
1900 String editable_warning; //none by default
1901
1902 if (is_resource) {
1903
1904 Resource *current_res = Object::cast_to<Resource>(current_obj);
1905 ERR_FAIL_COND(!current_res);
1906 get_inspector()->edit(current_res);
1907 scene_tree_dock->set_selected(NULL);
1908 node_dock->set_node(NULL);
1909 inspector_dock->update(NULL);
1910 EditorNode::get_singleton()->get_import_dock()->set_edit_path(current_res->get_path());
1911
1912 int subr_idx = current_res->get_path().find("::");
1913 if (subr_idx != -1) {
1914 String base_path = current_res->get_path().substr(0, subr_idx);
1915 if (FileAccess::exists(base_path + ".import")) {
1916 editable_warning = TTR("This resource belongs to a scene that was imported, so it's not editable.\nPlease read the documentation relevant to importing scenes to better understand this workflow.");
1917 } else {
1918 if ((!get_edited_scene() || get_edited_scene()->get_filename() != base_path) && ResourceLoader::get_resource_type(base_path) == "PackedScene") {
1919 editable_warning = TTR("This resource belongs to a scene that was instanced or inherited.\nChanges to it won't be kept when saving the current scene.");
1920 }
1921 }
1922 } else if (current_res->get_path().is_resource_file()) {
1923 if (FileAccess::exists(current_res->get_path() + ".import")) {
1924 editable_warning = TTR("This resource was imported, so it's not editable. Change its settings in the import panel and then re-import.");
1925 }
1926 }
1927 } else if (is_node) {
1928
1929 Node *current_node = Object::cast_to<Node>(current_obj);
1930 ERR_FAIL_COND(!current_node);
1931
1932 get_inspector()->edit(current_node);
1933 if (current_node->is_inside_tree()) {
1934 node_dock->set_node(current_node);
1935 scene_tree_dock->set_selected(current_node);
1936 inspector_dock->update(current_node);
1937 } else {
1938 node_dock->set_node(NULL);
1939 scene_tree_dock->set_selected(NULL);
1940 inspector_dock->update(NULL);
1941 }
1942
1943 if (get_edited_scene() && get_edited_scene()->get_filename() != String()) {
1944 String source_scene = get_edited_scene()->get_filename();
1945 if (FileAccess::exists(source_scene + ".import")) {
1946 editable_warning = TTR("This scene was imported, so changes to it won't be kept.\nInstancing it or inheriting will allow making changes to it.\nPlease read the documentation relevant to importing scenes to better understand this workflow.");
1947 }
1948 }
1949
1950 } else {
1951
1952 Node *selected_node = NULL;
1953
1954 if (current_obj->is_class("ScriptEditorDebuggerInspectedObject")) {
1955 editable_warning = TTR("This is a remote object, so changes to it won't be kept.\nPlease read the documentation relevant to debugging to better understand this workflow.");
1956 capitalize = false;
1957 disable_folding = true;
1958 } else if (current_obj->is_class("MultiNodeEdit")) {
1959 Node *scene = get_edited_scene();
1960 if (scene) {
1961 MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(current_obj);
1962 int node_count = multi_node_edit->get_node_count();
1963 if (node_count > 0) {
1964 List<Node *> multi_nodes;
1965 for (int node_index = 0; node_index < node_count; ++node_index) {
1966 Node *node = scene->get_node(multi_node_edit->get_node(node_index));
1967 if (node) {
1968 multi_nodes.push_back(node);
1969 }
1970 }
1971 if (!multi_nodes.empty()) {
1972 // Pick the top-most node
1973 multi_nodes.sort_custom<Node::Comparator>();
1974 selected_node = multi_nodes.front()->get();
1975 }
1976 }
1977 }
1978 }
1979
1980 get_inspector()->edit(current_obj);
1981 node_dock->set_node(NULL);
1982 scene_tree_dock->set_selected(selected_node);
1983 inspector_dock->update(NULL);
1984 }
1985
1986 if (current_obj == prev_inspected_object) {
1987 // Make sure inspected properties are restored.
1988 get_inspector()->update_tree();
1989 }
1990
1991 inspector_dock->set_warning(editable_warning);
1992
1993 if (get_inspector()->is_capitalize_paths_enabled() != capitalize) {
1994 get_inspector()->set_enable_capitalize_paths(capitalize);
1995 }
1996
1997 if (get_inspector()->is_using_folding() == disable_folding) {
1998 get_inspector()->set_use_folding(!disable_folding);
1999 }
2000
2001 /* Take care of PLUGIN EDITOR */
2002
2003 if (!inspector_only) {
2004
2005 EditorPlugin *main_plugin = editor_data.get_editor(current_obj);
2006
2007 for (int i = 0; i < editor_table.size(); i++) {
2008 if (editor_table[i] == main_plugin && !main_editor_buttons[i]->is_visible()) {
2009 main_plugin = NULL; //if button is not visible, then no plugin active
2010 }
2011 }
2012
2013 if (main_plugin) {
2014
2015 // special case if use of external editor is true
2016 if (main_plugin->get_name() == "Script" && current_obj->get_class_name() != StringName("VisualScript") && (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) {
2017 if (!changing_scene)
2018 main_plugin->edit(current_obj);
2019 }
2020
2021 else if (main_plugin != editor_plugin_screen && (!ScriptEditor::get_singleton() || !ScriptEditor::get_singleton()->is_visible_in_tree() || ScriptEditor::get_singleton()->can_take_away_focus())) {
2022 // update screen main_plugin
2023
2024 if (!changing_scene) {
2025
2026 if (editor_plugin_screen)
2027 editor_plugin_screen->make_visible(false);
2028 editor_plugin_screen = main_plugin;
2029 editor_plugin_screen->edit(current_obj);
2030
2031 editor_plugin_screen->make_visible(true);
2032
2033 int plugin_count = editor_data.get_editor_plugin_count();
2034 for (int i = 0; i < plugin_count; i++) {
2035 editor_data.get_editor_plugin(i)->notify_main_screen_changed(editor_plugin_screen->get_name());
2036 }
2037
2038 for (int i = 0; i < editor_table.size(); i++) {
2039
2040 main_editor_buttons[i]->set_pressed(editor_table[i] == main_plugin);
2041 }
2042 }
2043
2044 } else {
2045
2046 editor_plugin_screen->edit(current_obj);
2047 }
2048 }
2049
2050 Vector<EditorPlugin *> sub_plugins;
2051
2052 if (!_is_class_editor_disabled_by_feature_profile(current_obj->get_class())) {
2053 sub_plugins = editor_data.get_subeditors(current_obj);
2054 }
2055
2056 if (!sub_plugins.empty()) {
2057 _display_top_editors(false);
2058
2059 _set_top_editors(sub_plugins);
2060 _set_editing_top_editors(current_obj);
2061 _display_top_editors(true);
2062 } else if (!editor_plugins_over->get_plugins_list().empty()) {
2063
2064 hide_top_editors();
2065 }
2066 }
2067
2068 inspector_dock->update(current_obj);
2069 inspector_dock->update_keying();
2070 }
2071
_run(bool p_current,const String & p_custom)2072 void EditorNode::_run(bool p_current, const String &p_custom) {
2073
2074 if (editor_run.get_status() == EditorRun::STATUS_PLAY) {
2075 play_button->set_pressed(!_playing_edited);
2076 play_scene_button->set_pressed(_playing_edited);
2077 return;
2078 }
2079
2080 play_button->set_pressed(false);
2081 play_button->set_icon(gui_base->get_icon("MainPlay", "EditorIcons"));
2082 play_scene_button->set_pressed(false);
2083 play_scene_button->set_icon(gui_base->get_icon("PlayScene", "EditorIcons"));
2084 play_custom_scene_button->set_pressed(false);
2085 play_custom_scene_button->set_icon(gui_base->get_icon("PlayCustom", "EditorIcons"));
2086
2087 String run_filename;
2088 String args;
2089 bool skip_breakpoints;
2090
2091 if (p_current || (editor_data.get_edited_scene_root() && p_custom != String() && p_custom == editor_data.get_edited_scene_root()->get_filename())) {
2092 Node *scene = editor_data.get_edited_scene_root();
2093
2094 if (!scene) {
2095 show_accept(TTR("There is no defined scene to run."), TTR("OK"));
2096 return;
2097 }
2098
2099 if (scene->get_filename() == "") {
2100 current_option = -1;
2101 _menu_option_confirm(FILE_SAVE_BEFORE_RUN, false);
2102 return;
2103 }
2104
2105 run_filename = scene->get_filename();
2106 } else if (p_custom != "") {
2107 run_filename = p_custom;
2108 }
2109
2110 if (run_filename == "") {
2111
2112 //evidently, run the scene
2113 if (!ensure_main_scene(false)) {
2114 return;
2115 }
2116 }
2117
2118 if (bool(EDITOR_GET("run/auto_save/save_before_running"))) {
2119
2120 if (unsaved_cache) {
2121
2122 Node *scene = editor_data.get_edited_scene_root();
2123
2124 if (scene && scene->get_filename() != "") { // Only autosave if there is a scene and if it has a path.
2125 _save_scene_with_preview(scene->get_filename());
2126 }
2127 }
2128 _menu_option(FILE_SAVE_ALL_SCENES);
2129 editor_data.save_editor_external_data();
2130 }
2131
2132 if (!call_build())
2133 return;
2134
2135 if (bool(EDITOR_GET("run/output/always_clear_output_on_play"))) {
2136 log->clear();
2137 }
2138
2139 if (bool(EDITOR_GET("run/output/always_open_output_on_play"))) {
2140 make_bottom_panel_item_visible(log);
2141 }
2142
2143 List<String> breakpoints;
2144 editor_data.get_editor_breakpoints(&breakpoints);
2145
2146 args = ProjectSettings::get_singleton()->get("editor/main_run_args");
2147 skip_breakpoints = ScriptEditor::get_singleton()->get_debugger()->is_skip_breakpoints();
2148
2149 Error error = editor_run.run(run_filename, args, breakpoints, skip_breakpoints);
2150
2151 if (error != OK) {
2152
2153 show_accept(TTR("Could not start subprocess!"), TTR("OK"));
2154 return;
2155 }
2156
2157 emit_signal("play_pressed");
2158 if (p_current) {
2159 play_scene_button->set_pressed(true);
2160 play_scene_button->set_icon(gui_base->get_icon("Reload", "EditorIcons"));
2161 } else if (p_custom != "") {
2162 run_custom_filename = p_custom;
2163 play_custom_scene_button->set_pressed(true);
2164 play_custom_scene_button->set_icon(gui_base->get_icon("Reload", "EditorIcons"));
2165 } else {
2166 play_button->set_pressed(true);
2167 play_button->set_icon(gui_base->get_icon("Reload", "EditorIcons"));
2168 }
2169 stop_button->set_disabled(false);
2170
2171 _playing_edited = p_current;
2172 }
2173
_menu_option_confirm(int p_option,bool p_confirmed)2174 void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
2175
2176 if (!p_confirmed) //this may be a hack..
2177 current_option = (MenuOptions)p_option;
2178
2179 switch (p_option) {
2180 case FILE_NEW_SCENE: {
2181
2182 new_scene();
2183
2184 } break;
2185 case FILE_NEW_INHERITED_SCENE:
2186 case FILE_OPEN_SCENE: {
2187
2188 file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
2189 List<String> extensions;
2190 ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);
2191 file->clear_filters();
2192 for (int i = 0; i < extensions.size(); i++) {
2193
2194 file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
2195 }
2196
2197 Node *scene = editor_data.get_edited_scene_root();
2198 if (scene) {
2199 file->set_current_path(scene->get_filename());
2200 };
2201 file->set_title(p_option == FILE_OPEN_SCENE ? TTR("Open Scene") : TTR("Open Base Scene"));
2202 file->popup_centered_ratio();
2203
2204 } break;
2205 case FILE_QUICK_OPEN: {
2206
2207 quick_open->popup_dialog("Resource", true);
2208 quick_open->set_title(TTR("Quick Open..."));
2209
2210 } break;
2211 case FILE_QUICK_OPEN_SCENE: {
2212
2213 quick_open->popup_dialog("PackedScene", true);
2214 quick_open->set_title(TTR("Quick Open Scene..."));
2215
2216 } break;
2217 case FILE_QUICK_OPEN_SCRIPT: {
2218
2219 quick_open->popup_dialog("Script", true);
2220 quick_open->set_title(TTR("Quick Open Script..."));
2221
2222 } break;
2223 case FILE_OPEN_PREV: {
2224
2225 if (previous_scenes.empty())
2226 break;
2227 opening_prev = true;
2228 open_request(previous_scenes.back()->get());
2229 previous_scenes.pop_back();
2230
2231 } break;
2232 case FILE_CLOSE_OTHERS:
2233 case FILE_CLOSE_RIGHT:
2234 case FILE_CLOSE_ALL: {
2235 if (editor_data.get_edited_scene_count() > 1 && (current_option != FILE_CLOSE_RIGHT || editor_data.get_edited_scene() < editor_data.get_edited_scene_count() - 1)) {
2236 int next_tab = editor_data.get_edited_scene() + 1;
2237 next_tab %= editor_data.get_edited_scene_count();
2238 _scene_tab_closed(next_tab, current_option);
2239 } else {
2240 if (current_option != FILE_CLOSE_ALL)
2241 current_option = -1;
2242 else
2243 _scene_tab_closed(editor_data.get_edited_scene());
2244 }
2245
2246 if (p_confirmed)
2247 _menu_option_confirm(SCENE_TAB_CLOSE, true);
2248
2249 } break;
2250 case FILE_CLOSE_ALL_AND_QUIT:
2251 case FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER:
2252 case FILE_CLOSE: {
2253
2254 if (!p_confirmed) {
2255 tab_closing = p_option == FILE_CLOSE ? editor_data.get_edited_scene() : _next_unsaved_scene(false);
2256
2257 if (unsaved_cache || p_option == FILE_CLOSE_ALL_AND_QUIT || p_option == FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER) {
2258 String scene_filename = editor_data.get_edited_scene_root(tab_closing)->get_filename();
2259 save_confirmation->get_ok()->set_text(TTR("Save & Close"));
2260 save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), scene_filename != "" ? scene_filename : "unsaved scene"));
2261 save_confirmation->popup_centered_minsize();
2262 break;
2263 }
2264 } else if (p_option == FILE_CLOSE) {
2265 tab_closing = editor_data.get_edited_scene();
2266 }
2267 if (!editor_data.get_edited_scene_root(tab_closing)) {
2268 // empty tab
2269 _scene_tab_closed(tab_closing);
2270 break;
2271 }
2272
2273 FALLTHROUGH;
2274 }
2275 case SCENE_TAB_CLOSE:
2276 case FILE_SAVE_SCENE: {
2277
2278 int scene_idx = (p_option == FILE_SAVE_SCENE) ? -1 : tab_closing;
2279
2280 Node *scene = editor_data.get_edited_scene_root(scene_idx);
2281 if (scene && scene->get_filename() != "") {
2282
2283 if (scene_idx != editor_data.get_edited_scene())
2284 _save_scene_with_preview(scene->get_filename(), scene_idx);
2285 else
2286 _save_scene_with_preview(scene->get_filename());
2287
2288 if (scene_idx != -1)
2289 _discard_changes();
2290 save_layout();
2291
2292 break;
2293 }
2294 FALLTHROUGH;
2295 }
2296 case FILE_SAVE_AS_SCENE: {
2297 int scene_idx = (p_option == FILE_SAVE_SCENE || p_option == FILE_SAVE_AS_SCENE) ? -1 : tab_closing;
2298
2299 Node *scene = editor_data.get_edited_scene_root(scene_idx);
2300
2301 if (!scene) {
2302
2303 int saved = _save_external_resources();
2304 String err_text;
2305 if (saved > 0) {
2306 err_text = vformat(TTR("Saved %s modified resource(s)."), itos(saved));
2307 } else {
2308 err_text = TTR("A root node is required to save the scene.");
2309 }
2310
2311 show_accept(err_text, TTR("OK"));
2312 break;
2313 }
2314
2315 file->set_mode(EditorFileDialog::MODE_SAVE_FILE);
2316
2317 List<String> extensions;
2318 Ref<PackedScene> sd = memnew(PackedScene);
2319 ResourceSaver::get_recognized_extensions(sd, &extensions);
2320 file->clear_filters();
2321 for (int i = 0; i < extensions.size(); i++) {
2322
2323 file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
2324 }
2325
2326 if (scene->get_filename() != "") {
2327 file->set_current_path(scene->get_filename());
2328 if (extensions.size()) {
2329 String ext = scene->get_filename().get_extension().to_lower();
2330 if (extensions.find(ext) == NULL) {
2331 file->set_current_path(scene->get_filename().replacen("." + ext, "." + extensions.front()->get()));
2332 }
2333 }
2334 } else {
2335
2336 String existing;
2337 if (extensions.size()) {
2338 String root_name(scene->get_name());
2339 existing = root_name + "." + extensions.front()->get().to_lower();
2340 }
2341 file->set_current_path(existing);
2342 }
2343 file->popup_centered_ratio();
2344 file->set_title(TTR("Save Scene As..."));
2345
2346 } break;
2347
2348 case FILE_SAVE_ALL_SCENES: {
2349
2350 _save_all_scenes();
2351 } break;
2352 case FILE_SAVE_BEFORE_RUN: {
2353 if (!p_confirmed) {
2354 confirmation->get_cancel()->set_text(TTR("No"));
2355 confirmation->get_ok()->set_text(TTR("Yes"));
2356 confirmation->set_text(TTR("This scene has never been saved. Save before running?"));
2357 confirmation->popup_centered_minsize();
2358 break;
2359 }
2360
2361 _menu_option(FILE_SAVE_AS_SCENE);
2362 _menu_option_confirm(FILE_SAVE_AND_RUN, false);
2363 } break;
2364
2365 case FILE_EXPORT_PROJECT: {
2366
2367 project_export->popup_export();
2368 } break;
2369
2370 case FILE_EXPORT_MESH_LIBRARY: {
2371
2372 if (!editor_data.get_edited_scene_root()) {
2373
2374 show_accept(TTR("This operation can't be done without a scene."), TTR("OK"));
2375 break;
2376 }
2377
2378 List<String> extensions;
2379 Ref<MeshLibrary> ml(memnew(MeshLibrary));
2380 ResourceSaver::get_recognized_extensions(ml, &extensions);
2381 file_export_lib->clear_filters();
2382 for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
2383 file_export_lib->add_filter("*." + E->get());
2384 }
2385
2386 file_export_lib->popup_centered_ratio();
2387 file_export_lib->set_title(TTR("Export Mesh Library"));
2388
2389 } break;
2390 case FILE_EXPORT_TILESET: {
2391
2392 //Make sure that the scene has a root before trying to convert to tileset
2393 if (!editor_data.get_edited_scene_root()) {
2394 show_accept(TTR("This operation can't be done without a root node."), TTR("OK"));
2395 break;
2396 }
2397
2398 List<String> extensions;
2399 Ref<TileSet> ml(memnew(TileSet));
2400 ResourceSaver::get_recognized_extensions(ml, &extensions);
2401 file_export_lib->clear_filters();
2402 for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
2403 file_export_lib->add_filter("*." + E->get());
2404 }
2405
2406 file_export_lib->popup_centered_ratio();
2407 file_export_lib->set_title(TTR("Export Tile Set"));
2408
2409 } break;
2410
2411 case FILE_IMPORT_SUBSCENE: {
2412
2413 if (!editor_data.get_edited_scene_root()) {
2414
2415 show_accept(TTR("This operation can't be done without a selected node."), TTR("OK"));
2416 break;
2417 }
2418
2419 scene_tree_dock->import_subscene();
2420
2421 } break;
2422
2423 case FILE_EXTERNAL_OPEN_SCENE: {
2424
2425 if (unsaved_cache && !p_confirmed) {
2426
2427 confirmation->get_ok()->set_text(TTR("Open"));
2428 confirmation->set_text(TTR("Current scene not saved. Open anyway?"));
2429 confirmation->popup_centered_minsize();
2430 break;
2431 }
2432
2433 bool oprev = opening_prev;
2434 Error err = load_scene(external_file);
2435 if (err == OK && oprev) {
2436 previous_scenes.pop_back();
2437 opening_prev = false;
2438 }
2439
2440 } break;
2441
2442 case EDIT_UNDO: {
2443
2444 if (Input::get_singleton()->get_mouse_button_mask() & 0x7) {
2445 log->add_message("Can't undo while mouse buttons are pressed.", EditorLog::MSG_TYPE_EDITOR);
2446 } else {
2447 String action = editor_data.get_undo_redo().get_current_action_name();
2448
2449 if (!editor_data.get_undo_redo().undo()) {
2450 log->add_message("Nothing to undo.", EditorLog::MSG_TYPE_EDITOR);
2451 } else if (action != "") {
2452 log->add_message("Undo: " + action, EditorLog::MSG_TYPE_EDITOR);
2453 }
2454 }
2455 } break;
2456 case EDIT_REDO: {
2457
2458 if (Input::get_singleton()->get_mouse_button_mask() & 0x7) {
2459 log->add_message("Can't redo while mouse buttons are pressed.", EditorLog::MSG_TYPE_EDITOR);
2460 } else {
2461 if (!editor_data.get_undo_redo().redo()) {
2462 log->add_message("Nothing to redo.", EditorLog::MSG_TYPE_EDITOR);
2463 } else {
2464 String action = editor_data.get_undo_redo().get_current_action_name();
2465 log->add_message("Redo: " + action, EditorLog::MSG_TYPE_EDITOR);
2466 }
2467 }
2468 } break;
2469
2470 case EDIT_RELOAD_SAVED_SCENE: {
2471
2472 Node *scene = get_edited_scene();
2473
2474 if (!scene)
2475 break;
2476
2477 String filename = scene->get_filename();
2478
2479 if (filename == String()) {
2480 show_warning(TTR("Can't reload a scene that was never saved."));
2481 break;
2482 }
2483
2484 if (unsaved_cache && !p_confirmed) {
2485 confirmation->get_ok()->set_text(TTR("Reload Saved Scene"));
2486 confirmation->set_text(
2487 TTR("The current scene has unsaved changes.\nReload the saved scene anyway? This action cannot be undone."));
2488 confirmation->popup_centered_minsize();
2489 break;
2490 }
2491
2492 int cur_idx = editor_data.get_edited_scene();
2493 _remove_edited_scene();
2494 Error err = load_scene(filename);
2495 if (err != OK)
2496 ERR_PRINT("Failed to load scene");
2497 editor_data.move_edited_scene_to_index(cur_idx);
2498 get_undo_redo()->clear_history(false);
2499 scene_tabs->set_current_tab(cur_idx);
2500
2501 } break;
2502 case RUN_PLAY: {
2503 run_play();
2504
2505 } break;
2506 case RUN_PLAY_CUSTOM_SCENE: {
2507 if (run_custom_filename.empty() || editor_run.get_status() == EditorRun::STATUS_STOP) {
2508 _menu_option_confirm(RUN_STOP, true);
2509 quick_run->popup_dialog("PackedScene", true);
2510 quick_run->set_title(TTR("Quick Run Scene..."));
2511 play_custom_scene_button->set_pressed(false);
2512 } else {
2513 String last_custom_scene = run_custom_filename;
2514 run_play_custom(last_custom_scene);
2515 }
2516
2517 } break;
2518 case RUN_STOP: {
2519
2520 if (editor_run.get_status() == EditorRun::STATUS_STOP)
2521 break;
2522
2523 editor_run.stop();
2524 run_custom_filename.clear();
2525 play_button->set_pressed(false);
2526 play_button->set_icon(gui_base->get_icon("MainPlay", "EditorIcons"));
2527 play_scene_button->set_pressed(false);
2528 play_scene_button->set_icon(gui_base->get_icon("PlayScene", "EditorIcons"));
2529 play_custom_scene_button->set_pressed(false);
2530 play_custom_scene_button->set_icon(gui_base->get_icon("PlayCustom", "EditorIcons"));
2531 stop_button->set_disabled(true);
2532
2533 if (bool(EDITOR_GET("run/output/always_close_output_on_stop"))) {
2534 for (int i = 0; i < bottom_panel_items.size(); i++) {
2535 if (bottom_panel_items[i].control == log) {
2536 _bottom_panel_switch(false, i);
2537 break;
2538 }
2539 }
2540 }
2541 emit_signal("stop_pressed");
2542
2543 } break;
2544
2545 case FILE_SHOW_IN_FILESYSTEM: {
2546 String path = editor_data.get_scene_path(editor_data.get_edited_scene());
2547 if (path != String()) {
2548 filesystem_dock->navigate_to_path(path);
2549 }
2550 } break;
2551
2552 case RUN_PLAY_SCENE: {
2553 run_play_current();
2554
2555 } break;
2556 case RUN_PLAY_NATIVE: {
2557
2558 bool autosave = EDITOR_GET("run/auto_save/save_before_running");
2559 if (autosave) {
2560 _menu_option_confirm(FILE_SAVE_ALL_SCENES, false);
2561 }
2562 if (run_native->is_deploy_debug_remote_enabled()) {
2563 _menu_option_confirm(RUN_STOP, true);
2564
2565 if (!call_build())
2566 break; // build failed
2567
2568 emit_signal("play_pressed");
2569 editor_run.run_native_notify();
2570 }
2571 } break;
2572 case RUN_SCENE_SETTINGS: {
2573
2574 run_settings_dialog->popup_run_settings();
2575 } break;
2576 case RUN_SETTINGS: {
2577
2578 project_settings->popup_project_settings();
2579 } break;
2580 case FILE_INSTALL_ANDROID_SOURCE: {
2581
2582 if (p_confirmed) {
2583 export_template_manager->install_android_template();
2584 } else {
2585 if (DirAccess::exists("res://android/build")) {
2586 remove_android_build_template->popup_centered_minsize();
2587 } else if (export_template_manager->can_install_android_template()) {
2588 install_android_build_template->popup_centered_minsize();
2589 } else {
2590 custom_build_manage_templates->popup_centered_minsize();
2591 }
2592 }
2593 } break;
2594 case RUN_PROJECT_DATA_FOLDER: {
2595 OS::get_singleton()->shell_open(String("file://") + OS::get_singleton()->get_user_data_dir());
2596 } break;
2597 case FILE_EXPLORE_ANDROID_BUILD_TEMPLATES: {
2598 OS::get_singleton()->shell_open("file://" + ProjectSettings::get_singleton()->get_resource_path().plus_file("android"));
2599 } break;
2600 case FILE_QUIT:
2601 case RUN_PROJECT_MANAGER: {
2602
2603 if (!p_confirmed) {
2604 bool save_each = EDITOR_GET("interface/editor/save_each_scene_on_quit");
2605 if (_next_unsaved_scene(!save_each) == -1) {
2606
2607 bool confirm = EDITOR_GET("interface/editor/quit_confirmation");
2608 if (confirm) {
2609
2610 confirmation->get_ok()->set_text(p_option == FILE_QUIT ? TTR("Quit") : TTR("Yes"));
2611 confirmation->set_text(p_option == FILE_QUIT ? TTR("Exit the editor?") : TTR("Open Project Manager?"));
2612 confirmation->popup_centered_minsize();
2613 } else {
2614 _discard_changes();
2615 break;
2616 }
2617 } else {
2618
2619 if (save_each) {
2620
2621 _menu_option_confirm(p_option == FILE_QUIT ? FILE_CLOSE_ALL_AND_QUIT : FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER, false);
2622 } else {
2623
2624 String unsaved_scenes;
2625 int i = _next_unsaved_scene(true, 0);
2626 while (i != -1) {
2627 unsaved_scenes += "\n " + editor_data.get_edited_scene_root(i)->get_filename();
2628 i = _next_unsaved_scene(true, ++i);
2629 }
2630
2631 save_confirmation->get_ok()->set_text(TTR("Save & Quit"));
2632 save_confirmation->set_text((p_option == FILE_QUIT ? TTR("Save changes to the following scene(s) before quitting?") : TTR("Save changes the following scene(s) before opening Project Manager?")) + unsaved_scenes);
2633 save_confirmation->popup_centered_minsize();
2634 }
2635 }
2636
2637 OS::get_singleton()->request_attention();
2638 break;
2639 }
2640
2641 if (_next_unsaved_scene(true) != -1) {
2642 _save_all_scenes();
2643 }
2644 _discard_changes();
2645 } break;
2646 case RUN_FILE_SERVER: {
2647
2648 bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_FILE_SERVER));
2649
2650 if (ischecked) {
2651 file_server->stop();
2652 run_native->set_deploy_dumb(false);
2653 } else {
2654 file_server->start();
2655 run_native->set_deploy_dumb(true);
2656 }
2657
2658 debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_FILE_SERVER), !ischecked);
2659 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_file_server", !ischecked);
2660 } break;
2661 case RUN_LIVE_DEBUG: {
2662
2663 bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_LIVE_DEBUG));
2664
2665 debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_LIVE_DEBUG), !ischecked);
2666 ScriptEditor::get_singleton()->get_debugger()->set_live_debugging(!ischecked);
2667 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_live_debug", !ischecked);
2668
2669 } break;
2670 case RUN_DEPLOY_REMOTE_DEBUG: {
2671
2672 bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEPLOY_REMOTE_DEBUG));
2673 debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEPLOY_REMOTE_DEBUG), !ischecked);
2674 run_native->set_deploy_debug_remote(!ischecked);
2675 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_deploy_remote_debug", !ischecked);
2676
2677 } break;
2678 case RUN_DEBUG_COLLISONS: {
2679
2680 bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_COLLISONS));
2681 debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_COLLISONS), !ischecked);
2682 run_native->set_debug_collisions(!ischecked);
2683 editor_run.set_debug_collisions(!ischecked);
2684 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_collisons", !ischecked);
2685
2686 } break;
2687 case RUN_DEBUG_NAVIGATION: {
2688
2689 bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_NAVIGATION));
2690 debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_NAVIGATION), !ischecked);
2691 run_native->set_debug_navigation(!ischecked);
2692 editor_run.set_debug_navigation(!ischecked);
2693 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_navigation", !ischecked);
2694
2695 } break;
2696 case RUN_RELOAD_SCRIPTS: {
2697
2698 bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_RELOAD_SCRIPTS));
2699 debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_RELOAD_SCRIPTS), !ischecked);
2700
2701 ScriptEditor::get_singleton()->set_live_auto_reload_running_scripts(!ischecked);
2702 EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_reload_scripts", !ischecked);
2703
2704 } break;
2705 case SETTINGS_UPDATE_CONTINUOUSLY: {
2706
2707 EditorSettings::get_singleton()->set("interface/editor/update_continuously", true);
2708 _update_update_spinner();
2709 show_accept(TTR("This option is deprecated. Situations where refresh must be forced are now considered a bug. Please report."), TTR("OK"));
2710 } break;
2711 case SETTINGS_UPDATE_WHEN_CHANGED: {
2712
2713 EditorSettings::get_singleton()->set("interface/editor/update_continuously", false);
2714 _update_update_spinner();
2715 } break;
2716 case SETTINGS_UPDATE_SPINNER_HIDE: {
2717
2718 EditorSettings::get_singleton()->set("interface/editor/show_update_spinner", false);
2719 _update_update_spinner();
2720 } break;
2721 case SETTINGS_PREFERENCES: {
2722
2723 settings_config_dialog->popup_edit_settings();
2724 } break;
2725 case SETTINGS_EDITOR_DATA_FOLDER: {
2726
2727 OS::get_singleton()->shell_open(String("file://") + EditorSettings::get_singleton()->get_data_dir());
2728 } break;
2729 case SETTINGS_EDITOR_CONFIG_FOLDER: {
2730
2731 OS::get_singleton()->shell_open(String("file://") + EditorSettings::get_singleton()->get_settings_dir());
2732 } break;
2733 case SETTINGS_MANAGE_EXPORT_TEMPLATES: {
2734
2735 export_template_manager->popup_manager();
2736
2737 } break;
2738 case SETTINGS_MANAGE_FEATURE_PROFILES: {
2739
2740 feature_profile_manager->popup_centered_clamped(Size2(900, 800) * EDSCALE, 0.8);
2741 } break;
2742 case SETTINGS_TOGGLE_FULLSCREEN: {
2743
2744 OS::get_singleton()->set_window_fullscreen(!OS::get_singleton()->is_window_fullscreen());
2745
2746 } break;
2747 case SETTINGS_TOGGLE_CONSOLE: {
2748
2749 bool was_visible = OS::get_singleton()->is_console_visible();
2750 OS::get_singleton()->set_console_visible(!was_visible);
2751 EditorSettings::get_singleton()->set_setting("interface/editor/hide_console_window", was_visible);
2752 } break;
2753 case EDITOR_SCREENSHOT: {
2754
2755 screenshot_timer->start();
2756 } break;
2757 case SETTINGS_PICK_MAIN_SCENE: {
2758
2759 file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
2760 List<String> extensions;
2761 ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);
2762 file->clear_filters();
2763 for (int i = 0; i < extensions.size(); i++) {
2764
2765 file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
2766 }
2767
2768 Node *scene = editor_data.get_edited_scene_root();
2769 if (scene) {
2770 file->set_current_path(scene->get_filename());
2771 };
2772 file->set_title(TTR("Pick a Main Scene"));
2773 file->popup_centered_ratio();
2774
2775 } break;
2776 case HELP_SEARCH: {
2777 emit_signal("request_help_search", "");
2778 } break;
2779 case HELP_DOCS: {
2780 OS::get_singleton()->shell_open("https://docs.godotengine.org/");
2781 } break;
2782 case HELP_QA: {
2783 OS::get_singleton()->shell_open("https://godotengine.org/qa/");
2784 } break;
2785 case HELP_REPORT_A_BUG: {
2786 OS::get_singleton()->shell_open("https://github.com/godotengine/godot/issues");
2787 } break;
2788 case HELP_SEND_DOCS_FEEDBACK: {
2789 OS::get_singleton()->shell_open("https://github.com/godotengine/godot-docs/issues");
2790 } break;
2791 case HELP_COMMUNITY: {
2792 OS::get_singleton()->shell_open("https://godotengine.org/community");
2793 } break;
2794 case HELP_ABOUT: {
2795 about->popup_centered_minsize(Size2(780, 500) * EDSCALE);
2796 } break;
2797
2798 case SET_VIDEO_DRIVER_SAVE_AND_RESTART: {
2799
2800 ProjectSettings::get_singleton()->set("rendering/quality/driver/driver_name", video_driver_request);
2801 ProjectSettings::get_singleton()->save();
2802
2803 save_all_scenes();
2804 restart_editor();
2805 } break;
2806 }
2807 }
2808
_request_screenshot()2809 void EditorNode::_request_screenshot() {
2810 _screenshot();
2811 }
2812
_screenshot(bool p_use_utc)2813 void EditorNode::_screenshot(bool p_use_utc) {
2814 String name = "editor_screenshot_" + OS::get_singleton()->get_iso_date_time(p_use_utc).replace(":", "") + ".png";
2815 NodePath path = String("user://") + name;
2816 _save_screenshot(path);
2817 if (EditorSettings::get_singleton()->get("interface/editor/automatically_open_screenshots")) {
2818 OS::get_singleton()->shell_open(String("file://") + ProjectSettings::get_singleton()->globalize_path(path));
2819 }
2820 }
2821
_save_screenshot(NodePath p_path)2822 void EditorNode::_save_screenshot(NodePath p_path) {
2823
2824 Viewport *viewport = EditorInterface::get_singleton()->get_editor_viewport()->get_viewport();
2825 viewport->set_clear_mode(Viewport::CLEAR_MODE_ONLY_NEXT_FRAME);
2826 Ref<Image> img = viewport->get_texture()->get_data();
2827 img->flip_y();
2828 viewport->set_clear_mode(Viewport::CLEAR_MODE_ALWAYS);
2829 Error error = img->save_png(p_path);
2830 ERR_FAIL_COND_MSG(error != OK, "Cannot save screenshot to file '" + p_path + "'.");
2831 }
2832
_tool_menu_option(int p_idx)2833 void EditorNode::_tool_menu_option(int p_idx) {
2834 switch (tool_menu->get_item_id(p_idx)) {
2835 case TOOLS_ORPHAN_RESOURCES: {
2836 orphan_resources->show();
2837 } break;
2838 case TOOLS_CUSTOM: {
2839 if (tool_menu->get_item_submenu(p_idx) == "") {
2840 Array params = tool_menu->get_item_metadata(p_idx);
2841
2842 Object *handler = ObjectDB::get_instance(params[0]);
2843 String callback = params[1];
2844 Variant *ud = ¶ms[2];
2845 Variant::CallError ce;
2846
2847 handler->call(callback, (const Variant **)&ud, 1, ce);
2848 if (ce.error != Variant::CallError::CALL_OK) {
2849 String err = Variant::get_call_error_text(handler, callback, (const Variant **)&ud, 1, ce);
2850 ERR_PRINTS("Error calling function from tool menu: " + err);
2851 }
2852 } // else it's a submenu so don't do anything.
2853 } break;
2854 }
2855 }
2856
_next_unsaved_scene(bool p_valid_filename,int p_start)2857 int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) {
2858
2859 for (int i = p_start; i < editor_data.get_edited_scene_count(); i++) {
2860
2861 if (!editor_data.get_edited_scene_root(i))
2862 continue;
2863 int current = editor_data.get_edited_scene();
2864 bool unsaved = (i == current) ? saved_version != editor_data.get_undo_redo().get_version() : editor_data.get_scene_version(i) != 0;
2865 if (unsaved) {
2866 String scene_filename = editor_data.get_edited_scene_root(i)->get_filename();
2867 if (p_valid_filename && scene_filename.length() == 0)
2868 continue;
2869 return i;
2870 }
2871 }
2872 return -1;
2873 }
2874
_exit_editor()2875 void EditorNode::_exit_editor() {
2876 exiting = true;
2877 resource_preview->stop(); //stop early to avoid crashes
2878 _save_docks();
2879
2880 // Dim the editor window while it's quitting to make it clearer that it's busy
2881 dim_editor(true, true);
2882
2883 get_tree()->quit();
2884 }
2885
_discard_changes(const String & p_str)2886 void EditorNode::_discard_changes(const String &p_str) {
2887
2888 switch (current_option) {
2889
2890 case FILE_CLOSE_ALL_AND_QUIT:
2891 case FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER:
2892 case FILE_CLOSE:
2893 case FILE_CLOSE_OTHERS:
2894 case FILE_CLOSE_RIGHT:
2895 case FILE_CLOSE_ALL:
2896 case SCENE_TAB_CLOSE: {
2897
2898 Node *scene = editor_data.get_edited_scene_root(tab_closing);
2899 if (scene != NULL) {
2900 String scene_filename = scene->get_filename();
2901 if (scene_filename != "") {
2902 previous_scenes.push_back(scene_filename);
2903 }
2904 }
2905
2906 _remove_scene(tab_closing);
2907 _update_scene_tabs();
2908
2909 if (current_option == FILE_CLOSE_ALL_AND_QUIT || current_option == FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER) {
2910 if (_next_unsaved_scene(false) == -1) {
2911 current_option = current_option == FILE_CLOSE_ALL_AND_QUIT ? FILE_QUIT : RUN_PROJECT_MANAGER;
2912 _discard_changes();
2913 } else {
2914 _menu_option_confirm(current_option, false);
2915 }
2916 } else if (current_option == FILE_CLOSE_OTHERS || current_option == FILE_CLOSE_RIGHT) {
2917 if (editor_data.get_edited_scene_count() == 1 || (current_option == FILE_CLOSE_RIGHT && editor_data.get_edited_scene_count() <= editor_data.get_edited_scene() + 1)) {
2918 current_option = -1;
2919 save_confirmation->hide();
2920 } else {
2921 _menu_option_confirm(current_option, false);
2922 }
2923 } else if (current_option == FILE_CLOSE_ALL && editor_data.get_edited_scene_count() > 0) {
2924 _menu_option_confirm(current_option, false);
2925 } else {
2926 current_option = -1;
2927 save_confirmation->hide();
2928 }
2929 } break;
2930 case FILE_QUIT: {
2931
2932 _menu_option_confirm(RUN_STOP, true);
2933 _exit_editor();
2934
2935 } break;
2936 case RUN_PROJECT_MANAGER: {
2937
2938 _menu_option_confirm(RUN_STOP, true);
2939 _exit_editor();
2940 String exec = OS::get_singleton()->get_executable_path();
2941
2942 List<String> args;
2943 args.push_back("--path");
2944 args.push_back(exec.get_base_dir());
2945 args.push_back("--project-manager");
2946
2947 OS::ProcessID pid = 0;
2948 Error err = OS::get_singleton()->execute(exec, args, false, &pid);
2949 ERR_FAIL_COND(err);
2950 } break;
2951 }
2952 }
2953
_update_debug_options()2954 void EditorNode::_update_debug_options() {
2955
2956 bool check_deploy_remote = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_deploy_remote_debug", false);
2957 bool check_file_server = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_file_server", false);
2958 bool check_debug_collisons = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisons", false);
2959 bool check_debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false);
2960 bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", true);
2961 bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", true);
2962
2963 if (check_deploy_remote) _menu_option_confirm(RUN_DEPLOY_REMOTE_DEBUG, true);
2964 if (check_file_server) _menu_option_confirm(RUN_FILE_SERVER, true);
2965 if (check_debug_collisons) _menu_option_confirm(RUN_DEBUG_COLLISONS, true);
2966 if (check_debug_navigation) _menu_option_confirm(RUN_DEBUG_NAVIGATION, true);
2967 if (check_live_debug) _menu_option_confirm(RUN_LIVE_DEBUG, true);
2968 if (check_reload_scripts) _menu_option_confirm(RUN_RELOAD_SCRIPTS, true);
2969 }
2970
_update_file_menu_opened()2971 void EditorNode::_update_file_menu_opened() {
2972
2973 Ref<ShortCut> close_scene_sc = ED_GET_SHORTCUT("editor/close_scene");
2974 close_scene_sc->set_name(TTR("Close Scene"));
2975 Ref<ShortCut> reopen_closed_scene_sc = ED_GET_SHORTCUT("editor/reopen_closed_scene");
2976 reopen_closed_scene_sc->set_name(TTR("Reopen Closed Scene"));
2977 PopupMenu *pop = file_menu->get_popup();
2978 pop->set_item_disabled(pop->get_item_index(FILE_OPEN_PREV), previous_scenes.empty());
2979 }
2980
_update_file_menu_closed()2981 void EditorNode::_update_file_menu_closed() {
2982 PopupMenu *pop = file_menu->get_popup();
2983 pop->set_item_disabled(pop->get_item_index(FILE_OPEN_PREV), false);
2984 }
2985
get_viewport()2986 Control *EditorNode::get_viewport() {
2987
2988 return viewport;
2989 }
2990
_editor_select(int p_which)2991 void EditorNode::_editor_select(int p_which) {
2992
2993 static bool selecting = false;
2994 if (selecting || changing_scene)
2995 return;
2996
2997 ERR_FAIL_INDEX(p_which, editor_table.size());
2998
2999 if (!main_editor_buttons[p_which]->is_visible()) //button hidden, no editor
3000 return;
3001
3002 selecting = true;
3003
3004 for (int i = 0; i < main_editor_buttons.size(); i++) {
3005 main_editor_buttons[i]->set_pressed(i == p_which);
3006 }
3007
3008 selecting = false;
3009
3010 EditorPlugin *new_editor = editor_table[p_which];
3011 ERR_FAIL_COND(!new_editor);
3012
3013 if (editor_plugin_screen == new_editor)
3014 return;
3015
3016 if (editor_plugin_screen) {
3017 editor_plugin_screen->make_visible(false);
3018 }
3019
3020 editor_plugin_screen = new_editor;
3021 editor_plugin_screen->make_visible(true);
3022 editor_plugin_screen->selected_notify();
3023
3024 int plugin_count = editor_data.get_editor_plugin_count();
3025 for (int i = 0; i < plugin_count; i++) {
3026 editor_data.get_editor_plugin(i)->notify_main_screen_changed(editor_plugin_screen->get_name());
3027 }
3028
3029 if (EditorSettings::get_singleton()->get("interface/editor/separate_distraction_mode")) {
3030 if (p_which == EDITOR_SCRIPT) {
3031 set_distraction_free_mode(script_distraction);
3032 } else {
3033 set_distraction_free_mode(scene_distraction);
3034 }
3035 }
3036 }
3037
select_editor_by_name(const String & p_name)3038 void EditorNode::select_editor_by_name(const String &p_name) {
3039 ERR_FAIL_COND(p_name == "");
3040
3041 for (int i = 0; i < main_editor_buttons.size(); i++) {
3042 if (main_editor_buttons[i]->get_text() == p_name) {
3043 _editor_select(i);
3044 return;
3045 }
3046 }
3047
3048 ERR_FAIL_MSG("The editor name '" + p_name + "' was not found.");
3049 }
3050
add_editor_plugin(EditorPlugin * p_editor,bool p_config_changed)3051 void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed) {
3052
3053 if (p_editor->has_main_screen()) {
3054
3055 ToolButton *tb = memnew(ToolButton);
3056 tb->set_toggle_mode(true);
3057 tb->connect("pressed", singleton, "_editor_select", varray(singleton->main_editor_buttons.size()));
3058 tb->set_text(p_editor->get_name());
3059 Ref<Texture> icon = p_editor->get_icon();
3060
3061 if (icon.is_valid()) {
3062 tb->set_icon(icon);
3063 } else if (singleton->gui_base->has_icon(p_editor->get_name(), "EditorIcons")) {
3064 tb->set_icon(singleton->gui_base->get_icon(p_editor->get_name(), "EditorIcons"));
3065 }
3066
3067 tb->set_name(p_editor->get_name());
3068 singleton->main_editor_buttons.push_back(tb);
3069 singleton->main_editor_button_vb->add_child(tb);
3070 singleton->editor_table.push_back(p_editor);
3071
3072 singleton->distraction_free->raise();
3073 }
3074 singleton->editor_data.add_editor_plugin(p_editor);
3075 singleton->add_child(p_editor);
3076 if (p_config_changed)
3077 p_editor->enable_plugin();
3078 }
3079
remove_editor_plugin(EditorPlugin * p_editor,bool p_config_changed)3080 void EditorNode::remove_editor_plugin(EditorPlugin *p_editor, bool p_config_changed) {
3081
3082 if (p_editor->has_main_screen()) {
3083
3084 for (int i = 0; i < singleton->main_editor_buttons.size(); i++) {
3085
3086 if (p_editor->get_name() == singleton->main_editor_buttons[i]->get_text()) {
3087
3088 if (singleton->main_editor_buttons[i]->is_pressed()) {
3089 singleton->_editor_select(EDITOR_SCRIPT);
3090 }
3091
3092 memdelete(singleton->main_editor_buttons[i]);
3093 singleton->main_editor_buttons.remove(i);
3094
3095 break;
3096 }
3097 }
3098
3099 singleton->editor_table.erase(p_editor);
3100 }
3101 p_editor->make_visible(false);
3102 p_editor->clear();
3103 if (p_config_changed)
3104 p_editor->disable_plugin();
3105 singleton->editor_plugins_over->get_plugins_list().erase(p_editor);
3106 singleton->remove_child(p_editor);
3107 singleton->editor_data.remove_editor_plugin(p_editor);
3108 singleton->get_editor_plugins_force_input_forwarding()->remove_plugin(p_editor);
3109 }
3110
_update_addon_config()3111 void EditorNode::_update_addon_config() {
3112
3113 if (_initializing_addons)
3114 return;
3115
3116 Vector<String> enabled_addons;
3117
3118 for (Map<String, EditorPlugin *>::Element *E = plugin_addons.front(); E; E = E->next()) {
3119 enabled_addons.push_back(E->key());
3120 }
3121
3122 if (enabled_addons.size() == 0) {
3123 ProjectSettings::get_singleton()->set("editor_plugins/enabled", Variant());
3124 } else {
3125 ProjectSettings::get_singleton()->set("editor_plugins/enabled", enabled_addons);
3126 }
3127
3128 project_settings->queue_save();
3129 }
3130
set_addon_plugin_enabled(const String & p_addon,bool p_enabled,bool p_config_changed)3131 void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, bool p_config_changed) {
3132
3133 ERR_FAIL_COND(p_enabled && plugin_addons.has(p_addon));
3134 ERR_FAIL_COND(!p_enabled && !plugin_addons.has(p_addon));
3135
3136 if (!p_enabled) {
3137
3138 EditorPlugin *addon = plugin_addons[p_addon];
3139 remove_editor_plugin(addon, p_config_changed);
3140 memdelete(addon); //bye
3141 plugin_addons.erase(p_addon);
3142 _update_addon_config();
3143 return;
3144 }
3145
3146 Ref<ConfigFile> cf;
3147 cf.instance();
3148 String addon_path = String("res://addons").plus_file(p_addon).plus_file("plugin.cfg");
3149 if (!DirAccess::exists(addon_path.get_base_dir())) {
3150 ProjectSettings *ps = ProjectSettings::get_singleton();
3151 PoolStringArray enabled_plugins = ps->get("editor_plugins/enabled");
3152 for (int i = 0; i < enabled_plugins.size(); ++i) {
3153 if (enabled_plugins.get(i) == p_addon) {
3154 enabled_plugins.remove(i);
3155 break;
3156 }
3157 }
3158 ps->set("editor_plugins/enabled", enabled_plugins);
3159 ps->save();
3160 WARN_PRINTS("Addon '" + p_addon + "' failed to load. No directory found. Removing from enabled plugins.");
3161 return;
3162 }
3163 Error err = cf->load(addon_path);
3164 if (err != OK) {
3165 show_warning(vformat(TTR("Unable to enable addon plugin at: '%s' parsing of config failed."), addon_path));
3166 return;
3167 }
3168
3169 if (!cf->has_section_key("plugin", "script")) {
3170 show_warning(vformat(TTR("Unable to find script field for addon plugin at: 'res://addons/%s'."), p_addon));
3171 return;
3172 }
3173
3174 String script_path = cf->get_value("plugin", "script");
3175 Ref<Script> script; // We need to save it for creating "ep" below.
3176
3177 // Only try to load the script if it has a name. Else, the plugin has no init script.
3178 if (script_path.length() > 0) {
3179 script_path = String("res://addons").plus_file(p_addon).plus_file(script_path);
3180 script = ResourceLoader::load(script_path);
3181
3182 if (script.is_null()) {
3183 show_warning(vformat(TTR("Unable to load addon script from path: '%s'."), script_path));
3184 return;
3185 }
3186
3187 // Errors in the script cause the base_type to be an empty string.
3188 if (String(script->get_instance_base_type()) == "") {
3189 show_warning(vformat(TTR("Unable to load addon script from path: '%s' There seems to be an error in the code, please check the syntax."), script_path));
3190 return;
3191 }
3192
3193 // Plugin init scripts must inherit from EditorPlugin and be tools.
3194 if (String(script->get_instance_base_type()) != "EditorPlugin") {
3195 show_warning(vformat(TTR("Unable to load addon script from path: '%s' Base type is not EditorPlugin."), script_path));
3196 return;
3197 }
3198
3199 if (!script->is_tool()) {
3200 show_warning(vformat(TTR("Unable to load addon script from path: '%s' Script is not in tool mode."), script_path));
3201 return;
3202 }
3203 }
3204
3205 EditorPlugin *ep = memnew(EditorPlugin);
3206 ep->set_script(script.get_ref_ptr());
3207 plugin_addons[p_addon] = ep;
3208 add_editor_plugin(ep, p_config_changed);
3209
3210 _update_addon_config();
3211 }
3212
is_addon_plugin_enabled(const String & p_addon) const3213 bool EditorNode::is_addon_plugin_enabled(const String &p_addon) const {
3214
3215 return plugin_addons.has(p_addon);
3216 }
3217
_remove_edited_scene(bool p_change_tab)3218 void EditorNode::_remove_edited_scene(bool p_change_tab) {
3219 int new_index = editor_data.get_edited_scene();
3220 int old_index = new_index;
3221
3222 if (new_index > 0) {
3223 new_index = new_index - 1;
3224 } else if (editor_data.get_edited_scene_count() > 1) {
3225 new_index = 1;
3226 } else {
3227 editor_data.add_edited_scene(-1);
3228 new_index = 1;
3229 }
3230
3231 if (editor_data.get_scene_path(old_index) != String()) {
3232 ScriptEditor::get_singleton()->close_builtin_scripts_from_scene(editor_data.get_scene_path(old_index));
3233 }
3234
3235 if (p_change_tab) _scene_tab_changed(new_index);
3236 editor_data.remove_scene(old_index);
3237 editor_data.get_undo_redo().clear_history(false);
3238 _update_title();
3239 _update_scene_tabs();
3240 }
3241
_remove_scene(int index,bool p_change_tab)3242 void EditorNode::_remove_scene(int index, bool p_change_tab) {
3243
3244 if (editor_data.get_edited_scene() == index) {
3245 //Scene to remove is current scene
3246 _remove_edited_scene(p_change_tab);
3247 } else {
3248 //Scene to remove is not active scene
3249 editor_data.remove_scene(index);
3250 }
3251 }
3252
set_edited_scene(Node * p_scene)3253 void EditorNode::set_edited_scene(Node *p_scene) {
3254
3255 if (get_editor_data().get_edited_scene_root()) {
3256 if (get_editor_data().get_edited_scene_root()->get_parent() == scene_root)
3257 scene_root->remove_child(get_editor_data().get_edited_scene_root());
3258 }
3259 get_editor_data().set_edited_scene_root(p_scene);
3260
3261 if (Object::cast_to<Popup>(p_scene))
3262 Object::cast_to<Popup>(p_scene)->show(); //show popups
3263 scene_tree_dock->set_edited_scene(p_scene);
3264 if (get_tree())
3265 get_tree()->set_edited_scene_root(p_scene);
3266
3267 if (p_scene) {
3268 if (p_scene->get_parent() != scene_root)
3269 scene_root->add_child(p_scene);
3270 }
3271 }
3272
_get_current_main_editor()3273 int EditorNode::_get_current_main_editor() {
3274
3275 for (int i = 0; i < editor_table.size(); i++) {
3276 if (editor_table[i] == editor_plugin_screen)
3277 return i;
3278 }
3279
3280 return 0;
3281 }
3282
_get_main_scene_state()3283 Dictionary EditorNode::_get_main_scene_state() {
3284
3285 Dictionary state;
3286 state["main_tab"] = _get_current_main_editor();
3287 state["scene_tree_offset"] = scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value();
3288 state["property_edit_offset"] = get_inspector()->get_scroll_offset();
3289 state["saved_version"] = saved_version;
3290 state["node_filter"] = scene_tree_dock->get_filter();
3291 return state;
3292 }
3293
_set_main_scene_state(Dictionary p_state,Node * p_for_scene)3294 void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
3295
3296 if (get_edited_scene() != p_for_scene && p_for_scene != NULL)
3297 return; //not for this scene
3298
3299 changing_scene = false;
3300
3301 int current = -1;
3302 for (int i = 0; i < editor_table.size(); i++) {
3303 if (editor_plugin_screen == editor_table[i]) {
3304 current = i;
3305 break;
3306 }
3307 }
3308
3309 if (p_state.has("editor_index")) {
3310 int index = p_state["editor_index"];
3311 if (current < 2) { //if currently in spatial/2d, only switch to spatial/2d. if currently in script, stay there
3312 if (index < 2 || !get_edited_scene()) {
3313 _editor_select(index);
3314 }
3315 }
3316 }
3317
3318 if (get_edited_scene()) {
3319 if (current < 2) {
3320 //use heuristic instead
3321 int n2d = 0, n3d = 0;
3322 _find_node_types(get_edited_scene(), n2d, n3d);
3323 if (n2d > n3d) {
3324 _editor_select(EDITOR_2D);
3325 } else if (n3d > n2d) {
3326 _editor_select(EDITOR_3D);
3327 }
3328 }
3329 }
3330
3331 if (p_state.has("scene_tree_offset"))
3332 scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->set_value(p_state["scene_tree_offset"]);
3333 if (p_state.has("property_edit_offset"))
3334 get_inspector()->set_scroll_offset(p_state["property_edit_offset"]);
3335
3336 if (p_state.has("node_filter"))
3337 scene_tree_dock->set_filter(p_state["node_filter"]);
3338
3339 //this should only happen at the very end
3340
3341 ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root();
3342 ScriptEditor::get_singleton()->set_scene_root_script(editor_data.get_scene_root_script(editor_data.get_edited_scene()));
3343 editor_data.notify_edited_scene_changed();
3344 }
3345
set_current_version(uint64_t p_version)3346 void EditorNode::set_current_version(uint64_t p_version) {
3347
3348 saved_version = p_version;
3349 editor_data.set_edited_scene_version(p_version);
3350 }
3351
is_changing_scene() const3352 bool EditorNode::is_changing_scene() const {
3353 return changing_scene;
3354 }
3355
_clear_undo_history()3356 void EditorNode::_clear_undo_history() {
3357
3358 get_undo_redo()->clear_history(false);
3359 }
3360
set_current_scene(int p_idx)3361 void EditorNode::set_current_scene(int p_idx) {
3362
3363 //Save the folding in case the scene gets reloaded.
3364 if (editor_data.get_scene_path(p_idx) != "" && editor_data.get_edited_scene_root(p_idx))
3365 editor_folding.save_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx));
3366
3367 if (editor_data.check_and_update_scene(p_idx)) {
3368 if (editor_data.get_scene_path(p_idx) != "")
3369 editor_folding.load_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx));
3370
3371 call_deferred("_clear_undo_history");
3372 }
3373
3374 changing_scene = true;
3375 editor_data.save_edited_scene_state(editor_selection, &editor_history, _get_main_scene_state());
3376
3377 if (get_editor_data().get_edited_scene_root()) {
3378 if (get_editor_data().get_edited_scene_root()->get_parent() == scene_root)
3379 scene_root->remove_child(get_editor_data().get_edited_scene_root());
3380 }
3381
3382 editor_selection->clear();
3383 editor_data.set_edited_scene(p_idx);
3384
3385 Node *new_scene = editor_data.get_edited_scene_root();
3386
3387 if (Object::cast_to<Popup>(new_scene))
3388 Object::cast_to<Popup>(new_scene)->show(); //show popups
3389
3390 scene_tree_dock->set_edited_scene(new_scene);
3391 if (get_tree())
3392 get_tree()->set_edited_scene_root(new_scene);
3393
3394 if (new_scene) {
3395 if (new_scene->get_parent() != scene_root)
3396 scene_root->add_child(new_scene);
3397 }
3398
3399 Dictionary state = editor_data.restore_edited_scene_state(editor_selection, &editor_history);
3400 _edit_current();
3401
3402 _update_title();
3403
3404 call_deferred("_set_main_scene_state", state, get_edited_scene()); //do after everything else is done setting up
3405 }
3406
is_scene_open(const String & p_path)3407 bool EditorNode::is_scene_open(const String &p_path) {
3408
3409 for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
3410 if (editor_data.get_scene_path(i) == p_path)
3411 return true;
3412 }
3413
3414 return false;
3415 }
3416
fix_dependencies(const String & p_for_file)3417 void EditorNode::fix_dependencies(const String &p_for_file) {
3418 dependency_fixer->edit(p_for_file);
3419 }
3420
new_scene()3421 int EditorNode::new_scene() {
3422 int idx = editor_data.add_edited_scene(-1);
3423 _scene_tab_changed(idx);
3424 editor_data.clear_editor_states();
3425 _update_scene_tabs();
3426 return idx;
3427 }
3428
load_scene(const String & p_scene,bool p_ignore_broken_deps,bool p_set_inherited,bool p_clear_errors,bool p_force_open_imported)3429 Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, bool p_set_inherited, bool p_clear_errors, bool p_force_open_imported) {
3430
3431 if (!is_inside_tree()) {
3432 defer_load_scene = p_scene;
3433 return OK;
3434 }
3435
3436 if (!p_set_inherited) {
3437
3438 for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
3439
3440 if (editor_data.get_scene_path(i) == p_scene) {
3441 _scene_tab_changed(i);
3442 return OK;
3443 }
3444 }
3445
3446 if (!p_force_open_imported && FileAccess::exists(p_scene + ".import")) {
3447 open_imported->set_text(vformat(TTR("Scene '%s' was automatically imported, so it can't be modified.\nTo make changes to it, a new inherited scene can be created."), p_scene.get_file()));
3448 open_imported->popup_centered_minsize();
3449 new_inherited_button->grab_focus();
3450 open_import_request = p_scene;
3451 return OK;
3452 }
3453 }
3454
3455 if (p_clear_errors)
3456 load_errors->clear();
3457
3458 String lpath = ProjectSettings::get_singleton()->localize_path(p_scene);
3459
3460 if (!lpath.begins_with("res://")) {
3461
3462 show_accept(TTR("Error loading scene, it must be inside the project path. Use 'Import' to open the scene, then save it inside the project path."), TTR("OK"));
3463 opening_prev = false;
3464 return ERR_FILE_NOT_FOUND;
3465 }
3466
3467 int prev = editor_data.get_edited_scene();
3468 int idx = editor_data.add_edited_scene(-1);
3469
3470 if (!editor_data.get_edited_scene_root() && editor_data.get_edited_scene_count() == 2) {
3471 _remove_edited_scene();
3472 } else {
3473 _scene_tab_changed(idx);
3474 }
3475
3476 dependency_errors.clear();
3477
3478 Error err;
3479 Ref<PackedScene> sdata = ResourceLoader::load(lpath, "", true, &err);
3480 if (!sdata.is_valid()) {
3481
3482 _dialog_display_load_error(lpath, err);
3483 opening_prev = false;
3484
3485 if (prev != -1) {
3486 set_current_scene(prev);
3487 editor_data.remove_scene(idx);
3488 }
3489 return ERR_FILE_NOT_FOUND;
3490 }
3491
3492 if (!p_ignore_broken_deps && dependency_errors.has(lpath)) {
3493
3494 current_option = -1;
3495 Vector<String> errors;
3496 for (Set<String>::Element *E = dependency_errors[lpath].front(); E; E = E->next()) {
3497
3498 errors.push_back(E->get());
3499 }
3500 dependency_error->show(DependencyErrorDialog::MODE_SCENE, lpath, errors);
3501 opening_prev = false;
3502
3503 if (prev != -1) {
3504 set_current_scene(prev);
3505 editor_data.remove_scene(idx);
3506 }
3507 return ERR_FILE_MISSING_DEPENDENCIES;
3508 }
3509
3510 dependency_errors.erase(lpath); //at least not self path
3511
3512 for (Map<String, Set<String> >::Element *E = dependency_errors.front(); E; E = E->next()) {
3513
3514 String txt = vformat(TTR("Scene '%s' has broken dependencies:"), E->key()) + "\n";
3515 for (Set<String>::Element *F = E->get().front(); F; F = F->next()) {
3516 txt += "\t" + F->get() + "\n";
3517 }
3518 add_io_error(txt);
3519 }
3520
3521 if (ResourceCache::has(lpath)) {
3522 //used from somewhere else? no problem! update state and replace sdata
3523 Ref<PackedScene> ps = Ref<PackedScene>(Object::cast_to<PackedScene>(ResourceCache::get(lpath)));
3524 if (ps.is_valid()) {
3525 ps->replace_state(sdata->get_state());
3526 ps->set_last_modified_time(sdata->get_last_modified_time());
3527 sdata = ps;
3528 }
3529
3530 } else {
3531 sdata->set_path(lpath, true); //take over path
3532 }
3533
3534 Node *new_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_MAIN);
3535
3536 if (!new_scene) {
3537
3538 sdata.unref();
3539 _dialog_display_load_error(lpath, ERR_FILE_CORRUPT);
3540 opening_prev = false;
3541 if (prev != -1) {
3542 set_current_scene(prev);
3543 editor_data.remove_scene(idx);
3544 }
3545 return ERR_FILE_CORRUPT;
3546 }
3547
3548 if (p_set_inherited) {
3549 Ref<SceneState> state = sdata->get_state();
3550 state->set_path(lpath);
3551 new_scene->set_scene_inherited_state(state);
3552 new_scene->set_filename(String());
3553 }
3554
3555 new_scene->set_scene_instance_state(Ref<SceneState>());
3556
3557 set_edited_scene(new_scene);
3558 _get_scene_metadata(p_scene);
3559
3560 saved_version = editor_data.get_undo_redo().get_version();
3561 _update_title();
3562 _update_scene_tabs();
3563 _add_to_recent_scenes(lpath);
3564
3565 if (editor_folding.has_folding_data(lpath)) {
3566 editor_folding.load_scene_folding(new_scene, lpath);
3567 } else if (EDITOR_GET("interface/inspector/auto_unfold_foreign_scenes")) {
3568 editor_folding.unfold_scene(new_scene);
3569 editor_folding.save_scene_folding(new_scene, lpath);
3570 }
3571
3572 prev_scene->set_disabled(previous_scenes.size() == 0);
3573 opening_prev = false;
3574 scene_tree_dock->set_selected(new_scene);
3575
3576 ScriptEditor::get_singleton()->get_debugger()->update_live_edit_root();
3577
3578 push_item(new_scene);
3579
3580 if (!restoring_scenes) {
3581 save_layout();
3582 }
3583
3584 return OK;
3585 }
3586
open_request(const String & p_path)3587 void EditorNode::open_request(const String &p_path) {
3588
3589 if (!opening_prev) {
3590 List<String>::Element *prev_scene = previous_scenes.find(p_path);
3591 if (prev_scene != NULL) {
3592 prev_scene->erase();
3593 }
3594 }
3595
3596 load_scene(p_path); // as it will be opened in separate tab
3597 }
3598
request_instance_scene(const String & p_path)3599 void EditorNode::request_instance_scene(const String &p_path) {
3600
3601 scene_tree_dock->instance(p_path);
3602 }
3603
request_instance_scenes(const Vector<String> & p_files)3604 void EditorNode::request_instance_scenes(const Vector<String> &p_files) {
3605
3606 scene_tree_dock->instance_scenes(p_files);
3607 }
3608
get_import_dock()3609 ImportDock *EditorNode::get_import_dock() {
3610 return import_dock;
3611 }
3612
get_filesystem_dock()3613 FileSystemDock *EditorNode::get_filesystem_dock() {
3614
3615 return filesystem_dock;
3616 }
get_scene_tree_dock()3617 SceneTreeDock *EditorNode::get_scene_tree_dock() {
3618
3619 return scene_tree_dock;
3620 }
get_inspector_dock()3621 InspectorDock *EditorNode::get_inspector_dock() {
3622
3623 return inspector_dock;
3624 }
3625
_inherit_request(String p_file)3626 void EditorNode::_inherit_request(String p_file) {
3627
3628 current_option = FILE_NEW_INHERITED_SCENE;
3629 _dialog_action(p_file);
3630 }
3631
_instance_request(const Vector<String> & p_files)3632 void EditorNode::_instance_request(const Vector<String> &p_files) {
3633
3634 request_instance_scenes(p_files);
3635 }
3636
_close_messages()3637 void EditorNode::_close_messages() {
3638
3639 old_split_ofs = center_split->get_split_offset();
3640 center_split->set_split_offset(0);
3641 }
3642
_show_messages()3643 void EditorNode::_show_messages() {
3644
3645 center_split->set_split_offset(old_split_ofs);
3646 }
3647
_add_to_recent_scenes(const String & p_scene)3648 void EditorNode::_add_to_recent_scenes(const String &p_scene) {
3649
3650 Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scenes", Array());
3651 if (rc.find(p_scene) != -1)
3652 rc.erase(p_scene);
3653 rc.push_front(p_scene);
3654 if (rc.size() > 10)
3655 rc.resize(10);
3656
3657 EditorSettings::get_singleton()->set_project_metadata("recent_files", "scenes", rc);
3658 _update_recent_scenes();
3659 }
3660
_open_recent_scene(int p_idx)3661 void EditorNode::_open_recent_scene(int p_idx) {
3662
3663 if (p_idx == recent_scenes->get_item_count() - 1) {
3664
3665 EditorSettings::get_singleton()->set_project_metadata("recent_files", "scenes", Array());
3666 call_deferred("_update_recent_scenes");
3667 } else {
3668
3669 Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scenes", Array());
3670 ERR_FAIL_INDEX(p_idx, rc.size());
3671
3672 if (load_scene(rc[p_idx]) != OK) {
3673
3674 rc.remove(p_idx);
3675 EditorSettings::get_singleton()->set_project_metadata("recent_files", "scenes", rc);
3676 _update_recent_scenes();
3677 }
3678 }
3679 }
3680
_update_recent_scenes()3681 void EditorNode::_update_recent_scenes() {
3682
3683 Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scenes", Array());
3684 recent_scenes->clear();
3685
3686 String path;
3687 for (int i = 0; i < rc.size(); i++) {
3688
3689 path = rc[i];
3690 recent_scenes->add_item(path.replace("res://", ""), i);
3691 }
3692
3693 recent_scenes->add_separator();
3694 recent_scenes->add_shortcut(ED_SHORTCUT("editor/clear_recent", TTR("Clear Recent Scenes")));
3695 recent_scenes->set_as_minsize();
3696 }
3697
_quick_opened()3698 void EditorNode::_quick_opened() {
3699
3700 Vector<String> files = quick_open->get_selected_files();
3701
3702 bool open_scene_dialog = quick_open->get_base_type() == "PackedScene";
3703 for (int i = 0; i < files.size(); i++) {
3704 String res_path = files[i];
3705
3706 List<String> scene_extensions;
3707 ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions);
3708
3709 if (open_scene_dialog || scene_extensions.find(files[i].get_extension())) {
3710 open_request(res_path);
3711 } else {
3712 load_resource(res_path);
3713 }
3714 }
3715 }
3716
_quick_run()3717 void EditorNode::_quick_run() {
3718
3719 _run(false, quick_run->get_selected());
3720 }
3721
notify_child_process_exited()3722 void EditorNode::notify_child_process_exited() {
3723
3724 _menu_option_confirm(RUN_STOP, false);
3725 stop_button->set_pressed(false);
3726 editor_run.stop();
3727 }
3728
add_io_error(const String & p_error)3729 void EditorNode::add_io_error(const String &p_error) {
3730 _load_error_notify(singleton, p_error);
3731 }
3732
_load_error_notify(void * p_ud,const String & p_text)3733 void EditorNode::_load_error_notify(void *p_ud, const String &p_text) {
3734
3735 EditorNode *en = (EditorNode *)p_ud;
3736 en->load_errors->add_image(en->gui_base->get_icon("Error", "EditorIcons"));
3737 en->load_errors->add_text(p_text + "\n");
3738 en->load_error_dialog->popup_centered_ratio(0.5);
3739 }
3740
_find_scene_in_use(Node * p_node,const String & p_path) const3741 bool EditorNode::_find_scene_in_use(Node *p_node, const String &p_path) const {
3742
3743 if (p_node->get_filename() == p_path) {
3744 return true;
3745 }
3746
3747 for (int i = 0; i < p_node->get_child_count(); i++) {
3748
3749 if (_find_scene_in_use(p_node->get_child(i), p_path)) {
3750 return true;
3751 }
3752 }
3753
3754 return false;
3755 }
3756
is_scene_in_use(const String & p_path)3757 bool EditorNode::is_scene_in_use(const String &p_path) {
3758
3759 Node *es = get_edited_scene();
3760 if (es)
3761 return _find_scene_in_use(es, p_path);
3762 return false;
3763 }
3764
register_editor_types()3765 void EditorNode::register_editor_types() {
3766
3767 ResourceLoader::set_timestamp_on_load(true);
3768 ResourceSaver::set_timestamp_on_save(true);
3769
3770 ClassDB::register_class<EditorPlugin>();
3771 ClassDB::register_class<EditorImportPlugin>();
3772 ClassDB::register_class<EditorScript>();
3773 ClassDB::register_class<EditorSelection>();
3774 ClassDB::register_class<EditorFileDialog>();
3775 ClassDB::register_virtual_class<EditorSettings>();
3776 ClassDB::register_class<EditorSpatialGizmo>();
3777 ClassDB::register_class<EditorSpatialGizmoPlugin>();
3778 ClassDB::register_virtual_class<EditorResourcePreview>();
3779 ClassDB::register_class<EditorResourcePreviewGenerator>();
3780 ClassDB::register_virtual_class<EditorFileSystem>();
3781 ClassDB::register_class<EditorFileSystemDirectory>();
3782 ClassDB::register_class<EditorVCSInterface>();
3783 ClassDB::register_virtual_class<ScriptEditor>();
3784 ClassDB::register_virtual_class<EditorInterface>();
3785 ClassDB::register_class<EditorExportPlugin>();
3786 ClassDB::register_class<EditorResourceConversionPlugin>();
3787 ClassDB::register_class<EditorSceneImporter>();
3788 ClassDB::register_class<EditorInspector>();
3789 ClassDB::register_class<EditorInspectorPlugin>();
3790 ClassDB::register_class<EditorProperty>();
3791 ClassDB::register_class<AnimationTrackEditPlugin>();
3792 ClassDB::register_class<ScriptCreateDialog>();
3793 ClassDB::register_class<EditorFeatureProfile>();
3794 ClassDB::register_class<EditorSpinSlider>();
3795 ClassDB::register_virtual_class<FileSystemDock>();
3796
3797 // FIXME: Is this stuff obsolete, or should it be ported to new APIs?
3798 ClassDB::register_class<EditorScenePostImport>();
3799 //ClassDB::register_type<EditorImportExport>();
3800 }
3801
unregister_editor_types()3802 void EditorNode::unregister_editor_types() {
3803
3804 _init_callbacks.clear();
3805 }
3806
stop_child_process()3807 void EditorNode::stop_child_process() {
3808
3809 _menu_option_confirm(RUN_STOP, false);
3810 }
3811
get_object_custom_type_base(const Object * p_object) const3812 Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) const {
3813 ERR_FAIL_COND_V(!p_object, NULL);
3814
3815 Ref<Script> script = p_object->get_script();
3816
3817 if (script.is_valid()) {
3818 // Uncommenting would break things! Consider adding a parameter if you need it.
3819 // StringName name = EditorNode::get_editor_data().script_class_get_name(base_script->get_path());
3820 // if (name != StringName())
3821 // return name;
3822
3823 // should probably be deprecated in 4.x
3824 StringName base = script->get_instance_base_type();
3825 if (base != StringName() && EditorNode::get_editor_data().get_custom_types().has(base)) {
3826 const Vector<EditorData::CustomType> &types = EditorNode::get_editor_data().get_custom_types()[base];
3827
3828 Ref<Script> base_script = script;
3829 while (base_script.is_valid()) {
3830 for (int i = 0; i < types.size(); ++i) {
3831 if (types[i].script == base_script) {
3832 return types[i].script;
3833 }
3834 }
3835 base_script = base_script->get_base_script();
3836 }
3837 }
3838 }
3839
3840 return NULL;
3841 }
3842
get_object_custom_type_name(const Object * p_object) const3843 StringName EditorNode::get_object_custom_type_name(const Object *p_object) const {
3844 ERR_FAIL_COND_V(!p_object, StringName());
3845
3846 Ref<Script> script = p_object->get_script();
3847 if (script.is_null() && p_object->is_class("Script")) {
3848 script = p_object;
3849 }
3850
3851 if (script.is_valid()) {
3852 Ref<Script> base_script = script;
3853 while (base_script.is_valid()) {
3854 StringName name = EditorNode::get_editor_data().script_class_get_name(base_script->get_path());
3855 if (name != StringName())
3856 return name;
3857
3858 // should probably be deprecated in 4.x
3859 StringName base = base_script->get_instance_base_type();
3860 if (base != StringName() && EditorNode::get_editor_data().get_custom_types().has(base)) {
3861 const Vector<EditorData::CustomType> &types = EditorNode::get_editor_data().get_custom_types()[base];
3862 for (int i = 0; i < types.size(); ++i) {
3863 if (types[i].script == base_script) {
3864 return types[i].name;
3865 }
3866 }
3867 }
3868 base_script = base_script->get_base_script();
3869 }
3870 }
3871
3872 return StringName();
3873 }
3874
_load_custom_class_icon(const String & p_path) const3875 Ref<ImageTexture> EditorNode::_load_custom_class_icon(const String &p_path) const {
3876 if (p_path.length()) {
3877 Ref<Image> img = memnew(Image);
3878 Error err = ImageLoader::load_image(p_path, img);
3879 if (err == OK) {
3880 Ref<ImageTexture> icon = memnew(ImageTexture);
3881 img->resize(16 * EDSCALE, 16 * EDSCALE, Image::INTERPOLATE_LANCZOS);
3882 icon->create_from_image(img);
3883 return icon;
3884 }
3885 }
3886 return NULL;
3887 }
3888
get_object_icon(const Object * p_object,const String & p_fallback) const3889 Ref<Texture> EditorNode::get_object_icon(const Object *p_object, const String &p_fallback) const {
3890 ERR_FAIL_COND_V(!p_object || !gui_base, NULL);
3891
3892 Ref<Script> script = p_object->get_script();
3893 if (script.is_null() && p_object->is_class("Script")) {
3894 script = p_object;
3895 }
3896
3897 if (script.is_valid()) {
3898 Ref<Script> base_script = script;
3899 while (base_script.is_valid()) {
3900 StringName name = EditorNode::get_editor_data().script_class_get_name(base_script->get_path());
3901 String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name);
3902 Ref<ImageTexture> icon = _load_custom_class_icon(icon_path);
3903 if (icon.is_valid()) {
3904 return icon;
3905 }
3906
3907 // should probably be deprecated in 4.x
3908 StringName base = base_script->get_instance_base_type();
3909 if (base != StringName() && EditorNode::get_editor_data().get_custom_types().has(base)) {
3910 const Vector<EditorData::CustomType> &types = EditorNode::get_editor_data().get_custom_types()[base];
3911 for (int i = 0; i < types.size(); ++i) {
3912 if (types[i].script == base_script && types[i].icon.is_valid()) {
3913 return types[i].icon;
3914 }
3915 }
3916 }
3917 base_script = base_script->get_base_script();
3918 }
3919 }
3920
3921 // should probably be deprecated in 4.x
3922 if (p_object->has_meta("_editor_icon"))
3923 return p_object->get_meta("_editor_icon");
3924
3925 if (gui_base->has_icon(p_object->get_class(), "EditorIcons"))
3926 return gui_base->get_icon(p_object->get_class(), "EditorIcons");
3927
3928 if (p_fallback.length())
3929 return gui_base->get_icon(p_fallback, "EditorIcons");
3930
3931 return NULL;
3932 }
3933
get_class_icon(const String & p_class,const String & p_fallback) const3934 Ref<Texture> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) const {
3935 ERR_FAIL_COND_V_MSG(p_class.empty(), NULL, "Class name cannot be empty.");
3936
3937 if (ScriptServer::is_global_class(p_class)) {
3938 Ref<ImageTexture> icon;
3939 Ref<Script> script = EditorNode::get_editor_data().script_class_load_script(p_class);
3940 StringName name = p_class;
3941
3942 while (script.is_valid()) {
3943 name = EditorNode::get_editor_data().script_class_get_name(script->get_path());
3944 String current_icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name);
3945 icon = _load_custom_class_icon(current_icon_path);
3946 if (icon.is_valid()) {
3947 return icon;
3948 }
3949 script = script->get_base_script();
3950 }
3951
3952 if (icon.is_null()) {
3953 icon = gui_base->get_icon(ScriptServer::get_global_class_base(name), "EditorIcons");
3954 }
3955
3956 return icon;
3957 }
3958
3959 const Map<String, Vector<EditorData::CustomType> > &p_map = EditorNode::get_editor_data().get_custom_types();
3960 for (const Map<String, Vector<EditorData::CustomType> >::Element *E = p_map.front(); E; E = E->next()) {
3961 const Vector<EditorData::CustomType> &ct = E->value();
3962 for (int i = 0; i < ct.size(); ++i) {
3963 if (ct[i].name == p_class) {
3964 if (ct[i].icon.is_valid()) {
3965 return ct[i].icon;
3966 }
3967 }
3968 }
3969 }
3970
3971 if (gui_base->has_icon(p_class, "EditorIcons")) {
3972 return gui_base->get_icon(p_class, "EditorIcons");
3973 }
3974
3975 if (p_fallback.length() && gui_base->has_icon(p_fallback, "EditorIcons")) {
3976 return gui_base->get_icon(p_fallback, "EditorIcons");
3977 }
3978
3979 return NULL;
3980 }
3981
progress_add_task(const String & p_task,const String & p_label,int p_steps,bool p_can_cancel)3982 void EditorNode::progress_add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
3983
3984 if (singleton->cmdline_export_mode) {
3985 print_line(p_task + ": begin: " + p_label + " steps: " + itos(p_steps));
3986 } else {
3987 singleton->progress_dialog->add_task(p_task, p_label, p_steps, p_can_cancel);
3988 }
3989 }
3990
progress_task_step(const String & p_task,const String & p_state,int p_step,bool p_force_refresh)3991 bool EditorNode::progress_task_step(const String &p_task, const String &p_state, int p_step, bool p_force_refresh) {
3992
3993 if (singleton->cmdline_export_mode) {
3994 print_line("\t" + p_task + ": step " + itos(p_step) + ": " + p_state);
3995 return false;
3996 } else {
3997
3998 return singleton->progress_dialog->task_step(p_task, p_state, p_step, p_force_refresh);
3999 }
4000 }
4001
progress_end_task(const String & p_task)4002 void EditorNode::progress_end_task(const String &p_task) {
4003
4004 if (singleton->cmdline_export_mode) {
4005 print_line(p_task + ": end");
4006 } else {
4007 singleton->progress_dialog->end_task(p_task);
4008 }
4009 }
4010
progress_add_task_bg(const String & p_task,const String & p_label,int p_steps)4011 void EditorNode::progress_add_task_bg(const String &p_task, const String &p_label, int p_steps) {
4012
4013 singleton->progress_hb->add_task(p_task, p_label, p_steps);
4014 }
4015
progress_task_step_bg(const String & p_task,int p_step)4016 void EditorNode::progress_task_step_bg(const String &p_task, int p_step) {
4017
4018 singleton->progress_hb->task_step(p_task, p_step);
4019 }
4020
progress_end_task_bg(const String & p_task)4021 void EditorNode::progress_end_task_bg(const String &p_task) {
4022
4023 singleton->progress_hb->end_task(p_task);
4024 }
4025
_file_dialog_get_icon(const String & p_path)4026 Ref<Texture> EditorNode::_file_dialog_get_icon(const String &p_path) {
4027
4028 EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->get_filesystem_path(p_path.get_base_dir());
4029 if (efsd) {
4030
4031 String file = p_path.get_file();
4032 for (int i = 0; i < efsd->get_file_count(); i++) {
4033 if (efsd->get_file(i) == file) {
4034
4035 String type = efsd->get_file_type(i);
4036
4037 if (singleton->icon_type_cache.has(type)) {
4038 return singleton->icon_type_cache[type];
4039 } else {
4040 return singleton->icon_type_cache["Object"];
4041 }
4042 }
4043 }
4044 }
4045
4046 return singleton->icon_type_cache["Object"];
4047 }
4048
_build_icon_type_cache()4049 void EditorNode::_build_icon_type_cache() {
4050
4051 List<StringName> tl;
4052 StringName ei = "EditorIcons";
4053 theme_base->get_theme()->get_icon_list(ei, &tl);
4054 for (List<StringName>::Element *E = tl.front(); E; E = E->next()) {
4055
4056 if (!ClassDB::class_exists(E->get()))
4057 continue;
4058 icon_type_cache[E->get()] = theme_base->get_theme()->get_icon(E->get(), ei);
4059 }
4060 }
4061
_file_dialog_register(FileDialog * p_dialog)4062 void EditorNode::_file_dialog_register(FileDialog *p_dialog) {
4063
4064 singleton->file_dialogs.insert(p_dialog);
4065 }
4066
_file_dialog_unregister(FileDialog * p_dialog)4067 void EditorNode::_file_dialog_unregister(FileDialog *p_dialog) {
4068
4069 singleton->file_dialogs.erase(p_dialog);
4070 }
4071
_editor_file_dialog_register(EditorFileDialog * p_dialog)4072 void EditorNode::_editor_file_dialog_register(EditorFileDialog *p_dialog) {
4073
4074 singleton->editor_file_dialogs.insert(p_dialog);
4075 }
4076
_editor_file_dialog_unregister(EditorFileDialog * p_dialog)4077 void EditorNode::_editor_file_dialog_unregister(EditorFileDialog *p_dialog) {
4078
4079 singleton->editor_file_dialogs.erase(p_dialog);
4080 }
4081
4082 Vector<EditorNodeInitCallback> EditorNode::_init_callbacks;
4083
export_preset(const String & p_preset,const String & p_path,bool p_debug,bool p_pack_only)4084 Error EditorNode::export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only) {
4085
4086 export_defer.preset = p_preset;
4087 export_defer.path = p_path;
4088 export_defer.debug = p_debug;
4089 export_defer.pack_only = p_pack_only;
4090 cmdline_export_mode = true;
4091 return OK;
4092 }
4093
show_accept(const String & p_text,const String & p_title)4094 void EditorNode::show_accept(const String &p_text, const String &p_title) {
4095 current_option = -1;
4096 accept->get_ok()->set_text(p_title);
4097 accept->set_text(p_text);
4098 accept->popup_centered_minsize();
4099 }
4100
show_warning(const String & p_text,const String & p_title)4101 void EditorNode::show_warning(const String &p_text, const String &p_title) {
4102
4103 if (warning->is_inside_tree()) {
4104 warning->set_text(p_text);
4105 warning->set_title(p_title);
4106 warning->popup_centered_minsize();
4107 } else {
4108 WARN_PRINTS(p_title + " " + p_text);
4109 }
4110 }
4111
_copy_warning(const String & p_str)4112 void EditorNode::_copy_warning(const String &p_str) {
4113
4114 OS::get_singleton()->set_clipboard(warning->get_text());
4115 }
4116
_dock_select_input(const Ref<InputEvent> & p_input)4117 void EditorNode::_dock_select_input(const Ref<InputEvent> &p_input) {
4118
4119 Ref<InputEventMouse> me = p_input;
4120
4121 if (me.is_valid()) {
4122
4123 Vector2 point = me->get_position();
4124
4125 int nrect = -1;
4126 for (int i = 0; i < DOCK_SLOT_MAX; i++) {
4127 if (dock_select_rect[i].has_point(point)) {
4128 nrect = i;
4129 break;
4130 }
4131 }
4132
4133 if (nrect != dock_select_rect_over) {
4134 dock_select->update();
4135 dock_select_rect_over = nrect;
4136 }
4137
4138 if (nrect == -1)
4139 return;
4140
4141 Ref<InputEventMouseButton> mb = me;
4142
4143 if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed() && dock_popup_selected != nrect) {
4144 Control *dock = dock_slot[dock_popup_selected]->get_current_tab_control();
4145 if (dock) {
4146 dock_slot[dock_popup_selected]->remove_child(dock);
4147 }
4148 if (dock_slot[dock_popup_selected]->get_tab_count() == 0) {
4149 dock_slot[dock_popup_selected]->hide();
4150
4151 } else {
4152
4153 dock_slot[dock_popup_selected]->set_current_tab(0);
4154 }
4155
4156 dock_slot[nrect]->add_child(dock);
4157 dock_popup_selected = nrect;
4158 dock_slot[nrect]->set_current_tab(dock_slot[nrect]->get_tab_count() - 1);
4159 dock_slot[nrect]->show();
4160 dock_select->update();
4161
4162 for (int i = 0; i < vsplits.size(); i++) {
4163 bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count();
4164 if (in_use)
4165 vsplits[i]->show();
4166 else
4167 vsplits[i]->hide();
4168 }
4169
4170 if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible())
4171 right_hsplit->show();
4172 else
4173 right_hsplit->hide();
4174
4175 _edit_current();
4176 _save_docks();
4177 }
4178 }
4179 }
4180
_dock_popup_exit()4181 void EditorNode::_dock_popup_exit() {
4182
4183 dock_select_rect_over = -1;
4184 dock_select->update();
4185 }
4186
_dock_pre_popup(int p_which)4187 void EditorNode::_dock_pre_popup(int p_which) {
4188
4189 dock_popup_selected = p_which;
4190 }
4191
_dock_move_left()4192 void EditorNode::_dock_move_left() {
4193
4194 if (dock_popup_selected < 0 || dock_popup_selected >= DOCK_SLOT_MAX)
4195 return;
4196 Control *current = dock_slot[dock_popup_selected]->get_tab_control(dock_slot[dock_popup_selected]->get_current_tab());
4197 Control *prev = dock_slot[dock_popup_selected]->get_tab_control(dock_slot[dock_popup_selected]->get_current_tab() - 1);
4198 if (!current || !prev)
4199 return;
4200 dock_slot[dock_popup_selected]->move_child(current, prev->get_index());
4201 dock_slot[dock_popup_selected]->set_current_tab(dock_slot[dock_popup_selected]->get_current_tab() - 1);
4202 dock_select->update();
4203 _edit_current();
4204 _save_docks();
4205 }
4206
_dock_move_right()4207 void EditorNode::_dock_move_right() {
4208
4209 Control *current = dock_slot[dock_popup_selected]->get_tab_control(dock_slot[dock_popup_selected]->get_current_tab());
4210 Control *next = dock_slot[dock_popup_selected]->get_tab_control(dock_slot[dock_popup_selected]->get_current_tab() + 1);
4211 if (!current || !next)
4212 return;
4213 dock_slot[dock_popup_selected]->move_child(next, current->get_index());
4214 dock_slot[dock_popup_selected]->set_current_tab(dock_slot[dock_popup_selected]->get_current_tab() + 1);
4215 dock_select->update();
4216 _edit_current();
4217 _save_docks();
4218 }
4219
_dock_select_draw()4220 void EditorNode::_dock_select_draw() {
4221 Size2 s = dock_select->get_size();
4222 s.y /= 2.0;
4223 s.x /= 6.0;
4224
4225 Color used = Color(0.6, 0.6, 0.6, 0.8);
4226 Color used_selected = Color(0.8, 0.8, 0.8, 0.8);
4227 Color tab_selected = theme_base->get_color("mono_color", "Editor");
4228 Color unused = used;
4229 unused.a = 0.4;
4230 Color unusable = unused;
4231 unusable.a = 0.1;
4232
4233 Rect2 unr(s.x * 2, 0, s.x * 2, s.y * 2);
4234 unr.position += Vector2(2, 5);
4235 unr.size -= Vector2(4, 7);
4236
4237 dock_select->draw_rect(unr, unusable);
4238
4239 dock_tab_move_left->set_disabled(true);
4240 dock_tab_move_right->set_disabled(true);
4241
4242 if (dock_popup_selected != -1 && dock_slot[dock_popup_selected]->get_tab_count()) {
4243
4244 dock_tab_move_left->set_disabled(dock_slot[dock_popup_selected]->get_current_tab() == 0);
4245 dock_tab_move_right->set_disabled(dock_slot[dock_popup_selected]->get_current_tab() >= dock_slot[dock_popup_selected]->get_tab_count() - 1);
4246 }
4247
4248 for (int i = 0; i < DOCK_SLOT_MAX; i++) {
4249
4250 Vector2 ofs;
4251
4252 switch (i) {
4253 case DOCK_SLOT_LEFT_UL: {
4254
4255 } break;
4256 case DOCK_SLOT_LEFT_BL: {
4257 ofs.y += s.y;
4258 } break;
4259 case DOCK_SLOT_LEFT_UR: {
4260 ofs.x += s.x;
4261 } break;
4262 case DOCK_SLOT_LEFT_BR: {
4263 ofs += s;
4264 } break;
4265 case DOCK_SLOT_RIGHT_UL: {
4266 ofs.x += s.x * 4;
4267 } break;
4268 case DOCK_SLOT_RIGHT_BL: {
4269 ofs.x += s.x * 4;
4270 ofs.y += s.y;
4271
4272 } break;
4273 case DOCK_SLOT_RIGHT_UR: {
4274 ofs.x += s.x * 4;
4275 ofs.x += s.x;
4276
4277 } break;
4278 case DOCK_SLOT_RIGHT_BR: {
4279 ofs.x += s.x * 4;
4280 ofs += s;
4281
4282 } break;
4283 }
4284
4285 Rect2 r(ofs, s);
4286 dock_select_rect[i] = r;
4287 r.position += Vector2(2, 5);
4288 r.size -= Vector2(4, 7);
4289
4290 if (i == dock_select_rect_over) {
4291 dock_select->draw_rect(r, used_selected);
4292 } else if (dock_slot[i]->get_child_count() == 0) {
4293 dock_select->draw_rect(r, unused);
4294 } else {
4295
4296 dock_select->draw_rect(r, used);
4297 }
4298
4299 for (int j = 0; j < MIN(3, dock_slot[i]->get_child_count()); j++) {
4300 int xofs = (r.size.width / 3) * j;
4301 Color c = used;
4302 if (i == dock_popup_selected && (dock_slot[i]->get_current_tab() > 3 || dock_slot[i]->get_current_tab() == j))
4303 c = tab_selected;
4304 dock_select->draw_rect(Rect2(2 + ofs.x + xofs, ofs.y, r.size.width / 3 - 1, 3), c);
4305 }
4306 }
4307 }
4308
_save_docks()4309 void EditorNode::_save_docks() {
4310
4311 if (waiting_for_first_scan) {
4312 return; //scanning, do not touch docks
4313 }
4314 Ref<ConfigFile> config;
4315 config.instance();
4316
4317 _save_docks_to_config(config, "docks");
4318 _save_open_scenes_to_config(config, "EditorNode");
4319 editor_data.get_plugin_window_layout(config);
4320
4321 config->save(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg"));
4322 }
4323
_save_docks_to_config(Ref<ConfigFile> p_layout,const String & p_section)4324 void EditorNode::_save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section) {
4325
4326 for (int i = 0; i < DOCK_SLOT_MAX; i++) {
4327 String names;
4328 for (int j = 0; j < dock_slot[i]->get_tab_count(); j++) {
4329 String name = dock_slot[i]->get_tab_control(j)->get_name();
4330 if (names != "")
4331 names += ",";
4332 names += name;
4333 }
4334
4335 if (names != "") {
4336 p_layout->set_value(p_section, "dock_" + itos(i + 1), names);
4337 }
4338 }
4339
4340 p_layout->set_value(p_section, "dock_filesystem_split", filesystem_dock->get_split_offset());
4341 p_layout->set_value(p_section, "dock_filesystem_display_mode", filesystem_dock->get_display_mode());
4342 p_layout->set_value(p_section, "dock_filesystem_file_list_display_mode", filesystem_dock->get_file_list_display_mode());
4343
4344 for (int i = 0; i < vsplits.size(); i++) {
4345
4346 if (vsplits[i]->is_visible_in_tree()) {
4347 p_layout->set_value(p_section, "dock_split_" + itos(i + 1), vsplits[i]->get_split_offset());
4348 }
4349 }
4350
4351 for (int i = 0; i < hsplits.size(); i++) {
4352
4353 p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), hsplits[i]->get_split_offset());
4354 }
4355 }
4356
_save_open_scenes_to_config(Ref<ConfigFile> p_layout,const String & p_section)4357 void EditorNode::_save_open_scenes_to_config(Ref<ConfigFile> p_layout, const String &p_section) {
4358 Array scenes;
4359 for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
4360 String path = editor_data.get_scene_path(i);
4361 if (path == "") {
4362 continue;
4363 }
4364 scenes.push_back(path);
4365 }
4366 p_layout->set_value(p_section, "open_scenes", scenes);
4367 }
4368
save_layout()4369 void EditorNode::save_layout() {
4370
4371 dock_drag_timer->start();
4372 }
4373
_dock_split_dragged(int ofs)4374 void EditorNode::_dock_split_dragged(int ofs) {
4375
4376 dock_drag_timer->start();
4377 }
4378
_load_docks()4379 void EditorNode::_load_docks() {
4380
4381 Ref<ConfigFile> config;
4382 config.instance();
4383 Error err = config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg"));
4384 if (err != OK) {
4385 //no config
4386 if (overridden_default_layout >= 0) {
4387 _layout_menu_option(overridden_default_layout);
4388 }
4389 return;
4390 }
4391
4392 _load_docks_from_config(config, "docks");
4393 _load_open_scenes_from_config(config, "EditorNode");
4394
4395 editor_data.set_plugin_window_layout(config);
4396 }
4397
_update_dock_slots_visibility()4398 void EditorNode::_update_dock_slots_visibility() {
4399
4400 if (!docks_visible) {
4401
4402 for (int i = 0; i < DOCK_SLOT_MAX; i++) {
4403 dock_slot[i]->hide();
4404 }
4405
4406 for (int i = 0; i < vsplits.size(); i++) {
4407 vsplits[i]->hide();
4408 }
4409
4410 right_hsplit->hide();
4411 } else {
4412 for (int i = 0; i < DOCK_SLOT_MAX; i++) {
4413
4414 int tabs_visible = 0;
4415 for (int j = 0; j < dock_slot[i]->get_tab_count(); j++) {
4416 if (!dock_slot[i]->get_tab_hidden(j)) {
4417 tabs_visible++;
4418 }
4419 }
4420 if (tabs_visible)
4421 dock_slot[i]->show();
4422 else
4423 dock_slot[i]->hide();
4424 }
4425
4426 for (int i = 0; i < vsplits.size(); i++) {
4427 bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count();
4428 if (in_use)
4429 vsplits[i]->show();
4430 else
4431 vsplits[i]->hide();
4432 }
4433
4434 for (int i = 0; i < DOCK_SLOT_MAX; i++) {
4435
4436 if (dock_slot[i]->is_visible() && dock_slot[i]->get_tab_count()) {
4437 dock_slot[i]->set_current_tab(0);
4438 }
4439 }
4440
4441 if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible())
4442 right_hsplit->show();
4443 else
4444 right_hsplit->hide();
4445 }
4446 }
4447
_dock_tab_changed(int p_tab)4448 void EditorNode::_dock_tab_changed(int p_tab) {
4449
4450 // update visibility but don't set current tab
4451
4452 if (!docks_visible) {
4453
4454 for (int i = 0; i < DOCK_SLOT_MAX; i++) {
4455 dock_slot[i]->hide();
4456 }
4457
4458 for (int i = 0; i < vsplits.size(); i++) {
4459 vsplits[i]->hide();
4460 }
4461
4462 right_hsplit->hide();
4463 bottom_panel->hide();
4464 } else {
4465 for (int i = 0; i < DOCK_SLOT_MAX; i++) {
4466
4467 if (dock_slot[i]->get_tab_count())
4468 dock_slot[i]->show();
4469 else
4470 dock_slot[i]->hide();
4471 }
4472
4473 for (int i = 0; i < vsplits.size(); i++) {
4474 bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count();
4475 if (in_use)
4476 vsplits[i]->show();
4477 else
4478 vsplits[i]->hide();
4479 }
4480 bottom_panel->show();
4481
4482 if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible())
4483 right_hsplit->show();
4484 else
4485 right_hsplit->hide();
4486 }
4487 }
4488
_load_docks_from_config(Ref<ConfigFile> p_layout,const String & p_section)4489 void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
4490
4491 for (int i = 0; i < DOCK_SLOT_MAX; i++) {
4492
4493 if (!p_layout->has_section_key(p_section, "dock_" + itos(i + 1)))
4494 continue;
4495
4496 Vector<String> names = String(p_layout->get_value(p_section, "dock_" + itos(i + 1))).split(",");
4497
4498 for (int j = 0; j < names.size(); j++) {
4499
4500 String name = names[j];
4501 //find it, in a horribly inefficient way
4502 int atidx = -1;
4503 Control *node = NULL;
4504 for (int k = 0; k < DOCK_SLOT_MAX; k++) {
4505 if (!dock_slot[k]->has_node(name))
4506 continue;
4507 node = Object::cast_to<Control>(dock_slot[k]->get_node(name));
4508 if (!node)
4509 continue;
4510 atidx = k;
4511 break;
4512 }
4513 if (atidx == -1) //well, it's not anywhere
4514 continue;
4515
4516 if (atidx == i) {
4517 node->raise();
4518 continue;
4519 }
4520
4521 dock_slot[atidx]->remove_child(node);
4522
4523 if (dock_slot[atidx]->get_tab_count() == 0) {
4524 dock_slot[atidx]->hide();
4525 }
4526 dock_slot[i]->add_child(node);
4527 dock_slot[i]->show();
4528 }
4529 }
4530
4531 if (p_layout->has_section_key(p_section, "dock_filesystem_split")) {
4532 int fs_split_ofs = p_layout->get_value(p_section, "dock_filesystem_split");
4533 filesystem_dock->set_split_offset(fs_split_ofs);
4534 }
4535
4536 if (p_layout->has_section_key(p_section, "dock_filesystem_display_mode")) {
4537 FileSystemDock::DisplayMode dock_filesystem_display_mode = FileSystemDock::DisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_display_mode")));
4538 filesystem_dock->set_display_mode(dock_filesystem_display_mode);
4539 }
4540
4541 if (p_layout->has_section_key(p_section, "dock_filesystem_file_list_display_mode")) {
4542 FileSystemDock::FileListDisplayMode dock_filesystem_file_list_display_mode = FileSystemDock::FileListDisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_file_list_display_mode")));
4543 filesystem_dock->set_file_list_display_mode(dock_filesystem_file_list_display_mode);
4544 }
4545
4546 for (int i = 0; i < vsplits.size(); i++) {
4547
4548 if (!p_layout->has_section_key(p_section, "dock_split_" + itos(i + 1)))
4549 continue;
4550
4551 int ofs = p_layout->get_value(p_section, "dock_split_" + itos(i + 1));
4552 vsplits[i]->set_split_offset(ofs);
4553 }
4554
4555 for (int i = 0; i < hsplits.size(); i++) {
4556 if (!p_layout->has_section_key(p_section, "dock_hsplit_" + itos(i + 1)))
4557 continue;
4558 int ofs = p_layout->get_value(p_section, "dock_hsplit_" + itos(i + 1));
4559 hsplits[i]->set_split_offset(ofs);
4560 }
4561
4562 for (int i = 0; i < vsplits.size(); i++) {
4563 bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count();
4564 if (in_use)
4565 vsplits[i]->show();
4566 else
4567 vsplits[i]->hide();
4568 }
4569
4570 if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible())
4571 right_hsplit->show();
4572 else
4573 right_hsplit->hide();
4574
4575 for (int i = 0; i < DOCK_SLOT_MAX; i++) {
4576
4577 if (dock_slot[i]->is_visible() && dock_slot[i]->get_tab_count()) {
4578 dock_slot[i]->set_current_tab(0);
4579 }
4580 }
4581 }
4582
_load_open_scenes_from_config(Ref<ConfigFile> p_layout,const String & p_section)4583 void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
4584 if (!bool(EDITOR_GET("interface/scene_tabs/restore_scenes_on_load"))) {
4585 return;
4586 }
4587
4588 if (!p_layout->has_section(p_section) || !p_layout->has_section_key(p_section, "open_scenes")) {
4589 return;
4590 }
4591
4592 restoring_scenes = true;
4593
4594 Array scenes = p_layout->get_value(p_section, "open_scenes");
4595 for (int i = 0; i < scenes.size(); i++) {
4596 load_scene(scenes[i]);
4597 }
4598 save_layout();
4599
4600 restoring_scenes = false;
4601 }
4602
has_scenes_in_session()4603 bool EditorNode::has_scenes_in_session() {
4604 if (!bool(EDITOR_GET("interface/scene_tabs/restore_scenes_on_load"))) {
4605 return false;
4606 }
4607 Ref<ConfigFile> config;
4608 config.instance();
4609 Error err = config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg"));
4610 if (err != OK) {
4611 return false;
4612 }
4613 if (!config->has_section("EditorNode") || !config->has_section_key("EditorNode", "open_scenes")) {
4614 return false;
4615 }
4616 Array scenes = config->get_value("EditorNode", "open_scenes");
4617 return !scenes.empty();
4618 }
4619
ensure_main_scene(bool p_from_native)4620 bool EditorNode::ensure_main_scene(bool p_from_native) {
4621 pick_main_scene->set_meta("from_native", p_from_native); //whether from play button or native run
4622 String main_scene = GLOBAL_DEF("application/run/main_scene", "");
4623
4624 if (main_scene == "") {
4625
4626 current_option = -1;
4627 pick_main_scene->set_text(TTR("No main scene has ever been defined, select one?\nYou can change it later in \"Project Settings\" under the 'application' category."));
4628 pick_main_scene->popup_centered_minsize();
4629 return false;
4630 }
4631
4632 if (!FileAccess::exists(main_scene)) {
4633
4634 current_option = -1;
4635 pick_main_scene->set_text(vformat(TTR("Selected scene '%s' does not exist, select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene));
4636 pick_main_scene->popup_centered_minsize();
4637 return false;
4638 }
4639
4640 if (ResourceLoader::get_resource_type(main_scene) != "PackedScene") {
4641
4642 current_option = -1;
4643 pick_main_scene->set_text(vformat(TTR("Selected scene '%s' is not a scene file, select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene));
4644 pick_main_scene->popup_centered_minsize();
4645 return false;
4646 }
4647
4648 return true;
4649 }
4650
run_play()4651 void EditorNode::run_play() {
4652 _menu_option_confirm(RUN_STOP, true);
4653 _run(false);
4654 }
4655
run_play_current()4656 void EditorNode::run_play_current() {
4657 _save_default_environment();
4658 _menu_option_confirm(RUN_STOP, true);
4659 _run(true);
4660 }
4661
run_play_custom(const String & p_custom)4662 void EditorNode::run_play_custom(const String &p_custom) {
4663 _menu_option_confirm(RUN_STOP, true);
4664 _run(false, p_custom);
4665 }
4666
run_stop()4667 void EditorNode::run_stop() {
4668 _menu_option_confirm(RUN_STOP, false);
4669 }
4670
is_run_playing() const4671 bool EditorNode::is_run_playing() const {
4672 EditorRun::Status status = editor_run.get_status();
4673 return (status == EditorRun::STATUS_PLAY || status == EditorRun::STATUS_PAUSED);
4674 }
4675
get_run_playing_scene() const4676 String EditorNode::get_run_playing_scene() const {
4677 String run_filename = editor_run.get_running_scene();
4678 if (run_filename == "" && is_run_playing()) {
4679 run_filename = GLOBAL_DEF("application/run/main_scene", ""); // Must be the main scene then.
4680 }
4681
4682 return run_filename;
4683 }
4684
get_current_tab()4685 int EditorNode::get_current_tab() {
4686 return scene_tabs->get_current_tab();
4687 }
4688
set_current_tab(int p_tab)4689 void EditorNode::set_current_tab(int p_tab) {
4690 scene_tabs->set_current_tab(p_tab);
4691 }
4692
_update_layouts_menu()4693 void EditorNode::_update_layouts_menu() {
4694
4695 editor_layouts->clear();
4696 overridden_default_layout = -1;
4697
4698 editor_layouts->set_size(Vector2());
4699 editor_layouts->add_shortcut(ED_SHORTCUT("layout/save", TTR("Save Layout")), SETTINGS_LAYOUT_SAVE);
4700 editor_layouts->add_shortcut(ED_SHORTCUT("layout/delete", TTR("Delete Layout")), SETTINGS_LAYOUT_DELETE);
4701 editor_layouts->add_separator();
4702 editor_layouts->add_shortcut(ED_SHORTCUT("layout/default", TTR("Default")), SETTINGS_LAYOUT_DEFAULT);
4703
4704 Ref<ConfigFile> config;
4705 config.instance();
4706 Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config());
4707 if (err != OK) {
4708 return; //no config
4709 }
4710
4711 List<String> layouts;
4712 config.ptr()->get_sections(&layouts);
4713
4714 for (List<String>::Element *E = layouts.front(); E; E = E->next()) {
4715
4716 String layout = E->get();
4717
4718 if (layout == TTR("Default")) {
4719 editor_layouts->remove_item(editor_layouts->get_item_index(SETTINGS_LAYOUT_DEFAULT));
4720 overridden_default_layout = editor_layouts->get_item_count();
4721 }
4722
4723 editor_layouts->add_item(layout);
4724 }
4725 }
4726
_layout_menu_option(int p_id)4727 void EditorNode::_layout_menu_option(int p_id) {
4728
4729 switch (p_id) {
4730
4731 case SETTINGS_LAYOUT_SAVE: {
4732
4733 current_option = p_id;
4734 layout_dialog->set_title(TTR("Save Layout"));
4735 layout_dialog->get_ok()->set_text(TTR("Save"));
4736 layout_dialog->popup_centered();
4737 layout_dialog->set_name_line_enabled(true);
4738 } break;
4739 case SETTINGS_LAYOUT_DELETE: {
4740
4741 current_option = p_id;
4742 layout_dialog->set_title(TTR("Delete Layout"));
4743 layout_dialog->get_ok()->set_text(TTR("Delete"));
4744 layout_dialog->popup_centered();
4745 layout_dialog->set_name_line_enabled(false);
4746 } break;
4747 case SETTINGS_LAYOUT_DEFAULT: {
4748
4749 _load_docks_from_config(default_layout, "docks");
4750 _save_docks();
4751 } break;
4752 default: {
4753
4754 Ref<ConfigFile> config;
4755 config.instance();
4756 Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config());
4757 if (err != OK) {
4758 return; //no config
4759 }
4760
4761 _load_docks_from_config(config, editor_layouts->get_item_text(p_id));
4762 _save_docks();
4763 }
4764 }
4765 }
4766
_scene_tab_script_edited(int p_tab)4767 void EditorNode::_scene_tab_script_edited(int p_tab) {
4768
4769 Ref<Script> script = editor_data.get_scene_root_script(p_tab);
4770 if (script.is_valid())
4771 inspector_dock->edit_resource(script);
4772 }
4773
_scene_tab_closed(int p_tab,int option)4774 void EditorNode::_scene_tab_closed(int p_tab, int option) {
4775 current_option = option;
4776 tab_closing = p_tab;
4777 Node *scene = editor_data.get_edited_scene_root(p_tab);
4778 if (!scene) {
4779 _discard_changes();
4780 return;
4781 }
4782
4783 bool unsaved = (p_tab == editor_data.get_edited_scene()) ?
4784 saved_version != editor_data.get_undo_redo().get_version() :
4785 editor_data.get_scene_version(p_tab) != 0;
4786 if (unsaved) {
4787 save_confirmation->get_ok()->set_text(TTR("Save & Close"));
4788 save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), scene->get_filename() != "" ? scene->get_filename() : "unsaved scene"));
4789 save_confirmation->popup_centered_minsize();
4790 } else {
4791 _discard_changes();
4792 }
4793
4794 save_layout();
4795 _update_scene_tabs();
4796 }
4797
_scene_tab_hover(int p_tab)4798 void EditorNode::_scene_tab_hover(int p_tab) {
4799 if (!bool(EDITOR_GET("interface/scene_tabs/show_thumbnail_on_hover"))) {
4800 return;
4801 }
4802 int current_tab = scene_tabs->get_current_tab();
4803
4804 if (p_tab == current_tab || p_tab < 0) {
4805 tab_preview_panel->hide();
4806 } else {
4807 String path = editor_data.get_scene_path(p_tab);
4808 if (path != String()) {
4809 EditorResourcePreview::get_singleton()->queue_resource_preview(path, this, "_thumbnail_done", p_tab);
4810 }
4811 }
4812 }
4813
_scene_tab_exit()4814 void EditorNode::_scene_tab_exit() {
4815 tab_preview_panel->hide();
4816 }
4817
_scene_tab_input(const Ref<InputEvent> & p_input)4818 void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
4819 Ref<InputEventMouseButton> mb = p_input;
4820
4821 if (mb.is_valid()) {
4822
4823 if (scene_tabs->get_hovered_tab() >= 0) {
4824 if (mb->get_button_index() == BUTTON_MIDDLE && mb->is_pressed()) {
4825 _scene_tab_closed(scene_tabs->get_hovered_tab());
4826 }
4827 } else {
4828 if ((mb->get_button_index() == BUTTON_LEFT && mb->is_doubleclick()) || (mb->get_button_index() == BUTTON_MIDDLE && mb->is_pressed())) {
4829 _menu_option_confirm(FILE_NEW_SCENE, true);
4830 }
4831 }
4832 if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) {
4833
4834 // context menu
4835 scene_tabs_context_menu->clear();
4836 scene_tabs_context_menu->set_size(Size2(1, 1));
4837
4838 scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/new_scene"), FILE_NEW_SCENE);
4839 if (scene_tabs->get_hovered_tab() >= 0) {
4840 scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene"), FILE_SAVE_SCENE);
4841 scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene_as"), FILE_SAVE_AS_SCENE);
4842 }
4843 scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), FILE_SAVE_ALL_SCENES);
4844 if (scene_tabs->get_hovered_tab() >= 0) {
4845 scene_tabs_context_menu->add_separator();
4846 scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), FILE_SHOW_IN_FILESYSTEM);
4847 scene_tabs_context_menu->add_item(TTR("Play This Scene"), RUN_PLAY_SCENE);
4848
4849 scene_tabs_context_menu->add_separator();
4850 Ref<ShortCut> close_tab_sc = ED_GET_SHORTCUT("editor/close_scene");
4851 close_tab_sc->set_name(TTR("Close Tab"));
4852 scene_tabs_context_menu->add_shortcut(close_tab_sc, FILE_CLOSE);
4853 Ref<ShortCut> undo_close_tab_sc = ED_GET_SHORTCUT("editor/reopen_closed_scene");
4854 undo_close_tab_sc->set_name(TTR("Undo Close Tab"));
4855 scene_tabs_context_menu->add_shortcut(undo_close_tab_sc, FILE_OPEN_PREV);
4856 if (previous_scenes.empty()) {
4857 scene_tabs_context_menu->set_item_disabled(scene_tabs_context_menu->get_item_index(FILE_OPEN_PREV), true);
4858 }
4859 scene_tabs_context_menu->add_item(TTR("Close Other Tabs"), FILE_CLOSE_OTHERS);
4860 scene_tabs_context_menu->add_item(TTR("Close Tabs to the Right"), FILE_CLOSE_RIGHT);
4861 scene_tabs_context_menu->add_item(TTR("Close All Tabs"), FILE_CLOSE_ALL);
4862 }
4863 scene_tabs_context_menu->set_position(mb->get_global_position());
4864 scene_tabs_context_menu->popup();
4865 }
4866 }
4867 }
4868
_reposition_active_tab(int idx_to)4869 void EditorNode::_reposition_active_tab(int idx_to) {
4870 editor_data.move_edited_scene_to_index(idx_to);
4871 _update_scene_tabs();
4872 }
4873
_thumbnail_done(const String & p_path,const Ref<Texture> & p_preview,const Ref<Texture> & p_small_preview,const Variant & p_udata)4874 void EditorNode::_thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, const Variant &p_udata) {
4875 int p_tab = p_udata.operator signed int();
4876 if (p_preview.is_valid()) {
4877 Rect2 rect = scene_tabs->get_tab_rect(p_tab);
4878 rect.position += scene_tabs->get_global_position();
4879 tab_preview->set_texture(p_preview);
4880 tab_preview_panel->set_position(rect.position + Vector2(0, rect.size.height));
4881 tab_preview_panel->show();
4882 }
4883 }
4884
_scene_tab_changed(int p_tab)4885 void EditorNode::_scene_tab_changed(int p_tab) {
4886 tab_preview_panel->hide();
4887
4888 bool unsaved = (saved_version != editor_data.get_undo_redo().get_version());
4889
4890 if (p_tab == editor_data.get_edited_scene())
4891 return; //pointless
4892
4893 uint64_t next_scene_version = editor_data.get_scene_version(p_tab);
4894
4895 editor_data.get_undo_redo().create_action(TTR("Switch Scene Tab"));
4896 editor_data.get_undo_redo().add_do_method(this, "set_current_version", unsaved ? saved_version : 0);
4897 editor_data.get_undo_redo().add_do_method(this, "set_current_scene", p_tab);
4898 editor_data.get_undo_redo().add_do_method(this, "set_current_version", next_scene_version == 0 ? editor_data.get_undo_redo().get_version() + 1 : next_scene_version);
4899
4900 editor_data.get_undo_redo().add_undo_method(this, "set_current_version", next_scene_version);
4901 editor_data.get_undo_redo().add_undo_method(this, "set_current_scene", editor_data.get_edited_scene());
4902 editor_data.get_undo_redo().add_undo_method(this, "set_current_version", saved_version);
4903 editor_data.get_undo_redo().commit_action();
4904 }
4905
add_bottom_panel_item(String p_text,Control * p_item)4906 ToolButton *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) {
4907
4908 ToolButton *tb = memnew(ToolButton);
4909 tb->connect("toggled", this, "_bottom_panel_switch", varray(bottom_panel_items.size()));
4910 tb->set_text(p_text);
4911 tb->set_toggle_mode(true);
4912 tb->set_focus_mode(Control::FOCUS_NONE);
4913 bottom_panel_vb->add_child(p_item);
4914 bottom_panel_hb->raise();
4915 bottom_panel_hb_editors->add_child(tb);
4916 p_item->set_v_size_flags(Control::SIZE_EXPAND_FILL);
4917 p_item->hide();
4918 BottomPanelItem bpi;
4919 bpi.button = tb;
4920 bpi.control = p_item;
4921 bpi.name = p_text;
4922 bottom_panel_items.push_back(bpi);
4923
4924 return tb;
4925 }
4926
are_bottom_panels_hidden() const4927 bool EditorNode::are_bottom_panels_hidden() const {
4928
4929 for (int i = 0; i < bottom_panel_items.size(); i++) {
4930 if (bottom_panel_items[i].button->is_pressed())
4931 return false;
4932 }
4933
4934 return true;
4935 }
4936
hide_bottom_panel()4937 void EditorNode::hide_bottom_panel() {
4938
4939 for (int i = 0; i < bottom_panel_items.size(); i++) {
4940
4941 if (bottom_panel_items[i].control->is_visible()) {
4942 _bottom_panel_switch(false, i);
4943 break;
4944 }
4945 }
4946 }
4947
make_bottom_panel_item_visible(Control * p_item)4948 void EditorNode::make_bottom_panel_item_visible(Control *p_item) {
4949
4950 for (int i = 0; i < bottom_panel_items.size(); i++) {
4951
4952 if (bottom_panel_items[i].control == p_item) {
4953 _bottom_panel_switch(true, i);
4954 break;
4955 }
4956 }
4957 }
4958
raise_bottom_panel_item(Control * p_item)4959 void EditorNode::raise_bottom_panel_item(Control *p_item) {
4960
4961 for (int i = 0; i < bottom_panel_items.size(); i++) {
4962
4963 if (bottom_panel_items[i].control == p_item) {
4964 bottom_panel_items[i].button->raise();
4965 SWAP(bottom_panel_items.write[i], bottom_panel_items.write[bottom_panel_items.size() - 1]);
4966 break;
4967 }
4968 }
4969
4970 for (int i = 0; i < bottom_panel_items.size(); i++) {
4971 bottom_panel_items[i].button->disconnect("toggled", this, "_bottom_panel_switch");
4972 bottom_panel_items[i].button->connect("toggled", this, "_bottom_panel_switch", varray(i));
4973 }
4974 }
4975
remove_bottom_panel_item(Control * p_item)4976 void EditorNode::remove_bottom_panel_item(Control *p_item) {
4977
4978 for (int i = 0; i < bottom_panel_items.size(); i++) {
4979
4980 if (bottom_panel_items[i].control == p_item) {
4981 if (p_item->is_visible_in_tree()) {
4982 _bottom_panel_switch(false, i);
4983 }
4984 bottom_panel_vb->remove_child(bottom_panel_items[i].control);
4985 bottom_panel_hb_editors->remove_child(bottom_panel_items[i].button);
4986 memdelete(bottom_panel_items[i].button);
4987 bottom_panel_items.remove(i);
4988 break;
4989 }
4990 }
4991
4992 for (int i = 0; i < bottom_panel_items.size(); i++) {
4993 bottom_panel_items[i].button->disconnect("toggled", this, "_bottom_panel_switch");
4994 bottom_panel_items[i].button->connect("toggled", this, "_bottom_panel_switch", varray(i));
4995 }
4996 }
4997
_bottom_panel_switch(bool p_enable,int p_idx)4998 void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
4999
5000 ERR_FAIL_INDEX(p_idx, bottom_panel_items.size());
5001
5002 if (bottom_panel_items[p_idx].control->is_visible() == p_enable) {
5003 return;
5004 }
5005
5006 if (p_enable) {
5007 for (int i = 0; i < bottom_panel_items.size(); i++) {
5008
5009 bottom_panel_items[i].button->set_pressed(i == p_idx);
5010 bottom_panel_items[i].control->set_visible(i == p_idx);
5011 }
5012 if (ScriptEditor::get_singleton()->get_debugger() == bottom_panel_items[p_idx].control) { // this is the debug panel which uses tabs, so the top section should be smaller
5013 bottom_panel->add_style_override("panel", gui_base->get_stylebox("BottomPanelDebuggerOverride", "EditorStyles"));
5014 } else {
5015 bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer"));
5016 }
5017 center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
5018 center_split->set_collapsed(false);
5019 if (bottom_panel_raise->is_pressed()) {
5020 top_split->hide();
5021 }
5022 bottom_panel_raise->show();
5023
5024 } else {
5025 bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer"));
5026 bottom_panel_items[p_idx].button->set_pressed(false);
5027 bottom_panel_items[p_idx].control->set_visible(false);
5028 center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
5029 center_split->set_collapsed(true);
5030 bottom_panel_raise->hide();
5031 if (bottom_panel_raise->is_pressed()) {
5032 top_split->show();
5033 }
5034 }
5035 }
5036
set_docks_visible(bool p_show)5037 void EditorNode::set_docks_visible(bool p_show) {
5038 docks_visible = p_show;
5039 _update_dock_slots_visibility();
5040 }
5041
get_docks_visible() const5042 bool EditorNode::get_docks_visible() const {
5043 return docks_visible;
5044 }
5045
_toggle_distraction_free_mode()5046 void EditorNode::_toggle_distraction_free_mode() {
5047
5048 if (EditorSettings::get_singleton()->get("interface/editor/separate_distraction_mode")) {
5049 int screen = -1;
5050 for (int i = 0; i < editor_table.size(); i++) {
5051 if (editor_plugin_screen == editor_table[i]) {
5052 screen = i;
5053 break;
5054 }
5055 }
5056
5057 if (screen == EDITOR_SCRIPT) {
5058 script_distraction = !script_distraction;
5059 set_distraction_free_mode(script_distraction);
5060 } else {
5061 scene_distraction = !scene_distraction;
5062 set_distraction_free_mode(scene_distraction);
5063 }
5064 } else {
5065 set_distraction_free_mode(distraction_free->is_pressed());
5066 }
5067 }
5068
set_distraction_free_mode(bool p_enter)5069 void EditorNode::set_distraction_free_mode(bool p_enter) {
5070
5071 distraction_free->set_pressed(p_enter);
5072
5073 if (p_enter) {
5074 if (docks_visible) {
5075 set_docks_visible(false);
5076 }
5077 } else {
5078 set_docks_visible(true);
5079 }
5080 }
5081
is_distraction_free_mode_enabled() const5082 bool EditorNode::is_distraction_free_mode_enabled() const {
5083 return distraction_free->is_pressed();
5084 }
5085
add_control_to_dock(DockSlot p_slot,Control * p_control)5086 void EditorNode::add_control_to_dock(DockSlot p_slot, Control *p_control) {
5087 ERR_FAIL_INDEX(p_slot, DOCK_SLOT_MAX);
5088 dock_slot[p_slot]->add_child(p_control);
5089 _update_dock_slots_visibility();
5090 }
5091
remove_control_from_dock(Control * p_control)5092 void EditorNode::remove_control_from_dock(Control *p_control) {
5093
5094 Control *dock = NULL;
5095 for (int i = 0; i < DOCK_SLOT_MAX; i++) {
5096 if (p_control->get_parent() == dock_slot[i]) {
5097 dock = dock_slot[i];
5098 break;
5099 }
5100 }
5101
5102 ERR_FAIL_COND_MSG(!dock, "Control was not in dock.");
5103
5104 dock->remove_child(p_control);
5105 _update_dock_slots_visibility();
5106 }
5107
drag_resource(const Ref<Resource> & p_res,Control * p_from)5108 Variant EditorNode::drag_resource(const Ref<Resource> &p_res, Control *p_from) {
5109
5110 Control *drag_control = memnew(Control);
5111 TextureRect *drag_preview = memnew(TextureRect);
5112 Label *label = memnew(Label);
5113
5114 Ref<Texture> preview;
5115
5116 {
5117 //todo make proper previews
5118 Ref<ImageTexture> pic = gui_base->get_icon("FileBigThumb", "EditorIcons");
5119 Ref<Image> img = pic->get_data();
5120 img = img->duplicate();
5121 img->resize(48, 48); //meh
5122 Ref<ImageTexture> resized_pic = Ref<ImageTexture>(memnew(ImageTexture));
5123 resized_pic->create_from_image(img);
5124 preview = resized_pic;
5125 }
5126
5127 drag_preview->set_texture(preview);
5128 drag_control->add_child(drag_preview);
5129 if (p_res->get_path().is_resource_file()) {
5130 label->set_text(p_res->get_path().get_file());
5131 } else if (p_res->get_name() != "") {
5132 label->set_text(p_res->get_name());
5133 } else {
5134 label->set_text(p_res->get_class());
5135 }
5136
5137 drag_control->add_child(label);
5138
5139 p_from->set_drag_preview(drag_control); //wait until it enters scene
5140
5141 label->set_position(Point2((preview->get_width() - label->get_minimum_size().width) / 2, preview->get_height()));
5142
5143 Dictionary drag_data;
5144 drag_data["type"] = "resource";
5145 drag_data["resource"] = p_res;
5146 drag_data["from"] = p_from;
5147
5148 return drag_data;
5149 }
5150
drag_files_and_dirs(const Vector<String> & p_paths,Control * p_from)5151 Variant EditorNode::drag_files_and_dirs(const Vector<String> &p_paths, Control *p_from) {
5152 bool has_folder = false;
5153 bool has_file = false;
5154 for (int i = 0; i < p_paths.size(); i++) {
5155 bool is_folder = p_paths[i].ends_with("/");
5156 has_folder |= is_folder;
5157 has_file |= !is_folder;
5158 }
5159
5160 int max_rows = 6;
5161 int num_rows = p_paths.size() > max_rows ? max_rows - 1 : p_paths.size(); // Don't waste a row to say "1 more file" - list it instead.
5162 VBoxContainer *vbox = memnew(VBoxContainer);
5163 for (int i = 0; i < num_rows; i++) {
5164 HBoxContainer *hbox = memnew(HBoxContainer);
5165 TextureRect *icon = memnew(TextureRect);
5166 Label *label = memnew(Label);
5167
5168 if (p_paths[i].ends_with("/")) {
5169 label->set_text(p_paths[i].substr(0, p_paths[i].length() - 1).get_file());
5170 icon->set_texture(gui_base->get_icon("Folder", "EditorIcons"));
5171 } else {
5172 label->set_text(p_paths[i].get_file());
5173 icon->set_texture(gui_base->get_icon("File", "EditorIcons"));
5174 }
5175 icon->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
5176 icon->set_size(Size2(16, 16));
5177 hbox->add_child(icon);
5178 hbox->add_child(label);
5179 vbox->add_child(hbox);
5180 }
5181
5182 if (p_paths.size() > num_rows) {
5183 Label *label = memnew(Label);
5184 if (has_file && has_folder) {
5185 label->set_text(vformat(TTR("%d more files or folders"), p_paths.size() - num_rows));
5186 } else if (has_folder) {
5187 label->set_text(vformat(TTR("%d more folders"), p_paths.size() - num_rows));
5188 } else {
5189 label->set_text(vformat(TTR("%d more files"), p_paths.size() - num_rows));
5190 }
5191 vbox->add_child(label);
5192 }
5193 p_from->set_drag_preview(vbox); //wait until it enters scene
5194
5195 Dictionary drag_data;
5196 drag_data["type"] = has_folder ? "files_and_dirs" : "files";
5197 drag_data["files"] = p_paths;
5198 drag_data["from"] = p_from;
5199 return drag_data;
5200 }
5201
add_tool_menu_item(const String & p_name,Object * p_handler,const String & p_callback,const Variant & p_ud)5202 void EditorNode::add_tool_menu_item(const String &p_name, Object *p_handler, const String &p_callback, const Variant &p_ud) {
5203 ERR_FAIL_NULL(p_handler);
5204 int idx = tool_menu->get_item_count();
5205 tool_menu->add_item(p_name, TOOLS_CUSTOM);
5206
5207 Array parameters;
5208 parameters.push_back(p_handler->get_instance_id());
5209 parameters.push_back(p_callback);
5210 parameters.push_back(p_ud);
5211
5212 tool_menu->set_item_metadata(idx, parameters);
5213 }
5214
add_tool_submenu_item(const String & p_name,PopupMenu * p_submenu)5215 void EditorNode::add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu) {
5216 ERR_FAIL_NULL(p_submenu);
5217 ERR_FAIL_COND(p_submenu->get_parent() != NULL);
5218
5219 tool_menu->add_child(p_submenu);
5220 tool_menu->add_submenu_item(p_name, p_submenu->get_name(), TOOLS_CUSTOM);
5221 }
5222
remove_tool_menu_item(const String & p_name)5223 void EditorNode::remove_tool_menu_item(const String &p_name) {
5224 for (int i = 0; i < tool_menu->get_item_count(); i++) {
5225 if (tool_menu->get_item_id(i) != TOOLS_CUSTOM)
5226 continue;
5227
5228 if (tool_menu->get_item_text(i) == p_name) {
5229 if (tool_menu->get_item_submenu(i) != "") {
5230 Node *n = tool_menu->get_node(tool_menu->get_item_submenu(i));
5231 tool_menu->remove_child(n);
5232 memdelete(n);
5233 }
5234 tool_menu->remove_item(i);
5235 tool_menu->set_as_minsize();
5236 return;
5237 }
5238 }
5239 }
5240
_global_menu_action(const Variant & p_id,const Variant & p_meta)5241 void EditorNode::_global_menu_action(const Variant &p_id, const Variant &p_meta) {
5242
5243 int id = (int)p_id;
5244 if (id == GLOBAL_NEW_WINDOW) {
5245 if (OS::get_singleton()->get_main_loop()) {
5246 List<String> args;
5247 args.push_back("-e");
5248 String exec = OS::get_singleton()->get_executable_path();
5249
5250 OS::ProcessID pid = 0;
5251 OS::get_singleton()->execute(exec, args, false, &pid);
5252 }
5253 } else if (id == GLOBAL_SCENE) {
5254 int idx = (int)p_meta;
5255 scene_tabs->set_current_tab(idx);
5256 }
5257 }
5258
_dropped_files(const Vector<String> & p_files,int p_screen)5259 void EditorNode::_dropped_files(const Vector<String> &p_files, int p_screen) {
5260
5261 String to_path = ProjectSettings::get_singleton()->globalize_path(get_filesystem_dock()->get_selected_path());
5262
5263 _add_dropped_files_recursive(p_files, to_path);
5264
5265 EditorFileSystem::get_singleton()->scan_changes();
5266 }
5267
_add_dropped_files_recursive(const Vector<String> & p_files,String to_path)5268 void EditorNode::_add_dropped_files_recursive(const Vector<String> &p_files, String to_path) {
5269
5270 DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
5271 Vector<String> just_copy = String("ttf,otf").split(",");
5272
5273 for (int i = 0; i < p_files.size(); i++) {
5274
5275 String from = p_files[i];
5276 String to = to_path.plus_file(from.get_file());
5277
5278 if (dir->dir_exists(from)) {
5279
5280 Vector<String> sub_files;
5281
5282 DirAccessRef sub_dir = DirAccess::open(from);
5283 sub_dir->list_dir_begin();
5284
5285 String next_file = sub_dir->get_next();
5286 while (next_file != "") {
5287 if (next_file == "." || next_file == "..") {
5288 next_file = sub_dir->get_next();
5289 continue;
5290 }
5291
5292 sub_files.push_back(from.plus_file(next_file));
5293 next_file = sub_dir->get_next();
5294 }
5295
5296 if (!sub_files.empty()) {
5297 dir->make_dir(to);
5298 _add_dropped_files_recursive(sub_files, to);
5299 }
5300
5301 continue;
5302 }
5303
5304 if (!ResourceFormatImporter::get_singleton()->can_be_imported(from) && (just_copy.find(from.get_extension().to_lower()) == -1)) {
5305 continue;
5306 }
5307 dir->copy(from, to);
5308 }
5309 }
5310
_file_access_close_error_notify(const String & p_str)5311 void EditorNode::_file_access_close_error_notify(const String &p_str) {
5312
5313 add_io_error("Unable to write to file '" + p_str + "', file in use, locked or lacking permissions.");
5314 }
5315
reload_scene(const String & p_path)5316 void EditorNode::reload_scene(const String &p_path) {
5317
5318 //first of all, reload internal textures, materials, meshes, etc. as they might have changed on disk
5319
5320 List<Ref<Resource> > cached;
5321 ResourceCache::get_cached_resources(&cached);
5322 List<Ref<Resource> > to_clear; //clear internal resources from previous scene from being used
5323 for (List<Ref<Resource> >::Element *E = cached.front(); E; E = E->next()) {
5324
5325 if (E->get()->get_path().begins_with(p_path + "::")) { //subresources of existing scene
5326 to_clear.push_back(E->get());
5327 }
5328 }
5329
5330 //so reload reloads everything, clear subresources of previous scene
5331 while (to_clear.front()) {
5332 to_clear.front()->get()->set_path("");
5333 to_clear.pop_front();
5334 }
5335
5336 int scene_idx = -1;
5337 for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
5338
5339 if (editor_data.get_scene_path(i) == p_path) {
5340 scene_idx = i;
5341 break;
5342 }
5343 }
5344
5345 int current_tab = editor_data.get_edited_scene();
5346
5347 if (scene_idx == -1) {
5348 if (get_edited_scene()) {
5349 //scene is not open, so at it might be instanced. We'll refresh the whole scene later.
5350 editor_data.get_undo_redo().clear_history();
5351 }
5352 return;
5353 }
5354
5355 if (current_tab == scene_idx) {
5356 editor_data.apply_changes_in_editors();
5357 _set_scene_metadata(p_path);
5358 }
5359
5360 //remove scene
5361 _remove_scene(scene_idx, false);
5362
5363 //reload scene
5364 load_scene(p_path, true, false, true, true);
5365
5366 //adjust index so tab is back a the previous position
5367 editor_data.move_edited_scene_to_index(scene_idx);
5368 get_undo_redo()->clear_history();
5369
5370 //recover the tab
5371 scene_tabs->set_current_tab(current_tab);
5372 }
5373
5374 int EditorNode::plugin_init_callback_count = 0;
5375
add_plugin_init_callback(EditorPluginInitializeCallback p_callback)5376 void EditorNode::add_plugin_init_callback(EditorPluginInitializeCallback p_callback) {
5377
5378 ERR_FAIL_COND(plugin_init_callback_count == MAX_INIT_CALLBACKS);
5379
5380 plugin_init_callbacks[plugin_init_callback_count++] = p_callback;
5381 }
5382
5383 EditorPluginInitializeCallback EditorNode::plugin_init_callbacks[EditorNode::MAX_INIT_CALLBACKS];
5384
5385 int EditorNode::build_callback_count = 0;
5386
add_build_callback(EditorBuildCallback p_callback)5387 void EditorNode::add_build_callback(EditorBuildCallback p_callback) {
5388
5389 ERR_FAIL_COND(build_callback_count == MAX_INIT_CALLBACKS);
5390
5391 build_callbacks[build_callback_count++] = p_callback;
5392 }
5393
5394 EditorBuildCallback EditorNode::build_callbacks[EditorNode::MAX_BUILD_CALLBACKS];
5395
call_build()5396 bool EditorNode::call_build() {
5397
5398 bool builds_successful = true;
5399
5400 for (int i = 0; i < build_callback_count && builds_successful; i++) {
5401 if (!build_callbacks[i]()) {
5402 ERR_PRINT("A Godot Engine build callback failed.");
5403 builds_successful = false;
5404 }
5405 }
5406
5407 if (builds_successful && !editor_data.call_build()) {
5408 ERR_PRINT("An EditorPlugin build callback failed.");
5409 builds_successful = false;
5410 }
5411
5412 return builds_successful;
5413 }
5414
_inherit_imported(const String & p_action)5415 void EditorNode::_inherit_imported(const String &p_action) {
5416
5417 open_imported->hide();
5418 load_scene(open_import_request, true, true);
5419 }
5420
_open_imported()5421 void EditorNode::_open_imported() {
5422
5423 load_scene(open_import_request, true, false, true, true);
5424 }
5425
dim_editor(bool p_dimming,bool p_force_dim)5426 void EditorNode::dim_editor(bool p_dimming, bool p_force_dim) {
5427 // Dimming can be forced regardless of the editor setting, which is useful when quitting the editor.
5428 if ((p_force_dim || EditorSettings::get_singleton()->get("interface/editor/dim_editor_on_dialog_popup")) && p_dimming) {
5429 dimmed = true;
5430 gui_base->set_modulate(Color(0.5, 0.5, 0.5));
5431 } else {
5432 dimmed = false;
5433 gui_base->set_modulate(Color(1, 1, 1));
5434 }
5435 }
5436
is_editor_dimmed() const5437 bool EditorNode::is_editor_dimmed() const {
5438 return dimmed;
5439 }
5440
open_export_template_manager()5441 void EditorNode::open_export_template_manager() {
5442
5443 export_template_manager->popup_manager();
5444 }
5445
add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> & p_plugin)5446 void EditorNode::add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin) {
5447 resource_conversion_plugins.push_back(p_plugin);
5448 }
5449
remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> & p_plugin)5450 void EditorNode::remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin) {
5451 resource_conversion_plugins.erase(p_plugin);
5452 }
5453
find_resource_conversion_plugin(const Ref<Resource> & p_for_resource)5454 Vector<Ref<EditorResourceConversionPlugin> > EditorNode::find_resource_conversion_plugin(const Ref<Resource> &p_for_resource) {
5455
5456 Vector<Ref<EditorResourceConversionPlugin> > ret;
5457
5458 for (int i = 0; i < resource_conversion_plugins.size(); i++) {
5459 if (resource_conversion_plugins[i].is_valid() && resource_conversion_plugins[i]->handles(p_for_resource)) {
5460 ret.push_back(resource_conversion_plugins[i]);
5461 }
5462 }
5463
5464 return ret;
5465 }
5466
_bottom_panel_raise_toggled(bool p_pressed)5467 void EditorNode::_bottom_panel_raise_toggled(bool p_pressed) {
5468
5469 top_split->set_visible(!p_pressed);
5470 }
5471
_update_video_driver_color()5472 void EditorNode::_update_video_driver_color() {
5473
5474 // TODO: Probably should de-hardcode this and add to editor settings.
5475 if (video_driver->get_text() == "GLES2") {
5476 video_driver->add_color_override("font_color", Color::hex(0x5586a4ff));
5477 } else if (video_driver->get_text() == "GLES3") {
5478 video_driver->add_color_override("font_color", Color::hex(0xa5557dff));
5479 }
5480 }
5481
_video_driver_selected(int p_which)5482 void EditorNode::_video_driver_selected(int p_which) {
5483
5484 String driver = video_driver->get_item_metadata(p_which);
5485
5486 String current = OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver());
5487
5488 if (driver == current) {
5489 return;
5490 }
5491
5492 video_driver_request = driver;
5493 video_restart_dialog->popup_centered_minsize();
5494 video_driver->select(video_driver_current);
5495 _update_video_driver_color();
5496 }
5497
_resource_saved(RES p_resource,const String & p_path)5498 void EditorNode::_resource_saved(RES p_resource, const String &p_path) {
5499 if (EditorFileSystem::get_singleton()) {
5500 EditorFileSystem::get_singleton()->update_file(p_path);
5501 }
5502
5503 singleton->editor_folding.save_resource_folding(p_resource, p_path);
5504 }
5505
_resource_loaded(RES p_resource,const String & p_path)5506 void EditorNode::_resource_loaded(RES p_resource, const String &p_path) {
5507
5508 singleton->editor_folding.load_resource_folding(p_resource, p_path);
5509 }
5510
_feature_profile_changed()5511 void EditorNode::_feature_profile_changed() {
5512
5513 Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile();
5514 TabContainer *import_tabs = cast_to<TabContainer>(import_dock->get_parent());
5515 TabContainer *node_tabs = cast_to<TabContainer>(node_dock->get_parent());
5516 TabContainer *fs_tabs = cast_to<TabContainer>(filesystem_dock->get_parent());
5517 if (profile.is_valid()) {
5518
5519 import_tabs->set_tab_hidden(import_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_IMPORT_DOCK));
5520 node_tabs->set_tab_hidden(node_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_NODE_DOCK));
5521 fs_tabs->set_tab_hidden(filesystem_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_FILESYSTEM_DOCK));
5522
5523 main_editor_buttons[EDITOR_3D]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D));
5524 main_editor_buttons[EDITOR_SCRIPT]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT));
5525 if (StreamPeerSSL::is_available())
5526 main_editor_buttons[EDITOR_ASSETLIB]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB));
5527 if ((profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D) && singleton->main_editor_buttons[EDITOR_3D]->is_pressed()) ||
5528 (profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT) && singleton->main_editor_buttons[EDITOR_SCRIPT]->is_pressed()) ||
5529 (StreamPeerSSL::is_available() && profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB) && singleton->main_editor_buttons[EDITOR_ASSETLIB]->is_pressed())) {
5530 _editor_select(EDITOR_2D);
5531 }
5532 } else {
5533
5534 import_tabs->set_tab_hidden(import_dock->get_index(), false);
5535 node_tabs->set_tab_hidden(node_dock->get_index(), false);
5536 fs_tabs->set_tab_hidden(filesystem_dock->get_index(), false);
5537 import_dock->set_visible(true);
5538 node_dock->set_visible(true);
5539 filesystem_dock->set_visible(true);
5540 main_editor_buttons[EDITOR_3D]->set_visible(true);
5541 main_editor_buttons[EDITOR_SCRIPT]->set_visible(true);
5542 if (StreamPeerSSL::is_available())
5543 main_editor_buttons[EDITOR_ASSETLIB]->set_visible(true);
5544 }
5545
5546 _update_dock_slots_visibility();
5547 }
5548
_bind_methods()5549 void EditorNode::_bind_methods() {
5550
5551 ClassDB::bind_method("_menu_option", &EditorNode::_menu_option);
5552 ClassDB::bind_method("_tool_menu_option", &EditorNode::_tool_menu_option);
5553 ClassDB::bind_method("_menu_confirm_current", &EditorNode::_menu_confirm_current);
5554 ClassDB::bind_method("_dialog_action", &EditorNode::_dialog_action);
5555 ClassDB::bind_method("_editor_select", &EditorNode::_editor_select);
5556 ClassDB::bind_method("_node_renamed", &EditorNode::_node_renamed);
5557 ClassDB::bind_method("edit_node", &EditorNode::edit_node);
5558 ClassDB::bind_method("_unhandled_input", &EditorNode::_unhandled_input);
5559 ClassDB::bind_method("_update_file_menu_opened", &EditorNode::_update_file_menu_opened);
5560 ClassDB::bind_method("_update_file_menu_closed", &EditorNode::_update_file_menu_closed);
5561
5562 ClassDB::bind_method(D_METHOD("push_item", "object", "property", "inspector_only"), &EditorNode::push_item, DEFVAL(""), DEFVAL(false));
5563
5564 ClassDB::bind_method("_get_scene_metadata", &EditorNode::_get_scene_metadata);
5565 ClassDB::bind_method("set_edited_scene", &EditorNode::set_edited_scene);
5566 ClassDB::bind_method("open_request", &EditorNode::open_request);
5567 ClassDB::bind_method("_inherit_request", &EditorNode::_inherit_request);
5568 ClassDB::bind_method("_instance_request", &EditorNode::_instance_request);
5569 ClassDB::bind_method("_close_messages", &EditorNode::_close_messages);
5570 ClassDB::bind_method("_show_messages", &EditorNode::_show_messages);
5571 ClassDB::bind_method("_vp_resized", &EditorNode::_vp_resized);
5572 ClassDB::bind_method("_quick_opened", &EditorNode::_quick_opened);
5573 ClassDB::bind_method("_quick_run", &EditorNode::_quick_run);
5574
5575 ClassDB::bind_method("_open_recent_scene", &EditorNode::_open_recent_scene);
5576
5577 ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process);
5578
5579 ClassDB::bind_method("get_script_create_dialog", &EditorNode::get_script_create_dialog);
5580
5581 ClassDB::bind_method("_sources_changed", &EditorNode::_sources_changed);
5582 ClassDB::bind_method("_fs_changed", &EditorNode::_fs_changed);
5583 ClassDB::bind_method("_dock_select_draw", &EditorNode::_dock_select_draw);
5584 ClassDB::bind_method("_dock_select_input", &EditorNode::_dock_select_input);
5585 ClassDB::bind_method("_dock_pre_popup", &EditorNode::_dock_pre_popup);
5586 ClassDB::bind_method("_dock_split_dragged", &EditorNode::_dock_split_dragged);
5587 ClassDB::bind_method("_save_docks", &EditorNode::_save_docks);
5588 ClassDB::bind_method("_dock_popup_exit", &EditorNode::_dock_popup_exit);
5589 ClassDB::bind_method("_dock_move_left", &EditorNode::_dock_move_left);
5590 ClassDB::bind_method("_dock_move_right", &EditorNode::_dock_move_right);
5591 ClassDB::bind_method("_dock_tab_changed", &EditorNode::_dock_tab_changed);
5592
5593 ClassDB::bind_method("_layout_menu_option", &EditorNode::_layout_menu_option);
5594
5595 ClassDB::bind_method("set_current_scene", &EditorNode::set_current_scene);
5596 ClassDB::bind_method("set_current_version", &EditorNode::set_current_version);
5597 ClassDB::bind_method("_scene_tab_changed", &EditorNode::_scene_tab_changed);
5598 ClassDB::bind_method("_scene_tab_closed", &EditorNode::_scene_tab_closed);
5599 ClassDB::bind_method("_scene_tab_hover", &EditorNode::_scene_tab_hover);
5600 ClassDB::bind_method("_scene_tab_exit", &EditorNode::_scene_tab_exit);
5601 ClassDB::bind_method("_scene_tab_input", &EditorNode::_scene_tab_input);
5602 ClassDB::bind_method("_reposition_active_tab", &EditorNode::_reposition_active_tab);
5603 ClassDB::bind_method("_thumbnail_done", &EditorNode::_thumbnail_done);
5604 ClassDB::bind_method("_scene_tab_script_edited", &EditorNode::_scene_tab_script_edited);
5605 ClassDB::bind_method("_set_main_scene_state", &EditorNode::_set_main_scene_state);
5606 ClassDB::bind_method("_update_scene_tabs", &EditorNode::_update_scene_tabs);
5607 ClassDB::bind_method("_discard_changes", &EditorNode::_discard_changes);
5608 ClassDB::bind_method("_update_recent_scenes", &EditorNode::_update_recent_scenes);
5609
5610 ClassDB::bind_method("_clear_undo_history", &EditorNode::_clear_undo_history);
5611 ClassDB::bind_method("_dropped_files", &EditorNode::_dropped_files);
5612 ClassDB::bind_method(D_METHOD("_global_menu_action"), &EditorNode::_global_menu_action, DEFVAL(Variant()));
5613 ClassDB::bind_method("_toggle_distraction_free_mode", &EditorNode::_toggle_distraction_free_mode);
5614 ClassDB::bind_method("_version_control_menu_option", &EditorNode::_version_control_menu_option);
5615 ClassDB::bind_method("edit_item_resource", &EditorNode::edit_item_resource);
5616
5617 ClassDB::bind_method(D_METHOD("get_gui_base"), &EditorNode::get_gui_base);
5618 ClassDB::bind_method(D_METHOD("_bottom_panel_switch"), &EditorNode::_bottom_panel_switch);
5619
5620 ClassDB::bind_method(D_METHOD("_open_imported"), &EditorNode::_open_imported);
5621 ClassDB::bind_method(D_METHOD("_inherit_imported"), &EditorNode::_inherit_imported);
5622
5623 ClassDB::bind_method("_copy_warning", &EditorNode::_copy_warning);
5624
5625 ClassDB::bind_method(D_METHOD("_resources_reimported"), &EditorNode::_resources_reimported);
5626 ClassDB::bind_method(D_METHOD("_bottom_panel_raise_toggled"), &EditorNode::_bottom_panel_raise_toggled);
5627
5628 ClassDB::bind_method(D_METHOD("_on_plugin_ready"), &EditorNode::_on_plugin_ready);
5629
5630 ClassDB::bind_method(D_METHOD("_video_driver_selected"), &EditorNode::_video_driver_selected);
5631
5632 ClassDB::bind_method(D_METHOD("_resources_changed"), &EditorNode::_resources_changed);
5633 ClassDB::bind_method(D_METHOD("_feature_profile_changed"), &EditorNode::_feature_profile_changed);
5634
5635 ClassDB::bind_method("_screenshot", &EditorNode::_screenshot);
5636 ClassDB::bind_method("_request_screenshot", &EditorNode::_request_screenshot);
5637 ClassDB::bind_method("_save_screenshot", &EditorNode::_save_screenshot);
5638
5639 ADD_SIGNAL(MethodInfo("play_pressed"));
5640 ADD_SIGNAL(MethodInfo("pause_pressed"));
5641 ADD_SIGNAL(MethodInfo("stop_pressed"));
5642 ADD_SIGNAL(MethodInfo("request_help_search"));
5643 ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::POOL_STRING_ARRAY, "args")));
5644 ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj")));
5645 }
5646
_resource_get_edited_scene()5647 static Node *_resource_get_edited_scene() {
5648
5649 return EditorNode::get_singleton()->get_edited_scene();
5650 }
5651
_print_handler(void * p_this,const String & p_string,bool p_error)5652 void EditorNode::_print_handler(void *p_this, const String &p_string, bool p_error) {
5653 EditorNode *en = (EditorNode *)p_this;
5654 en->log->add_message(p_string, p_error ? EditorLog::MSG_TYPE_ERROR : EditorLog::MSG_TYPE_STD);
5655 }
5656
_execute_thread(void * p_ud)5657 static void _execute_thread(void *p_ud) {
5658
5659 EditorNode::ExecuteThreadArgs *eta = (EditorNode::ExecuteThreadArgs *)p_ud;
5660 Error err = OS::get_singleton()->execute(eta->path, eta->args, true, NULL, &eta->output, &eta->exitcode, true, eta->execute_output_mutex);
5661 print_verbose("Thread exit status: " + itos(eta->exitcode));
5662 if (err != OK) {
5663 eta->exitcode = err;
5664 }
5665
5666 eta->done = true;
5667 }
5668
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)5669 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) {
5670
5671 execute_output_dialog->set_title(p_title);
5672 execute_output_dialog->get_ok()->set_disabled(true);
5673 execute_outputs->clear();
5674 execute_outputs->set_scroll_follow(true);
5675 execute_output_dialog->popup_centered_ratio();
5676
5677 ExecuteThreadArgs eta;
5678 eta.path = p_path;
5679 eta.args = p_arguments;
5680 eta.execute_output_mutex = Mutex::create();
5681 eta.exitcode = 255;
5682 eta.done = false;
5683
5684 int prev_len = 0;
5685
5686 eta.execute_output_thread = Thread::create(_execute_thread, &eta);
5687
5688 ERR_FAIL_COND_V(!eta.execute_output_thread, 0);
5689
5690 while (!eta.done) {
5691 eta.execute_output_mutex->lock();
5692 if (prev_len != eta.output.length()) {
5693 String to_add = eta.output.substr(prev_len, eta.output.length());
5694 prev_len = eta.output.length();
5695 execute_outputs->add_text(to_add);
5696 Main::iteration();
5697 }
5698 eta.execute_output_mutex->unlock();
5699 OS::get_singleton()->delay_usec(1000);
5700 }
5701
5702 Thread::wait_to_finish(eta.execute_output_thread);
5703 memdelete(eta.execute_output_thread);
5704 memdelete(eta.execute_output_mutex);
5705 execute_outputs->add_text("\nExit Code: " + itos(eta.exitcode));
5706
5707 if (p_close_on_errors && eta.exitcode != 0) {
5708 execute_output_dialog->hide();
5709 }
5710 if (p_close_on_ok && eta.exitcode == 0) {
5711 execute_output_dialog->hide();
5712 }
5713
5714 execute_output_dialog->get_ok()->set_disabled(false);
5715
5716 return eta.exitcode;
5717 }
5718
EditorNode()5719 EditorNode::EditorNode() {
5720
5721 Input::get_singleton()->set_use_accumulated_input(true);
5722 Resource::_get_local_scene_func = _resource_get_edited_scene;
5723
5724 VisualServer::get_singleton()->textures_keep_original(true);
5725 VisualServer::get_singleton()->set_debug_generate_wireframes(true);
5726
5727 PhysicsServer::get_singleton()->set_active(false); // no physics by default if editor
5728 Physics2DServer::get_singleton()->set_active(false); // no physics by default if editor
5729 ScriptServer::set_scripting_enabled(false); // no scripting by default if editor
5730
5731 EditorHelp::generate_doc(); //before any editor classes are created
5732 SceneState::set_disable_placeholders(true);
5733 ResourceLoader::clear_translation_remaps(); //no remaps using during editor
5734 ResourceLoader::clear_path_remaps();
5735
5736 InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton());
5737
5738 if (id) {
5739
5740 if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton()) {
5741 //only if no touchscreen ui hint, set emulation
5742 id->set_emulate_touch_from_mouse(false); //just disable just in case
5743 }
5744 id->set_custom_mouse_cursor(RES());
5745 }
5746
5747 singleton = this;
5748 exiting = false;
5749 dimmed = false;
5750 last_checked_version = 0;
5751 changing_scene = false;
5752 _initializing_addons = false;
5753 docks_visible = true;
5754 restoring_scenes = false;
5755 cmdline_export_mode = false;
5756 scene_distraction = false;
5757 script_distraction = false;
5758
5759 TranslationServer::get_singleton()->set_enabled(false);
5760 // load settings
5761 if (!EditorSettings::get_singleton())
5762 EditorSettings::create();
5763
5764 FileAccess::set_backup_save(EDITOR_GET("filesystem/on_save/safe_save_on_backup_then_rename"));
5765
5766 {
5767 int display_scale = EditorSettings::get_singleton()->get("interface/editor/display_scale");
5768 float custom_display_scale = EditorSettings::get_singleton()->get("interface/editor/custom_display_scale");
5769
5770 switch (display_scale) {
5771 case 0: {
5772 // Try applying a suitable display scale automatically
5773 #ifdef OSX_ENABLED
5774 editor_set_scale(OS::get_singleton()->get_screen_max_scale());
5775 #else
5776 const int screen = OS::get_singleton()->get_current_screen();
5777 editor_set_scale(OS::get_singleton()->get_screen_dpi(screen) >= 192 && OS::get_singleton()->get_screen_size(screen).x > 2000 ? 2.0 : 1.0);
5778 #endif
5779 } break;
5780
5781 case 1: {
5782 editor_set_scale(0.75);
5783 } break;
5784
5785 case 2: {
5786 editor_set_scale(1.0);
5787 } break;
5788
5789 case 3: {
5790 editor_set_scale(1.25);
5791 } break;
5792
5793 case 4: {
5794 editor_set_scale(1.5);
5795 } break;
5796
5797 case 5: {
5798 editor_set_scale(1.75);
5799 } break;
5800
5801 case 6: {
5802 editor_set_scale(2.0);
5803 } break;
5804
5805 default: {
5806 editor_set_scale(custom_display_scale);
5807 } break;
5808 }
5809 }
5810
5811 // Define a minimum window size to prevent UI elements from overlapping or being cut off
5812 OS::get_singleton()->set_min_window_size(Size2(1024, 600) * EDSCALE);
5813
5814 ResourceLoader::set_abort_on_missing_resources(false);
5815 FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files"));
5816 EditorFileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files"));
5817 EditorFileDialog::set_default_display_mode((EditorFileDialog::DisplayMode)EditorSettings::get_singleton()->get("filesystem/file_dialog/display_mode").operator int());
5818 ResourceLoader::set_error_notify_func(this, _load_error_notify);
5819 ResourceLoader::set_dependency_error_notify_func(this, _dependency_error_report);
5820
5821 { //register importers at the beginning, so dialogs are created with the right extensions
5822 Ref<ResourceImporterTexture> import_texture;
5823 import_texture.instance();
5824 ResourceFormatImporter::get_singleton()->add_importer(import_texture);
5825
5826 Ref<ResourceImporterLayeredTexture> import_3d;
5827 import_3d.instance();
5828 import_3d->set_3d(true);
5829 ResourceFormatImporter::get_singleton()->add_importer(import_3d);
5830
5831 Ref<ResourceImporterLayeredTexture> import_array;
5832 import_array.instance();
5833 import_array->set_3d(false);
5834 ResourceFormatImporter::get_singleton()->add_importer(import_array);
5835
5836 Ref<ResourceImporterImage> import_image;
5837 import_image.instance();
5838 ResourceFormatImporter::get_singleton()->add_importer(import_image);
5839
5840 Ref<ResourceImporterTextureAtlas> import_texture_atlas;
5841 import_texture_atlas.instance();
5842 ResourceFormatImporter::get_singleton()->add_importer(import_texture_atlas);
5843
5844 Ref<ResourceImporterCSVTranslation> import_csv_translation;
5845 import_csv_translation.instance();
5846 ResourceFormatImporter::get_singleton()->add_importer(import_csv_translation);
5847
5848 Ref<ResourceImporterCSV> import_csv;
5849 import_csv.instance();
5850 ResourceFormatImporter::get_singleton()->add_importer(import_csv);
5851
5852 Ref<ResourceImporterWAV> import_wav;
5853 import_wav.instance();
5854 ResourceFormatImporter::get_singleton()->add_importer(import_wav);
5855
5856 Ref<ResourceImporterOBJ> import_obj;
5857 import_obj.instance();
5858 ResourceFormatImporter::get_singleton()->add_importer(import_obj);
5859
5860 Ref<ResourceImporterScene> import_scene;
5861 import_scene.instance();
5862 ResourceFormatImporter::get_singleton()->add_importer(import_scene);
5863
5864 {
5865 Ref<EditorSceneImporterCollada> import_collada;
5866 import_collada.instance();
5867 import_scene->add_importer(import_collada);
5868
5869 Ref<EditorOBJImporter> import_obj2;
5870 import_obj2.instance();
5871 import_scene->add_importer(import_obj2);
5872
5873 Ref<EditorSceneImporterGLTF> import_gltf;
5874 import_gltf.instance();
5875 import_scene->add_importer(import_gltf);
5876
5877 Ref<EditorSceneImporterESCN> import_escn;
5878 import_escn.instance();
5879 import_scene->add_importer(import_escn);
5880 }
5881
5882 Ref<ResourceImporterBitMap> import_bitmap;
5883 import_bitmap.instance();
5884 ResourceFormatImporter::get_singleton()->add_importer(import_bitmap);
5885 }
5886
5887 {
5888 Ref<EditorInspectorDefaultPlugin> eidp;
5889 eidp.instance();
5890 EditorInspector::add_inspector_plugin(eidp);
5891
5892 Ref<EditorInspectorRootMotionPlugin> rmp;
5893 rmp.instance();
5894 EditorInspector::add_inspector_plugin(rmp);
5895
5896 Ref<EditorInspectorShaderModePlugin> smp;
5897 smp.instance();
5898 EditorInspector::add_inspector_plugin(smp);
5899 }
5900
5901 _pvrtc_register_compressors();
5902
5903 editor_selection = memnew(EditorSelection);
5904
5905 EditorFileSystem *efs = memnew(EditorFileSystem);
5906 add_child(efs);
5907
5908 //used for previews
5909 FileDialog::get_icon_func = _file_dialog_get_icon;
5910 FileDialog::register_func = _file_dialog_register;
5911 FileDialog::unregister_func = _file_dialog_unregister;
5912
5913 EditorFileDialog::get_icon_func = _file_dialog_get_icon;
5914 EditorFileDialog::register_func = _editor_file_dialog_register;
5915 EditorFileDialog::unregister_func = _editor_file_dialog_unregister;
5916
5917 editor_export = memnew(EditorExport);
5918 add_child(editor_export);
5919
5920 // Exporters might need the theme
5921 theme = create_custom_theme();
5922
5923 register_exporters();
5924
5925 GLOBAL_DEF("editor/main_run_args", "");
5926
5927 ClassDB::set_class_enabled("RootMotionView", true);
5928
5929 //defs here, use EDITOR_GET in logic
5930 EDITOR_DEF_RST("interface/scene_tabs/always_show_close_button", false);
5931 EDITOR_DEF_RST("interface/scene_tabs/resize_if_many_tabs", true);
5932 EDITOR_DEF_RST("interface/scene_tabs/minimum_width", 50);
5933 EDITOR_DEF("run/output/always_clear_output_on_play", true);
5934 EDITOR_DEF("run/output/always_open_output_on_play", true);
5935 EDITOR_DEF("run/output/always_close_output_on_stop", true);
5936 EDITOR_DEF("run/auto_save/save_before_running", true);
5937 EDITOR_DEF_RST("interface/editor/save_each_scene_on_quit", true);
5938 EDITOR_DEF("interface/editor/quit_confirmation", true);
5939 EDITOR_DEF("interface/editor/show_update_spinner", false);
5940 EDITOR_DEF("interface/editor/update_continuously", false);
5941 EDITOR_DEF_RST("interface/scene_tabs/restore_scenes_on_load", false);
5942 EDITOR_DEF_RST("interface/scene_tabs/show_thumbnail_on_hover", true);
5943 EDITOR_DEF_RST("interface/inspector/capitalize_properties", true);
5944 EDITOR_DEF_RST("interface/inspector/default_float_step", 0.001);
5945 EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::REAL, "interface/inspector/default_float_step", PROPERTY_HINT_RANGE, "0,1,0"));
5946 EDITOR_DEF_RST("interface/inspector/disable_folding", false);
5947 EDITOR_DEF_RST("interface/inspector/auto_unfold_foreign_scenes", true);
5948 EDITOR_DEF("interface/inspector/horizontal_vector2_editing", false);
5949 EDITOR_DEF("interface/inspector/horizontal_vector_types_editing", true);
5950 EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true);
5951 EDITOR_DEF("interface/inspector/resources_to_open_in_new_inspector", "SpatialMaterial,Script,MeshLibrary,TileSet");
5952 EDITOR_DEF("interface/inspector/default_color_picker_mode", 0);
5953 EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/inspector/default_color_picker_mode", PROPERTY_HINT_ENUM, "RGB,HSV,RAW", PROPERTY_USAGE_DEFAULT));
5954 EDITOR_DEF("run/auto_save/save_before_running", true);
5955
5956 theme_base = memnew(Control);
5957 add_child(theme_base);
5958 theme_base->set_anchors_and_margins_preset(Control::PRESET_WIDE);
5959
5960 gui_base = memnew(Panel);
5961 theme_base->add_child(gui_base);
5962 gui_base->set_anchors_and_margins_preset(Control::PRESET_WIDE);
5963
5964 theme_base->set_theme(theme);
5965 gui_base->set_theme(theme);
5966 gui_base->add_style_override("panel", gui_base->get_stylebox("Background", "EditorStyles"));
5967
5968 resource_preview = memnew(EditorResourcePreview);
5969 add_child(resource_preview);
5970 progress_dialog = memnew(ProgressDialog);
5971 gui_base->add_child(progress_dialog);
5972
5973 // take up all screen
5974 gui_base->set_anchor(MARGIN_RIGHT, Control::ANCHOR_END);
5975 gui_base->set_anchor(MARGIN_BOTTOM, Control::ANCHOR_END);
5976 gui_base->set_end(Point2(0, 0));
5977
5978 main_vbox = memnew(VBoxContainer);
5979 gui_base->add_child(main_vbox);
5980 main_vbox->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 8);
5981 main_vbox->add_constant_override("separation", 8 * EDSCALE);
5982
5983 menu_hb = memnew(HBoxContainer);
5984 main_vbox->add_child(menu_hb);
5985
5986 left_l_hsplit = memnew(HSplitContainer);
5987 main_vbox->add_child(left_l_hsplit);
5988
5989 left_l_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
5990
5991 left_l_vsplit = memnew(VSplitContainer);
5992 left_l_hsplit->add_child(left_l_vsplit);
5993 dock_slot[DOCK_SLOT_LEFT_UL] = memnew(TabContainer);
5994 left_l_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_UL]);
5995 dock_slot[DOCK_SLOT_LEFT_BL] = memnew(TabContainer);
5996 left_l_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_BL]);
5997
5998 left_r_hsplit = memnew(HSplitContainer);
5999 left_l_hsplit->add_child(left_r_hsplit);
6000 left_r_vsplit = memnew(VSplitContainer);
6001 left_r_hsplit->add_child(left_r_vsplit);
6002 dock_slot[DOCK_SLOT_LEFT_UR] = memnew(TabContainer);
6003 left_r_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_UR]);
6004 dock_slot[DOCK_SLOT_LEFT_BR] = memnew(TabContainer);
6005 left_r_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_BR]);
6006
6007 main_hsplit = memnew(HSplitContainer);
6008 left_r_hsplit->add_child(main_hsplit);
6009 VBoxContainer *center_vb = memnew(VBoxContainer);
6010 main_hsplit->add_child(center_vb);
6011 center_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
6012
6013 center_split = memnew(VSplitContainer);
6014 center_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
6015 center_split->set_collapsed(false);
6016 center_vb->add_child(center_split);
6017
6018 right_hsplit = memnew(HSplitContainer);
6019 main_hsplit->add_child(right_hsplit);
6020
6021 right_l_vsplit = memnew(VSplitContainer);
6022 right_hsplit->add_child(right_l_vsplit);
6023 dock_slot[DOCK_SLOT_RIGHT_UL] = memnew(TabContainer);
6024 right_l_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_UL]);
6025 dock_slot[DOCK_SLOT_RIGHT_BL] = memnew(TabContainer);
6026 right_l_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_BL]);
6027
6028 right_r_vsplit = memnew(VSplitContainer);
6029 right_hsplit->add_child(right_r_vsplit);
6030 dock_slot[DOCK_SLOT_RIGHT_UR] = memnew(TabContainer);
6031 right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_UR]);
6032 dock_slot[DOCK_SLOT_RIGHT_BR] = memnew(TabContainer);
6033 right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_BR]);
6034
6035 // Store them for easier access
6036 vsplits.push_back(left_l_vsplit);
6037 vsplits.push_back(left_r_vsplit);
6038 vsplits.push_back(right_l_vsplit);
6039 vsplits.push_back(right_r_vsplit);
6040
6041 hsplits.push_back(left_l_hsplit);
6042 hsplits.push_back(left_r_hsplit);
6043 hsplits.push_back(main_hsplit);
6044 hsplits.push_back(right_hsplit);
6045
6046 for (int i = 0; i < vsplits.size(); i++) {
6047 vsplits[i]->connect("dragged", this, "_dock_split_dragged");
6048 hsplits[i]->connect("dragged", this, "_dock_split_dragged");
6049 }
6050
6051 dock_select_popup = memnew(PopupPanel);
6052 gui_base->add_child(dock_select_popup);
6053 VBoxContainer *dock_vb = memnew(VBoxContainer);
6054 dock_select_popup->add_child(dock_vb);
6055
6056 HBoxContainer *dock_hb = memnew(HBoxContainer);
6057 dock_tab_move_left = memnew(ToolButton);
6058 dock_tab_move_left->set_icon(theme->get_icon("Back", "EditorIcons"));
6059 dock_tab_move_left->set_focus_mode(Control::FOCUS_NONE);
6060 dock_tab_move_left->connect("pressed", this, "_dock_move_left");
6061 dock_hb->add_child(dock_tab_move_left);
6062
6063 Label *dock_label = memnew(Label);
6064 dock_label->set_text(TTR("Dock Position"));
6065 dock_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
6066 dock_label->set_align(Label::ALIGN_CENTER);
6067 dock_hb->add_child(dock_label);
6068
6069 dock_tab_move_right = memnew(ToolButton);
6070 dock_tab_move_right->set_icon(theme->get_icon("Forward", "EditorIcons"));
6071 dock_tab_move_right->set_focus_mode(Control::FOCUS_NONE);
6072 dock_tab_move_right->connect("pressed", this, "_dock_move_right");
6073
6074 dock_hb->add_child(dock_tab_move_right);
6075 dock_vb->add_child(dock_hb);
6076
6077 dock_select = memnew(Control);
6078 dock_select->set_custom_minimum_size(Size2(128, 64) * EDSCALE);
6079 dock_select->connect("gui_input", this, "_dock_select_input");
6080 dock_select->connect("draw", this, "_dock_select_draw");
6081 dock_select->connect("mouse_exited", this, "_dock_popup_exit");
6082 dock_select->set_v_size_flags(Control::SIZE_EXPAND_FILL);
6083 dock_vb->add_child(dock_select);
6084
6085 dock_select_popup->set_as_minsize();
6086 dock_select_rect_over = -1;
6087 dock_popup_selected = -1;
6088 for (int i = 0; i < DOCK_SLOT_MAX; i++) {
6089 dock_slot[i]->set_custom_minimum_size(Size2(170, 0) * EDSCALE);
6090 dock_slot[i]->set_v_size_flags(Control::SIZE_EXPAND_FILL);
6091 dock_slot[i]->set_popup(dock_select_popup);
6092 dock_slot[i]->connect("pre_popup_pressed", this, "_dock_pre_popup", varray(i));
6093 dock_slot[i]->set_tab_align(TabContainer::ALIGN_LEFT);
6094 dock_slot[i]->set_drag_to_rearrange_enabled(true);
6095 dock_slot[i]->set_tabs_rearrange_group(1);
6096 dock_slot[i]->connect("tab_changed", this, "_dock_tab_changed");
6097 dock_slot[i]->set_use_hidden_tabs_for_min_size(true);
6098 }
6099
6100 dock_drag_timer = memnew(Timer);
6101 add_child(dock_drag_timer);
6102 dock_drag_timer->set_wait_time(0.5);
6103 dock_drag_timer->set_one_shot(true);
6104 dock_drag_timer->connect("timeout", this, "_save_docks");
6105
6106 top_split = memnew(VSplitContainer);
6107 center_split->add_child(top_split);
6108 top_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
6109 top_split->set_collapsed(true);
6110
6111 VBoxContainer *srt = memnew(VBoxContainer);
6112 srt->set_v_size_flags(Control::SIZE_EXPAND_FILL);
6113 top_split->add_child(srt);
6114 srt->add_constant_override("separation", 0);
6115
6116 tab_preview_panel = memnew(Panel);
6117 tab_preview_panel->set_size(Size2(100, 100) * EDSCALE);
6118 tab_preview_panel->hide();
6119 tab_preview_panel->set_self_modulate(Color(1, 1, 1, 0.7));
6120 gui_base->add_child(tab_preview_panel);
6121
6122 tab_preview = memnew(TextureRect);
6123 tab_preview->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
6124 tab_preview->set_size(Size2(96, 96) * EDSCALE);
6125 tab_preview->set_position(Point2(2, 2) * EDSCALE);
6126 tab_preview_panel->add_child(tab_preview);
6127
6128 scene_tabs = memnew(Tabs);
6129 scene_tabs->add_style_override("tab_fg", gui_base->get_stylebox("SceneTabFG", "EditorStyles"));
6130 scene_tabs->add_style_override("tab_bg", gui_base->get_stylebox("SceneTabBG", "EditorStyles"));
6131 scene_tabs->set_select_with_rmb(true);
6132 scene_tabs->add_tab("unsaved");
6133 scene_tabs->set_tab_align(Tabs::ALIGN_LEFT);
6134 scene_tabs->set_tab_close_display_policy((bool(EDITOR_DEF("interface/scene_tabs/always_show_close_button", false)) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY));
6135 scene_tabs->set_min_width(int(EDITOR_DEF("interface/scene_tabs/minimum_width", 50)) * EDSCALE);
6136 scene_tabs->set_drag_to_rearrange_enabled(true);
6137 scene_tabs->connect("tab_changed", this, "_scene_tab_changed");
6138 scene_tabs->connect("right_button_pressed", this, "_scene_tab_script_edited");
6139 scene_tabs->connect("tab_close", this, "_scene_tab_closed", varray(SCENE_TAB_CLOSE));
6140 scene_tabs->connect("tab_hover", this, "_scene_tab_hover");
6141 scene_tabs->connect("mouse_exited", this, "_scene_tab_exit");
6142 scene_tabs->connect("gui_input", this, "_scene_tab_input");
6143 scene_tabs->connect("reposition_active_tab_request", this, "_reposition_active_tab");
6144 scene_tabs->connect("resized", this, "_update_scene_tabs");
6145
6146 tabbar_container = memnew(HBoxContainer);
6147 scene_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL);
6148
6149 scene_tabs_context_menu = memnew(PopupMenu);
6150 tabbar_container->add_child(scene_tabs_context_menu);
6151 scene_tabs_context_menu->connect("id_pressed", this, "_menu_option");
6152 scene_tabs_context_menu->set_hide_on_window_lose_focus(true);
6153
6154 srt->add_child(tabbar_container);
6155 tabbar_container->add_child(scene_tabs);
6156 distraction_free = memnew(ToolButton);
6157 #ifdef OSX_ENABLED
6158 distraction_free->set_shortcut(ED_SHORTCUT("editor/distraction_free_mode", TTR("Distraction Free Mode"), KEY_MASK_CMD | KEY_MASK_CTRL | KEY_D));
6159 #else
6160 distraction_free->set_shortcut(ED_SHORTCUT("editor/distraction_free_mode", TTR("Distraction Free Mode"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F11));
6161 #endif
6162 distraction_free->set_tooltip(TTR("Toggle distraction-free mode."));
6163 distraction_free->connect("pressed", this, "_toggle_distraction_free_mode");
6164 distraction_free->set_icon(gui_base->get_icon("DistractionFree", "EditorIcons"));
6165 distraction_free->set_toggle_mode(true);
6166
6167 scene_tab_add = memnew(ToolButton);
6168 tabbar_container->add_child(scene_tab_add);
6169 tabbar_container->add_child(distraction_free);
6170 scene_tab_add->set_tooltip(TTR("Add a new scene."));
6171 scene_tab_add->set_icon(gui_base->get_icon("Add", "EditorIcons"));
6172 scene_tab_add->add_color_override("icon_color_normal", Color(0.6f, 0.6f, 0.6f, 0.8f));
6173 scene_tab_add->connect("pressed", this, "_menu_option", make_binds(FILE_NEW_SCENE));
6174
6175 scene_root_parent = memnew(PanelContainer);
6176 scene_root_parent->set_custom_minimum_size(Size2(0, 80) * EDSCALE);
6177 scene_root_parent->add_style_override("panel", gui_base->get_stylebox("Content", "EditorStyles"));
6178 scene_root_parent->set_draw_behind_parent(true);
6179 srt->add_child(scene_root_parent);
6180 scene_root_parent->set_v_size_flags(Control::SIZE_EXPAND_FILL);
6181
6182 scene_root = memnew(Viewport);
6183 //scene_root->set_usage(Viewport::USAGE_2D); canvas BG mode prevents usage of this as 2D
6184 scene_root->set_disable_3d(true);
6185
6186 VisualServer::get_singleton()->viewport_set_hide_scenario(scene_root->get_viewport_rid(), true);
6187 scene_root->set_disable_input(true);
6188 scene_root->set_as_audio_listener_2d(true);
6189
6190 viewport = memnew(VBoxContainer);
6191 viewport->set_v_size_flags(Control::SIZE_EXPAND_FILL);
6192 viewport->add_constant_override("separation", 0);
6193 scene_root_parent->add_child(viewport);
6194
6195 HBoxContainer *left_menu_hb = memnew(HBoxContainer);
6196 menu_hb->add_child(left_menu_hb);
6197
6198 file_menu = memnew(MenuButton);
6199 file_menu->set_flat(false);
6200 file_menu->set_switch_on_hover(true);
6201 file_menu->set_text(TTR("Scene"));
6202 file_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
6203 left_menu_hb->add_child(file_menu);
6204
6205 prev_scene = memnew(ToolButton);
6206 prev_scene->set_icon(gui_base->get_icon("PrevScene", "EditorIcons"));
6207 prev_scene->set_tooltip(TTR("Go to previously opened scene."));
6208 prev_scene->set_disabled(true);
6209 prev_scene->connect("pressed", this, "_menu_option", make_binds(FILE_OPEN_PREV));
6210 gui_base->add_child(prev_scene);
6211 prev_scene->set_position(Point2(3, 24));
6212 prev_scene->hide();
6213
6214 accept = memnew(AcceptDialog);
6215 gui_base->add_child(accept);
6216 accept->connect("confirmed", this, "_menu_confirm_current");
6217
6218 project_export = memnew(ProjectExportDialog);
6219 gui_base->add_child(project_export);
6220
6221 dependency_error = memnew(DependencyErrorDialog);
6222 gui_base->add_child(dependency_error);
6223
6224 dependency_fixer = memnew(DependencyEditor);
6225 gui_base->add_child(dependency_fixer);
6226
6227 settings_config_dialog = memnew(EditorSettingsDialog);
6228 gui_base->add_child(settings_config_dialog);
6229
6230 project_settings = memnew(ProjectSettingsEditor(&editor_data));
6231 gui_base->add_child(project_settings);
6232
6233 run_settings_dialog = memnew(RunSettingsDialog);
6234 gui_base->add_child(run_settings_dialog);
6235
6236 export_template_manager = memnew(ExportTemplateManager);
6237 gui_base->add_child(export_template_manager);
6238
6239 feature_profile_manager = memnew(EditorFeatureProfileManager);
6240 gui_base->add_child(feature_profile_manager);
6241 about = memnew(EditorAbout);
6242 gui_base->add_child(about);
6243 feature_profile_manager->connect("current_feature_profile_changed", this, "_feature_profile_changed");
6244
6245 warning = memnew(AcceptDialog);
6246 warning->add_button(TTR("Copy Text"), true, "copy");
6247 gui_base->add_child(warning);
6248 warning->connect("custom_action", this, "_copy_warning");
6249
6250 ED_SHORTCUT("editor/next_tab", TTR("Next tab"), KEY_MASK_CMD + KEY_TAB);
6251 ED_SHORTCUT("editor/prev_tab", TTR("Previous tab"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_TAB);
6252 ED_SHORTCUT("editor/filter_files", TTR("Filter Files..."), KEY_MASK_ALT + KEY_MASK_CMD + KEY_P);
6253 PopupMenu *p;
6254
6255 file_menu->set_tooltip(TTR("Operations with scene files."));
6256
6257 p = file_menu->get_popup();
6258 p->set_hide_on_window_lose_focus(true);
6259 p->add_shortcut(ED_SHORTCUT("editor/new_scene", TTR("New Scene")), FILE_NEW_SCENE);
6260 p->add_shortcut(ED_SHORTCUT("editor/new_inherited_scene", TTR("New Inherited Scene...")), FILE_NEW_INHERITED_SCENE);
6261 p->add_shortcut(ED_SHORTCUT("editor/open_scene", TTR("Open Scene..."), KEY_MASK_CMD + KEY_O), FILE_OPEN_SCENE);
6262 p->add_shortcut(ED_SHORTCUT("editor/reopen_closed_scene", TTR("Reopen Closed Scene"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_T), FILE_OPEN_PREV);
6263 p->add_submenu_item(TTR("Open Recent"), "RecentScenes", FILE_OPEN_RECENT);
6264
6265 p->add_separator();
6266 p->add_shortcut(ED_SHORTCUT("editor/save_scene", TTR("Save Scene"), KEY_MASK_CMD + KEY_S), FILE_SAVE_SCENE);
6267 p->add_shortcut(ED_SHORTCUT("editor/save_scene_as", TTR("Save Scene As..."), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_AS_SCENE);
6268 p->add_shortcut(ED_SHORTCUT("editor/save_all_scenes", TTR("Save All Scenes"), KEY_MASK_ALT + KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_ALL_SCENES);
6269
6270 p->add_separator();
6271 p->add_shortcut(ED_SHORTCUT("editor/quick_open", TTR("Quick Open..."), KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_O), FILE_QUICK_OPEN);
6272 p->add_shortcut(ED_SHORTCUT("editor/quick_open_scene", TTR("Quick Open Scene..."), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_O), FILE_QUICK_OPEN_SCENE);
6273 p->add_shortcut(ED_SHORTCUT("editor/quick_open_script", TTR("Quick Open Script..."), KEY_MASK_ALT + KEY_MASK_CMD + KEY_O), FILE_QUICK_OPEN_SCRIPT);
6274
6275 p->add_separator();
6276 PopupMenu *pm_export = memnew(PopupMenu);
6277 pm_export->set_name("Export");
6278 p->add_child(pm_export);
6279 p->add_submenu_item(TTR("Convert To..."), "Export");
6280 pm_export->add_shortcut(ED_SHORTCUT("editor/convert_to_MeshLibrary", TTR("MeshLibrary...")), FILE_EXPORT_MESH_LIBRARY);
6281 pm_export->add_shortcut(ED_SHORTCUT("editor/convert_to_TileSet", TTR("TileSet...")), FILE_EXPORT_TILESET);
6282 pm_export->connect("id_pressed", this, "_menu_option");
6283
6284 p->add_separator();
6285 p->add_shortcut(ED_SHORTCUT("editor/undo", TTR("Undo"), KEY_MASK_CMD + KEY_Z), EDIT_UNDO, true);
6286 p->add_shortcut(ED_SHORTCUT("editor/redo", TTR("Redo"), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_Z), EDIT_REDO, true);
6287
6288 p->add_separator();
6289 p->add_shortcut(ED_SHORTCUT("editor/reload_saved_scene", TTR("Reload Saved Scene")), EDIT_RELOAD_SAVED_SCENE);
6290 p->add_shortcut(ED_SHORTCUT("editor/close_scene", TTR("Close Scene"), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_W), FILE_CLOSE);
6291
6292 recent_scenes = memnew(PopupMenu);
6293 recent_scenes->set_name("RecentScenes");
6294 p->add_child(recent_scenes);
6295 recent_scenes->connect("id_pressed", this, "_open_recent_scene");
6296
6297 p->add_separator();
6298 p->add_shortcut(ED_SHORTCUT("editor/file_quit", TTR("Quit"), KEY_MASK_CMD + KEY_Q), FILE_QUIT, true);
6299
6300 project_menu = memnew(MenuButton);
6301 project_menu->set_flat(false);
6302 project_menu->set_switch_on_hover(true);
6303 project_menu->set_tooltip(TTR("Miscellaneous project or scene-wide tools."));
6304 project_menu->set_text(TTR("Project"));
6305 project_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
6306 left_menu_hb->add_child(project_menu);
6307
6308 p = project_menu->get_popup();
6309 p->set_hide_on_window_lose_focus(true);
6310 p->add_shortcut(ED_SHORTCUT("editor/project_settings", TTR("Project Settings...")), RUN_SETTINGS);
6311 p->connect("id_pressed", this, "_menu_option");
6312
6313 vcs_actions_menu = VersionControlEditorPlugin::get_singleton()->get_version_control_actions_panel();
6314 vcs_actions_menu->set_name("Version Control");
6315 vcs_actions_menu->connect("index_pressed", this, "_version_control_menu_option");
6316 p->add_separator();
6317 p->add_child(vcs_actions_menu);
6318 p->add_submenu_item(TTR("Version Control"), "Version Control");
6319 vcs_actions_menu->add_item(TTR("Set Up Version Control"), RUN_VCS_SETTINGS);
6320 vcs_actions_menu->add_item(TTR("Shut Down Version Control"), RUN_VCS_SHUT_DOWN);
6321
6322 p->add_separator();
6323 p->add_shortcut(ED_SHORTCUT("editor/export", TTR("Export...")), FILE_EXPORT_PROJECT);
6324 p->add_item(TTR("Install Android Build Template..."), FILE_INSTALL_ANDROID_SOURCE);
6325 p->add_item(TTR("Open Project Data Folder"), RUN_PROJECT_DATA_FOLDER);
6326
6327 plugin_config_dialog = memnew(PluginConfigDialog);
6328 plugin_config_dialog->connect("plugin_ready", this, "_on_plugin_ready");
6329 gui_base->add_child(plugin_config_dialog);
6330
6331 tool_menu = memnew(PopupMenu);
6332 tool_menu->set_name("Tools");
6333 tool_menu->connect("index_pressed", this, "_tool_menu_option");
6334 p->add_child(tool_menu);
6335 p->add_submenu_item(TTR("Tools"), "Tools");
6336 tool_menu->add_item(TTR("Orphan Resource Explorer..."), TOOLS_ORPHAN_RESOURCES);
6337
6338 p->add_separator();
6339 #ifdef OSX_ENABLED
6340 p->add_shortcut(ED_SHORTCUT("editor/quit_to_project_list", TTR("Quit to Project List"), KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_Q), RUN_PROJECT_MANAGER, true);
6341 #else
6342 p->add_shortcut(ED_SHORTCUT("editor/quit_to_project_list", TTR("Quit to Project List"), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_Q), RUN_PROJECT_MANAGER, true);
6343 #endif
6344
6345 menu_hb->add_spacer();
6346
6347 main_editor_button_vb = memnew(HBoxContainer);
6348 menu_hb->add_child(main_editor_button_vb);
6349
6350 debug_menu = memnew(MenuButton);
6351 debug_menu->set_flat(false);
6352 debug_menu->set_switch_on_hover(true);
6353 debug_menu->set_text(TTR("Debug"));
6354 debug_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
6355 left_menu_hb->add_child(debug_menu);
6356
6357 p = debug_menu->get_popup();
6358 p->set_hide_on_window_lose_focus(true);
6359 p->set_hide_on_checkable_item_selection(false);
6360 p->add_check_shortcut(ED_SHORTCUT("editor/deploy_with_remote_debug", TTR("Deploy with Remote Debug")), RUN_DEPLOY_REMOTE_DEBUG);
6361 p->set_item_tooltip(p->get_item_count() - 1, TTR("When exporting or deploying, the resulting executable will attempt to connect to the IP of this computer in order to be debugged."));
6362 p->add_check_shortcut(ED_SHORTCUT("editor/small_deploy_with_network_fs", TTR("Small Deploy with Network FS")), RUN_FILE_SERVER);
6363 p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is enabled, export or deploy will produce a minimal executable.\nThe filesystem will be provided from the project by the editor over the network.\nOn Android, deploy will use the USB cable for faster performance. This option speeds up testing for games with a large footprint."));
6364 p->add_separator();
6365 p->add_check_shortcut(ED_SHORTCUT("editor/visible_collision_shapes", TTR("Visible Collision Shapes")), RUN_DEBUG_COLLISONS);
6366 p->set_item_tooltip(p->get_item_count() - 1, TTR("Collision shapes and raycast nodes (for 2D and 3D) will be visible on the running game if this option is turned on."));
6367 p->add_check_shortcut(ED_SHORTCUT("editor/visible_navigation", TTR("Visible Navigation")), RUN_DEBUG_NAVIGATION);
6368 p->set_item_tooltip(p->get_item_count() - 1, TTR("Navigation meshes and polygons will be visible on the running game if this option is turned on."));
6369 p->add_separator();
6370 p->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTR("Sync Scene Changes")), RUN_LIVE_DEBUG);
6371 p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any changes made to the scene in the editor will be replicated in the running game.\nWhen used remotely on a device, this is more efficient with network filesystem."));
6372 p->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTR("Sync Script Changes")), RUN_RELOAD_SCRIPTS);
6373 p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any script that is saved will be reloaded on the running game.\nWhen used remotely on a device, this is more efficient with network filesystem."));
6374 p->connect("id_pressed", this, "_menu_option");
6375
6376 menu_hb->add_spacer();
6377
6378 settings_menu = memnew(MenuButton);
6379 settings_menu->set_flat(false);
6380 settings_menu->set_switch_on_hover(true);
6381 settings_menu->set_text(TTR("Editor"));
6382 settings_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
6383 left_menu_hb->add_child(settings_menu);
6384
6385 p = settings_menu->get_popup();
6386 p->set_hide_on_window_lose_focus(true);
6387 #ifdef OSX_ENABLED
6388 p->add_shortcut(ED_SHORTCUT("editor/editor_settings", TTR("Editor Settings..."), KEY_MASK_CMD + KEY_COMMA), SETTINGS_PREFERENCES);
6389 #else
6390 p->add_shortcut(ED_SHORTCUT("editor/editor_settings", TTR("Editor Settings...")), SETTINGS_PREFERENCES);
6391 #endif
6392 p->add_separator();
6393
6394 editor_layouts = memnew(PopupMenu);
6395 editor_layouts->set_name("Layouts");
6396 p->add_child(editor_layouts);
6397 editor_layouts->connect("id_pressed", this, "_layout_menu_option");
6398 p->add_submenu_item(TTR("Editor Layout"), "Layouts");
6399 p->add_separator();
6400 #ifdef OSX_ENABLED
6401 p->add_shortcut(ED_SHORTCUT("editor/take_screenshot", TTR("Take Screenshot"), KEY_MASK_CMD | KEY_F12), EDITOR_SCREENSHOT);
6402 #else
6403 p->add_shortcut(ED_SHORTCUT("editor/take_screenshot", TTR("Take Screenshot"), KEY_MASK_CTRL | KEY_F12), EDITOR_SCREENSHOT);
6404 #endif
6405 p->set_item_tooltip(p->get_item_count() - 1, TTR("Screenshots are stored in the Editor Data/Settings Folder."));
6406 #ifdef OSX_ENABLED
6407 p->add_shortcut(ED_SHORTCUT("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KEY_MASK_CMD | KEY_MASK_CTRL | KEY_F), SETTINGS_TOGGLE_FULLSCREEN);
6408 #else
6409 p->add_shortcut(ED_SHORTCUT("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KEY_MASK_SHIFT | KEY_F11), SETTINGS_TOGGLE_FULLSCREEN);
6410 #endif
6411 #ifdef WINDOWS_ENABLED
6412 p->add_item(TTR("Toggle System Console"), SETTINGS_TOGGLE_CONSOLE);
6413 #endif
6414 p->add_separator();
6415
6416 if (OS::get_singleton()->get_data_path() == OS::get_singleton()->get_config_path()) {
6417 // Configuration and data folders are located in the same place (Windows/macOS)
6418 p->add_item(TTR("Open Editor Data/Settings Folder"), SETTINGS_EDITOR_DATA_FOLDER);
6419 } else {
6420 // Separate configuration and data folders (Linux)
6421 p->add_item(TTR("Open Editor Data Folder"), SETTINGS_EDITOR_DATA_FOLDER);
6422 p->add_item(TTR("Open Editor Settings Folder"), SETTINGS_EDITOR_CONFIG_FOLDER);
6423 }
6424 p->add_separator();
6425
6426 p->add_item(TTR("Manage Editor Features..."), SETTINGS_MANAGE_FEATURE_PROFILES);
6427 p->add_item(TTR("Manage Export Templates..."), SETTINGS_MANAGE_EXPORT_TEMPLATES);
6428
6429 // Help Menu
6430 help_menu = memnew(MenuButton);
6431 help_menu->set_flat(false);
6432 help_menu->set_switch_on_hover(true);
6433 help_menu->set_text(TTR("Help"));
6434 help_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
6435 left_menu_hb->add_child(help_menu);
6436
6437 p = help_menu->get_popup();
6438 p->set_hide_on_window_lose_focus(true);
6439 p->connect("id_pressed", this, "_menu_option");
6440 p->add_icon_shortcut(gui_base->get_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("editor/editor_help", TTR("Search"), KEY_MASK_SHIFT | KEY_F1), HELP_SEARCH);
6441 p->add_separator();
6442 p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/online_docs", TTR("Online Docs")), HELP_DOCS);
6443 p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/q&a", TTR("Q&A")), HELP_QA);
6444 p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG);
6445 p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK);
6446 p->add_icon_shortcut(gui_base->get_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/community", TTR("Community")), HELP_COMMUNITY);
6447 p->add_separator();
6448 p->add_icon_shortcut(gui_base->get_icon("Godot", "EditorIcons"), ED_SHORTCUT("editor/about", TTR("About")), HELP_ABOUT);
6449
6450 HBoxContainer *play_hb = memnew(HBoxContainer);
6451 menu_hb->add_child(play_hb);
6452
6453 play_button = memnew(ToolButton);
6454 play_hb->add_child(play_button);
6455 play_button->set_toggle_mode(true);
6456 play_button->set_icon(gui_base->get_icon("MainPlay", "EditorIcons"));
6457 play_button->set_focus_mode(Control::FOCUS_NONE);
6458 play_button->connect("pressed", this, "_menu_option", make_binds(RUN_PLAY));
6459 play_button->set_tooltip(TTR("Play the project."));
6460 #ifdef OSX_ENABLED
6461 play_button->set_shortcut(ED_SHORTCUT("editor/play", TTR("Play"), KEY_MASK_CMD | KEY_B));
6462 #else
6463 play_button->set_shortcut(ED_SHORTCUT("editor/play", TTR("Play"), KEY_F5));
6464 #endif
6465
6466 pause_button = memnew(ToolButton);
6467 pause_button->set_toggle_mode(true);
6468 pause_button->set_icon(gui_base->get_icon("Pause", "EditorIcons"));
6469 pause_button->set_focus_mode(Control::FOCUS_NONE);
6470 pause_button->set_tooltip(TTR("Pause the scene execution for debugging."));
6471 pause_button->set_disabled(true);
6472 play_hb->add_child(pause_button);
6473 #ifdef OSX_ENABLED
6474 pause_button->set_shortcut(ED_SHORTCUT("editor/pause_scene", TTR("Pause Scene"), KEY_MASK_CMD | KEY_MASK_CTRL | KEY_Y));
6475 #else
6476 pause_button->set_shortcut(ED_SHORTCUT("editor/pause_scene", TTR("Pause Scene"), KEY_F7));
6477 #endif
6478
6479 stop_button = memnew(ToolButton);
6480 play_hb->add_child(stop_button);
6481 stop_button->set_focus_mode(Control::FOCUS_NONE);
6482 stop_button->set_icon(gui_base->get_icon("Stop", "EditorIcons"));
6483 stop_button->connect("pressed", this, "_menu_option", make_binds(RUN_STOP));
6484 stop_button->set_tooltip(TTR("Stop the scene."));
6485 stop_button->set_disabled(true);
6486 #ifdef OSX_ENABLED
6487 stop_button->set_shortcut(ED_SHORTCUT("editor/stop", TTR("Stop"), KEY_MASK_CMD | KEY_PERIOD));
6488 #else
6489 stop_button->set_shortcut(ED_SHORTCUT("editor/stop", TTR("Stop"), KEY_F8));
6490 #endif
6491
6492 run_native = memnew(EditorRunNative);
6493 play_hb->add_child(run_native);
6494 run_native->connect("native_run", this, "_menu_option", varray(RUN_PLAY_NATIVE));
6495
6496 play_scene_button = memnew(ToolButton);
6497 play_hb->add_child(play_scene_button);
6498 play_scene_button->set_toggle_mode(true);
6499 play_scene_button->set_focus_mode(Control::FOCUS_NONE);
6500 play_scene_button->set_icon(gui_base->get_icon("PlayScene", "EditorIcons"));
6501 play_scene_button->connect("pressed", this, "_menu_option", make_binds(RUN_PLAY_SCENE));
6502 play_scene_button->set_tooltip(TTR("Play the edited scene."));
6503 #ifdef OSX_ENABLED
6504 play_scene_button->set_shortcut(ED_SHORTCUT("editor/play_scene", TTR("Play Scene"), KEY_MASK_CMD | KEY_R));
6505 #else
6506 play_scene_button->set_shortcut(ED_SHORTCUT("editor/play_scene", TTR("Play Scene"), KEY_F6));
6507 #endif
6508
6509 play_custom_scene_button = memnew(ToolButton);
6510 play_hb->add_child(play_custom_scene_button);
6511 play_custom_scene_button->set_toggle_mode(true);
6512 play_custom_scene_button->set_focus_mode(Control::FOCUS_NONE);
6513 play_custom_scene_button->set_icon(gui_base->get_icon("PlayCustom", "EditorIcons"));
6514 play_custom_scene_button->connect("pressed", this, "_menu_option", make_binds(RUN_PLAY_CUSTOM_SCENE));
6515 play_custom_scene_button->set_tooltip(TTR("Play custom scene"));
6516 #ifdef OSX_ENABLED
6517 play_custom_scene_button->set_shortcut(ED_SHORTCUT("editor/play_custom_scene", TTR("Play Custom Scene"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R));
6518 #else
6519 play_custom_scene_button->set_shortcut(ED_SHORTCUT("editor/play_custom_scene", TTR("Play Custom Scene"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F5));
6520 #endif
6521
6522 HBoxContainer *right_menu_hb = memnew(HBoxContainer);
6523 menu_hb->add_child(right_menu_hb);
6524
6525 // Toggle for video driver
6526 video_driver = memnew(OptionButton);
6527 video_driver->set_flat(true);
6528 video_driver->set_focus_mode(Control::FOCUS_NONE);
6529 video_driver->connect("item_selected", this, "_video_driver_selected");
6530 video_driver->add_font_override("font", gui_base->get_font("bold", "EditorFonts"));
6531 right_menu_hb->add_child(video_driver);
6532
6533 String video_drivers = ProjectSettings::get_singleton()->get_custom_property_info()["rendering/quality/driver/driver_name"].hint_string;
6534 String current_video_driver = OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver());
6535 video_driver_current = 0;
6536 for (int i = 0; i < video_drivers.get_slice_count(","); i++) {
6537 String driver = video_drivers.get_slice(",", i);
6538 video_driver->add_item(driver);
6539 video_driver->set_item_metadata(i, driver);
6540
6541 if (current_video_driver == driver) {
6542 video_driver->select(i);
6543 video_driver_current = i;
6544 }
6545 }
6546
6547 _update_video_driver_color();
6548
6549 video_restart_dialog = memnew(ConfirmationDialog);
6550 video_restart_dialog->set_text(TTR("Changing the video driver requires restarting the editor."));
6551 video_restart_dialog->get_ok()->set_text(TTR("Save & Restart"));
6552 video_restart_dialog->connect("confirmed", this, "_menu_option", varray(SET_VIDEO_DRIVER_SAVE_AND_RESTART));
6553 gui_base->add_child(video_restart_dialog);
6554
6555 progress_hb = memnew(BackgroundProgress);
6556
6557 layout_dialog = memnew(EditorLayoutsDialog);
6558 gui_base->add_child(layout_dialog);
6559 layout_dialog->set_hide_on_ok(false);
6560 layout_dialog->set_size(Size2(225, 270) * EDSCALE);
6561 layout_dialog->connect("name_confirmed", this, "_dialog_action");
6562
6563 update_spinner = memnew(MenuButton);
6564 update_spinner->set_tooltip(TTR("Spins when the editor window redraws."));
6565 right_menu_hb->add_child(update_spinner);
6566 update_spinner->set_icon(gui_base->get_icon("Progress1", "EditorIcons"));
6567 update_spinner->get_popup()->connect("id_pressed", this, "_menu_option");
6568 p = update_spinner->get_popup();
6569 p->add_radio_check_item(TTR("Update Continuously"), SETTINGS_UPDATE_CONTINUOUSLY);
6570 p->add_radio_check_item(TTR("Update When Changed"), SETTINGS_UPDATE_WHEN_CHANGED);
6571 p->add_separator();
6572 p->add_item(TTR("Hide Update Spinner"), SETTINGS_UPDATE_SPINNER_HIDE);
6573 _update_update_spinner();
6574
6575 // Instantiate and place editor docks
6576
6577 scene_tree_dock = memnew(SceneTreeDock(this, scene_root, editor_selection, editor_data));
6578 inspector_dock = memnew(InspectorDock(this, editor_data));
6579 import_dock = memnew(ImportDock);
6580 node_dock = memnew(NodeDock);
6581
6582 filesystem_dock = memnew(FileSystemDock(this));
6583 filesystem_dock->connect("inherit", this, "_inherit_request");
6584 filesystem_dock->connect("instance", this, "_instance_request");
6585 filesystem_dock->connect("display_mode_changed", this, "_save_docks");
6586
6587 // Scene: Top left
6588 dock_slot[DOCK_SLOT_LEFT_UR]->add_child(scene_tree_dock);
6589 dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(scene_tree_dock->get_index(), TTR("Scene"));
6590
6591 // Import: Top left, behind Scene
6592 dock_slot[DOCK_SLOT_LEFT_UR]->add_child(import_dock);
6593 dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(import_dock->get_index(), TTR("Import"));
6594
6595 // FileSystem: Bottom left
6596 dock_slot[DOCK_SLOT_LEFT_BR]->add_child(filesystem_dock);
6597 dock_slot[DOCK_SLOT_LEFT_BR]->set_tab_title(filesystem_dock->get_index(), TTR("FileSystem"));
6598
6599 // Inspector: Full height right
6600 dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(inspector_dock);
6601 dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(inspector_dock->get_index(), TTR("Inspector"));
6602
6603 // Node: Full height right, behind Inspector
6604 dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(node_dock);
6605 dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(node_dock->get_index(), TTR("Node"));
6606
6607 // Hide unused dock slots and vsplits
6608 dock_slot[DOCK_SLOT_LEFT_UL]->hide();
6609 dock_slot[DOCK_SLOT_LEFT_BL]->hide();
6610 dock_slot[DOCK_SLOT_RIGHT_BL]->hide();
6611 dock_slot[DOCK_SLOT_RIGHT_UR]->hide();
6612 dock_slot[DOCK_SLOT_RIGHT_BR]->hide();
6613 left_l_vsplit->hide();
6614 right_r_vsplit->hide();
6615
6616 // Add some offsets to left_r and main hsplits to make LEFT_R and RIGHT_L docks wider than minsize
6617 left_r_hsplit->set_split_offset(70 * EDSCALE);
6618 main_hsplit->set_split_offset(-70 * EDSCALE);
6619
6620 // Define corresponding default layout
6621
6622 const String docks_section = "docks";
6623 overridden_default_layout = -1;
6624 default_layout.instance();
6625 // Dock numbers are based on DockSlot enum value + 1
6626 default_layout->set_value(docks_section, "dock_3", "Scene,Import");
6627 default_layout->set_value(docks_section, "dock_4", "FileSystem");
6628 default_layout->set_value(docks_section, "dock_5", "Inspector,Node");
6629
6630 for (int i = 0; i < vsplits.size(); i++)
6631 default_layout->set_value(docks_section, "dock_split_" + itos(i + 1), 0);
6632 default_layout->set_value(docks_section, "dock_hsplit_1", 0);
6633 default_layout->set_value(docks_section, "dock_hsplit_2", 70 * EDSCALE);
6634 default_layout->set_value(docks_section, "dock_hsplit_3", -70 * EDSCALE);
6635 default_layout->set_value(docks_section, "dock_hsplit_4", 0);
6636
6637 _update_layouts_menu();
6638
6639 // Bottom panels
6640
6641 bottom_panel = memnew(PanelContainer);
6642 bottom_panel->add_style_override("panel", gui_base->get_stylebox("panel", "TabContainer"));
6643 center_split->add_child(bottom_panel);
6644 center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
6645
6646 bottom_panel_vb = memnew(VBoxContainer);
6647 bottom_panel->add_child(bottom_panel_vb);
6648
6649 bottom_panel_hb = memnew(HBoxContainer);
6650 bottom_panel_hb->set_custom_minimum_size(Size2(0, 24 * EDSCALE)); // Adjust for the height of the "Expand Bottom Dock" icon.
6651 bottom_panel_vb->add_child(bottom_panel_hb);
6652
6653 bottom_panel_hb_editors = memnew(HBoxContainer);
6654 bottom_panel_hb_editors->set_h_size_flags(Control::SIZE_EXPAND_FILL);
6655 bottom_panel_hb->add_child(bottom_panel_hb_editors);
6656
6657 version_label = memnew(Label);
6658 version_label->set_text(VERSION_FULL_CONFIG);
6659 // Fade out the version label to be less prominent, but still readable
6660 version_label->set_self_modulate(Color(1, 1, 1, 0.6));
6661 bottom_panel_hb->add_child(version_label);
6662
6663 bottom_panel_raise = memnew(ToolButton);
6664 bottom_panel_raise->set_icon(gui_base->get_icon("ExpandBottomDock", "EditorIcons"));
6665
6666 bottom_panel_raise->set_shortcut(ED_SHORTCUT("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KEY_MASK_SHIFT | KEY_F12));
6667
6668 bottom_panel_hb->add_child(bottom_panel_raise);
6669 bottom_panel_raise->hide();
6670 bottom_panel_raise->set_toggle_mode(true);
6671 bottom_panel_raise->connect("toggled", this, "_bottom_panel_raise_toggled");
6672
6673 log = memnew(EditorLog);
6674 ToolButton *output_button = add_bottom_panel_item(TTR("Output"), log);
6675 log->set_tool_button(output_button);
6676
6677 old_split_ofs = 0;
6678
6679 center_split->connect("resized", this, "_vp_resized");
6680
6681 orphan_resources = memnew(OrphanResourcesDialog);
6682 gui_base->add_child(orphan_resources);
6683
6684 confirmation = memnew(ConfirmationDialog);
6685 gui_base->add_child(confirmation);
6686 confirmation->connect("confirmed", this, "_menu_confirm_current");
6687
6688 save_confirmation = memnew(ConfirmationDialog);
6689 save_confirmation->add_button(TTR("Don't Save"), OS::get_singleton()->get_swap_ok_cancel(), "discard");
6690 gui_base->add_child(save_confirmation);
6691 save_confirmation->connect("confirmed", this, "_menu_confirm_current");
6692 save_confirmation->connect("custom_action", this, "_discard_changes");
6693
6694 custom_build_manage_templates = memnew(ConfirmationDialog);
6695 custom_build_manage_templates->set_text(TTR("Android build template is missing, please install relevant templates."));
6696 custom_build_manage_templates->get_ok()->set_text(TTR("Manage Templates"));
6697 custom_build_manage_templates->connect("confirmed", this, "_menu_option", varray(SETTINGS_MANAGE_EXPORT_TEMPLATES));
6698 gui_base->add_child(custom_build_manage_templates);
6699
6700 install_android_build_template = memnew(ConfirmationDialog);
6701 install_android_build_template->set_text(TTR("This will set up your project for custom Android builds by installing the source template to \"res://android/build\".\nYou can then apply modifications and build your own custom APK on export (adding modules, changing the AndroidManifest.xml, etc.).\nNote that in order to make custom builds instead of using pre-built APKs, the \"Use Custom Build\" option should be enabled in the Android export preset."));
6702 install_android_build_template->get_ok()->set_text(TTR("Install"));
6703 install_android_build_template->connect("confirmed", this, "_menu_confirm_current");
6704 gui_base->add_child(install_android_build_template);
6705
6706 remove_android_build_template = memnew(ConfirmationDialog);
6707 remove_android_build_template->set_text(TTR("The Android build template is already installed in this project and it won't be overwritten.\nRemove the \"res://android/build\" directory manually before attempting this operation again."));
6708 remove_android_build_template->get_ok()->set_text(TTR("Show in File Manager"));
6709 remove_android_build_template->connect("confirmed", this, "_menu_option", varray(FILE_EXPLORE_ANDROID_BUILD_TEMPLATES));
6710 gui_base->add_child(remove_android_build_template);
6711
6712 file_templates = memnew(EditorFileDialog);
6713 file_templates->set_title(TTR("Import Templates From ZIP File"));
6714
6715 gui_base->add_child(file_templates);
6716 file_templates->set_mode(EditorFileDialog::MODE_OPEN_FILE);
6717 file_templates->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
6718 file_templates->clear_filters();
6719 file_templates->add_filter("*.tpz ; " + TTR("Template Package"));
6720
6721 file = memnew(EditorFileDialog);
6722 gui_base->add_child(file);
6723 file->set_current_dir("res://");
6724
6725 file_export_lib = memnew(EditorFileDialog);
6726 file_export_lib->set_title(TTR("Export Library"));
6727 file_export_lib->set_mode(EditorFileDialog::MODE_SAVE_FILE);
6728 file_export_lib->connect("file_selected", this, "_dialog_action");
6729 file_export_lib_merge = memnew(CheckBox);
6730 file_export_lib_merge->set_text(TTR("Merge With Existing"));
6731 file_export_lib_merge->set_pressed(true);
6732 file_export_lib->get_vbox()->add_child(file_export_lib_merge);
6733 gui_base->add_child(file_export_lib);
6734
6735 file_script = memnew(EditorFileDialog);
6736 file_script->set_title(TTR("Open & Run a Script"));
6737 file_script->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
6738 file_script->set_mode(EditorFileDialog::MODE_OPEN_FILE);
6739 List<String> sexts;
6740 ResourceLoader::get_recognized_extensions_for_type("Script", &sexts);
6741 for (List<String>::Element *E = sexts.front(); E; E = E->next()) {
6742 file_script->add_filter("*." + E->get());
6743 }
6744 gui_base->add_child(file_script);
6745 file_script->connect("file_selected", this, "_dialog_action");
6746
6747 file_menu->get_popup()->connect("id_pressed", this, "_menu_option");
6748 file_menu->connect("about_to_show", this, "_update_file_menu_opened");
6749 file_menu->get_popup()->connect("popup_hide", this, "_update_file_menu_closed");
6750
6751 settings_menu->get_popup()->connect("id_pressed", this, "_menu_option");
6752
6753 file->connect("file_selected", this, "_dialog_action");
6754 file_templates->connect("file_selected", this, "_dialog_action");
6755
6756 preview_gen = memnew(AudioStreamPreviewGenerator);
6757 add_child(preview_gen);
6758 //plugin stuff
6759
6760 file_server = memnew(EditorFileServer);
6761
6762 add_editor_plugin(memnew(AnimationPlayerEditorPlugin(this)));
6763 add_editor_plugin(memnew(CanvasItemEditorPlugin(this)));
6764 add_editor_plugin(memnew(SpatialEditorPlugin(this)));
6765 add_editor_plugin(memnew(ScriptEditorPlugin(this)));
6766
6767 EditorAudioBuses *audio_bus_editor = EditorAudioBuses::register_editor();
6768
6769 ScriptTextEditor::register_editor(); //register one for text scripts
6770 TextEditor::register_editor();
6771
6772 if (StreamPeerSSL::is_available()) {
6773 add_editor_plugin(memnew(AssetLibraryEditorPlugin(this)));
6774 } else {
6775 WARN_PRINT("Asset Library not available, as it requires SSL to work.");
6776 }
6777
6778 //add interface before adding plugins
6779
6780 editor_interface = memnew(EditorInterface);
6781 add_child(editor_interface);
6782
6783 //more visually meaningful to have this later
6784 raise_bottom_panel_item(AnimationPlayerEditor::singleton);
6785
6786 add_editor_plugin(VersionControlEditorPlugin::get_singleton());
6787 add_editor_plugin(memnew(ShaderEditorPlugin(this)));
6788 add_editor_plugin(memnew(VisualShaderEditorPlugin(this)));
6789
6790 add_editor_plugin(memnew(CameraEditorPlugin(this)));
6791 add_editor_plugin(memnew(ThemeEditorPlugin(this)));
6792 add_editor_plugin(memnew(MultiMeshEditorPlugin(this)));
6793 add_editor_plugin(memnew(MeshInstanceEditorPlugin(this)));
6794 add_editor_plugin(memnew(AnimationTreeEditorPlugin(this)));
6795 add_editor_plugin(memnew(AnimationTreePlayerEditorPlugin(this)));
6796 add_editor_plugin(memnew(MeshLibraryEditorPlugin(this)));
6797 add_editor_plugin(memnew(StyleBoxEditorPlugin(this)));
6798 add_editor_plugin(memnew(SpriteEditorPlugin(this)));
6799 add_editor_plugin(memnew(Skeleton2DEditorPlugin(this)));
6800 add_editor_plugin(memnew(ParticlesEditorPlugin(this)));
6801 add_editor_plugin(memnew(CPUParticles2DEditorPlugin(this)));
6802 add_editor_plugin(memnew(CPUParticlesEditorPlugin(this)));
6803 add_editor_plugin(memnew(ResourcePreloaderEditorPlugin(this)));
6804 add_editor_plugin(memnew(ItemListEditorPlugin(this)));
6805 add_editor_plugin(memnew(Polygon3DEditorPlugin(this)));
6806 add_editor_plugin(memnew(CollisionPolygon2DEditorPlugin(this)));
6807 add_editor_plugin(memnew(TileSetEditorPlugin(this)));
6808 add_editor_plugin(memnew(TileMapEditorPlugin(this)));
6809 add_editor_plugin(memnew(SpriteFramesEditorPlugin(this)));
6810 add_editor_plugin(memnew(TextureRegionEditorPlugin(this)));
6811 add_editor_plugin(memnew(Particles2DEditorPlugin(this)));
6812 add_editor_plugin(memnew(GIProbeEditorPlugin(this)));
6813 add_editor_plugin(memnew(BakedLightmapEditorPlugin(this)));
6814 add_editor_plugin(memnew(Path2DEditorPlugin(this)));
6815 add_editor_plugin(memnew(PathEditorPlugin(this)));
6816 add_editor_plugin(memnew(Line2DEditorPlugin(this)));
6817 add_editor_plugin(memnew(Polygon2DEditorPlugin(this)));
6818 add_editor_plugin(memnew(LightOccluder2DEditorPlugin(this)));
6819 add_editor_plugin(memnew(NavigationPolygonEditorPlugin(this)));
6820 add_editor_plugin(memnew(GradientEditorPlugin(this)));
6821 add_editor_plugin(memnew(CollisionShape2DEditorPlugin(this)));
6822 add_editor_plugin(memnew(CurveEditorPlugin(this)));
6823 add_editor_plugin(memnew(TextureEditorPlugin(this)));
6824 add_editor_plugin(memnew(AudioStreamEditorPlugin(this)));
6825 add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
6826 add_editor_plugin(memnew(SkeletonEditorPlugin(this)));
6827 add_editor_plugin(memnew(SkeletonIKEditorPlugin(this)));
6828 add_editor_plugin(memnew(PhysicalBonePlugin(this)));
6829 add_editor_plugin(memnew(MeshEditorPlugin(this)));
6830 add_editor_plugin(memnew(MaterialEditorPlugin(this)));
6831
6832 for (int i = 0; i < EditorPlugins::get_plugin_count(); i++)
6833 add_editor_plugin(EditorPlugins::create(i, this));
6834
6835 for (int i = 0; i < plugin_init_callback_count; i++) {
6836 plugin_init_callbacks[i]();
6837 }
6838
6839 resource_preview->add_preview_generator(Ref<EditorTexturePreviewPlugin>(memnew(EditorTexturePreviewPlugin)));
6840 resource_preview->add_preview_generator(Ref<EditorImagePreviewPlugin>(memnew(EditorImagePreviewPlugin)));
6841 resource_preview->add_preview_generator(Ref<EditorPackedScenePreviewPlugin>(memnew(EditorPackedScenePreviewPlugin)));
6842 resource_preview->add_preview_generator(Ref<EditorMaterialPreviewPlugin>(memnew(EditorMaterialPreviewPlugin)));
6843 resource_preview->add_preview_generator(Ref<EditorScriptPreviewPlugin>(memnew(EditorScriptPreviewPlugin)));
6844 resource_preview->add_preview_generator(Ref<EditorAudioStreamPreviewPlugin>(memnew(EditorAudioStreamPreviewPlugin)));
6845 resource_preview->add_preview_generator(Ref<EditorMeshPreviewPlugin>(memnew(EditorMeshPreviewPlugin)));
6846 resource_preview->add_preview_generator(Ref<EditorBitmapPreviewPlugin>(memnew(EditorBitmapPreviewPlugin)));
6847 resource_preview->add_preview_generator(Ref<EditorFontPreviewPlugin>(memnew(EditorFontPreviewPlugin)));
6848
6849 {
6850 Ref<SpatialMaterialConversionPlugin> spatial_mat_convert;
6851 spatial_mat_convert.instance();
6852 resource_conversion_plugins.push_back(spatial_mat_convert);
6853
6854 Ref<CanvasItemMaterialConversionPlugin> canvas_item_mat_convert;
6855 canvas_item_mat_convert.instance();
6856 resource_conversion_plugins.push_back(canvas_item_mat_convert);
6857
6858 Ref<ParticlesMaterialConversionPlugin> particles_mat_convert;
6859 particles_mat_convert.instance();
6860 resource_conversion_plugins.push_back(particles_mat_convert);
6861
6862 Ref<VisualShaderConversionPlugin> vshader_convert;
6863 vshader_convert.instance();
6864 resource_conversion_plugins.push_back(vshader_convert);
6865 }
6866 update_spinner_step_msec = OS::get_singleton()->get_ticks_msec();
6867 update_spinner_step_frame = Engine::get_singleton()->get_frames_drawn();
6868 update_spinner_step = 0;
6869
6870 editor_plugin_screen = NULL;
6871 editor_plugins_over = memnew(EditorPluginList);
6872 editor_plugins_force_over = memnew(EditorPluginList);
6873 editor_plugins_force_input_forwarding = memnew(EditorPluginList);
6874
6875 Ref<EditorExportTextSceneToBinaryPlugin> export_text_to_binary_plugin;
6876 export_text_to_binary_plugin.instance();
6877
6878 EditorExport::get_singleton()->add_export_plugin(export_text_to_binary_plugin);
6879
6880 _edit_current();
6881 current = NULL;
6882 saving_resource = Ref<Resource>();
6883
6884 reference_resource_mem = true;
6885 save_external_resources_mem = true;
6886
6887 set_process(true);
6888
6889 open_imported = memnew(ConfirmationDialog);
6890 open_imported->get_ok()->set_text(TTR("Open Anyway"));
6891 new_inherited_button = open_imported->add_button(TTR("New Inherited"), !OS::get_singleton()->get_swap_ok_cancel(), "inherit");
6892 open_imported->connect("confirmed", this, "_open_imported");
6893 open_imported->connect("custom_action", this, "_inherit_imported");
6894 gui_base->add_child(open_imported);
6895
6896 saved_version = 1;
6897 unsaved_cache = true;
6898 _last_instanced_scene = NULL;
6899
6900 quick_open = memnew(EditorQuickOpen);
6901 gui_base->add_child(quick_open);
6902 quick_open->connect("quick_open", this, "_quick_opened");
6903
6904 quick_run = memnew(EditorQuickOpen);
6905 gui_base->add_child(quick_run);
6906 quick_run->connect("quick_open", this, "_quick_run");
6907
6908 _update_recent_scenes();
6909
6910 editor_data.restore_editor_global_states();
6911 convert_old = false;
6912 opening_prev = false;
6913 set_process_unhandled_input(true);
6914 _playing_edited = false;
6915
6916 load_errors = memnew(RichTextLabel);
6917 load_error_dialog = memnew(AcceptDialog);
6918 load_error_dialog->add_child(load_errors);
6919 load_error_dialog->set_title(TTR("Load Errors"));
6920 gui_base->add_child(load_error_dialog);
6921
6922 execute_outputs = memnew(RichTextLabel);
6923 execute_outputs->set_selection_enabled(true);
6924 execute_output_dialog = memnew(AcceptDialog);
6925 execute_output_dialog->add_child(execute_outputs);
6926 execute_output_dialog->set_title("");
6927 gui_base->add_child(execute_output_dialog);
6928
6929 EditorFileSystem::get_singleton()->connect("sources_changed", this, "_sources_changed");
6930 EditorFileSystem::get_singleton()->connect("filesystem_changed", this, "_fs_changed");
6931 EditorFileSystem::get_singleton()->connect("resources_reimported", this, "_resources_reimported");
6932 EditorFileSystem::get_singleton()->connect("resources_reload", this, "_resources_changed");
6933
6934 _build_icon_type_cache();
6935
6936 Node::set_human_readable_collision_renaming(true);
6937
6938 pick_main_scene = memnew(ConfirmationDialog);
6939 gui_base->add_child(pick_main_scene);
6940 pick_main_scene->get_ok()->set_text(TTR("Select"));
6941 pick_main_scene->connect("confirmed", this, "_menu_option", varray(SETTINGS_PICK_MAIN_SCENE));
6942
6943 for (int i = 0; i < _init_callbacks.size(); i++)
6944 _init_callbacks[i]();
6945
6946 editor_data.add_edited_scene(-1);
6947 editor_data.set_edited_scene(0);
6948 _update_scene_tabs();
6949
6950 import_dock->initialize_import_options();
6951
6952 FileAccess::set_file_close_fail_notify_callback(_file_access_close_error_notify);
6953
6954 waiting_for_first_scan = true;
6955
6956 print_handler.printfunc = _print_handler;
6957 print_handler.userdata = this;
6958 add_print_handler(&print_handler);
6959
6960 ResourceSaver::set_save_callback(_resource_saved);
6961 ResourceLoader::set_load_callback(_resource_loaded);
6962
6963 #ifdef OSX_ENABLED
6964 ED_SHORTCUT("editor/editor_2d", TTR("Open 2D Editor"), KEY_MASK_ALT | KEY_1);
6965 ED_SHORTCUT("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_ALT | KEY_2);
6966 ED_SHORTCUT("editor/editor_script", TTR("Open Script Editor"), KEY_MASK_ALT | KEY_3);
6967 ED_SHORTCUT("editor/editor_help", TTR("Search Help"), KEY_MASK_ALT | KEY_SPACE);
6968 #else
6969 ED_SHORTCUT("editor/editor_2d", TTR("Open 2D Editor"), KEY_F1);
6970 ED_SHORTCUT("editor/editor_3d", TTR("Open 3D Editor"), KEY_F2);
6971 ED_SHORTCUT("editor/editor_script", TTR("Open Script Editor"), KEY_F3); //hack needed for script editor F3 search to work :) Assign like this or don't use F3
6972 ED_SHORTCUT("editor/editor_help", TTR("Search Help"), KEY_MASK_SHIFT | KEY_F1);
6973 #endif
6974 ED_SHORTCUT("editor/editor_assetlib", TTR("Open Asset Library"));
6975 ED_SHORTCUT("editor/editor_next", TTR("Open the next Editor"));
6976 ED_SHORTCUT("editor/editor_prev", TTR("Open the previous Editor"));
6977
6978 screenshot_timer = memnew(Timer);
6979 screenshot_timer->set_one_shot(true);
6980 screenshot_timer->set_wait_time(settings_menu->get_popup()->get_submenu_popup_delay() + 0.1f);
6981 screenshot_timer->connect("timeout", this, "_request_screenshot");
6982 add_child(screenshot_timer);
6983 screenshot_timer->set_owner(get_owner());
6984
6985 String exec = OS::get_singleton()->get_executable_path();
6986 EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "executable_path", exec); // Save editor executable path for third-party tools
6987 }
6988
~EditorNode()6989 EditorNode::~EditorNode() {
6990
6991 EditorInspector::cleanup_plugins();
6992
6993 remove_print_handler(&print_handler);
6994 memdelete(EditorHelp::get_doc_data());
6995 memdelete(editor_selection);
6996 memdelete(editor_plugins_over);
6997 memdelete(editor_plugins_force_over);
6998 memdelete(editor_plugins_force_input_forwarding);
6999 memdelete(file_server);
7000 memdelete(progress_hb);
7001
7002 EditorSettings::destroy();
7003 }
7004
7005 /*
7006 * EDITOR PLUGIN LIST
7007 */
7008
make_visible(bool p_visible)7009 void EditorPluginList::make_visible(bool p_visible) {
7010
7011 for (int i = 0; i < plugins_list.size(); i++) {
7012 plugins_list[i]->make_visible(p_visible);
7013 }
7014 }
7015
edit(Object * p_object)7016 void EditorPluginList::edit(Object *p_object) {
7017
7018 for (int i = 0; i < plugins_list.size(); i++) {
7019 plugins_list[i]->edit(p_object);
7020 }
7021 }
7022
forward_gui_input(const Ref<InputEvent> & p_event)7023 bool EditorPluginList::forward_gui_input(const Ref<InputEvent> &p_event) {
7024
7025 bool discard = false;
7026
7027 for (int i = 0; i < plugins_list.size(); i++) {
7028 if (plugins_list[i]->forward_canvas_gui_input(p_event)) {
7029 discard = true;
7030 }
7031 }
7032
7033 return discard;
7034 }
7035
forward_spatial_gui_input(Camera * p_camera,const Ref<InputEvent> & p_event,bool serve_when_force_input_enabled)7036 bool EditorPluginList::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event, bool serve_when_force_input_enabled) {
7037 bool discard = false;
7038
7039 for (int i = 0; i < plugins_list.size(); i++) {
7040 if ((!serve_when_force_input_enabled) && plugins_list[i]->is_input_event_forwarding_always_enabled()) {
7041 continue;
7042 }
7043
7044 if (plugins_list[i]->forward_spatial_gui_input(p_camera, p_event)) {
7045 discard = true;
7046 }
7047 }
7048
7049 return discard;
7050 }
7051
forward_canvas_draw_over_viewport(Control * p_overlay)7052 void EditorPluginList::forward_canvas_draw_over_viewport(Control *p_overlay) {
7053
7054 for (int i = 0; i < plugins_list.size(); i++) {
7055 plugins_list[i]->forward_canvas_draw_over_viewport(p_overlay);
7056 }
7057 }
7058
forward_canvas_force_draw_over_viewport(Control * p_overlay)7059 void EditorPluginList::forward_canvas_force_draw_over_viewport(Control *p_overlay) {
7060
7061 for (int i = 0; i < plugins_list.size(); i++) {
7062 plugins_list[i]->forward_canvas_force_draw_over_viewport(p_overlay);
7063 }
7064 }
7065
forward_spatial_draw_over_viewport(Control * p_overlay)7066 void EditorPluginList::forward_spatial_draw_over_viewport(Control *p_overlay) {
7067
7068 for (int i = 0; i < plugins_list.size(); i++) {
7069 plugins_list[i]->forward_spatial_draw_over_viewport(p_overlay);
7070 }
7071 }
7072
forward_spatial_force_draw_over_viewport(Control * p_overlay)7073 void EditorPluginList::forward_spatial_force_draw_over_viewport(Control *p_overlay) {
7074
7075 for (int i = 0; i < plugins_list.size(); i++) {
7076 plugins_list[i]->forward_spatial_force_draw_over_viewport(p_overlay);
7077 }
7078 }
7079
add_plugin(EditorPlugin * p_plugin)7080 void EditorPluginList::add_plugin(EditorPlugin *p_plugin) {
7081 plugins_list.push_back(p_plugin);
7082 }
7083
remove_plugin(EditorPlugin * p_plugin)7084 void EditorPluginList::remove_plugin(EditorPlugin *p_plugin) {
7085 plugins_list.erase(p_plugin);
7086 }
7087
empty()7088 bool EditorPluginList::empty() {
7089 return plugins_list.empty();
7090 }
7091
clear()7092 void EditorPluginList::clear() {
7093 plugins_list.clear();
7094 }
7095
EditorPluginList()7096 EditorPluginList::EditorPluginList() {
7097 }
7098
~EditorPluginList()7099 EditorPluginList::~EditorPluginList() {
7100 }
7101