Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/editor_node.cpp
20852 views
1
/**************************************************************************/
2
/* editor_node.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
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/config/project_settings.h"
34
#include "core/extension/gdextension_manager.h"
35
#include "core/input/input.h"
36
#include "core/io/config_file.h"
37
#include "core/io/file_access.h"
38
#include "core/io/image.h"
39
#include "core/io/resource_loader.h"
40
#include "core/io/resource_saver.h"
41
#include "core/object/class_db.h"
42
#include "core/os/keyboard.h"
43
#include "core/os/os.h"
44
#include "core/os/time.h"
45
#include "core/string/print_string.h"
46
#include "core/string/translation_server.h"
47
#include "core/version.h"
48
#include "editor/editor_string_names.h"
49
#include "editor/inspector/editor_context_menu_plugin.h"
50
#include "editor/plugins/editor_plugin_list.h"
51
#include "main/main.h"
52
#include "scene/2d/node_2d.h"
53
#include "scene/3d/bone_attachment_3d.h"
54
#include "scene/animation/animation_tree.h"
55
#include "scene/gui/color_picker.h"
56
#include "scene/gui/dialogs.h"
57
#include "scene/gui/file_dialog.h"
58
#include "scene/gui/menu_bar.h"
59
#include "scene/gui/menu_button.h"
60
#include "scene/gui/panel.h"
61
#include "scene/gui/popup.h"
62
#include "scene/gui/rich_text_label.h"
63
#include "scene/gui/split_container.h"
64
#include "scene/gui/tab_container.h"
65
#include "scene/main/timer.h"
66
#include "scene/main/window.h"
67
#include "scene/property_utils.h"
68
#include "scene/resources/dpi_texture.h"
69
#include "scene/resources/image_texture.h"
70
#include "scene/resources/packed_scene.h"
71
#include "scene/resources/portable_compressed_texture.h"
72
#include "scene/theme/theme_db.h"
73
#include "servers/display/display_server.h"
74
#include "servers/navigation_2d/navigation_server_2d.h"
75
#include "servers/navigation_3d/navigation_server_3d.h"
76
#include "servers/rendering/rendering_server.h"
77
78
#include "editor/animation/animation_player_editor_plugin.h"
79
#include "editor/asset_library/asset_library_editor_plugin.h"
80
#include "editor/audio/audio_stream_preview.h"
81
#include "editor/audio/editor_audio_buses.h"
82
#include "editor/debugger/debugger_editor_plugin.h"
83
#include "editor/debugger/editor_debugger_node.h"
84
#include "editor/debugger/script_editor_debugger.h"
85
#include "editor/doc/editor_help.h"
86
#include "editor/docks/editor_dock_manager.h"
87
#include "editor/docks/filesystem_dock.h"
88
#include "editor/docks/groups_dock.h"
89
#include "editor/docks/history_dock.h"
90
#include "editor/docks/import_dock.h"
91
#include "editor/docks/inspector_dock.h"
92
#include "editor/docks/scene_tree_dock.h"
93
#include "editor/docks/signals_dock.h"
94
#include "editor/editor_data.h"
95
#include "editor/editor_interface.h"
96
#include "editor/editor_log.h"
97
#include "editor/editor_main_screen.h"
98
#include "editor/editor_undo_redo_manager.h"
99
#include "editor/export/dedicated_server_export_plugin.h"
100
#include "editor/export/editor_export.h"
101
#include "editor/export/export_template_manager.h"
102
#include "editor/export/gdextension_export_plugin.h"
103
#include "editor/export/project_export.h"
104
#include "editor/export/project_zip_packer.h"
105
#include "editor/export/register_exporters.h"
106
#include "editor/export/shader_baker_export_plugin.h"
107
#include "editor/file_system/dependency_editor.h"
108
#include "editor/file_system/editor_paths.h"
109
#include "editor/gui/editor_about.h"
110
#include "editor/gui/editor_bottom_panel.h"
111
#include "editor/gui/editor_file_dialog.h"
112
#include "editor/gui/editor_quick_open_dialog.h"
113
#include "editor/gui/editor_title_bar.h"
114
#include "editor/gui/editor_toaster.h"
115
#include "editor/gui/progress_dialog.h"
116
#include "editor/gui/window_wrapper.h"
117
#include "editor/import/3d/editor_import_collada.h"
118
#include "editor/import/3d/resource_importer_obj.h"
119
#include "editor/import/3d/resource_importer_scene.h"
120
#include "editor/import/3d/scene_import_settings.h"
121
#include "editor/import/audio_stream_import_settings.h"
122
#include "editor/import/dynamic_font_import_settings.h"
123
#include "editor/import/fbx_importer_manager.h"
124
#include "editor/import/resource_importer_bitmask.h"
125
#include "editor/import/resource_importer_bmfont.h"
126
#include "editor/import/resource_importer_csv_translation.h"
127
#include "editor/import/resource_importer_dynamic_font.h"
128
#include "editor/import/resource_importer_image.h"
129
#include "editor/import/resource_importer_imagefont.h"
130
#include "editor/import/resource_importer_layered_texture.h"
131
#include "editor/import/resource_importer_shader_file.h"
132
#include "editor/import/resource_importer_svg.h"
133
#include "editor/import/resource_importer_texture.h"
134
#include "editor/import/resource_importer_texture_atlas.h"
135
#include "editor/import/resource_importer_wav.h"
136
#include "editor/inspector/editor_inspector.h"
137
#include "editor/inspector/editor_preview_plugins.h"
138
#include "editor/inspector/editor_properties.h"
139
#include "editor/inspector/editor_property_name_processor.h"
140
#include "editor/inspector/editor_resource_picker.h"
141
#include "editor/inspector/editor_resource_preview.h"
142
#include "editor/inspector/multi_node_edit.h"
143
#include "editor/plugins/editor_plugin.h"
144
#include "editor/plugins/editor_resource_conversion_plugin.h"
145
#include "editor/plugins/plugin_config_dialog.h"
146
#include "editor/project_upgrade/project_upgrade_tool.h"
147
#include "editor/run/editor_run.h"
148
#include "editor/run/editor_run_bar.h"
149
#include "editor/run/game_view_plugin.h"
150
#include "editor/scene/3d/material_3d_conversion_plugins.h"
151
#include "editor/scene/3d/mesh_library_editor_plugin.h"
152
#include "editor/scene/3d/node_3d_editor_plugin.h"
153
#include "editor/scene/3d/root_motion_editor_plugin.h"
154
#include "editor/scene/canvas_item_editor_plugin.h"
155
#include "editor/scene/editor_scene_tabs.h"
156
#include "editor/scene/material_editor_plugin.h"
157
#include "editor/scene/particle_process_material_editor_plugin.h"
158
#include "editor/script/editor_script.h"
159
#include "editor/script/script_text_editor.h"
160
#include "editor/script/text_editor.h"
161
#include "editor/settings/editor_build_profile.h"
162
#include "editor/settings/editor_command_palette.h"
163
#include "editor/settings/editor_feature_profile.h"
164
#include "editor/settings/editor_layouts_dialog.h"
165
#include "editor/settings/editor_settings.h"
166
#include "editor/settings/editor_settings_dialog.h"
167
#include "editor/settings/project_settings_editor.h"
168
#include "editor/shader/editor_native_shader_source_visualizer.h"
169
#include "editor/shader/visual_shader_editor_plugin.h"
170
#include "editor/themes/editor_color_map.h"
171
#include "editor/themes/editor_scale.h"
172
#include "editor/themes/editor_theme_manager.h"
173
#include "editor/translations/editor_translation_parser.h"
174
#include "editor/translations/packed_scene_translation_parser_plugin.h"
175
#include "editor/version_control/version_control_editor_plugin.h"
176
177
#ifdef VULKAN_ENABLED
178
#include "editor/shader/shader_baker/shader_baker_export_plugin_platform_vulkan.h"
179
#endif
180
181
#ifdef D3D12_ENABLED
182
#include "editor/shader/shader_baker/shader_baker_export_plugin_platform_d3d12.h"
183
#endif
184
185
#ifdef METAL_ENABLED
186
#include "editor/shader/shader_baker/shader_baker_export_plugin_platform_metal.h"
187
#endif
188
189
#include "modules/modules_enabled.gen.h" // For gdscript, mono.
190
191
#ifndef PHYSICS_2D_DISABLED
192
#include "servers/physics_2d/physics_server_2d.h"
193
#endif // PHYSICS_2D_DISABLED
194
195
#ifndef PHYSICS_3D_DISABLED
196
#include "servers/physics_3d/physics_server_3d.h"
197
#endif // PHYSICS_3D_DISABLED
198
199
#ifdef ANDROID_ENABLED
200
#include "editor/gui/touch_actions_panel.h"
201
#endif // ANDROID_ENABLED
202
203
#include <cstdlib>
204
205
EditorNode *EditorNode::singleton = nullptr;
206
207
static const String EDITOR_NODE_CONFIG_SECTION = "EditorNode";
208
209
static const String REMOVE_ANDROID_BUILD_TEMPLATE_MESSAGE = TTRC("The Android build template is already installed in this project and it won't be overwritten.\nRemove the \"%s\" directory manually before attempting this operation again.");
210
static const String INSTALL_ANDROID_BUILD_TEMPLATE_MESSAGE = TTRC("This will set up your project for gradle Android builds by installing the source template to \"%s\".\nNote that in order to make gradle builds instead of using pre-built APKs, the \"Use Gradle Build\" option should be enabled in the Android export preset.");
211
212
constexpr int LARGE_RESOURCE_WARNING_SIZE_THRESHOLD = 512'000; // 500 KB
213
214
bool EditorProgress::step(const String &p_state, int p_step, bool p_force_refresh) {
215
if (!force_background && Thread::is_main_thread()) {
216
return EditorNode::progress_task_step(task, p_state, p_step, p_force_refresh);
217
} else {
218
EditorNode::progress_task_step_bg(task, p_step);
219
return false;
220
}
221
}
222
223
EditorProgress::EditorProgress(const String &p_task, const String &p_label, int p_amount, bool p_can_cancel, bool p_force_background) {
224
if (!p_force_background && Thread::is_main_thread()) {
225
EditorNode::progress_add_task(p_task, p_label, p_amount, p_can_cancel);
226
} else {
227
EditorNode::progress_add_task_bg(p_task, p_label, p_amount);
228
}
229
task = p_task;
230
force_background = p_force_background;
231
}
232
233
EditorProgress::~EditorProgress() {
234
if (!force_background && Thread::is_main_thread()) {
235
EditorNode::progress_end_task(task);
236
} else {
237
EditorNode::progress_end_task_bg(task);
238
}
239
}
240
241
void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames) {
242
ERR_FAIL_COND_MSG(p_full_paths.size() != r_filenames.size(), vformat("disambiguate_filenames requires two string vectors of same length (%d != %d).", p_full_paths.size(), r_filenames.size()));
243
244
// Keep track of a list of "index sets," i.e. sets of indices
245
// within disambiguated_scene_names which contain the same name.
246
Vector<RBSet<int>> index_sets;
247
HashMap<String, int> scene_name_to_set_index;
248
for (int i = 0; i < r_filenames.size(); i++) {
249
const String &scene_name = r_filenames[i];
250
if (!scene_name_to_set_index.has(scene_name)) {
251
index_sets.append(RBSet<int>());
252
scene_name_to_set_index.insert(r_filenames[i], index_sets.size() - 1);
253
}
254
index_sets.write[scene_name_to_set_index[scene_name]].insert(i);
255
}
256
257
// For each index set with a size > 1, we need to disambiguate.
258
for (int i = 0; i < index_sets.size(); i++) {
259
RBSet<int> iset = index_sets[i];
260
while (iset.size() > 1) {
261
// Append the parent folder to each scene name.
262
for (const int &E : iset) {
263
int set_idx = E;
264
String scene_name = r_filenames[set_idx];
265
String full_path = p_full_paths[set_idx];
266
267
// Get rid of file extensions and res:// prefixes.
268
scene_name = scene_name.get_basename();
269
if (full_path.begins_with("res://")) {
270
full_path = full_path.substr(6);
271
}
272
full_path = full_path.get_basename();
273
274
// Normalize trailing slashes when normalizing directory names.
275
scene_name = scene_name.trim_suffix("/");
276
full_path = full_path.trim_suffix("/");
277
278
int scene_name_size = scene_name.size();
279
int full_path_size = full_path.size();
280
int difference = full_path_size - scene_name_size;
281
282
// Find just the parent folder of the current path and append it.
283
// If the current name is foo.tscn, and the full path is /some/folder/foo.tscn
284
// then slash_idx is the second '/', so that we select just "folder", and
285
// append that to yield "folder/foo.tscn".
286
if (difference > 0) {
287
String parent = full_path.substr(0, difference);
288
int slash_idx = parent.rfind_char('/');
289
slash_idx = parent.rfind_char('/', slash_idx - 1);
290
parent = (slash_idx >= 0 && parent.length() > 1) ? parent.substr(slash_idx + 1) : parent;
291
r_filenames.write[set_idx] = parent + r_filenames[set_idx];
292
}
293
}
294
295
// Loop back through scene names and remove non-ambiguous names.
296
bool can_proceed = false;
297
RBSet<int>::Element *E = iset.front();
298
while (E) {
299
String scene_name = r_filenames[E->get()];
300
bool duplicate_found = false;
301
for (const int &F : iset) {
302
if (E->get() == F) {
303
continue;
304
}
305
const String &other_scene_name = r_filenames[F];
306
if (other_scene_name == scene_name) {
307
duplicate_found = true;
308
break;
309
}
310
}
311
312
RBSet<int>::Element *to_erase = duplicate_found ? nullptr : E;
313
314
// We need to check that we could actually append anymore names
315
// if we wanted to for disambiguation. If we can't, then we have
316
// to abort even with ambiguous names. We clean the full path
317
// and the scene name first to remove extensions so that this
318
// comparison actually works.
319
String path = p_full_paths[E->get()];
320
321
// Get rid of file extensions and res:// prefixes.
322
scene_name = scene_name.get_basename();
323
if (path.begins_with("res://")) {
324
path = path.substr(6);
325
}
326
path = path.get_basename();
327
328
// Normalize trailing slashes when normalizing directory names.
329
scene_name = scene_name.trim_suffix("/");
330
path = path.trim_suffix("/");
331
332
// We can proceed if the full path is longer than the scene name,
333
// meaning that there is at least one more parent folder we can
334
// tack onto the name.
335
can_proceed = can_proceed || (path.size() - scene_name.size()) >= 1;
336
337
E = E->next();
338
if (to_erase) {
339
iset.erase(to_erase);
340
}
341
}
342
343
if (!can_proceed) {
344
break;
345
}
346
}
347
}
348
}
349
350
void EditorNode::_version_control_menu_option(int p_idx) {
351
switch (vcs_actions_menu->get_item_id(p_idx)) {
352
case VCS_METADATA: {
353
VersionControlEditorPlugin::get_singleton()->popup_vcs_metadata_dialog();
354
} break;
355
case VCS_SETTINGS: {
356
VersionControlEditorPlugin::get_singleton()->popup_vcs_set_up_dialog(gui_base);
357
} break;
358
}
359
}
360
361
void EditorNode::_update_title() {
362
const String appname = GLOBAL_GET("application/config/name");
363
String title = (appname.is_empty() ? TTR("Unnamed Project") : appname);
364
const String edited = editor_data.get_edited_scene_root() ? editor_data.get_edited_scene_root()->get_scene_file_path() : String();
365
if (!edited.is_empty()) {
366
// Display the edited scene name before the program name so that it can be seen in the OS task bar.
367
title = vformat("%s - %s", edited.get_file(), title);
368
}
369
if (unsaved_cache) {
370
// Display the "modified" mark before anything else so that it can always be seen in the OS task bar.
371
title = vformat("(*) %s", title);
372
}
373
DisplayServer::get_singleton()->window_set_title(title + String(" - ") + GODOT_VERSION_NAME);
374
if (project_title) {
375
project_title->set_text(title);
376
}
377
}
378
379
void EditorNode::_update_unsaved_cache() {
380
bool is_unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(EditorUndoRedoManager::GLOBAL_HISTORY) ||
381
EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_current_edited_scene_history_id());
382
383
if (unsaved_cache != is_unsaved) {
384
unsaved_cache = is_unsaved;
385
_update_title();
386
}
387
}
388
389
void EditorNode::input(const Ref<InputEvent> &p_event) {
390
// EditorNode::get_singleton()->set_process_input is set to true in ProgressDialog
391
// only when the progress dialog is visible.
392
// We need to discard all key events to disable all shortcuts while the progress
393
// dialog is displayed, simulating an exclusive popup. Mouse events are
394
// captured by a full-screen container in front of the EditorNode in ProgressDialog,
395
// allowing interaction with the actual dialog where a Cancel button may be visible.
396
Ref<InputEventKey> k = p_event;
397
if (k.is_valid()) {
398
get_tree()->get_root()->set_input_as_handled();
399
}
400
}
401
402
void EditorNode::shortcut_input(const Ref<InputEvent> &p_event) {
403
ERR_FAIL_COND(p_event.is_null());
404
405
Ref<InputEventKey> k = p_event;
406
if ((k.is_valid() && k->is_pressed() && !k->is_echo()) || Object::cast_to<InputEventShortcut>(*p_event)) {
407
bool is_handled = true;
408
if (ED_IS_SHORTCUT("editor/filter_files", p_event)) {
409
FileSystemDock::get_singleton()->focus_on_filter();
410
} else if (ED_IS_SHORTCUT("editor/editor_2d", p_event)) {
411
editor_main_screen->select(EditorMainScreen::EDITOR_2D);
412
} else if (ED_IS_SHORTCUT("editor/editor_3d", p_event)) {
413
editor_main_screen->select(EditorMainScreen::EDITOR_3D);
414
} else if (ED_IS_SHORTCUT("editor/editor_script", p_event)) {
415
editor_main_screen->select(EditorMainScreen::EDITOR_SCRIPT);
416
} else if (ED_IS_SHORTCUT("editor/editor_game", p_event)) {
417
editor_main_screen->select(EditorMainScreen::EDITOR_GAME);
418
} else if (ED_IS_SHORTCUT("editor/editor_help", p_event)) {
419
emit_signal(SNAME("request_help_search"), "");
420
} else if (ED_IS_SHORTCUT("editor/editor_assetlib", p_event) && AssetLibraryEditorPlugin::is_available()) {
421
editor_main_screen->select(EditorMainScreen::EDITOR_ASSETLIB);
422
} else if (ED_IS_SHORTCUT("editor/editor_next", p_event)) {
423
editor_main_screen->select_next();
424
} else if (ED_IS_SHORTCUT("editor/editor_prev", p_event)) {
425
editor_main_screen->select_prev();
426
} else if (ED_IS_SHORTCUT("editor/command_palette", p_event)) {
427
_open_command_palette();
428
} else if (ED_IS_SHORTCUT("editor/toggle_last_opened_bottom_panel", p_event)) {
429
bottom_panel->toggle_last_opened_bottom_panel();
430
} else {
431
is_handled = false;
432
}
433
434
if (is_handled) {
435
get_tree()->get_root()->set_input_as_handled();
436
}
437
}
438
}
439
440
void EditorNode::_update_vsync_mode() {
441
const DisplayServer::VSyncMode window_vsync_mode = DisplayServer::VSyncMode(int(EDITOR_GET("interface/editor/vsync_mode")));
442
DisplayServer::get_singleton()->window_set_vsync_mode(window_vsync_mode);
443
}
444
445
void EditorNode::_update_from_settings() {
446
if (!is_inside_tree()) {
447
return;
448
}
449
_update_title();
450
451
int current_filter = GLOBAL_GET("rendering/textures/canvas_textures/default_texture_filter");
452
if (current_filter != scene_root->get_default_canvas_item_texture_filter()) {
453
Viewport::DefaultCanvasItemTextureFilter tf = (Viewport::DefaultCanvasItemTextureFilter)current_filter;
454
scene_root->set_default_canvas_item_texture_filter(tf);
455
}
456
int current_repeat = GLOBAL_GET("rendering/textures/canvas_textures/default_texture_repeat");
457
if (current_repeat != scene_root->get_default_canvas_item_texture_repeat()) {
458
Viewport::DefaultCanvasItemTextureRepeat tr = (Viewport::DefaultCanvasItemTextureRepeat)current_repeat;
459
scene_root->set_default_canvas_item_texture_repeat(tr);
460
}
461
String current_fallback_locale = GLOBAL_GET("internationalization/locale/fallback");
462
if (current_fallback_locale != TranslationServer::get_singleton()->get_fallback_locale()) {
463
TranslationServer::get_singleton()->set_fallback_locale(current_fallback_locale);
464
Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_main_domain();
465
if (!domain->is_enabled()) {
466
domain->set_locale_override(current_fallback_locale);
467
}
468
scene_root->propagate_notification(Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED);
469
}
470
471
RS::DOFBokehShape dof_shape = RS::DOFBokehShape(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_shape")));
472
RS::get_singleton()->camera_attributes_set_dof_blur_bokeh_shape(dof_shape);
473
RS::DOFBlurQuality dof_quality = RS::DOFBlurQuality(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_quality")));
474
bool dof_jitter = GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_use_jitter");
475
RS::get_singleton()->camera_attributes_set_dof_blur_quality(dof_quality, dof_jitter);
476
RS::get_singleton()->environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/environment/ssao/quality"))), GLOBAL_GET("rendering/environment/ssao/half_size"), GLOBAL_GET("rendering/environment/ssao/adaptive_target"), GLOBAL_GET("rendering/environment/ssao/blur_passes"), GLOBAL_GET("rendering/environment/ssao/fadeout_from"), GLOBAL_GET("rendering/environment/ssao/fadeout_to"));
477
RS::get_singleton()->screen_space_roughness_limiter_set_active(GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/enabled"), GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/amount"), GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/limit"));
478
bool glow_bicubic = int(GLOBAL_GET("rendering/environment/glow/upscale_mode")) > 0;
479
RS::get_singleton()->environment_set_ssil_quality(RS::EnvironmentSSILQuality(int(GLOBAL_GET("rendering/environment/ssil/quality"))), GLOBAL_GET("rendering/environment/ssil/half_size"), GLOBAL_GET("rendering/environment/ssil/adaptive_target"), GLOBAL_GET("rendering/environment/ssil/blur_passes"), GLOBAL_GET("rendering/environment/ssil/fadeout_from"), GLOBAL_GET("rendering/environment/ssil/fadeout_to"));
480
RS::get_singleton()->environment_glow_set_use_bicubic_upscale(glow_bicubic);
481
RS::get_singleton()->environment_set_ssr_half_size(GLOBAL_GET("rendering/environment/screen_space_reflection/half_size"));
482
RS::SubSurfaceScatteringQuality sss_quality = RS::SubSurfaceScatteringQuality(int(GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_quality")));
483
RS::get_singleton()->sub_surface_scattering_set_quality(sss_quality);
484
float sss_scale = GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_scale");
485
float sss_depth_scale = GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale");
486
RS::get_singleton()->sub_surface_scattering_set_scale(sss_scale, sss_depth_scale);
487
488
uint32_t directional_shadow_size = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/size");
489
uint32_t directional_shadow_16_bits = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/16_bits");
490
RS::get_singleton()->directional_shadow_atlas_set_size(directional_shadow_size, directional_shadow_16_bits);
491
492
RS::ShadowQuality shadows_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality")));
493
RS::get_singleton()->positional_soft_shadow_filter_set_quality(shadows_quality);
494
RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality")));
495
RS::get_singleton()->directional_soft_shadow_filter_set_quality(directional_shadow_quality);
496
float probe_update_speed = GLOBAL_GET("rendering/lightmapping/probe_capture/update_speed");
497
RS::get_singleton()->lightmap_set_probe_capture_update_speed(probe_update_speed);
498
RS::EnvironmentSDFGIFramesToConverge frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(int(GLOBAL_GET("rendering/global_illumination/sdfgi/frames_to_converge")));
499
RS::get_singleton()->environment_set_sdfgi_frames_to_converge(frames_to_converge);
500
RS::EnvironmentSDFGIRayCount ray_count = RS::EnvironmentSDFGIRayCount(int(GLOBAL_GET("rendering/global_illumination/sdfgi/probe_ray_count")));
501
RS::get_singleton()->environment_set_sdfgi_ray_count(ray_count);
502
RS::VoxelGIQuality voxel_gi_quality = RS::VoxelGIQuality(int(GLOBAL_GET("rendering/global_illumination/voxel_gi/quality")));
503
RS::get_singleton()->voxel_gi_set_quality(voxel_gi_quality);
504
RS::get_singleton()->environment_set_volumetric_fog_volume_size(GLOBAL_GET("rendering/environment/volumetric_fog/volume_size"), GLOBAL_GET("rendering/environment/volumetric_fog/volume_depth"));
505
RS::get_singleton()->environment_set_volumetric_fog_filter_active(bool(GLOBAL_GET("rendering/environment/volumetric_fog/use_filter")));
506
RS::get_singleton()->canvas_set_shadow_texture_size(GLOBAL_GET("rendering/2d/shadow_atlas/size"));
507
508
bool use_half_res_gi = GLOBAL_GET("rendering/global_illumination/gi/use_half_resolution");
509
RS::get_singleton()->gi_set_use_half_resolution(use_half_res_gi);
510
511
bool snap_2d_transforms = GLOBAL_GET("rendering/2d/snap/snap_2d_transforms_to_pixel");
512
scene_root->set_snap_2d_transforms_to_pixel(snap_2d_transforms);
513
bool snap_2d_vertices = GLOBAL_GET("rendering/2d/snap/snap_2d_vertices_to_pixel");
514
scene_root->set_snap_2d_vertices_to_pixel(snap_2d_vertices);
515
516
Viewport::SDFOversize sdf_oversize = Viewport::SDFOversize(int(GLOBAL_GET("rendering/2d/sdf/oversize")));
517
scene_root->set_sdf_oversize(sdf_oversize);
518
Viewport::SDFScale sdf_scale = Viewport::SDFScale(int(GLOBAL_GET("rendering/2d/sdf/scale")));
519
scene_root->set_sdf_scale(sdf_scale);
520
521
Viewport::MSAA msaa = Viewport::MSAA(int(GLOBAL_GET("rendering/anti_aliasing/quality/msaa_2d")));
522
scene_root->set_msaa_2d(msaa);
523
524
// 2D doesn't use a dedicated SubViewport like 3D does, so we apply it on the root viewport instead.
525
bool use_debanding = GLOBAL_GET("rendering/anti_aliasing/quality/use_debanding");
526
scene_root->set_use_debanding(use_debanding);
527
get_viewport()->set_use_debanding(use_debanding);
528
529
bool use_hdr_2d = GLOBAL_GET("rendering/viewport/hdr_2d");
530
scene_root->set_use_hdr_2d(use_hdr_2d);
531
get_viewport()->set_use_hdr_2d(use_hdr_2d);
532
533
float mesh_lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels");
534
scene_root->set_mesh_lod_threshold(mesh_lod_threshold);
535
536
RS::get_singleton()->decals_set_filter(RS::DecalFilter(int(GLOBAL_GET("rendering/textures/decals/filter"))));
537
RS::get_singleton()->light_projectors_set_filter(RS::LightProjectorFilter(int(GLOBAL_GET("rendering/textures/light_projectors/filter"))));
538
RS::get_singleton()->lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter"));
539
RS::get_singleton()->material_set_use_debanding(GLOBAL_GET("rendering/anti_aliasing/quality/use_debanding"));
540
541
SceneTree *tree = get_tree();
542
tree->set_debug_collisions_color(GLOBAL_GET("debug/shapes/collision/shape_color"));
543
tree->set_debug_collision_contact_color(GLOBAL_GET("debug/shapes/collision/contact_color"));
544
545
ResourceImporterTexture::get_singleton()->update_imports();
546
547
_update_translations();
548
549
#ifdef DEBUG_ENABLED
550
NavigationServer2D::get_singleton()->set_debug_navigation_edge_connection_color(GLOBAL_GET("debug/shapes/navigation/2d/edge_connection_color"));
551
NavigationServer2D::get_singleton()->set_debug_navigation_geometry_edge_color(GLOBAL_GET("debug/shapes/navigation/2d/geometry_edge_color"));
552
NavigationServer2D::get_singleton()->set_debug_navigation_geometry_face_color(GLOBAL_GET("debug/shapes/navigation/2d/geometry_face_color"));
553
NavigationServer2D::get_singleton()->set_debug_navigation_geometry_edge_disabled_color(GLOBAL_GET("debug/shapes/navigation/2d/geometry_edge_disabled_color"));
554
NavigationServer2D::get_singleton()->set_debug_navigation_geometry_face_disabled_color(GLOBAL_GET("debug/shapes/navigation/2d/geometry_face_disabled_color"));
555
NavigationServer2D::get_singleton()->set_debug_navigation_enable_edge_connections(GLOBAL_GET("debug/shapes/navigation/2d/enable_edge_connections"));
556
NavigationServer2D::get_singleton()->set_debug_navigation_enable_edge_lines(GLOBAL_GET("debug/shapes/navigation/2d/enable_edge_lines"));
557
NavigationServer2D::get_singleton()->set_debug_navigation_enable_geometry_face_random_color(GLOBAL_GET("debug/shapes/navigation/2d/enable_geometry_face_random_color"));
558
559
NavigationServer3D::get_singleton()->set_debug_navigation_edge_connection_color(GLOBAL_GET("debug/shapes/navigation/3d/edge_connection_color"));
560
NavigationServer3D::get_singleton()->set_debug_navigation_geometry_edge_color(GLOBAL_GET("debug/shapes/navigation/3d/geometry_edge_color"));
561
NavigationServer3D::get_singleton()->set_debug_navigation_geometry_face_color(GLOBAL_GET("debug/shapes/navigation/3d/geometry_face_color"));
562
NavigationServer3D::get_singleton()->set_debug_navigation_geometry_edge_disabled_color(GLOBAL_GET("debug/shapes/navigation/3d/geometry_edge_disabled_color"));
563
NavigationServer3D::get_singleton()->set_debug_navigation_geometry_face_disabled_color(GLOBAL_GET("debug/shapes/navigation/3d/geometry_face_disabled_color"));
564
NavigationServer3D::get_singleton()->set_debug_navigation_enable_edge_connections(GLOBAL_GET("debug/shapes/navigation/3d/enable_edge_connections"));
565
NavigationServer3D::get_singleton()->set_debug_navigation_enable_edge_connections_xray(GLOBAL_GET("debug/shapes/navigation/3d/enable_edge_connections_xray"));
566
NavigationServer3D::get_singleton()->set_debug_navigation_enable_edge_lines(GLOBAL_GET("debug/shapes/navigation/3d/enable_edge_lines"));
567
NavigationServer3D::get_singleton()->set_debug_navigation_enable_edge_lines_xray(GLOBAL_GET("debug/shapes/navigation/3d/enable_edge_lines_xray"));
568
NavigationServer3D::get_singleton()->set_debug_navigation_enable_geometry_face_random_color(GLOBAL_GET("debug/shapes/navigation/3d/enable_geometry_face_random_color"));
569
#endif // DEBUG_ENABLED
570
}
571
572
void EditorNode::_gdextensions_reloaded() {
573
// In case the developer is inspecting an object that will be changed by the reload.
574
InspectorDock::get_inspector_singleton()->update_tree();
575
576
// Reload script editor to revalidate GDScript if classes are added or removed.
577
ScriptEditor::get_singleton()->reload_scripts(true);
578
579
// Regenerate documentation without using script documentation cache since that would
580
// revert doc changes during this session.
581
EditorHelp::generate_doc(true, false);
582
}
583
584
void EditorNode::_update_translations() {
585
Ref<TranslationDomain> main = TranslationServer::get_singleton()->get_main_domain();
586
587
TranslationServer::get_singleton()->load_project_translations(main);
588
589
if (main->is_enabled()) {
590
// Check for the exact locale.
591
if (main->has_translation_for_locale(main->get_locale_override(), true)) {
592
// The set of translation resources for the current locale changed.
593
const HashSet<Ref<Translation>> translations = main->find_translations(main->get_locale_override(), false);
594
if (translations != tracked_translations) {
595
_translation_resources_changed();
596
}
597
} else {
598
// Translations for the current preview locale is removed.
599
main->set_enabled(false);
600
main->set_locale_override(String());
601
_translation_resources_changed();
602
}
603
}
604
}
605
606
void EditorNode::_translation_resources_changed() {
607
for (const Ref<Translation> &E : tracked_translations) {
608
E->disconnect_changed(callable_mp(this, &EditorNode::_queue_translation_notification));
609
}
610
tracked_translations.clear();
611
612
const Ref<TranslationDomain> main = TranslationServer::get_singleton()->get_main_domain();
613
if (main->is_enabled()) {
614
const HashSet<Ref<Translation>> translations = main->find_translations(main->get_locale_override(), false);
615
tracked_translations.reserve(translations.size());
616
for (const Ref<Translation> &translation : translations) {
617
translation->connect_changed(callable_mp(this, &EditorNode::_queue_translation_notification));
618
tracked_translations.insert(translation);
619
}
620
}
621
622
_queue_translation_notification();
623
emit_signal(SNAME("preview_locale_changed"));
624
}
625
626
void EditorNode::_queue_translation_notification() {
627
if (pending_translation_notification) {
628
return;
629
}
630
pending_translation_notification = true;
631
callable_mp(this, &EditorNode::_propagate_translation_notification).call_deferred();
632
}
633
634
void EditorNode::_propagate_translation_notification() {
635
pending_translation_notification = false;
636
scene_root->propagate_notification(NOTIFICATION_TRANSLATION_CHANGED);
637
}
638
639
void EditorNode::_update_theme(bool p_skip_creation) {
640
if (!p_skip_creation) {
641
theme = EditorThemeManager::generate_theme(theme);
642
DisplayServer::set_early_window_clear_color_override(true, theme->get_color(SNAME("background"), EditorStringName(Editor)));
643
644
#if defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED)
645
if (EditorHelpHighlighter::get_singleton()) {
646
// Update syntax colors.
647
EditorHelpHighlighter::free_singleton();
648
EditorHelpHighlighter::create_singleton();
649
}
650
#endif
651
}
652
653
Vector<Ref<Theme>> editor_themes;
654
editor_themes.push_back(theme);
655
editor_themes.push_back(ThemeDB::get_singleton()->get_default_theme());
656
657
ThemeContext *node_tc = ThemeDB::get_singleton()->get_theme_context(this);
658
if (node_tc) {
659
node_tc->set_themes(editor_themes);
660
} else {
661
ThemeDB::get_singleton()->create_theme_context(this, editor_themes);
662
}
663
664
Window *window = get_window();
665
if (window) {
666
ThemeContext *window_tc = ThemeDB::get_singleton()->get_theme_context(window);
667
if (window_tc) {
668
window_tc->set_themes(editor_themes);
669
} else {
670
ThemeDB::get_singleton()->create_theme_context(window, editor_themes);
671
}
672
}
673
674
if (CanvasItemEditor::get_singleton()->get_theme_preview() == CanvasItemEditor::THEME_PREVIEW_EDITOR) {
675
update_preview_themes(CanvasItemEditor::THEME_PREVIEW_EDITOR);
676
}
677
678
// Update styles.
679
{
680
bool dark_mode = DisplayServer::get_singleton()->is_dark_mode_supported() && DisplayServer::get_singleton()->is_dark_mode();
681
682
gui_base->add_theme_style_override(SceneStringName(panel), theme->get_stylebox(SNAME("Background"), EditorStringName(EditorStyles)));
683
main_vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, theme->get_constant(SNAME("window_border_margin"), EditorStringName(Editor)));
684
main_vbox->add_theme_constant_override("separation", theme->get_constant(SNAME("top_bar_separation"), EditorStringName(Editor)));
685
686
if (main_menu_button != nullptr) {
687
main_menu_button->set_button_icon(theme->get_icon(SNAME("TripleBar"), EditorStringName(EditorIcons)));
688
}
689
690
editor_main_screen->add_theme_style_override(SceneStringName(panel), theme->get_stylebox(SNAME("Content"), EditorStringName(EditorStyles)));
691
bottom_panel->_theme_changed();
692
distraction_free->set_button_icon(theme->get_icon(SNAME("DistractionFree"), EditorStringName(EditorIcons)));
693
update_distraction_free_button_theme();
694
695
help_menu->set_item_icon(help_menu->get_item_index(HELP_SEARCH), get_editor_theme_native_menu_icon(SNAME("HelpSearch"), menu_type == MENU_TYPE_GLOBAL, dark_mode));
696
help_menu->set_item_icon(help_menu->get_item_index(HELP_COPY_SYSTEM_INFO), get_editor_theme_native_menu_icon(SNAME("ActionCopy"), menu_type == MENU_TYPE_GLOBAL, dark_mode));
697
help_menu->set_item_icon(help_menu->get_item_index(HELP_ABOUT), get_editor_theme_native_menu_icon(SNAME("Godot"), menu_type == MENU_TYPE_GLOBAL, dark_mode));
698
help_menu->set_item_icon(help_menu->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), get_editor_theme_native_menu_icon(SNAME("Heart"), menu_type == MENU_TYPE_GLOBAL, dark_mode));
699
700
_update_renderer_color();
701
}
702
703
Ref<Texture2D> thumbnail_icon = gui_base->get_theme_icon(SNAME("file_thumbnail"), SNAME("FileDialog"));
704
default_thumbnail.instantiate();
705
default_thumbnail->set_image(thumbnail_icon->get_image());
706
707
editor_dock_manager->update_tab_styles();
708
editor_dock_manager->update_docks_menu();
709
editor_dock_manager->set_tab_icon_max_width(theme->get_constant(SNAME("class_icon_size"), EditorStringName(Editor)));
710
#ifdef ANDROID_ENABLED
711
DisplayServer::get_singleton()->window_set_color(theme->get_color(SNAME("background"), EditorStringName(Editor)));
712
#endif
713
}
714
715
Ref<Texture2D> EditorNode::get_editor_theme_native_menu_icon(const StringName &p_name, bool p_global_menu, bool p_dark_mode) const {
716
Ref<Texture2D> tx = theme->get_icon(p_name, SNAME("EditorIcons"));
717
if (!p_global_menu || p_dark_mode == EditorThemeManager::is_dark_icon_and_font()) {
718
return tx;
719
}
720
721
Ref<DPITexture> new_tx = tx;
722
if (new_tx.is_null()) {
723
return tx;
724
}
725
new_tx = new_tx->duplicate();
726
727
Dictionary color_conversion_map;
728
if (!p_dark_mode) {
729
for (KeyValue<Color, Color> &E : EditorColorMap::get_color_conversion_map()) {
730
color_conversion_map[E.key] = E.value;
731
}
732
}
733
new_tx->set_color_map(color_conversion_map);
734
735
return new_tx;
736
}
737
738
void EditorNode::update_preview_themes(int p_mode) {
739
if (!scene_root->is_inside_tree()) {
740
return; // Too early.
741
}
742
743
Vector<Ref<Theme>> preview_themes;
744
745
switch (p_mode) {
746
case CanvasItemEditor::THEME_PREVIEW_PROJECT:
747
preview_themes.push_back(ThemeDB::get_singleton()->get_project_theme());
748
break;
749
750
case CanvasItemEditor::THEME_PREVIEW_EDITOR:
751
preview_themes.push_back(get_editor_theme());
752
break;
753
754
default:
755
break;
756
}
757
758
preview_themes.push_back(ThemeDB::get_singleton()->get_default_theme());
759
760
ThemeContext *preview_context = ThemeDB::get_singleton()->get_theme_context(scene_root);
761
if (preview_context) {
762
preview_context->set_themes(preview_themes);
763
} else {
764
ThemeDB::get_singleton()->create_theme_context(scene_root, preview_themes);
765
}
766
}
767
768
bool EditorNode::_is_project_data_missing() {
769
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
770
const String project_data_dir = EditorPaths::get_singleton()->get_project_data_dir();
771
if (!da->dir_exists(project_data_dir)) {
772
return true;
773
}
774
775
String project_data_gdignore_file_path = project_data_dir.path_join(".gdignore");
776
if (!FileAccess::exists(project_data_gdignore_file_path)) {
777
Ref<FileAccess> f = FileAccess::open(project_data_gdignore_file_path, FileAccess::WRITE);
778
if (f.is_valid()) {
779
f->store_line("");
780
} else {
781
ERR_PRINT("Failed to create file " + project_data_gdignore_file_path.quote() + ".");
782
}
783
}
784
785
String uid_cache = ResourceUID::get_singleton()->get_cache_file();
786
if (!da->file_exists(uid_cache)) {
787
Error err = ResourceUID::get_singleton()->save_to_cache();
788
if (err != OK) {
789
ERR_PRINT("Failed to create file " + uid_cache.quote() + ".");
790
}
791
}
792
793
const String dirs[] = {
794
EditorPaths::get_singleton()->get_project_settings_dir(),
795
ProjectSettings::get_singleton()->get_imported_files_path()
796
};
797
for (const String &dir : dirs) {
798
if (!da->dir_exists(dir)) {
799
return true;
800
}
801
}
802
return false;
803
}
804
805
void EditorNode::_notification(int p_what) {
806
switch (p_what) {
807
case NOTIFICATION_TRANSLATION_CHANGED: {
808
_update_title();
809
callable_mp(this, &EditorNode::_titlebar_resized).call_deferred();
810
811
// The rendering method selector.
812
const String current_renderer_ps = String(GLOBAL_GET("rendering/renderer/rendering_method")).to_lower();
813
const String current_renderer_os = OS::get_singleton()->get_current_rendering_method().to_lower();
814
if (current_renderer_ps == current_renderer_os) {
815
for (int i = 0; i < renderer->get_item_count(); i++) {
816
renderer->set_item_text(i, _to_rendering_method_display_name(renderer->get_item_metadata(i)));
817
}
818
} else {
819
// TRANSLATORS: The placeholder is the rendering method that has overridden the default one.
820
renderer->set_item_text(0, vformat(TTR("%s (Overridden)"), _to_rendering_method_display_name(current_renderer_os)));
821
}
822
} break;
823
824
case NOTIFICATION_POSTINITIALIZE: {
825
EditorHelp::generate_doc();
826
#if defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED)
827
EditorHelpHighlighter::create_singleton();
828
#endif
829
} break;
830
831
case NOTIFICATION_PROCESS: {
832
if (editor_data.is_scene_changed(-1)) {
833
scene_tabs->update_scene_tabs();
834
}
835
836
// Update the animation frame of the update spinner.
837
uint64_t frame = Engine::get_singleton()->get_frames_drawn();
838
uint64_t tick = OS::get_singleton()->get_ticks_msec();
839
840
if (frame != update_spinner_step_frame && (tick - update_spinner_step_msec) > (1000 / 8)) {
841
update_spinner_step++;
842
if (update_spinner_step >= 8) {
843
update_spinner_step = 0;
844
}
845
846
update_spinner_step_msec = tick;
847
update_spinner_step_frame = frame + 1;
848
849
// Update the icon itself only when the spinner is visible.
850
if (_should_display_update_spinner()) {
851
update_spinner->set_button_icon(theme->get_icon("Progress" + itos(update_spinner_step + 1), EditorStringName(EditorIcons)));
852
}
853
}
854
855
editor_selection->update();
856
857
ResourceImporterTexture::get_singleton()->update_imports();
858
859
if (requested_first_scan) {
860
requested_first_scan = false;
861
862
OS::get_singleton()->benchmark_begin_measure("Editor", "First Scan");
863
864
EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &EditorNode::_execute_upgrades), CONNECT_ONE_SHOT);
865
EditorFileSystem::get_singleton()->scan();
866
}
867
868
if (settings_overrides_changed) {
869
EditorSettings::get_singleton()->notify_changes();
870
EditorSettings::get_singleton()->emit_signal(SNAME("settings_changed"));
871
settings_overrides_changed = false;
872
}
873
} break;
874
875
case NOTIFICATION_ENTER_TREE: {
876
get_tree()->set_disable_node_threading(true); // No node threading while running editor.
877
878
Engine::get_singleton()->set_editor_hint(true);
879
880
Window *window = get_window();
881
if (window) {
882
// Handle macOS fullscreen and extend-to-title changes.
883
window->connect("titlebar_changed", callable_mp(this, &EditorNode::_titlebar_resized));
884
}
885
886
// Theme has already been created in the constructor, so we can skip that step.
887
_update_theme(true);
888
889
OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/low_processor_mode_sleep_usec")));
890
get_tree()->get_root()->set_as_audio_listener_3d(false);
891
get_tree()->get_root()->set_as_audio_listener_2d(false);
892
get_tree()->get_root()->set_snap_2d_transforms_to_pixel(false);
893
get_tree()->get_root()->set_snap_2d_vertices_to_pixel(false);
894
get_tree()->set_auto_accept_quit(false);
895
#ifdef ANDROID_ENABLED
896
get_tree()->set_quit_on_go_back(false);
897
bool is_fullscreen = EDITOR_DEF("_is_editor_fullscreen", false);
898
if (is_fullscreen) {
899
DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_FULLSCREEN);
900
}
901
#endif
902
get_tree()->get_root()->connect("files_dropped", callable_mp(this, &EditorNode::_dropped_files));
903
904
command_palette->register_shortcuts_as_command();
905
906
_begin_first_scan();
907
908
last_dark_mode_state = DisplayServer::get_singleton()->is_dark_mode();
909
last_system_accent_color = DisplayServer::get_singleton()->get_accent_color();
910
last_system_base_color = DisplayServer::get_singleton()->get_base_color();
911
DisplayServer::get_singleton()->set_system_theme_change_callback(callable_mp(this, &EditorNode::_check_system_theme_changed));
912
913
get_viewport()->connect("size_changed", callable_mp(this, &EditorNode::_viewport_resized));
914
915
/* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */
916
} break;
917
918
case NOTIFICATION_EXIT_TREE: {
919
singleton->active_plugins.clear();
920
921
if (progress_dialog) {
922
progress_dialog->queue_free();
923
}
924
if (load_error_dialog) {
925
load_error_dialog->queue_free();
926
}
927
if (execute_output_dialog) {
928
execute_output_dialog->queue_free();
929
}
930
if (warning) {
931
warning->queue_free();
932
}
933
if (accept) {
934
accept->queue_free();
935
}
936
if (save_accept) {
937
save_accept->queue_free();
938
}
939
EditorHelp::save_script_doc_cache();
940
editor_data.save_editor_external_data();
941
EditorSettings::get_singleton()->save_project_metadata();
942
FileAccess::set_file_close_fail_notify_callback(nullptr);
943
log->deinit(); // Do not get messages anymore.
944
editor_data.clear_edited_scenes();
945
get_viewport()->disconnect("size_changed", callable_mp(this, &EditorNode::_viewport_resized));
946
} break;
947
948
case NOTIFICATION_READY: {
949
started_timestamp = Time::get_singleton()->get_unix_time_from_system();
950
951
// Store the default order of bottom docks. It can only be determined dynamically.
952
PackedStringArray bottom_docks;
953
bottom_docks.reserve_exact(bottom_panel->get_tab_count());
954
for (int i = 0; i < bottom_panel->get_tab_count(); i++) {
955
EditorDock *dock = Object::cast_to<EditorDock>(bottom_panel->get_tab_control(i));
956
bottom_docks.append(dock->get_effective_layout_key());
957
}
958
default_layout->set_value("docks", "dock_9", String(",").join(bottom_docks));
959
960
RenderingServer::get_singleton()->viewport_set_disable_2d(get_scene_root()->get_viewport_rid(), true);
961
RenderingServer::get_singleton()->viewport_set_environment_mode(get_viewport()->get_viewport_rid(), RenderingServer::VIEWPORT_ENVIRONMENT_DISABLED);
962
DisplayServer::get_singleton()->screen_set_keep_on(EDITOR_GET("interface/editor/keep_screen_on"));
963
964
feature_profile_manager->notify_changed();
965
966
// Save the project after opening to mark it as last modified, except in headless mode.
967
// Also use this opportunity to ensure default settings are applied to new projects created from the command line
968
// using `touch project.godot`.
969
if (DisplayServer::get_singleton()->window_can_draw()) {
970
const String project_settings_path = ProjectSettings::get_singleton()->get_resource_path().path_join("project.godot");
971
// Check the file's size in bytes as an optimization. If it's under 10 bytes, the file is assumed to be empty.
972
if (FileAccess::get_size(project_settings_path) < 10) {
973
const HashMap<String, Variant> initial_settings = get_initial_settings();
974
for (const KeyValue<String, Variant> &initial_setting : initial_settings) {
975
ProjectSettings::get_singleton()->set_setting(initial_setting.key, initial_setting.value);
976
}
977
}
978
ProjectSettings::get_singleton()->save();
979
}
980
981
_titlebar_resized();
982
983
// Set up a theme context for the 2D preview viewport using the stored preview theme.
984
CanvasItemEditor::ThemePreviewMode theme_preview_mode = (CanvasItemEditor::ThemePreviewMode)(int)EditorSettings::get_singleton()->get_project_metadata("2d_editor", "theme_preview", CanvasItemEditor::THEME_PREVIEW_PROJECT);
985
update_preview_themes(theme_preview_mode);
986
987
// Remember the selected locale to preview node translations.
988
const String preview_locale = EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "preview_locale", String());
989
if (!preview_locale.is_empty() && TranslationServer::get_singleton()->has_translation_for_locale(preview_locale, true)) {
990
set_preview_locale(preview_locale);
991
}
992
993
if (Engine::get_singleton()->is_recovery_mode_hint()) {
994
EditorToaster::get_singleton()->popup_str(TTR("Recovery Mode is enabled. Editor functionality has been restricted."), EditorToaster::SEVERITY_WARNING);
995
}
996
997
/* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */
998
} break;
999
1000
case NOTIFICATION_APPLICATION_FOCUS_IN: {
1001
// Restore the original FPS cap after focusing back on the editor.
1002
OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/low_processor_mode_sleep_usec")));
1003
1004
if (_is_project_data_missing()) {
1005
project_data_missing->popup_centered();
1006
} else {
1007
EditorFileSystem::get_singleton()->scan_changes();
1008
}
1009
_scan_external_changes();
1010
1011
GDExtensionManager *gdextension_manager = GDExtensionManager::get_singleton();
1012
callable_mp(gdextension_manager, &GDExtensionManager::reload_extensions).call_deferred();
1013
} break;
1014
1015
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
1016
// Save on focus loss before applying the FPS limit to avoid slowing down the saving process.
1017
if (EDITOR_GET("interface/editor/save_on_focus_loss")) {
1018
_save_scene_silently();
1019
}
1020
1021
// Set a low FPS cap to decrease CPU/GPU usage while the editor is unfocused.
1022
if (unfocused_low_processor_usage_mode_enabled) {
1023
OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/unfocused_low_processor_mode_sleep_usec")));
1024
}
1025
} break;
1026
1027
case NOTIFICATION_WM_ABOUT: {
1028
show_about();
1029
} break;
1030
1031
case NOTIFICATION_WM_CLOSE_REQUEST: {
1032
_menu_option_confirm(SCENE_QUIT, false);
1033
} break;
1034
1035
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
1036
if (EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog")) {
1037
FileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files"));
1038
FileDialog::set_default_display_mode(EDITOR_GET("filesystem/file_dialog/display_mode"));
1039
}
1040
1041
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/tablet_driver")) {
1042
String tablet_driver = GLOBAL_GET("input_devices/pen_tablet/driver");
1043
int tablet_driver_idx = EDITOR_GET("interface/editor/tablet_driver");
1044
if (tablet_driver_idx != -1) {
1045
tablet_driver = DisplayServer::get_singleton()->tablet_get_driver_name(tablet_driver_idx);
1046
}
1047
if (tablet_driver.is_empty()) {
1048
tablet_driver = DisplayServer::get_singleton()->tablet_get_driver_name(0);
1049
}
1050
DisplayServer::get_singleton()->tablet_set_current_driver(tablet_driver);
1051
print_verbose("Using \"" + DisplayServer::get_singleton()->tablet_get_current_driver() + "\" pen tablet driver...");
1052
}
1053
1054
if (EDITOR_GET("interface/editor/import_resources_when_unfocused")) {
1055
scan_changes_timer->start();
1056
} else {
1057
scan_changes_timer->stop();
1058
}
1059
1060
follow_system_theme = EDITOR_GET("interface/theme/follow_system_theme");
1061
use_system_accent_color = EDITOR_GET("interface/theme/use_system_accent_color");
1062
1063
if (EditorThemeManager::is_generated_theme_outdated()) {
1064
class_icon_cache.clear();
1065
_update_theme();
1066
_build_icon_type_cache();
1067
recent_scenes->reset_size();
1068
}
1069
1070
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/dragging_")) {
1071
theme->set_constant("dragging_unfold_wait_msec", "Tree", (float)EDITOR_GET("interface/editor/dragging_hover_wait_seconds") * 1000);
1072
theme->set_constant("hover_switch_wait_msec", "TabBar", (float)EDITOR_GET("interface/editor/dragging_hover_wait_seconds") * 1000);
1073
}
1074
1075
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor")) {
1076
editor_dock_manager->update_tab_styles();
1077
}
1078
1079
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/scene_tabs")) {
1080
scene_tabs->update_scene_tabs();
1081
}
1082
1083
if (EditorSettings::get_singleton()->check_changed_settings_in_group("docks/filesystem")) {
1084
HashSet<String> updated_textfile_extensions;
1085
HashSet<String> updated_other_file_extensions;
1086
bool extensions_match = true;
1087
const Vector<String> textfile_ext = ((String)(EDITOR_GET("docks/filesystem/textfile_extensions"))).split(",", false);
1088
for (const String &E : textfile_ext) {
1089
updated_textfile_extensions.insert(E);
1090
if (extensions_match && !textfile_extensions.has(E)) {
1091
extensions_match = false;
1092
}
1093
}
1094
const Vector<String> other_file_ext = ((String)(EDITOR_GET("docks/filesystem/other_file_extensions"))).split(",", false);
1095
for (const String &E : other_file_ext) {
1096
updated_other_file_extensions.insert(E);
1097
if (extensions_match && !other_file_extensions.has(E)) {
1098
extensions_match = false;
1099
}
1100
}
1101
1102
if (!extensions_match || updated_textfile_extensions.size() < textfile_extensions.size() || updated_other_file_extensions.size() < other_file_extensions.size()) {
1103
textfile_extensions = updated_textfile_extensions;
1104
other_file_extensions = updated_other_file_extensions;
1105
EditorFileSystem::get_singleton()->scan();
1106
}
1107
}
1108
1109
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor")) {
1110
_update_update_spinner();
1111
_update_vsync_mode();
1112
_update_main_menu_type();
1113
DisplayServer::get_singleton()->screen_set_keep_on(EDITOR_GET("interface/editor/keep_screen_on"));
1114
}
1115
1116
#if defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED)
1117
if (EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/theme/highlighting")) {
1118
EditorHelpHighlighter::get_singleton()->reset_cache();
1119
}
1120
#endif
1121
#ifdef ANDROID_ENABLED
1122
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/touch_actions_panel")) {
1123
_touch_actions_panel_mode_changed();
1124
}
1125
#endif
1126
} break;
1127
}
1128
}
1129
1130
void EditorNode::_update_update_spinner() {
1131
update_spinner->set_visible(!RenderingServer::get_singleton()->canvas_item_get_debug_redraw() && _should_display_update_spinner());
1132
1133
const bool update_continuously = EDITOR_GET("interface/editor/update_continuously");
1134
PopupMenu *update_popup = update_spinner->get_popup();
1135
update_popup->set_item_checked(update_popup->get_item_index(SPINNER_UPDATE_CONTINUOUSLY), update_continuously);
1136
update_popup->set_item_checked(update_popup->get_item_index(SPINNER_UPDATE_WHEN_CHANGED), !update_continuously);
1137
1138
if (update_continuously) {
1139
update_spinner->set_tooltip_text(TTRC("Spins when the editor window redraws.\nUpdate Continuously is enabled, which can increase power usage. Click to disable it."));
1140
1141
// Use a different color for the update spinner when Update Continuously is enabled,
1142
// as this feature should only be enabled for troubleshooting purposes.
1143
// Make the icon modulate color overbright because icons are not completely white on a dark theme.
1144
// On a light theme, icons are dark, so we need to modulate them with an even brighter color.
1145
const bool dark_icon_and_font = EditorThemeManager::is_dark_icon_and_font();
1146
update_spinner->set_self_modulate(theme->get_color(SNAME("error_color"), EditorStringName(Editor)) * (dark_icon_and_font ? Color(1.1, 1.1, 1.1) : Color(4.25, 4.25, 4.25)));
1147
} else {
1148
update_spinner->set_tooltip_text(TTRC("Spins when the editor window redraws."));
1149
update_spinner->set_self_modulate(Color(1, 1, 1));
1150
}
1151
1152
OS::get_singleton()->set_low_processor_usage_mode(!update_continuously);
1153
}
1154
1155
void EditorNode::_execute_upgrades() {
1156
if (run_project_upgrade_tool) {
1157
run_project_upgrade_tool = false;
1158
// Execute another scan to reimport the modified files.
1159
project_upgrade_tool->connect(project_upgrade_tool->UPGRADE_FINISHED, callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan), CONNECT_ONE_SHOT);
1160
project_upgrade_tool->finish_upgrade();
1161
}
1162
}
1163
1164
void EditorNode::init_plugins() {
1165
_initializing_plugins = true;
1166
Vector<String> addons;
1167
if (ProjectSettings::get_singleton()->has_setting("editor_plugins/enabled")) {
1168
addons = GLOBAL_GET("editor_plugins/enabled");
1169
}
1170
1171
for (const String &addon : addons) {
1172
set_addon_plugin_enabled(addon, true);
1173
}
1174
_initializing_plugins = false;
1175
1176
if (!pending_addons.is_empty()) {
1177
EditorFileSystem::get_singleton()->connect("script_classes_updated", callable_mp(this, &EditorNode::_enable_pending_addons), CONNECT_ONE_SHOT);
1178
}
1179
}
1180
1181
void EditorNode::_on_plugin_ready(Object *p_script, const String &p_activate_name) {
1182
Ref<Script> scr = Object::cast_to<Script>(p_script);
1183
if (scr.is_null()) {
1184
return;
1185
}
1186
project_settings_editor->update_plugins();
1187
project_settings_editor->hide();
1188
push_item(scr.operator->());
1189
if (p_activate_name.length()) {
1190
set_addon_plugin_enabled(p_activate_name, true);
1191
}
1192
}
1193
1194
void EditorNode::_remove_plugin_from_enabled(const String &p_name) {
1195
ProjectSettings *ps = ProjectSettings::get_singleton();
1196
PackedStringArray enabled_plugins = ps->get("editor_plugins/enabled");
1197
for (int i = 0; i < enabled_plugins.size(); ++i) {
1198
if (enabled_plugins.get(i) == p_name) {
1199
enabled_plugins.remove_at(i);
1200
break;
1201
}
1202
}
1203
ps->set("editor_plugins/enabled", enabled_plugins);
1204
}
1205
1206
void EditorNode::_plugin_over_edit(EditorPlugin *p_plugin, Object *p_object) {
1207
if (p_object) {
1208
editor_plugins_over->add_plugin(p_plugin);
1209
p_plugin->edit(p_object);
1210
p_plugin->make_visible(true);
1211
} else {
1212
editor_plugins_over->remove_plugin(p_plugin);
1213
p_plugin->edit(nullptr);
1214
p_plugin->make_visible(false);
1215
}
1216
}
1217
1218
void EditorNode::_plugin_over_self_own(EditorPlugin *p_plugin) {
1219
active_plugins[p_plugin->get_instance_id()].insert(p_plugin);
1220
}
1221
1222
void EditorNode::_resources_changed(const Vector<String> &p_resources) {
1223
List<Ref<Resource>> changed;
1224
1225
int rc = p_resources.size();
1226
for (int i = 0; i < rc; i++) {
1227
Ref<Resource> res = ResourceCache::get_ref(p_resources.get(i));
1228
if (res.is_null()) {
1229
continue;
1230
}
1231
1232
if (!res->editor_can_reload_from_file()) {
1233
continue;
1234
}
1235
if (!res->get_path().is_resource_file() && !res->get_path().is_absolute_path()) {
1236
continue;
1237
}
1238
if (!FileAccess::exists(res->get_path())) {
1239
continue;
1240
}
1241
1242
if (!res->get_import_path().is_empty()) {
1243
// This is an imported resource, will be reloaded if reimported via the _resources_reimported() callback.
1244
continue;
1245
}
1246
1247
changed.push_back(res);
1248
}
1249
1250
if (changed.size()) {
1251
for (Ref<Resource> &res : changed) {
1252
res->reload_from_file();
1253
}
1254
}
1255
}
1256
1257
void EditorNode::_fs_changed() {
1258
for (FileDialog *E : file_dialogs) {
1259
E->invalidate();
1260
}
1261
1262
_mark_unsaved_scenes();
1263
1264
// FIXME: Move this to a cleaner location, it's hacky to do this in _fs_changed.
1265
String export_error;
1266
Error err = OK;
1267
// It's important to wait for the first scan to finish; otherwise, scripts or resources might not be imported.
1268
if (!export_defer.preset.is_empty() && !EditorFileSystem::get_singleton()->is_scanning()) {
1269
String preset_name = export_defer.preset;
1270
// Ensures export_project does not loop infinitely, because notifications may
1271
// come during the export.
1272
export_defer.preset = "";
1273
Ref<EditorExportPreset> export_preset;
1274
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); ++i) {
1275
export_preset = EditorExport::get_singleton()->get_export_preset(i);
1276
if (export_preset->get_name() == preset_name) {
1277
break;
1278
}
1279
export_preset.unref();
1280
}
1281
1282
if (export_preset.is_null()) {
1283
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
1284
if (da->file_exists("res://export_presets.cfg")) {
1285
err = FAILED;
1286
export_error = vformat(
1287
"Invalid export preset name: %s.\nThe following presets were detected in this project's `export_presets.cfg`:\n\n",
1288
preset_name);
1289
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); ++i) {
1290
// Write the preset name between double quotes since it needs to be written between quotes on the command line if it contains spaces.
1291
export_error += vformat(" \"%s\"\n", EditorExport::get_singleton()->get_export_preset(i)->get_name());
1292
}
1293
} else {
1294
err = FAILED;
1295
export_error = "This project doesn't have an `export_presets.cfg` file at its root.\nCreate an export preset from the \"Project > Export\" dialog and try again.";
1296
}
1297
} else {
1298
Ref<EditorExportPlatform> platform = export_preset->get_platform();
1299
const String export_path = export_defer.path.is_empty() ? export_preset->get_export_path() : export_defer.path;
1300
if (export_path.is_empty()) {
1301
err = FAILED;
1302
export_error = vformat("Export preset \"%s\" doesn't have a default export path, and none was specified.", preset_name);
1303
} else if (platform.is_null()) {
1304
err = FAILED;
1305
export_error = vformat("Export preset \"%s\" doesn't have a matching platform.", preset_name);
1306
} else {
1307
export_preset->update_value_overrides();
1308
if (export_defer.pack_only) { // Only export .pck or .zip data pack.
1309
if (export_path.ends_with(".zip")) {
1310
if (export_defer.patch) {
1311
err = platform->export_zip_patch(export_preset, export_defer.debug, export_path, export_defer.patches);
1312
} else {
1313
err = platform->export_zip(export_preset, export_defer.debug, export_path);
1314
}
1315
} else if (export_path.ends_with(".pck")) {
1316
if (export_defer.patch) {
1317
err = platform->export_pack_patch(export_preset, export_defer.debug, export_path, export_defer.patches);
1318
} else {
1319
err = platform->export_pack(export_preset, export_defer.debug, export_path);
1320
}
1321
} else {
1322
ERR_PRINT(vformat("Export path \"%s\" doesn't end with a supported extension.", export_path));
1323
err = FAILED;
1324
}
1325
} else { // Normal project export.
1326
String config_error;
1327
bool missing_templates;
1328
if (export_defer.android_build_template) {
1329
export_template_manager->install_android_template(export_preset);
1330
}
1331
if (!platform->can_export(export_preset, config_error, missing_templates, export_defer.debug)) {
1332
ERR_PRINT(vformat("Cannot export project with preset \"%s\" due to configuration errors:\n%s", preset_name, config_error));
1333
err = missing_templates ? ERR_FILE_NOT_FOUND : ERR_UNCONFIGURED;
1334
} else {
1335
platform->clear_messages();
1336
err = platform->export_project(export_preset, export_defer.debug, export_path);
1337
}
1338
}
1339
if (err != OK) {
1340
export_error = vformat("Project export for preset \"%s\" failed.", preset_name);
1341
} else if (platform->get_worst_message_type() >= EditorExportPlatform::EXPORT_MESSAGE_WARNING) {
1342
export_error = vformat("Project export for preset \"%s\" completed with warnings.", preset_name);
1343
}
1344
}
1345
}
1346
1347
if (err != OK) {
1348
ERR_PRINT(export_error);
1349
_exit_editor(EXIT_FAILURE);
1350
return;
1351
}
1352
if (!export_error.is_empty()) {
1353
WARN_PRINT(export_error);
1354
}
1355
_exit_editor(EXIT_SUCCESS);
1356
}
1357
}
1358
1359
void EditorNode::_resources_reimporting(const Vector<String> &p_resources) {
1360
// This will copy all the modified properties of the nodes into 'scenes_modification_table'
1361
// before they are actually reimported. It's important to do this before the reimportation
1362
// because if a mesh is present in an inherited scene, the resource will be modified in
1363
// the inherited scene. Then, get_modified_properties_for_node will return the mesh property,
1364
// which will trigger a recopy of the previous mesh, preventing the reload.
1365
scenes_modification_table.clear();
1366
scenes_reimported.clear();
1367
resources_reimported.clear();
1368
EditorFileSystem *editor_file_system = EditorFileSystem::get_singleton();
1369
for (const String &res_path : p_resources) {
1370
// It's faster to use EditorFileSystem::get_file_type than fetching the resource type from disk.
1371
// This makes a big difference when reimporting many resources.
1372
String file_type = editor_file_system->get_file_type(res_path);
1373
if (file_type.is_empty()) {
1374
file_type = ResourceLoader::get_resource_type(res_path);
1375
}
1376
if (file_type == "PackedScene") {
1377
scenes_reimported.push_back(res_path);
1378
} else {
1379
resources_reimported.push_back(res_path);
1380
}
1381
}
1382
1383
if (scenes_reimported.size() > 0) {
1384
preload_reimporting_with_path_in_edited_scenes(scenes_reimported);
1385
}
1386
}
1387
1388
void EditorNode::_resources_reimported(const Vector<String> &p_resources) {
1389
int current_tab = scene_tabs->get_current_tab();
1390
1391
for (const String &res_path : resources_reimported) {
1392
if (!ResourceCache::has(res_path)) {
1393
// Not loaded, no need to reload.
1394
continue;
1395
}
1396
// Reload normally.
1397
Ref<Resource> resource = ResourceCache::get_ref(res_path);
1398
if (resource.is_valid()) {
1399
resource->reload_from_file();
1400
}
1401
}
1402
1403
// Editor may crash when related animation is playing while re-importing GLTF scene, stop it in advance.
1404
AnimationPlayer *ap = AnimationPlayerEditor::get_singleton()->get_player();
1405
if (ap && scenes_reimported.size() > 0) {
1406
ap->stop(true);
1407
}
1408
1409
// Only refresh the current scene tab if it's been reimported.
1410
// Otherwise the scene tab will try to grab focus unnecessarily.
1411
bool should_refresh_current_scene_tab = false;
1412
const String current_scene_tab = editor_data.get_scene_path(current_tab);
1413
for (const String &E : scenes_reimported) {
1414
if (!should_refresh_current_scene_tab && E == current_scene_tab) {
1415
should_refresh_current_scene_tab = true;
1416
}
1417
reload_scene(E);
1418
}
1419
1420
reload_instances_with_path_in_edited_scenes();
1421
1422
scenes_modification_table.clear();
1423
scenes_reimported.clear();
1424
resources_reimported.clear();
1425
1426
if (should_refresh_current_scene_tab) {
1427
_set_current_scene_nocheck(current_tab);
1428
}
1429
}
1430
1431
void EditorNode::_sources_changed(bool p_exist) {
1432
if (waiting_for_first_scan) {
1433
waiting_for_first_scan = false;
1434
1435
OS::get_singleton()->benchmark_end_measure("Editor", "First Scan");
1436
1437
// Reload the global shader variables, but this time
1438
// loading textures, as they are now properly imported.
1439
RenderingServer::get_singleton()->global_shader_parameters_load_settings(true);
1440
1441
_load_editor_layout();
1442
1443
if (!defer_load_scene.is_empty()) {
1444
OS::get_singleton()->benchmark_begin_measure("Editor", "Load Scene");
1445
1446
load_scene(defer_load_scene);
1447
defer_load_scene = "";
1448
1449
OS::get_singleton()->benchmark_end_measure("Editor", "Load Scene");
1450
OS::get_singleton()->benchmark_dump();
1451
}
1452
1453
// Start preview thread now that it's safe.
1454
if (!singleton->cmdline_mode) {
1455
EditorResourcePreview::get_singleton()->start();
1456
}
1457
1458
// Set initial focus for screen reader users.
1459
if (get_tree()->is_accessibility_enabled()) {
1460
if (SceneTreeDock::get_singleton()->is_visible_in_tree()) {
1461
SceneTreeDock::get_singleton()->get_tree_editor()->get_scene_tree()->grab_focus();
1462
} else {
1463
TabContainer *tab_container = SceneTreeDock::get_singleton()->get_parent_container();
1464
if (tab_container) {
1465
// Another tab is active (e.g., Import) - focus the tab bar so user can switch.
1466
tab_container->get_tab_bar()->grab_focus();
1467
}
1468
}
1469
}
1470
1471
get_tree()->create_timer(1.0f)->connect("timeout", callable_mp(this, &EditorNode::_remove_lock_file));
1472
}
1473
}
1474
1475
void EditorNode::_remove_lock_file() {
1476
OS::get_singleton()->remove_lock_file();
1477
}
1478
1479
void EditorNode::_scan_external_changes() {
1480
disk_changed_list->clear();
1481
TreeItem *r = disk_changed_list->create_item();
1482
disk_changed_list->set_hide_root(true);
1483
bool need_reload = false;
1484
1485
disk_changed_scenes.clear();
1486
disk_changed_project = false;
1487
1488
// Check if any edited scene has changed.
1489
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
1490
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
1491
1492
const String scene_path = editor_data.get_scene_path(i);
1493
1494
if (scene_path == "" || !da->file_exists(scene_path)) {
1495
continue;
1496
}
1497
1498
uint64_t last_date = editor_data.get_scene_modified_time(i);
1499
uint64_t date = FileAccess::get_modified_time(scene_path);
1500
1501
if (date > last_date) {
1502
TreeItem *ti = disk_changed_list->create_item(r);
1503
ti->set_text(0, scene_path.get_file());
1504
need_reload = true;
1505
disk_changed_scenes.push_back(scene_path);
1506
}
1507
}
1508
1509
String project_settings_path = ProjectSettings::get_singleton()->get_resource_path().path_join("project.godot");
1510
if (FileAccess::get_modified_time(project_settings_path) > ProjectSettings::get_singleton()->get_last_saved_time()) {
1511
TreeItem *ti = disk_changed_list->create_item(r);
1512
ti->set_text(0, "project.godot");
1513
need_reload = true;
1514
disk_changed_project = true;
1515
}
1516
1517
if (need_reload) {
1518
callable_mp((Window *)disk_changed, &Window::popup_centered_ratio).call_deferred(0.3);
1519
}
1520
}
1521
1522
void EditorNode::_resave_externally_modified_scenes(String p_str) {
1523
for (const String &scene_path : disk_changed_scenes) {
1524
_save_scene(scene_path);
1525
}
1526
1527
if (disk_changed_project) {
1528
ProjectSettings::get_singleton()->save();
1529
}
1530
1531
disk_changed->hide();
1532
}
1533
1534
void EditorNode::_reload_modified_scenes() {
1535
int current_idx = editor_data.get_edited_scene();
1536
1537
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
1538
if (editor_data.get_scene_path(i) == "") {
1539
continue;
1540
}
1541
1542
uint64_t last_date = editor_data.get_scene_modified_time(i);
1543
uint64_t date = FileAccess::get_modified_time(editor_data.get_scene_path(i));
1544
1545
if (date > last_date) {
1546
String filename = editor_data.get_scene_path(i);
1547
editor_data.set_edited_scene(i);
1548
_remove_edited_scene(false);
1549
1550
Error err = load_scene(filename, false, false, false, true);
1551
if (err != OK) {
1552
ERR_PRINT(vformat("Failed to load scene: %s", filename));
1553
}
1554
editor_data.move_edited_scene_to_index(i);
1555
}
1556
}
1557
1558
_set_current_scene(current_idx);
1559
scene_tabs->update_scene_tabs();
1560
disk_changed->hide();
1561
}
1562
1563
void EditorNode::_reload_project_settings() {
1564
ProjectSettings::get_singleton()->setup(ProjectSettings::get_singleton()->get_resource_path(), String(), true, true);
1565
}
1566
1567
void EditorNode::_vp_resized() {
1568
}
1569
1570
void EditorNode::_viewport_resized() {
1571
Window *w = get_window();
1572
if (w) {
1573
was_window_windowed_last = w->get_mode() == Window::MODE_WINDOWED;
1574
}
1575
}
1576
1577
void EditorNode::_titlebar_resized() {
1578
DisplayServer::get_singleton()->window_set_window_buttons_offset(Vector2i(title_bar->get_global_position().y + title_bar->get_size().y / 2, title_bar->get_global_position().y + title_bar->get_size().y / 2), DisplayServer::MAIN_WINDOW_ID);
1579
const Vector3i &margin = DisplayServer::get_singleton()->window_get_safe_title_margins(DisplayServer::MAIN_WINDOW_ID);
1580
if (left_menu_spacer) {
1581
int w = (gui_base->is_layout_rtl()) ? margin.y : margin.x;
1582
left_menu_spacer->set_custom_minimum_size(Size2(w, 0));
1583
}
1584
if (right_menu_spacer) {
1585
int w = (gui_base->is_layout_rtl()) ? margin.x : margin.y;
1586
right_menu_spacer->set_custom_minimum_size(Size2(w, 0));
1587
}
1588
if (title_bar) {
1589
title_bar->set_custom_minimum_size(Size2(0, margin.z - title_bar->get_global_position().y));
1590
}
1591
}
1592
1593
void EditorNode::_update_undo_redo_allowed() {
1594
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
1595
file_menu->set_item_disabled(file_menu->get_item_index(SCENE_UNDO), !undo_redo->has_undo());
1596
file_menu->set_item_disabled(file_menu->get_item_index(SCENE_REDO), !undo_redo->has_redo());
1597
}
1598
1599
void EditorNode::_node_renamed() {
1600
if (InspectorDock::get_inspector_singleton()) {
1601
InspectorDock::get_inspector_singleton()->update_tree();
1602
}
1603
}
1604
1605
void EditorNode::_open_command_palette() {
1606
command_palette->open_popup();
1607
}
1608
1609
Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_deps) {
1610
dependency_errors.clear();
1611
1612
Error err;
1613
1614
Ref<Resource> res;
1615
if (force_textfile_extensions.has(p_resource.get_extension())) {
1616
res = ResourceCache::get_ref(p_resource);
1617
if (res.is_null() || !res->is_class("TextFile")) {
1618
res = ScriptEditor::get_singleton()->open_file(p_resource);
1619
}
1620
} else if (ResourceLoader::exists(p_resource, "")) {
1621
res = ResourceLoader::load(p_resource, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
1622
} else if (textfile_extensions.has(p_resource.get_extension())) {
1623
res = ScriptEditor::get_singleton()->open_file(p_resource);
1624
} else if (other_file_extensions.has(p_resource.get_extension())) {
1625
OS::get_singleton()->shell_open(ProjectSettings::get_singleton()->globalize_path(p_resource));
1626
return OK;
1627
}
1628
ERR_FAIL_COND_V(res.is_null(), ERR_CANT_OPEN);
1629
1630
if (!p_ignore_broken_deps && !dependency_errors.is_empty()) {
1631
dependency_error->show(p_resource, dependency_errors);
1632
dependency_errors.clear();
1633
1634
return ERR_FILE_MISSING_DEPENDENCIES;
1635
}
1636
1637
InspectorDock::get_singleton()->edit_resource(res);
1638
return OK;
1639
}
1640
1641
Error EditorNode::load_scene_or_resource(const String &p_path, bool p_ignore_broken_deps, bool p_change_scene_tab_if_already_open) {
1642
if (ClassDB::is_parent_class(ResourceLoader::get_resource_type(p_path), "PackedScene")) {
1643
if (!p_change_scene_tab_if_already_open && EditorNode::get_singleton()->is_scene_open(p_path)) {
1644
return OK;
1645
}
1646
return EditorNode::get_singleton()->load_scene(p_path, p_ignore_broken_deps);
1647
}
1648
return EditorNode::get_singleton()->load_resource(p_path, p_ignore_broken_deps);
1649
}
1650
1651
void EditorNode::edit_node(Node *p_node) {
1652
push_item(p_node);
1653
}
1654
1655
void EditorNode::edit_resource(const Ref<Resource> &p_resource) {
1656
InspectorDock::get_singleton()->edit_resource(p_resource);
1657
}
1658
1659
void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path) {
1660
editor_data.apply_changes_in_editors();
1661
1662
if (saving_resources_in_path.has(p_resource)) {
1663
return;
1664
}
1665
saving_resources_in_path.insert(p_resource);
1666
1667
int flg = 0;
1668
if (EDITOR_GET("filesystem/on_save/compress_binary_resources")) {
1669
flg |= ResourceSaver::FLAG_COMPRESS;
1670
}
1671
1672
String path = ProjectSettings::get_singleton()->localize_path(p_path);
1673
Error err = ResourceSaver::save(p_resource, path, flg | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS);
1674
1675
if (err != OK) {
1676
if (ResourceLoader::is_imported(p_resource->get_path())) {
1677
show_accept(TTR("Imported resources can't be saved."), TTR("OK"));
1678
} else {
1679
show_accept(TTR("Error saving resource!"), TTR("OK"));
1680
}
1681
1682
saving_resources_in_path.erase(p_resource);
1683
return;
1684
}
1685
1686
Ref<Resource> prev_resource = ResourceCache::get_ref(p_path);
1687
if (prev_resource.is_null() || prev_resource != p_resource) {
1688
p_resource->set_path(path, true);
1689
}
1690
if (prev_resource.is_valid() && prev_resource != p_resource) {
1691
replace_resources_in_scenes({ prev_resource }, { p_resource });
1692
}
1693
saving_resources_in_path.erase(p_resource);
1694
1695
_resource_saved(p_resource, path);
1696
clear_node_reference(p_resource); // // Check if Resource is saved to disk to potentially remove it from resource_count
1697
emit_signal(SNAME("resource_saved"), p_resource);
1698
editor_data.notify_resource_saved(p_resource);
1699
1700
if (EDITOR_GET("filesystem/on_save/warn_on_saving_large_text_resources")) {
1701
if (p_path.ends_with(".tres")) {
1702
const int64_t file_size = FileAccess::get_size(p_path);
1703
if (file_size >= LARGE_RESOURCE_WARNING_SIZE_THRESHOLD) {
1704
// File is larger than 500 KiB, likely because it contains binary data serialized as Base64.
1705
// This is slow to save and load, so warn the user.
1706
EditorToaster::get_singleton()->popup_str(
1707
vformat(TTR("The text-based resource at path \"%s\" is large on disk (%s), likely because it has embedded binary data.\nThis slows down resource saving and loading.\nConsider saving its binary subresource(s) to a binary `.res` file or saving the resource as a binary `.res` file.\nThis warning can be disabled in the Editor Settings (FileSystem > On Save > Warn on Saving Large Text Resources)."), p_path, String::humanize_size(file_size)), EditorToaster::SEVERITY_WARNING);
1708
}
1709
}
1710
}
1711
}
1712
1713
void EditorNode::save_resource(const Ref<Resource> &p_resource) {
1714
// If built-in resource, save the scene instead.
1715
if (p_resource->is_built_in()) {
1716
const String scene_path = p_resource->get_path().get_slice("::", 0);
1717
if (!scene_path.is_empty()) {
1718
if (ResourceLoader::exists(scene_path) && ResourceLoader::get_resource_type(scene_path) == "PackedScene") {
1719
save_scene_if_open(scene_path);
1720
} else {
1721
// Not a packed scene, so save it as regular resource.
1722
Ref<Resource> parent_resource = ResourceCache::get_ref(scene_path);
1723
ERR_FAIL_COND_MSG(parent_resource.is_null(), "Parent resource not loaded, can't save.");
1724
save_resource(parent_resource);
1725
}
1726
return;
1727
}
1728
}
1729
1730
// If the resource has been imported, ask the user to use a different path in order to save it.
1731
String path = p_resource->get_path();
1732
if (path.is_resource_file() && !FileAccess::exists(path + ".import")) {
1733
save_resource_in_path(p_resource, p_resource->get_path());
1734
} else {
1735
save_resource_as(p_resource);
1736
}
1737
}
1738
1739
void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String &p_at_path) {
1740
String resource_path = p_resource->get_path();
1741
bool is_resource = resource_path.is_resource_file();
1742
1743
{
1744
// Early exit checks.
1745
1746
if (is_resource) {
1747
if (FileAccess::exists(resource_path + ".import")) {
1748
show_warning(TTR("This resource can't be saved because it was imported from another file. Make it unique first."));
1749
return;
1750
}
1751
} else {
1752
int separator_pos = resource_path.find("::");
1753
if (separator_pos != -1) {
1754
String base = resource_path.substr(0, separator_pos);
1755
String base_resource_type = ResourceLoader::get_resource_type(base);
1756
if (base_resource_type == "PackedScene" && (!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base)) {
1757
show_warning(TTR("This resource can't be saved because it does not belong to the edited scene. Make it unique first."));
1758
return;
1759
}
1760
}
1761
}
1762
}
1763
1764
file->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
1765
saving_resource = p_resource;
1766
1767
current_menu_option = RESOURCE_SAVE_AS;
1768
List<String> extensions;
1769
ResourceSaver::get_recognized_extensions(p_resource, &extensions);
1770
file->clear_filters();
1771
1772
List<String> preferred;
1773
for (const String &E : extensions) {
1774
if (p_resource->is_class("Script") && (E == "tres" || E == "res")) {
1775
// This serves no purpose and confused people.
1776
continue;
1777
}
1778
file->add_filter("*." + E, E.to_upper());
1779
preferred.push_back(E);
1780
}
1781
// Lowest provided extension priority.
1782
List<String>::Element *res_element = preferred.find("res");
1783
if (res_element) {
1784
preferred.move_to_back(res_element);
1785
}
1786
1787
String resource_name_snake_case = p_resource->get_class().to_snake_case();
1788
String new_resource_name_snake_case;
1789
if (!preferred.is_empty()) {
1790
new_resource_name_snake_case = "new_" + resource_name_snake_case + "." + preferred.front()->get().to_lower();
1791
}
1792
1793
if (!p_at_path.is_empty()) {
1794
file->set_current_dir(p_at_path);
1795
if (is_resource) {
1796
file->set_current_file(resource_path.get_file());
1797
} else {
1798
file->set_current_file(new_resource_name_snake_case);
1799
}
1800
} else if (!p_resource->get_path().get_base_dir().is_empty()) {
1801
if (is_resource) {
1802
if (!extensions.is_empty()) {
1803
const String ext = resource_path.get_extension().to_lower();
1804
if (extensions.find(ext) == nullptr) {
1805
file->set_current_path(resource_path.replacen("." + ext, "." + extensions.front()->get()));
1806
}
1807
}
1808
} else {
1809
file->set_current_file(new_resource_name_snake_case);
1810
}
1811
} else if (!preferred.is_empty()) {
1812
file->set_current_file(new_resource_name_snake_case);
1813
file->set_current_path(new_resource_name_snake_case);
1814
}
1815
file->set_title(TTR("Save Resource As..."));
1816
file->popup_file_dialog();
1817
}
1818
1819
bool EditorNode::is_resource_internal_to_scene(Ref<Resource> p_resource) {
1820
bool inside_scene = !get_edited_scene() || get_edited_scene()->get_scene_file_path() == p_resource->get_path().get_slice("::", 0);
1821
return inside_scene || p_resource->get_path().is_empty();
1822
}
1823
1824
void EditorNode::gather_resources(const Variant &p_variant, List<Ref<Resource>> &r_list, bool p_subresources, bool p_allow_external) {
1825
Variant::Type type = p_variant.get_type();
1826
if (type == Variant::OBJECT && p_variant.get_validated_object() == nullptr) {
1827
return;
1828
}
1829
1830
if (type != Variant::OBJECT && type != Variant::ARRAY && type != Variant::DICTIONARY) {
1831
return;
1832
}
1833
1834
if (type == Variant::ARRAY) {
1835
Array arr = p_variant;
1836
for (const Variant &v : arr) {
1837
Ref<Resource> res = v;
1838
if (res.is_valid()) {
1839
if (p_allow_external) {
1840
r_list.push_back(res);
1841
} else if (is_resource_internal_to_scene(res)) {
1842
r_list.push_back(res);
1843
}
1844
}
1845
if (Object::cast_to<Node>(v) == nullptr) {
1846
gather_resources(v, r_list, p_subresources, p_allow_external);
1847
}
1848
}
1849
return;
1850
}
1851
1852
if (type == Variant::DICTIONARY) {
1853
Dictionary dict = p_variant;
1854
for (const KeyValue<Variant, Variant> &kv : dict) {
1855
Ref<Resource> res_key = kv.key;
1856
Ref<Resource> res_value = kv.value;
1857
if (res_key.is_valid()) {
1858
if (p_allow_external) {
1859
r_list.push_back(res_key);
1860
} else if (is_resource_internal_to_scene(res_key)) {
1861
r_list.push_back(res_key);
1862
}
1863
}
1864
if (res_value.is_valid()) {
1865
if (p_allow_external) {
1866
r_list.push_back(res_value);
1867
} else if (is_resource_internal_to_scene(res_value)) {
1868
r_list.push_back(res_value);
1869
}
1870
}
1871
if (Object::cast_to<Node>(kv.key) == nullptr) {
1872
gather_resources(kv.key, r_list, p_subresources, p_allow_external);
1873
}
1874
if (Object::cast_to<Node>(kv.value) == nullptr) {
1875
gather_resources(kv.value, r_list, p_subresources, p_allow_external);
1876
}
1877
}
1878
return;
1879
}
1880
1881
List<PropertyInfo> pinfo;
1882
p_variant.get_property_list(&pinfo);
1883
1884
for (const PropertyInfo &E : pinfo) {
1885
if (!(E.usage & PROPERTY_USAGE_EDITOR)) {
1886
continue;
1887
}
1888
1889
Variant property_value = p_variant.get(E.name);
1890
Variant::Type property_type = property_value.get_type();
1891
if (property_type == Variant::ARRAY || property_type == Variant::DICTIONARY) {
1892
gather_resources(property_value, r_list, p_subresources, p_allow_external);
1893
continue;
1894
}
1895
Ref<Resource> res = property_value;
1896
if (res.is_null()) {
1897
continue;
1898
}
1899
if (!p_allow_external) {
1900
if (!res->is_built_in() || res->get_path().get_slice("::", 0) != get_edited_scene()->get_scene_file_path()) {
1901
if (!res->get_path().is_empty()) {
1902
continue;
1903
}
1904
}
1905
}
1906
r_list.push_back(res);
1907
if (p_subresources) {
1908
gather_resources(res, r_list, p_subresources, p_allow_external);
1909
}
1910
}
1911
}
1912
1913
void EditorNode::update_resource_count(Node *p_node, bool p_remove) {
1914
if (!get_edited_scene()) {
1915
return;
1916
}
1917
1918
List<Ref<Resource>> res_list;
1919
gather_resources(p_node, res_list, true);
1920
1921
for (Ref<Resource> &R : res_list) {
1922
List<Node *>::Element *E = resource_count[R].find(p_node);
1923
if (E) {
1924
if (p_remove) {
1925
resource_count[R].erase(E);
1926
}
1927
} else {
1928
resource_count[R].push_back(p_node);
1929
}
1930
}
1931
1932
emit_signal(SNAME("resource_counter_changed"));
1933
}
1934
1935
int EditorNode::get_resource_count(Ref<Resource> p_res) {
1936
List<Node *> *L = resource_count.getptr(p_res);
1937
return L ? L->size() : 0;
1938
}
1939
1940
List<Node *> EditorNode::get_resource_node_list(Ref<Resource> p_res) {
1941
List<Node *> *L = resource_count.getptr(p_res);
1942
return L == nullptr ? List<Node *>() : *L;
1943
}
1944
1945
void EditorNode::update_node_reference(const Variant &p_value, Node *p_node, bool p_remove) {
1946
List<Ref<Resource>> list;
1947
Ref<Resource> res = p_value;
1948
gather_resources(p_value, list, true); //Gather all Resources and their SubResources to remove p_node from their lists.
1949
1950
if (res.is_valid() && is_resource_internal_to_scene(res)) {
1951
// Avoid external Resources from being added in.
1952
list.push_back(res);
1953
}
1954
1955
for (Ref<Resource> &R : list) {
1956
if (!p_remove) {
1957
resource_count[R].push_back(p_node);
1958
} else {
1959
List<Node *>::Element *E = resource_count[R].find(p_node);
1960
if (E) {
1961
resource_count[R].erase(E);
1962
}
1963
}
1964
}
1965
emit_signal(SNAME("resource_counter_changed"));
1966
}
1967
1968
void EditorNode::clear_node_reference(Ref<Resource> p_res) {
1969
if (is_resource_internal_to_scene(p_res)) {
1970
return;
1971
}
1972
List<Node *> *node_list = resource_count.getptr(p_res);
1973
if (node_list != nullptr) {
1974
node_list->clear();
1975
}
1976
}
1977
1978
void EditorNode::_menu_option(int p_option) {
1979
_menu_option_confirm(p_option, false);
1980
}
1981
1982
void EditorNode::_menu_confirm_current() {
1983
_menu_option_confirm(current_menu_option, true);
1984
}
1985
1986
void EditorNode::trigger_menu_option(int p_option, bool p_confirmed) {
1987
_menu_option_confirm(p_option, p_confirmed);
1988
}
1989
1990
void EditorNode::_dialog_display_save_error(String p_file, Error p_error) {
1991
if (p_error) {
1992
switch (p_error) {
1993
case ERR_FILE_CANT_WRITE: {
1994
show_accept(TTR("Can't open file for writing:") + " " + p_file.get_extension(), TTR("OK"));
1995
} break;
1996
case ERR_FILE_UNRECOGNIZED: {
1997
show_accept(TTR("Requested file format unknown:") + " " + p_file.get_extension(), TTR("OK"));
1998
} break;
1999
default: {
2000
show_accept(TTR("Error while saving."), TTR("OK"));
2001
} break;
2002
}
2003
}
2004
}
2005
2006
void EditorNode::_dialog_display_load_error(String p_file, Error p_error) {
2007
if (p_error) {
2008
switch (p_error) {
2009
case ERR_CANT_OPEN: {
2010
show_accept(vformat(TTR("Can't open file '%s'. The file could have been moved or deleted."), p_file.get_file()), TTR("OK"));
2011
} break;
2012
case ERR_PARSE_ERROR: {
2013
show_accept(vformat(TTR("Error while parsing file '%s'."), p_file.get_file()), TTR("OK"));
2014
} break;
2015
case ERR_FILE_CORRUPT: {
2016
show_accept(vformat(TTR("Scene file '%s' appears to be invalid/corrupt."), p_file.get_file()), TTR("OK"));
2017
} break;
2018
case ERR_FILE_NOT_FOUND: {
2019
show_accept(vformat(TTR("Missing file '%s' or one of its dependencies."), p_file.get_file()), TTR("OK"));
2020
} break;
2021
case ERR_FILE_UNRECOGNIZED: {
2022
show_accept(vformat(TTR("File '%s' is saved in a format that is newer than the formats supported by this version of Godot, so it can't be opened."), p_file.get_file()), TTR("OK"));
2023
} break;
2024
default: {
2025
show_accept(vformat(TTR("Error while loading file '%s'."), p_file.get_file()), TTR("OK"));
2026
} break;
2027
}
2028
}
2029
}
2030
2031
void EditorNode::_load_editor_plugin_states_from_config(const Ref<ConfigFile> &p_config_file) {
2032
Node *scene = editor_data.get_edited_scene_root();
2033
2034
if (!scene) {
2035
return;
2036
}
2037
2038
Vector<String> esl = p_config_file->get_section_keys("editor_states");
2039
2040
Dictionary md;
2041
for (const String &E : esl) {
2042
Variant st = p_config_file->get_value("editor_states", E);
2043
if (st.get_type() != Variant::NIL) {
2044
md[E] = st;
2045
}
2046
}
2047
2048
editor_data.set_editor_plugin_states(md);
2049
}
2050
2051
void EditorNode::_save_editor_states(const String &p_file, int p_idx) {
2052
Node *scene = editor_data.get_edited_scene_root(p_idx);
2053
2054
if (!scene) {
2055
return;
2056
}
2057
2058
String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join(p_file.get_file() + "-editstate-" + p_file.md5_text() + ".cfg");
2059
2060
Ref<ConfigFile> cf;
2061
cf.instantiate();
2062
2063
Dictionary md;
2064
if (p_idx < 0 || editor_data.get_edited_scene() == p_idx) {
2065
md = editor_data.get_editor_plugin_states();
2066
} else {
2067
md = editor_data.get_scene_editor_states(p_idx);
2068
}
2069
2070
for (const KeyValue<Variant, Variant> &kv : md) {
2071
cf->set_value("editor_states", kv.key, kv.value);
2072
}
2073
2074
// Save the currently selected nodes.
2075
2076
List<Node *> selection = editor_selection->get_full_selected_node_list();
2077
TypedArray<NodePath> selection_paths;
2078
for (Node *selected_node : selection) {
2079
selection_paths.push_back(selected_node->get_path());
2080
}
2081
cf->set_value("editor_states", "selected_nodes", selection_paths);
2082
2083
Error err = cf->save(path);
2084
ERR_FAIL_COND_MSG(err != OK, "Cannot save config file to '" + path + "'.");
2085
}
2086
2087
bool EditorNode::_find_and_save_resource(Ref<Resource> p_res, HashMap<Ref<Resource>, bool> &processed, int32_t flags) {
2088
if (p_res.is_null()) {
2089
return false;
2090
}
2091
2092
if (processed.has(p_res)) {
2093
return processed[p_res];
2094
}
2095
2096
bool changed = p_res->is_edited();
2097
p_res->set_edited(false);
2098
2099
bool subchanged = _find_and_save_edited_subresources(p_res.ptr(), processed, flags);
2100
2101
if (p_res->get_path().is_resource_file()) {
2102
if (changed || subchanged) {
2103
ResourceSaver::save(p_res, p_res->get_path(), flags);
2104
}
2105
processed[p_res] = false; // Because it's a file.
2106
return false;
2107
} else {
2108
processed[p_res] = changed;
2109
return changed;
2110
}
2111
}
2112
2113
bool EditorNode::_find_and_save_edited_subresources(Object *obj, HashMap<Ref<Resource>, bool> &processed, int32_t flags) {
2114
bool ret_changed = false;
2115
List<PropertyInfo> pi;
2116
obj->get_property_list(&pi);
2117
for (const PropertyInfo &E : pi) {
2118
if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
2119
continue;
2120
}
2121
2122
switch (E.type) {
2123
case Variant::OBJECT: {
2124
Ref<Resource> res = obj->get(E.name);
2125
2126
if (_find_and_save_resource(res, processed, flags)) {
2127
ret_changed = true;
2128
}
2129
2130
} break;
2131
case Variant::ARRAY: {
2132
Array varray = obj->get(E.name);
2133
int len = varray.size();
2134
for (int i = 0; i < len; i++) {
2135
const Variant &v = varray.get(i);
2136
Ref<Resource> res = v;
2137
if (_find_and_save_resource(res, processed, flags)) {
2138
ret_changed = true;
2139
}
2140
}
2141
2142
} break;
2143
case Variant::DICTIONARY: {
2144
Dictionary d = obj->get(E.name);
2145
for (const KeyValue<Variant, Variant> &kv : d) {
2146
Ref<Resource> res = kv.value;
2147
if (_find_and_save_resource(res, processed, flags)) {
2148
ret_changed = true;
2149
}
2150
}
2151
} break;
2152
default: {
2153
}
2154
}
2155
}
2156
2157
return ret_changed;
2158
}
2159
2160
void EditorNode::_save_edited_subresources(Node *scene, HashMap<Ref<Resource>, bool> &processed, int32_t flags) {
2161
_find_and_save_edited_subresources(scene, processed, flags);
2162
2163
for (int i = 0; i < scene->get_child_count(); i++) {
2164
Node *n = scene->get_child(i);
2165
if (n->get_owner() != editor_data.get_edited_scene_root()) {
2166
continue;
2167
}
2168
_save_edited_subresources(n, processed, flags);
2169
}
2170
}
2171
2172
void EditorNode::_find_node_types(Node *p_node, int &count_2d, int &count_3d) {
2173
if (p_node->is_class("Viewport") || (p_node != editor_data.get_edited_scene_root() && p_node->get_owner() != editor_data.get_edited_scene_root())) {
2174
return;
2175
}
2176
2177
if (p_node->is_class("CanvasItem")) {
2178
count_2d++;
2179
} else if (p_node->is_class("Node3D")) {
2180
count_3d++;
2181
}
2182
2183
for (int i = 0; i < p_node->get_child_count(); i++) {
2184
_find_node_types(p_node->get_child(i), count_2d, count_3d);
2185
}
2186
}
2187
2188
void EditorNode::_save_scene_with_preview(String p_file, int p_idx) {
2189
save_scene_progress = memnew(EditorProgress("save", TTR("Saving Scene"), 4));
2190
2191
if (editor_data.get_edited_scene_root() != nullptr) {
2192
save_scene_progress->step(TTR("Analyzing"), 0);
2193
2194
int c2d = 0;
2195
int c3d = 0;
2196
2197
_find_node_types(editor_data.get_edited_scene_root(), c2d, c3d);
2198
2199
save_scene_progress->step(TTR("Creating Thumbnail"), 1);
2200
// Current view?
2201
2202
Ref<Image> img;
2203
// If neither 3D or 2D nodes are present, make a 1x1 black texture.
2204
// We cannot fallback on the 2D editor, because it may not have been used yet,
2205
// which would result in an invalid texture.
2206
if (c3d == 0 && c2d == 0) {
2207
img.instantiate();
2208
img->initialize_data(1, 1, false, Image::FORMAT_RGB8);
2209
} else if (c3d < c2d) {
2210
Ref<ViewportTexture> viewport_texture = scene_root->get_texture();
2211
if (viewport_texture->get_width() > 0 && viewport_texture->get_height() > 0) {
2212
img = viewport_texture->get_image();
2213
}
2214
} else {
2215
// The 3D editor may be disabled as a feature, but scenes can still be opened.
2216
// This check prevents the preview from regenerating in case those scenes are then saved.
2217
// The preview will be generated if no feature profile is set (as the 3D editor is enabled by default).
2218
Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile();
2219
if (profile.is_null() || !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)) {
2220
img = Node3DEditor::get_singleton()->get_editor_viewport(0)->get_viewport_node()->get_texture()->get_image();
2221
}
2222
}
2223
2224
if (img.is_valid() && img->get_width() > 0 && img->get_height() > 0) {
2225
img = img->duplicate();
2226
2227
save_scene_progress->step(TTR("Creating Thumbnail"), 3);
2228
2229
int preview_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
2230
preview_size *= EDSCALE;
2231
2232
// Consider a square region.
2233
int vp_size = MIN(img->get_width(), img->get_height());
2234
int x = (img->get_width() - vp_size) / 2;
2235
int y = (img->get_height() - vp_size) / 2;
2236
2237
if (vp_size < preview_size) {
2238
// Just square it.
2239
img->crop_from_point(x, y, vp_size, vp_size);
2240
} else {
2241
int ratio = vp_size / preview_size;
2242
int size = preview_size * MAX(1, ratio / 2);
2243
2244
x = (img->get_width() - size) / 2;
2245
y = (img->get_height() - size) / 2;
2246
2247
img->crop_from_point(x, y, size, size);
2248
img->resize(preview_size, preview_size, Image::INTERPOLATE_LANCZOS);
2249
}
2250
img->convert(Image::FORMAT_RGB8);
2251
2252
// Save thumbnail directly, as thumbnailer may not update due to actual scene not changing md5.
2253
String temp_path = EditorPaths::get_singleton()->get_cache_dir();
2254
String cache_base = ProjectSettings::get_singleton()->globalize_path(p_file).md5_text();
2255
cache_base = temp_path.path_join("resthumb-" + cache_base);
2256
2257
// Does not have it, try to load a cached thumbnail.
2258
post_process_preview(img);
2259
img->save_png(cache_base + ".png");
2260
}
2261
}
2262
2263
save_scene_progress->step(TTR("Saving Scene"), 4);
2264
_save_scene(p_file, p_idx);
2265
2266
if (!singleton->cmdline_mode) {
2267
EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
2268
}
2269
2270
_close_save_scene_progress();
2271
}
2272
2273
void EditorNode::_close_save_scene_progress() {
2274
memdelete_notnull(save_scene_progress);
2275
save_scene_progress = nullptr;
2276
}
2277
2278
bool EditorNode::_validate_scene_recursive(const String &p_filename, Node *p_node) {
2279
for (int i = 0; i < p_node->get_child_count(); i++) {
2280
Node *child = p_node->get_child(i);
2281
if (child->get_scene_file_path() == p_filename) {
2282
return true;
2283
}
2284
2285
if (_validate_scene_recursive(p_filename, child)) {
2286
return true;
2287
}
2288
}
2289
2290
return false;
2291
}
2292
2293
int EditorNode::_save_external_resources(bool p_also_save_external_data) {
2294
// Save external resources and its subresources if any was modified.
2295
2296
int flg = 0;
2297
if (EDITOR_GET("filesystem/on_save/compress_binary_resources")) {
2298
flg |= ResourceSaver::FLAG_COMPRESS;
2299
}
2300
flg |= ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
2301
2302
HashSet<String> edited_resources;
2303
int saved = 0;
2304
List<Ref<Resource>> cached;
2305
ResourceCache::get_cached_resources(&cached);
2306
2307
for (Ref<Resource> res : cached) {
2308
if (!res->is_edited()) {
2309
continue;
2310
}
2311
2312
String path = res->get_path();
2313
if (path.begins_with("res://")) {
2314
int subres_pos = path.find("::");
2315
if (subres_pos == -1) {
2316
// Actual resource.
2317
edited_resources.insert(path);
2318
} else {
2319
edited_resources.insert(path.substr(0, subres_pos));
2320
}
2321
}
2322
2323
res->set_edited(false);
2324
}
2325
2326
bool script_was_saved = false;
2327
for (const String &E : edited_resources) {
2328
Ref<Resource> res = ResourceCache::get_ref(E);
2329
if (res.is_null()) {
2330
continue; // Maybe it was erased in a thread, who knows.
2331
}
2332
Ref<PackedScene> ps = res;
2333
if (ps.is_valid()) {
2334
continue; // Do not save PackedScenes, this will mess up the editor.
2335
}
2336
if (!script_was_saved) {
2337
Ref<Script> scr = res;
2338
script_was_saved = scr.is_valid();
2339
}
2340
ResourceSaver::save(res, res->get_path(), flg);
2341
saved++;
2342
}
2343
2344
if (script_was_saved) {
2345
ScriptEditor::get_singleton()->update_script_times();
2346
}
2347
2348
if (p_also_save_external_data) {
2349
for (int i = 0; i < editor_data.get_editor_plugin_count(); i++) {
2350
EditorPlugin *plugin = editor_data.get_editor_plugin(i);
2351
if (!plugin->get_unsaved_status().is_empty()) {
2352
plugin->save_external_data();
2353
saved++;
2354
}
2355
}
2356
}
2357
2358
EditorSettings::get_singleton()->save_project_metadata();
2359
EditorUndoRedoManager::get_singleton()->set_history_as_saved(EditorUndoRedoManager::GLOBAL_HISTORY);
2360
_update_unsaved_cache();
2361
2362
return saved;
2363
}
2364
2365
void EditorNode::_save_scene_silently() {
2366
// Save scene without displaying progress dialog. Used to work around
2367
// errors about parent node being busy setting up children
2368
// when Save on Focus Loss kicks in.
2369
Node *scene = editor_data.get_edited_scene_root();
2370
if (scene && !scene->get_scene_file_path().is_empty() && DirAccess::exists(scene->get_scene_file_path().get_base_dir())) {
2371
_save_scene(scene->get_scene_file_path());
2372
save_editor_layout_delayed();
2373
}
2374
}
2375
2376
static void _reset_animation_mixers(Node *p_node, List<Pair<AnimationMixer *, Ref<AnimatedValuesBackup>>> *r_anim_backups) {
2377
for (int i = 0; i < p_node->get_child_count(); i++) {
2378
AnimationMixer *mixer = Object::cast_to<AnimationMixer>(p_node->get_child(i));
2379
if (mixer && mixer->is_active() && mixer->is_reset_on_save_enabled() && mixer->can_apply_reset()) {
2380
AnimationTree *tree = Object::cast_to<AnimationTree>(p_node->get_child(i));
2381
if (tree) {
2382
AnimationPlayer *player = Object::cast_to<AnimationPlayer>(tree->get_node_or_null(tree->get_animation_player()));
2383
if (player && player->is_active() && player->is_reset_on_save_enabled() && player->can_apply_reset()) {
2384
continue; // Avoid to process reset/restore many times.
2385
}
2386
}
2387
Ref<AnimatedValuesBackup> backup = mixer->apply_reset();
2388
if (backup.is_valid()) {
2389
Pair<AnimationMixer *, Ref<AnimatedValuesBackup>> pair;
2390
pair.first = mixer;
2391
pair.second = backup;
2392
r_anim_backups->push_back(pair);
2393
}
2394
}
2395
_reset_animation_mixers(p_node->get_child(i), r_anim_backups);
2396
}
2397
}
2398
2399
void EditorNode::_save_scene(String p_file, int idx) {
2400
ERR_FAIL_COND_MSG(!saving_scene.is_empty() && saving_scene == p_file, "Scene saved while already being saved!");
2401
2402
Node *scene = editor_data.get_edited_scene_root(idx);
2403
2404
if (!scene) {
2405
show_accept(TTR("This operation can't be done without a tree root."), TTR("OK"));
2406
return;
2407
}
2408
2409
if (!scene->get_scene_file_path().is_empty() && _validate_scene_recursive(scene->get_scene_file_path(), scene)) {
2410
show_accept(TTR("This scene can't be saved because there is a cyclic instance inclusion.\nPlease resolve it and then attempt to save again."), TTR("OK"));
2411
return;
2412
}
2413
2414
scene->propagate_notification(NOTIFICATION_EDITOR_PRE_SAVE);
2415
2416
editor_data.apply_changes_in_editors();
2417
save_default_environment();
2418
List<Pair<AnimationMixer *, Ref<AnimatedValuesBackup>>> anim_backups;
2419
_reset_animation_mixers(scene, &anim_backups);
2420
_save_editor_states(p_file, idx);
2421
2422
Ref<PackedScene> sdata;
2423
2424
if (ResourceCache::has(p_file)) {
2425
// Something may be referencing this resource and we are good with that.
2426
// We must update it, but also let the previous scene state go, as
2427
// old version still work for referencing changes in instantiated or inherited scenes.
2428
2429
sdata = ResourceCache::get_ref(p_file);
2430
if (sdata.is_valid()) {
2431
sdata->recreate_state();
2432
} else {
2433
sdata.instantiate();
2434
}
2435
} else {
2436
sdata.instantiate();
2437
}
2438
Error err = sdata->pack(scene);
2439
2440
if (err != OK) {
2441
show_accept(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."), TTR("OK"));
2442
return;
2443
}
2444
2445
int flg = 0;
2446
if (EDITOR_GET("filesystem/on_save/compress_binary_resources")) {
2447
flg |= ResourceSaver::FLAG_COMPRESS;
2448
}
2449
flg |= ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
2450
2451
err = ResourceSaver::save(sdata, p_file, flg);
2452
2453
// This needs to be emitted before saving external resources.
2454
emit_signal(SNAME("scene_saved"), p_file);
2455
editor_data.notify_scene_saved(p_file);
2456
2457
_save_external_resources();
2458
saving_scene = p_file; // Some editors may save scenes of built-in resources as external data, so avoid saving this scene again.
2459
editor_data.save_editor_external_data();
2460
saving_scene = "";
2461
2462
for (Pair<AnimationMixer *, Ref<AnimatedValuesBackup>> &E : anim_backups) {
2463
E.first->restore(E.second);
2464
}
2465
2466
if (err == OK) {
2467
scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(p_file));
2468
editor_data.set_scene_as_saved(idx);
2469
editor_data.set_scene_modified_time(idx, FileAccess::get_modified_time(p_file));
2470
2471
if (EDITOR_GET("filesystem/on_save/warn_on_saving_large_text_resources")) {
2472
if (p_file.ends_with(".tscn") || p_file.ends_with(".tres")) {
2473
const int64_t file_size = FileAccess::get_size(p_file);
2474
if (file_size >= LARGE_RESOURCE_WARNING_SIZE_THRESHOLD) {
2475
// File is larger than 500 KiB, likely because it contains binary data serialized as Base64.
2476
// This is slow to save and load, so warn the user.
2477
EditorToaster::get_singleton()->popup_str(
2478
vformat(TTR("The text-based scene at path \"%s\" is large on disk (%s), likely because it has embedded binary data.\nThis slows down scene saving and loading.\nConsider saving its binary subresource(s) to a binary `.res` file or saving the scene as a binary `.scn` file.\nThis warning can be disabled in the Editor Settings (FileSystem > On Save > Warn on Saving Large Text Resources)."), p_file, String::humanize_size(file_size)), EditorToaster::SEVERITY_WARNING);
2479
}
2480
}
2481
}
2482
2483
editor_folding.save_scene_folding(scene, p_file);
2484
2485
_update_title();
2486
scene_tabs->update_scene_tabs();
2487
} else {
2488
_dialog_display_save_error(p_file, err);
2489
}
2490
2491
scene->propagate_notification(NOTIFICATION_EDITOR_POST_SAVE);
2492
_update_unsaved_cache();
2493
}
2494
2495
void EditorNode::save_all_scenes() {
2496
project_run_bar->stop_playing();
2497
_save_all_scenes();
2498
}
2499
2500
void EditorNode::save_scene_if_open(const String &p_scene_path) {
2501
int idx = editor_data.get_edited_scene_from_path(p_scene_path);
2502
if (idx >= 0) {
2503
_save_scene(p_scene_path, idx);
2504
}
2505
}
2506
2507
void EditorNode::save_scene_list(const HashSet<String> &p_scene_paths) {
2508
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
2509
Node *scene = editor_data.get_edited_scene_root(i);
2510
2511
if (scene && p_scene_paths.has(scene->get_scene_file_path())) {
2512
_save_scene(scene->get_scene_file_path(), i);
2513
}
2514
}
2515
}
2516
2517
void EditorNode::save_before_run() {
2518
current_menu_option = SAVE_AND_RUN;
2519
_menu_option_confirm(SCENE_SAVE_AS_SCENE, true);
2520
file->set_title(TTR("Save scene before running..."));
2521
}
2522
2523
void EditorNode::try_autosave() {
2524
if (!bool(EDITOR_GET("run/auto_save/save_before_running"))) {
2525
return;
2526
}
2527
2528
if (unsaved_cache) {
2529
Node *scene = editor_data.get_edited_scene_root();
2530
2531
if (scene && !scene->get_scene_file_path().is_empty()) { // Only autosave if there is a scene and if it has a path.
2532
_save_scene_with_preview(scene->get_scene_file_path());
2533
}
2534
}
2535
_menu_option(SCENE_SAVE_ALL_SCENES);
2536
editor_data.save_editor_external_data();
2537
}
2538
2539
void EditorNode::restart_editor(bool p_goto_project_manager) {
2540
_menu_option_confirm(p_goto_project_manager ? PROJECT_QUIT_TO_PROJECT_MANAGER : PROJECT_RELOAD_CURRENT_PROJECT, false);
2541
}
2542
2543
void EditorNode::_save_all_scenes() {
2544
scenes_to_save_as.clear(); // In case saving was canceled before.
2545
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
2546
if (!_is_scene_unsaved(i)) {
2547
continue;
2548
}
2549
2550
const Node *scene = editor_data.get_edited_scene_root(i);
2551
ERR_FAIL_NULL(scene);
2552
2553
const String &scene_path = scene->get_scene_file_path();
2554
if (scene_path.is_empty() || !DirAccess::exists(scene_path.get_base_dir())) {
2555
scenes_to_save_as.push_back(i);
2556
continue;
2557
}
2558
2559
if (i == editor_data.get_edited_scene()) {
2560
_save_scene_with_preview(scene_path);
2561
} else {
2562
_save_scene(scene_path, i);
2563
}
2564
}
2565
save_default_environment();
2566
2567
if (!scenes_to_save_as.is_empty()) {
2568
_proceed_save_asing_scene_tabs();
2569
}
2570
}
2571
2572
void EditorNode::_mark_unsaved_scenes() {
2573
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
2574
Node *node = editor_data.get_edited_scene_root(i);
2575
if (!node) {
2576
continue;
2577
}
2578
2579
String path = node->get_scene_file_path();
2580
if (!path.is_empty() && !FileAccess::exists(path)) {
2581
// Mark scene tab as unsaved if the file is gone.
2582
EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(editor_data.get_scene_history_id(i));
2583
}
2584
}
2585
2586
_update_title();
2587
scene_tabs->update_scene_tabs();
2588
}
2589
2590
bool EditorNode::_is_scene_unsaved(int p_idx) {
2591
const Node *scene = editor_data.get_edited_scene_root(p_idx);
2592
if (!scene) {
2593
return false;
2594
}
2595
2596
if (EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(p_idx))) {
2597
return true;
2598
}
2599
2600
const String &scene_path = scene->get_scene_file_path();
2601
if (!scene_path.is_empty()) {
2602
// Check if scene has unsaved changes in built-in resources.
2603
for (int j = 0; j < editor_data.get_editor_plugin_count(); j++) {
2604
if (!editor_data.get_editor_plugin(j)->get_unsaved_status(scene_path).is_empty()) {
2605
return true;
2606
}
2607
}
2608
}
2609
return false;
2610
}
2611
2612
void EditorNode::_dialog_action(String p_file) {
2613
switch (current_menu_option) {
2614
case SCENE_NEW_INHERITED_SCENE: {
2615
Node *scene = editor_data.get_edited_scene_root();
2616
// If the previous scene is rootless, just close it in favor of the new one.
2617
if (!scene) {
2618
_menu_option_confirm(SCENE_CLOSE, true);
2619
}
2620
2621
load_scene(p_file, false, true);
2622
} break;
2623
case SCENE_OPEN_SCENE: {
2624
load_scene(p_file);
2625
} break;
2626
case SETTINGS_PICK_MAIN_SCENE: {
2627
ProjectSettings::get_singleton()->set("application/run/main_scene", ResourceUID::path_to_uid(p_file));
2628
ProjectSettings::get_singleton()->save();
2629
// TODO: Would be nice to show the project manager opened with the highlighted field.
2630
2631
project_run_bar->play_main_scene((bool)pick_main_scene->get_meta("from_native", false));
2632
} break;
2633
case SCENE_CLOSE:
2634
case SCENE_TAB_CLOSE:
2635
case SCENE_SAVE_SCENE:
2636
case SCENE_MULTI_SAVE_AS_SCENE:
2637
case SCENE_SAVE_AS_SCENE: {
2638
int scene_idx = (current_menu_option == SCENE_SAVE_SCENE || current_menu_option == SCENE_SAVE_AS_SCENE || current_menu_option == SCENE_MULTI_SAVE_AS_SCENE) ? -1 : tab_closing_idx;
2639
2640
if (file->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
2641
bool same_open_scene = false;
2642
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
2643
if (editor_data.get_scene_path(i) == p_file && i != scene_idx) {
2644
same_open_scene = true;
2645
}
2646
}
2647
2648
if (same_open_scene) {
2649
show_warning(TTR("Can't overwrite scene that is still open!"));
2650
return;
2651
}
2652
2653
save_default_environment();
2654
_save_scene_with_preview(p_file, scene_idx);
2655
_add_to_recent_scenes(p_file);
2656
save_editor_layout_delayed();
2657
2658
if (scene_idx != -1) {
2659
_discard_changes();
2660
} else {
2661
// Update the path of the edited scene to ensure later do/undo action history matches.
2662
editor_data.set_scene_path(editor_data.get_edited_scene(), p_file);
2663
}
2664
}
2665
2666
if (current_menu_option == SCENE_MULTI_SAVE_AS_SCENE) {
2667
_proceed_save_asing_scene_tabs();
2668
}
2669
2670
} break;
2671
2672
case SAVE_AND_RUN: {
2673
if (file->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
2674
save_default_environment();
2675
_save_scene_with_preview(p_file);
2676
project_run_bar->play_custom_scene(p_file);
2677
}
2678
} break;
2679
2680
case SAVE_AND_RUN_MAIN_SCENE: {
2681
ProjectSettings::get_singleton()->set("application/run/main_scene", ResourceUID::path_to_uid(p_file));
2682
ProjectSettings::get_singleton()->save();
2683
2684
if (file->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
2685
save_default_environment();
2686
_save_scene_with_preview(p_file);
2687
project_run_bar->play_main_scene((bool)pick_main_scene->get_meta("from_native", false));
2688
}
2689
} break;
2690
2691
case SAVE_AND_SET_MAIN_SCENE: {
2692
_save_scene(p_file);
2693
_menu_option_confirm(SCENE_TAB_SET_AS_MAIN_SCENE, true);
2694
} break;
2695
2696
case FILE_EXPORT_MESH_LIBRARY: {
2697
const Dictionary &fd_options = file_export_lib->get_selected_options();
2698
bool merge_with_existing_library = fd_options.get(TTR("Merge With Existing"), true);
2699
bool apply_mesh_instance_transforms = fd_options.get(TTR("Apply MeshInstance Transforms"), false);
2700
2701
Ref<MeshLibrary> ml;
2702
if (merge_with_existing_library && FileAccess::exists(p_file)) {
2703
ml = ResourceLoader::load(p_file, "MeshLibrary");
2704
2705
if (ml.is_null()) {
2706
show_accept(TTR("Can't load MeshLibrary for merging!"), TTR("OK"));
2707
return;
2708
}
2709
}
2710
2711
if (ml.is_null()) {
2712
ml.instantiate();
2713
}
2714
2715
MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(), ml, merge_with_existing_library, apply_mesh_instance_transforms);
2716
2717
Error err = ResourceSaver::save(ml, p_file);
2718
if (err) {
2719
show_accept(TTR("Error saving MeshLibrary!"), TTR("OK"));
2720
return;
2721
} else if (ResourceCache::has(p_file)) {
2722
// Make sure MeshLibrary is updated in the editor.
2723
ResourceLoader::load(p_file)->reload_from_file();
2724
}
2725
2726
} break;
2727
2728
case PROJECT_PACK_AS_ZIP: {
2729
ProjectZIPPacker::pack_project_zip(p_file);
2730
{
2731
Ref<FileAccess> f = FileAccess::open(p_file, FileAccess::READ);
2732
ERR_FAIL_COND_MSG(f.is_null(), vformat("Unable to create ZIP file at: %s. Check for write permissions and whether you have enough disk space left.", p_file));
2733
}
2734
2735
} break;
2736
2737
case RESOURCE_SAVE:
2738
case RESOURCE_SAVE_AS: {
2739
ERR_FAIL_COND(saving_resource.is_null());
2740
save_resource_in_path(saving_resource, p_file);
2741
2742
saving_resource = Ref<Resource>();
2743
ObjectID current_id = editor_history.get_current();
2744
Object *current_obj = current_id.is_valid() ? ObjectDB::get_instance(current_id) : nullptr;
2745
ERR_FAIL_NULL(current_obj);
2746
current_obj->notify_property_list_changed();
2747
} break;
2748
case LAYOUT_SAVE: {
2749
if (p_file.is_empty()) {
2750
return;
2751
}
2752
2753
Ref<ConfigFile> config;
2754
config.instantiate();
2755
Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config());
2756
2757
if (err == ERR_FILE_CANT_OPEN || err == ERR_FILE_NOT_FOUND) {
2758
config.instantiate();
2759
} else if (err != OK) {
2760
show_warning(TTR("An error occurred while trying to save the editor layout.\nMake sure the editor's user data path is writable."));
2761
return;
2762
}
2763
2764
editor_dock_manager->save_docks_to_config(config, p_file);
2765
2766
config->save(EditorSettings::get_singleton()->get_editor_layouts_config());
2767
2768
layout_dialog->hide();
2769
_update_layouts_menu();
2770
2771
if (p_file == "Default") {
2772
show_warning(TTR("Default editor layout overridden.\nTo restore the Default layout to its base settings, use the Delete Layout option and delete the Default layout."));
2773
}
2774
2775
} break;
2776
case LAYOUT_DELETE: {
2777
Ref<ConfigFile> config;
2778
config.instantiate();
2779
Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config());
2780
2781
if (err != OK || !config->has_section(p_file)) {
2782
show_warning(TTR("Layout name not found!"));
2783
return;
2784
}
2785
2786
// Erase key values.
2787
Vector<String> keys = config->get_section_keys(p_file);
2788
for (const String &key : keys) {
2789
config->set_value(p_file, key, Variant());
2790
}
2791
2792
config->save(EditorSettings::get_singleton()->get_editor_layouts_config());
2793
2794
layout_dialog->hide();
2795
_update_layouts_menu();
2796
2797
if (p_file == "Default") {
2798
show_warning(TTR("Restored the Default layout to its base settings."));
2799
}
2800
2801
} break;
2802
default: {
2803
// Save scene?
2804
if (file->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
2805
_save_scene_with_preview(p_file);
2806
}
2807
2808
} break;
2809
}
2810
}
2811
2812
bool EditorNode::_is_class_editor_disabled_by_feature_profile(const StringName &p_class) {
2813
Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile();
2814
if (profile.is_null()) {
2815
return false;
2816
}
2817
2818
StringName class_name = p_class;
2819
2820
while (class_name != StringName()) {
2821
if (profile->is_class_disabled(class_name)) {
2822
return true;
2823
}
2824
if (profile->is_class_editor_disabled(class_name)) {
2825
return true;
2826
}
2827
class_name = ClassDB::get_parent_class(class_name);
2828
}
2829
2830
return false;
2831
}
2832
2833
void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) {
2834
ERR_FAIL_NULL(p_editing_owner);
2835
2836
// Editing for this type of object may be disabled by user's feature profile.
2837
if (!p_object || _is_class_editor_disabled_by_feature_profile(p_object->get_class())) {
2838
// Nothing to edit, clean up the owner context and return.
2839
hide_unused_editors(p_editing_owner);
2840
return;
2841
}
2842
2843
// Get a list of editor plugins that can handle this type of object.
2844
Vector<EditorPlugin *> available_plugins = editor_data.get_handling_sub_editors(p_object);
2845
if (available_plugins.is_empty()) {
2846
// None, clean up the owner context and return.
2847
hide_unused_editors(p_editing_owner);
2848
return;
2849
}
2850
2851
ObjectID owner_id = p_editing_owner->get_instance_id();
2852
2853
// Remove editor plugins no longer used by this editing owner. Keep the ones that can
2854
// still be reused by the new edited object.
2855
2856
List<EditorPlugin *> to_remove;
2857
for (EditorPlugin *plugin : active_plugins[owner_id]) {
2858
if (!available_plugins.has(plugin)) {
2859
to_remove.push_back(plugin);
2860
if (plugin->can_auto_hide()) {
2861
_plugin_over_edit(plugin, nullptr);
2862
} else {
2863
// If plugin can't be hidden, make it own itself and become responsible for closing.
2864
_plugin_over_self_own(plugin);
2865
}
2866
}
2867
}
2868
2869
for (EditorPlugin *plugin : to_remove) {
2870
active_plugins[owner_id].erase(plugin);
2871
}
2872
2873
LocalVector<EditorPlugin *> to_over_edit;
2874
2875
// Send the edited object to the plugins.
2876
for (EditorPlugin *plugin : available_plugins) {
2877
if (active_plugins[owner_id].has(plugin)) {
2878
// Plugin was already active, just change the object and ensure it's visible.
2879
plugin->make_visible(true);
2880
plugin->edit(p_object);
2881
continue;
2882
}
2883
2884
if (active_plugins.has(plugin->get_instance_id())) {
2885
// Plugin is already active, but as self-owning, so it needs a separate check.
2886
plugin->make_visible(true);
2887
plugin->edit(p_object);
2888
continue;
2889
}
2890
2891
bool need_to_add = true;
2892
List<EditorPropertyResource *> to_fold;
2893
2894
// If plugin is already associated with another owner, remove it from there first.
2895
for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) {
2896
if (kv.key == owner_id || !kv.value.has(plugin)) {
2897
continue;
2898
}
2899
EditorPropertyResource *epres = ObjectDB::get_instance<EditorPropertyResource>(kv.key);
2900
if (epres) {
2901
// If it's resource property editing the same resource type, fold it later to avoid premature modifications
2902
// that may result in unsafe iteration of active_plugins.
2903
to_fold.push_back(epres);
2904
} else {
2905
kv.value.erase(plugin);
2906
need_to_add = false;
2907
}
2908
}
2909
2910
if (!need_to_add && to_fold.is_empty()) {
2911
plugin->make_visible(true);
2912
plugin->edit(p_object);
2913
} else {
2914
for (EditorPropertyResource *epres : to_fold) {
2915
epres->fold_resource();
2916
}
2917
2918
// TODO: Call the function directly once a proper priority system is implemented.
2919
to_over_edit.push_back(plugin);
2920
}
2921
2922
// Activate previously inactive plugin and edit the object.
2923
active_plugins[owner_id].insert(plugin);
2924
}
2925
2926
for (EditorPlugin *plugin : to_over_edit) {
2927
_plugin_over_edit(plugin, p_object);
2928
}
2929
}
2930
2931
void EditorNode::push_node_item(Node *p_node) {
2932
if (p_node || !InspectorDock::get_inspector_singleton()->get_edited_object() || Object::cast_to<Node>(InspectorDock::get_inspector_singleton()->get_edited_object()) || Object::cast_to<MultiNodeEdit>(InspectorDock::get_inspector_singleton()->get_edited_object())) {
2933
// Don't push null if the currently edited object is not a Node.
2934
push_item(p_node);
2935
}
2936
}
2937
2938
void EditorNode::push_item(Object *p_object, const String &p_property, bool p_inspector_only) {
2939
if (!p_object) {
2940
InspectorDock::get_inspector_singleton()->edit(nullptr);
2941
SignalsDock::get_singleton()->set_object(nullptr);
2942
GroupsDock::get_singleton()->set_selection(Vector<Node *>());
2943
SceneTreeDock::get_singleton()->set_selected(nullptr);
2944
InspectorDock::get_singleton()->update(nullptr);
2945
hide_unused_editors();
2946
return;
2947
}
2948
_add_to_history(p_object, p_property, p_inspector_only);
2949
_edit_current();
2950
}
2951
2952
void EditorNode::edit_previous_item() {
2953
if (editor_history.previous()) {
2954
_edit_current();
2955
}
2956
}
2957
2958
void EditorNode::push_item_no_inspector(Object *p_object) {
2959
_add_to_history(p_object, "", false);
2960
_edit_current(false, true);
2961
}
2962
2963
void EditorNode::save_default_environment() {
2964
Ref<Environment> fallback = get_tree()->get_root()->get_world_3d()->get_fallback_environment();
2965
2966
if (fallback.is_valid() && fallback->get_path().is_resource_file()) {
2967
HashMap<Ref<Resource>, bool> processed;
2968
_find_and_save_edited_subresources(fallback.ptr(), processed, 0);
2969
save_resource_in_path(fallback, fallback->get_path());
2970
}
2971
}
2972
2973
void EditorNode::hide_unused_editors(const Object *p_editing_owner) {
2974
if (p_editing_owner) {
2975
const ObjectID id = p_editing_owner->get_instance_id();
2976
for (EditorPlugin *plugin : active_plugins[id]) {
2977
if (plugin->can_auto_hide()) {
2978
_plugin_over_edit(plugin, nullptr);
2979
} else {
2980
_plugin_over_self_own(plugin);
2981
}
2982
}
2983
active_plugins.erase(id);
2984
} else {
2985
// If no editing owner is provided, this method will go over all owners and check if they are valid.
2986
// This is to sweep properties that were removed from the inspector.
2987
List<ObjectID> to_remove;
2988
for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) {
2989
Object *context = ObjectDB::get_instance(kv.key);
2990
if (context) {
2991
// In case of self-owning plugins, they are disabled here if they can auto hide.
2992
const EditorPlugin *self_owning = Object::cast_to<EditorPlugin>(context);
2993
if (self_owning && self_owning->can_auto_hide()) {
2994
context = nullptr;
2995
}
2996
}
2997
2998
if (!context || context->call(SNAME("_should_stop_editing"))) {
2999
to_remove.push_back(kv.key);
3000
for (EditorPlugin *plugin : kv.value) {
3001
if (plugin->can_auto_hide()) {
3002
_plugin_over_edit(plugin, nullptr);
3003
} else {
3004
_plugin_over_self_own(plugin);
3005
}
3006
}
3007
}
3008
}
3009
3010
for (const ObjectID &id : to_remove) {
3011
active_plugins.erase(id);
3012
}
3013
}
3014
}
3015
3016
void EditorNode::_add_to_history(const Object *p_object, const String &p_property, bool p_inspector_only) {
3017
ObjectID id = p_object->get_instance_id();
3018
ObjectID history_id = editor_history.get_current();
3019
if (id != history_id) {
3020
const MultiNodeEdit *multi_node_edit = Object::cast_to<const MultiNodeEdit>(p_object);
3021
const MultiNodeEdit *history_multi_node_edit = ObjectDB::get_instance<MultiNodeEdit>(history_id);
3022
if (multi_node_edit && history_multi_node_edit && multi_node_edit->is_same_selection(history_multi_node_edit)) {
3023
return;
3024
}
3025
if (p_inspector_only) {
3026
editor_history.add_object(id, String(), true);
3027
} else if (p_property.is_empty()) {
3028
editor_history.add_object(id);
3029
} else {
3030
editor_history.add_object(id, p_property);
3031
}
3032
}
3033
}
3034
3035
void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update) {
3036
ObjectID current_id = editor_history.get_current();
3037
Object *current_obj = current_id.is_valid() ? ObjectDB::get_instance(current_id) : nullptr;
3038
3039
Ref<Resource> res = Object::cast_to<Resource>(current_obj);
3040
if (p_skip_foreign && res.is_valid()) {
3041
const int current_tab = scene_tabs->get_current_tab();
3042
if (res->get_path().contains("::") && res->get_path().get_slice("::", 0) != editor_data.get_scene_path(current_tab)) {
3043
// Trying to edit resource that belongs to another scene; abort.
3044
current_obj = nullptr;
3045
}
3046
}
3047
3048
bool inspector_only = editor_history.is_current_inspector_only();
3049
3050
if (!current_obj) {
3051
SceneTreeDock::get_singleton()->set_selected(nullptr);
3052
InspectorDock::get_inspector_singleton()->edit(nullptr);
3053
SignalsDock::get_singleton()->set_object(nullptr);
3054
GroupsDock::get_singleton()->set_selection(Vector<Node *>());
3055
InspectorDock::get_singleton()->update(nullptr);
3056
EditorDebuggerNode::get_singleton()->clear_remote_tree_selection();
3057
hide_unused_editors();
3058
return;
3059
}
3060
3061
// Update the use folding setting and state.
3062
bool disable_folding = bool(EDITOR_GET("interface/inspector/disable_folding")) || current_obj->is_class("EditorDebuggerRemoteObjects");
3063
if (InspectorDock::get_inspector_singleton()->is_using_folding() == disable_folding) {
3064
InspectorDock::get_inspector_singleton()->set_use_folding(!disable_folding, false);
3065
}
3066
3067
bool is_resource = current_obj->is_class("Resource");
3068
bool is_node = current_obj->is_class("Node");
3069
bool stay_in_script_editor_on_node_selected = bool(EDITOR_GET("text_editor/behavior/navigation/stay_in_script_editor_on_node_selected"));
3070
bool skip_main_plugin = false;
3071
3072
String editable_info; // None by default.
3073
bool info_is_warning = false;
3074
3075
if (current_obj->has_method("_is_read_only")) {
3076
if (current_obj->call("_is_read_only")) {
3077
editable_info = TTR("This object is marked as read-only, so it's not editable.");
3078
}
3079
}
3080
3081
if (is_resource) {
3082
Resource *current_res = Object::cast_to<Resource>(current_obj);
3083
ERR_FAIL_NULL(current_res);
3084
3085
if (!p_skip_inspector_update) {
3086
InspectorDock::get_inspector_singleton()->edit(current_res);
3087
SceneTreeDock::get_singleton()->set_selected(nullptr);
3088
SignalsDock::get_singleton()->set_object(current_res);
3089
GroupsDock::get_singleton()->set_selection(Vector<Node *>());
3090
InspectorDock::get_singleton()->update(nullptr);
3091
EditorDebuggerNode::get_singleton()->clear_remote_tree_selection();
3092
ImportDock::get_singleton()->set_edit_path(current_res->get_path());
3093
}
3094
3095
int subr_idx = current_res->get_path().find("::");
3096
if (subr_idx != -1) {
3097
String base_path = current_res->get_path().substr(0, subr_idx);
3098
if (FileAccess::exists(base_path + ".import")) {
3099
if (!base_path.is_resource_file()) {
3100
if (get_edited_scene() && get_edited_scene()->get_scene_file_path() == base_path) {
3101
info_is_warning = true;
3102
}
3103
}
3104
editable_info = 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.");
3105
} else if ((!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base_path) && ResourceLoader::get_resource_type(base_path) == "PackedScene") {
3106
editable_info = TTR("This resource belongs to a scene that was instantiated or inherited.\nChanges to it must be made inside the original scene.");
3107
}
3108
} else if (current_res->get_path().is_resource_file()) {
3109
if (FileAccess::exists(current_res->get_path() + ".import")) {
3110
editable_info = TTR("This resource was imported, so it's not editable. Change its settings in the import panel and then re-import.");
3111
}
3112
}
3113
} else if (is_node) {
3114
Node *current_node = Object::cast_to<Node>(current_obj);
3115
ERR_FAIL_NULL(current_node);
3116
3117
InspectorDock::get_inspector_singleton()->edit(current_node);
3118
if (current_node->is_inside_tree()) {
3119
SignalsDock::get_singleton()->set_object(current_node);
3120
GroupsDock::get_singleton()->set_selection(Vector<Node *>{ current_node });
3121
SceneTreeDock::get_singleton()->set_selected(current_node);
3122
SceneTreeDock::get_singleton()->set_selection({ current_node });
3123
InspectorDock::get_singleton()->update(current_node);
3124
if (!inspector_only && !skip_main_plugin) {
3125
if (!ScriptEditor::get_singleton()->is_editor_floating() && ScriptEditor::get_singleton()->is_visible_in_tree()) {
3126
skip_main_plugin = stay_in_script_editor_on_node_selected;
3127
} else {
3128
skip_main_plugin = !editor_main_screen->can_auto_switch_screens();
3129
}
3130
}
3131
} else {
3132
SignalsDock::get_singleton()->set_object(nullptr);
3133
GroupsDock::get_singleton()->set_selection(Vector<Node *>());
3134
SceneTreeDock::get_singleton()->set_selected(nullptr);
3135
InspectorDock::get_singleton()->update(nullptr);
3136
}
3137
EditorDebuggerNode::get_singleton()->clear_remote_tree_selection();
3138
3139
if (get_edited_scene() && !get_edited_scene()->get_scene_file_path().is_empty()) {
3140
String source_scene = get_edited_scene()->get_scene_file_path();
3141
if (FileAccess::exists(source_scene + ".import")) {
3142
editable_info = TTR("This scene was imported, so changes to it won't be kept.\nInstantiating or inheriting it will allow you to make changes to it.\nPlease read the documentation relevant to importing scenes to better understand this workflow.");
3143
info_is_warning = true;
3144
}
3145
}
3146
} else {
3147
Node *selected_node = nullptr;
3148
3149
Vector<Node *> multi_nodes;
3150
if (current_obj->is_class("MultiNodeEdit")) {
3151
Node *scene = get_edited_scene();
3152
if (scene) {
3153
MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(current_obj);
3154
int node_count = multi_node_edit->get_node_count();
3155
if (node_count > 0) {
3156
for (int node_index = 0; node_index < node_count; ++node_index) {
3157
Node *node = scene->get_node(multi_node_edit->get_node(node_index));
3158
if (node) {
3159
multi_nodes.push_back(node);
3160
}
3161
}
3162
if (!multi_nodes.is_empty()) {
3163
// Pick the top-most node.
3164
selected_node = multi_nodes[0];
3165
Node::Comparator comparator;
3166
for (Node *node : multi_nodes) {
3167
if (comparator(node, selected_node)) {
3168
selected_node = node;
3169
}
3170
}
3171
}
3172
}
3173
}
3174
}
3175
3176
if (!current_obj->is_class("EditorDebuggerRemoteObjects")) {
3177
EditorDebuggerNode::get_singleton()->clear_remote_tree_selection();
3178
}
3179
3180
InspectorDock::get_inspector_singleton()->edit(current_obj);
3181
SignalsDock::get_singleton()->set_object(nullptr);
3182
GroupsDock::get_singleton()->set_selection(multi_nodes);
3183
SceneTreeDock::get_singleton()->set_selected(selected_node);
3184
SceneTreeDock::get_singleton()->set_selection(multi_nodes);
3185
InspectorDock::get_singleton()->update(nullptr);
3186
}
3187
3188
InspectorDock::get_singleton()->set_info(
3189
info_is_warning ? TTR("Changes may be lost!") : TTR("This object is read-only."),
3190
editable_info,
3191
info_is_warning);
3192
3193
Object *editor_owner = (is_node || current_obj->is_class("MultiNodeEdit")) ? (Object *)SceneTreeDock::get_singleton() : is_resource ? (Object *)InspectorDock::get_inspector_singleton()
3194
: (Object *)this;
3195
3196
// Take care of the main editor plugin.
3197
3198
if (!inspector_only) {
3199
EditorPlugin *main_plugin = editor_data.get_handling_main_editor(current_obj);
3200
3201
int plugin_index = editor_main_screen->get_plugin_index(main_plugin);
3202
if (main_plugin && plugin_index >= 0 && !editor_main_screen->is_button_enabled(plugin_index)) {
3203
main_plugin = nullptr;
3204
}
3205
EditorPlugin *editor_plugin_screen = editor_main_screen->get_selected_plugin();
3206
3207
ObjectID editor_owner_id = editor_owner->get_instance_id();
3208
if (main_plugin && !skip_main_plugin) {
3209
// Special case if current_obj is a script.
3210
Script *current_script = Object::cast_to<Script>(current_obj);
3211
if (current_script) {
3212
if (!changing_scene) {
3213
// Only update main editor screen if using in-engine editor.
3214
if (current_script->is_built_in() || (!bool(EDITOR_GET("text_editor/external/use_external_editor")) && !current_script->get_language()->overrides_external_editor())) {
3215
editor_main_screen->select(plugin_index);
3216
}
3217
3218
main_plugin->edit(current_script);
3219
}
3220
} else if (main_plugin != editor_plugin_screen) {
3221
// Unedit previous plugin.
3222
editor_plugin_screen->edit(nullptr);
3223
active_plugins[editor_owner_id].erase(editor_plugin_screen);
3224
// Update screen main_plugin.
3225
editor_main_screen->select(plugin_index);
3226
main_plugin->edit(current_obj);
3227
} else {
3228
editor_plugin_screen->edit(current_obj);
3229
}
3230
is_main_screen_editing = true;
3231
} else if (!main_plugin && editor_plugin_screen && is_main_screen_editing) {
3232
editor_plugin_screen->edit(nullptr);
3233
is_main_screen_editing = false;
3234
}
3235
3236
edit_item(current_obj, editor_owner);
3237
}
3238
3239
InspectorDock::get_singleton()->update(current_obj);
3240
}
3241
3242
void EditorNode::_android_build_source_selected(const String &p_file) {
3243
export_template_manager->install_android_template_from_file(p_file, android_export_preset);
3244
}
3245
3246
void EditorNode::_android_export_preset_selected(int p_index) {
3247
if (p_index >= 0) {
3248
android_export_preset = EditorExport::get_singleton()->get_export_preset(choose_android_export_profile->get_item_id(p_index));
3249
} else {
3250
android_export_preset.unref();
3251
}
3252
install_android_build_template_message->set_text(vformat(TTR(INSTALL_ANDROID_BUILD_TEMPLATE_MESSAGE), export_template_manager->get_android_build_directory(android_export_preset)));
3253
}
3254
3255
void EditorNode::_android_install_build_template() {
3256
gradle_build_manage_templates->hide();
3257
file_android_build_source->popup_centered_ratio();
3258
}
3259
3260
void EditorNode::_android_explore_build_templates() {
3261
OS::get_singleton()->shell_show_in_file_manager(ProjectSettings::get_singleton()->globalize_path(export_template_manager->get_android_build_directory(android_export_preset).get_base_dir()), true);
3262
}
3263
3264
static String _get_unsaved_scene_dialog_text(String p_scene_filename, uint64_t p_started_timestamp) {
3265
String unsaved_message;
3266
3267
// Consider editor startup to be a point of saving, so that when you
3268
// close and reopen the editor, you don't get an excessively long
3269
// "modified X hours ago".
3270
const uint64_t last_modified_seconds = Time::get_singleton()->get_unix_time_from_system() - MAX(p_started_timestamp, FileAccess::get_modified_time(p_scene_filename));
3271
String last_modified_string;
3272
if (last_modified_seconds < 120) {
3273
last_modified_string = vformat(TTRN("%d second ago", "%d seconds ago", last_modified_seconds), last_modified_seconds);
3274
} else if (last_modified_seconds < 7200) {
3275
last_modified_string = vformat(TTRN("%d minute ago", "%d minutes ago", last_modified_seconds / 60), last_modified_seconds / 60);
3276
} else {
3277
last_modified_string = vformat(TTRN("%d hour ago", "%d hours ago", last_modified_seconds / 3600), last_modified_seconds / 3600);
3278
}
3279
unsaved_message = vformat(TTR("Scene \"%s\" has unsaved changes.\nLast saved: %s."), p_scene_filename, last_modified_string);
3280
3281
return unsaved_message;
3282
}
3283
3284
void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
3285
if (!p_confirmed) { // FIXME: this may be a hack.
3286
current_menu_option = (MenuOptions)p_option;
3287
}
3288
3289
switch (p_option) {
3290
case SCENE_NEW_SCENE: {
3291
new_scene();
3292
3293
} break;
3294
case SCENE_NEW_INHERITED_SCENE:
3295
case SCENE_OPEN_SCENE: {
3296
file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
3297
List<String> extensions;
3298
ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);
3299
file->clear_filters();
3300
for (const String &extension : extensions) {
3301
file->add_filter("*." + extension, extension.to_upper());
3302
}
3303
3304
Node *scene = editor_data.get_edited_scene_root();
3305
if (scene) {
3306
file->set_current_path(scene->get_scene_file_path());
3307
};
3308
file->set_title(p_option == SCENE_OPEN_SCENE ? TTR("Open Scene") : TTR("Open Base Scene"));
3309
file->popup_file_dialog();
3310
3311
} break;
3312
case SCENE_QUICK_OPEN: {
3313
quick_open_dialog->popup_dialog({ "Resource" }, callable_mp(this, &EditorNode::_quick_opened));
3314
} break;
3315
case SCENE_QUICK_OPEN_SCENE: {
3316
quick_open_dialog->popup_dialog({ "PackedScene" }, callable_mp(this, &EditorNode::_quick_opened));
3317
} break;
3318
case SCENE_QUICK_OPEN_SCRIPT: {
3319
quick_open_dialog->popup_dialog({ "Script" }, callable_mp(this, &EditorNode::_quick_opened));
3320
} break;
3321
case SCENE_OPEN_PREV: {
3322
if (!prev_closed_scenes.is_empty()) {
3323
load_scene(prev_closed_scenes.back()->get());
3324
}
3325
} break;
3326
case EditorSceneTabs::SCENE_CLOSE_OTHERS: {
3327
tab_closing_menu_option = -1;
3328
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
3329
if (i == editor_data.get_edited_scene()) {
3330
continue;
3331
}
3332
tabs_to_close.push_back(editor_data.get_scene_path(i));
3333
}
3334
_proceed_closing_scene_tabs();
3335
} break;
3336
case EditorSceneTabs::SCENE_CLOSE_RIGHT: {
3337
tab_closing_menu_option = -1;
3338
for (int i = editor_data.get_edited_scene() + 1; i < editor_data.get_edited_scene_count(); i++) {
3339
tabs_to_close.push_back(editor_data.get_scene_path(i));
3340
}
3341
_proceed_closing_scene_tabs();
3342
} break;
3343
case SCENE_CLOSE_ALL: {
3344
tab_closing_menu_option = -1;
3345
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
3346
tabs_to_close.push_back(editor_data.get_scene_path(i));
3347
}
3348
_proceed_closing_scene_tabs();
3349
} break;
3350
case SCENE_CLOSE: {
3351
_scene_tab_closed(editor_data.get_edited_scene());
3352
} break;
3353
case SCENE_TAB_CLOSE:
3354
case SCENE_SAVE_SCENE: {
3355
int scene_idx = (p_option == SCENE_SAVE_SCENE) ? -1 : tab_closing_idx;
3356
Node *scene = editor_data.get_edited_scene_root(scene_idx);
3357
if (scene && !scene->get_scene_file_path().is_empty()) {
3358
if (DirAccess::exists(scene->get_scene_file_path().get_base_dir())) {
3359
if (scene_idx != editor_data.get_edited_scene()) {
3360
_save_scene_with_preview(scene->get_scene_file_path(), scene_idx);
3361
} else {
3362
_save_scene_with_preview(scene->get_scene_file_path());
3363
}
3364
3365
if (scene_idx != -1) {
3366
_discard_changes();
3367
}
3368
save_editor_layout_delayed();
3369
} else {
3370
show_save_accept(vformat(TTR("%s no longer exists! Please specify a new save location."), scene->get_scene_file_path().get_base_dir()), TTR("OK"));
3371
}
3372
break;
3373
}
3374
[[fallthrough]];
3375
}
3376
case SCENE_MULTI_SAVE_AS_SCENE:
3377
case SCENE_SAVE_AS_SCENE: {
3378
int scene_idx = (p_option == SCENE_SAVE_SCENE || p_option == SCENE_SAVE_AS_SCENE || p_option == SCENE_MULTI_SAVE_AS_SCENE) ? -1 : tab_closing_idx;
3379
3380
Node *scene = editor_data.get_edited_scene_root(scene_idx);
3381
3382
if (!scene) {
3383
if (p_option == SCENE_SAVE_SCENE) {
3384
// Pressing Ctrl + S saves the current script if a scene is currently open, but it won't if the scene has no root node.
3385
// Work around this by explicitly saving the script in this case (similar to pressing Ctrl + Alt + S).
3386
ScriptEditor::get_singleton()->save_current_script();
3387
}
3388
3389
const int saved = _save_external_resources(true);
3390
if (saved > 0) {
3391
EditorToaster::get_singleton()->popup_str(vformat(TTR("The current scene has no root node, but %d modified external resource(s) and/or plugin data were saved anyway."), saved), EditorToaster::SEVERITY_INFO);
3392
} else if (p_option == SCENE_SAVE_AS_SCENE) {
3393
// Don't show this dialog when pressing Ctrl + S to avoid interfering with script saving.
3394
show_accept(
3395
TTR("A root node is required to save the scene. You can add a root node using the Scene tree dock."),
3396
TTR("OK"));
3397
}
3398
3399
break;
3400
}
3401
3402
file->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
3403
3404
List<String> extensions;
3405
Ref<PackedScene> sd = memnew(PackedScene);
3406
ResourceSaver::get_recognized_extensions(sd, &extensions);
3407
file->clear_filters();
3408
for (const String &extension : extensions) {
3409
file->add_filter("*." + extension, extension.to_upper());
3410
}
3411
3412
if (!scene->get_scene_file_path().is_empty()) {
3413
String path = scene->get_scene_file_path();
3414
String root_name = EditorNode::adjust_scene_name_casing(scene->get_name());
3415
String ext = path.get_extension().to_lower();
3416
path = path.get_base_dir().path_join(root_name + "." + ext);
3417
3418
file->set_current_path(path);
3419
if (extensions.size()) {
3420
if (extensions.find(ext) == nullptr) {
3421
file->set_current_path(path.replacen("." + ext, "." + extensions.front()->get()));
3422
}
3423
}
3424
} else if (extensions.size()) {
3425
String root_name = scene->get_name();
3426
root_name = EditorNode::adjust_scene_name_casing(root_name);
3427
file->set_current_path(root_name + "." + extensions.front()->get().to_lower());
3428
}
3429
file->set_title(TTR("Save Scene As..."));
3430
file->popup_file_dialog();
3431
3432
} break;
3433
3434
case SCENE_TAB_SET_AS_MAIN_SCENE: {
3435
const String scene_path = editor_data.get_scene_path(editor_data.get_edited_scene());
3436
if (scene_path.is_empty()) {
3437
current_menu_option = SAVE_AND_SET_MAIN_SCENE;
3438
_menu_option_confirm(SCENE_SAVE_AS_SCENE, true);
3439
file->set_title(TTR("Save new main scene..."));
3440
} else {
3441
ProjectSettings::get_singleton()->set("application/run/main_scene", ResourceUID::path_to_uid(scene_path));
3442
ProjectSettings::get_singleton()->save();
3443
FileSystemDock::get_singleton()->update_all();
3444
}
3445
} break;
3446
3447
case SCENE_SAVE_ALL_SCENES: {
3448
_save_all_scenes();
3449
} break;
3450
3451
case EditorSceneTabs::SCENE_RUN: {
3452
project_run_bar->play_current_scene();
3453
} break;
3454
3455
case PROJECT_EXPORT: {
3456
project_export->popup_export();
3457
} break;
3458
3459
case PROJECT_PACK_AS_ZIP: {
3460
String resource_path = ProjectSettings::get_singleton()->get_resource_path();
3461
const String base_path = resource_path.substr(0, resource_path.rfind_char('/')) + "/";
3462
3463
file_pack_zip->set_current_path(base_path);
3464
file_pack_zip->set_current_file(ProjectZIPPacker::get_project_zip_safe_name());
3465
file_pack_zip->popup_file_dialog();
3466
} break;
3467
3468
case SCENE_UNDO: {
3469
if ((int)Input::get_singleton()->get_mouse_button_mask() & 0x7) {
3470
log->add_message(TTR("Can't undo while mouse buttons are pressed."), EditorLog::MSG_TYPE_EDITOR);
3471
} else {
3472
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
3473
String action = undo_redo->get_current_action_name();
3474
int id = undo_redo->get_current_action_history_id();
3475
if (!undo_redo->undo()) {
3476
log->add_message(TTR("Nothing to undo."), EditorLog::MSG_TYPE_EDITOR);
3477
} else if (!action.is_empty()) {
3478
switch (id) {
3479
case EditorUndoRedoManager::GLOBAL_HISTORY:
3480
log->add_message(vformat(TTR("Global Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
3481
break;
3482
case EditorUndoRedoManager::REMOTE_HISTORY:
3483
log->add_message(vformat(TTR("Remote Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
3484
break;
3485
default:
3486
log->add_message(vformat(TTR("Scene Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
3487
}
3488
}
3489
}
3490
_update_unsaved_cache();
3491
} break;
3492
case SCENE_REDO: {
3493
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
3494
if ((int)Input::get_singleton()->get_mouse_button_mask() & 0x7) {
3495
log->add_message(TTR("Can't redo while mouse buttons are pressed."), EditorLog::MSG_TYPE_EDITOR);
3496
} else {
3497
if (!undo_redo->redo()) {
3498
log->add_message(TTR("Nothing to redo."), EditorLog::MSG_TYPE_EDITOR);
3499
} else {
3500
String action = undo_redo->get_current_action_name();
3501
if (action.is_empty()) {
3502
break;
3503
}
3504
3505
switch (undo_redo->get_current_action_history_id()) {
3506
case EditorUndoRedoManager::GLOBAL_HISTORY:
3507
log->add_message(vformat(TTR("Global Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
3508
break;
3509
case EditorUndoRedoManager::REMOTE_HISTORY:
3510
log->add_message(vformat(TTR("Remote Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
3511
break;
3512
default:
3513
log->add_message(vformat(TTR("Scene Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
3514
}
3515
}
3516
}
3517
_update_unsaved_cache();
3518
} break;
3519
3520
case SCENE_RELOAD_SAVED_SCENE: {
3521
const String scene_filename = editor_data.get_scene_path(editor_data.get_edited_scene());
3522
if (scene_filename.is_empty()) {
3523
show_warning(TTR("Can't reload a scene that was never saved."));
3524
break;
3525
}
3526
3527
if (unsaved_cache) {
3528
if (!p_confirmed) {
3529
confirmation->set_ok_button_text(TTRC("Save & Reload"));
3530
const String unsaved_message = _get_unsaved_scene_dialog_text(scene_filename, started_timestamp);
3531
confirmation->set_text(unsaved_message + "\n\n" + TTR("Save before reloading the scene?"));
3532
confirmation->popup_centered();
3533
confirmation_button->show();
3534
confirmation_button->grab_focus();
3535
break;
3536
} else {
3537
_save_scene_with_preview(scene_filename);
3538
}
3539
}
3540
3541
_discard_changes();
3542
} break;
3543
3544
case EditorSceneTabs::SCENE_SHOW_IN_FILESYSTEM: {
3545
String path = editor_data.get_scene_path(editor_data.get_edited_scene());
3546
if (!path.is_empty()) {
3547
FileSystemDock::get_singleton()->navigate_to_path(path);
3548
}
3549
} break;
3550
3551
case PROJECT_OPEN_SETTINGS: {
3552
project_settings_editor->popup_project_settings();
3553
} break;
3554
3555
case PROJECT_FIND_IN_FILES: {
3556
ScriptEditor::get_singleton()->open_find_in_files_dialog("");
3557
} break;
3558
3559
case PROJECT_INSTALL_ANDROID_SOURCE: {
3560
if (p_confirmed) {
3561
if (export_template_manager->is_android_template_installed(android_export_preset)) {
3562
remove_android_build_template->set_text(vformat(TTR(REMOVE_ANDROID_BUILD_TEMPLATE_MESSAGE), export_template_manager->get_android_build_directory(android_export_preset)));
3563
remove_android_build_template->popup_centered();
3564
} else if (!export_template_manager->can_install_android_template(android_export_preset)) {
3565
gradle_build_manage_templates->popup_centered();
3566
} else {
3567
export_template_manager->install_android_template(android_export_preset);
3568
}
3569
} else {
3570
bool has_custom_gradle_build = false;
3571
choose_android_export_profile->clear();
3572
for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
3573
Ref<EditorExportPreset> export_preset = EditorExport::get_singleton()->get_export_preset(i);
3574
if (export_preset->get_platform()->get_class_name() == "EditorExportPlatformAndroid" && (bool)export_preset->get("gradle_build/use_gradle_build")) {
3575
choose_android_export_profile->add_item(export_preset->get_name(), i);
3576
String gradle_build_directory = export_preset->get("gradle_build/gradle_build_directory");
3577
String android_source_template = export_preset->get("gradle_build/android_source_template");
3578
if (!android_source_template.is_empty() || (gradle_build_directory != "" && gradle_build_directory != "res://android")) {
3579
has_custom_gradle_build = true;
3580
}
3581
}
3582
}
3583
_android_export_preset_selected(choose_android_export_profile->get_item_count() >= 1 ? 0 : -1);
3584
3585
if (choose_android_export_profile->get_item_count() > 1 && has_custom_gradle_build) {
3586
// If there's multiple options and at least one of them uses a custom gradle build then prompt the user to choose.
3587
choose_android_export_profile->show();
3588
install_android_build_template->popup_centered();
3589
} else {
3590
choose_android_export_profile->hide();
3591
3592
if (export_template_manager->is_android_template_installed(android_export_preset)) {
3593
remove_android_build_template->set_text(vformat(TTR(REMOVE_ANDROID_BUILD_TEMPLATE_MESSAGE), export_template_manager->get_android_build_directory(android_export_preset)));
3594
remove_android_build_template->popup_centered();
3595
} else if (export_template_manager->can_install_android_template(android_export_preset)) {
3596
install_android_build_template->popup_centered();
3597
} else {
3598
gradle_build_manage_templates->popup_centered();
3599
}
3600
}
3601
}
3602
} break;
3603
case PROJECT_OPEN_USER_DATA_FOLDER: {
3604
// Ensure_user_data_dir() to prevent the edge case: "Open User Data Folder" won't work after the project was renamed in ProjectSettingsEditor unless the project is saved.
3605
OS::get_singleton()->ensure_user_data_dir();
3606
OS::get_singleton()->shell_show_in_file_manager(OS::get_singleton()->get_user_data_dir(), true);
3607
} break;
3608
case SCENE_QUIT:
3609
case PROJECT_QUIT_TO_PROJECT_MANAGER:
3610
case PROJECT_RELOAD_CURRENT_PROJECT: {
3611
if (p_confirmed && plugin_to_save) {
3612
plugin_to_save->save_external_data();
3613
p_confirmed = false;
3614
}
3615
3616
if (p_confirmed && stop_project_confirmation && project_run_bar->is_playing()) {
3617
project_run_bar->stop_playing();
3618
stop_project_confirmation = false;
3619
p_confirmed = false;
3620
}
3621
3622
if (!p_confirmed) {
3623
if (!stop_project_confirmation && project_run_bar->is_playing()) {
3624
if (p_option == PROJECT_RELOAD_CURRENT_PROJECT) {
3625
confirmation->set_text(TTR("Stop running project before reloading the current project?"));
3626
confirmation->set_ok_button_text(TTR("Stop & Reload"));
3627
} else {
3628
confirmation->set_text(TTR("Stop running project before exiting the editor?"));
3629
confirmation->set_ok_button_text(TTR("Stop & Quit"));
3630
}
3631
confirmation->reset_size();
3632
confirmation->popup_centered();
3633
confirmation_button->hide();
3634
stop_project_confirmation = true;
3635
break;
3636
}
3637
3638
bool save_each = EDITOR_GET("interface/editor/save_each_scene_on_quit");
3639
if (_next_unsaved_scene(!save_each) == -1) {
3640
if (EditorUndoRedoManager::get_singleton()->is_history_unsaved(EditorUndoRedoManager::GLOBAL_HISTORY)) {
3641
if (p_option == PROJECT_RELOAD_CURRENT_PROJECT) {
3642
save_confirmation->set_ok_button_text(TTR("Save & Reload"));
3643
save_confirmation->set_text(TTR("Save modified resources before reloading?"));
3644
} else {
3645
save_confirmation->set_ok_button_text(TTR("Save & Quit"));
3646
save_confirmation->set_text(TTR("Save modified resources before closing?"));
3647
}
3648
save_confirmation->reset_size();
3649
save_confirmation->popup_centered();
3650
break;
3651
}
3652
3653
plugin_to_save = nullptr;
3654
for (int i = 0; i < editor_data.get_editor_plugin_count(); i++) {
3655
const String unsaved_status = editor_data.get_editor_plugin(i)->get_unsaved_status();
3656
if (!unsaved_status.is_empty()) {
3657
if (p_option == PROJECT_RELOAD_CURRENT_PROJECT) {
3658
save_confirmation->set_ok_button_text(TTR("Save & Reload"));
3659
save_confirmation->set_text(unsaved_status);
3660
} else {
3661
save_confirmation->set_ok_button_text(TTR("Save & Quit"));
3662
save_confirmation->set_text(unsaved_status);
3663
}
3664
save_confirmation->reset_size();
3665
save_confirmation->popup_centered();
3666
plugin_to_save = editor_data.get_editor_plugin(i);
3667
break;
3668
}
3669
}
3670
3671
if (plugin_to_save) {
3672
break;
3673
}
3674
3675
_discard_changes();
3676
break;
3677
}
3678
3679
if (save_each) {
3680
tab_closing_menu_option = current_menu_option;
3681
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
3682
tabs_to_close.push_back(editor_data.get_scene_path(i));
3683
}
3684
_proceed_closing_scene_tabs();
3685
} else {
3686
String unsaved_scenes;
3687
int i = _next_unsaved_scene(true, 0);
3688
while (i != -1) {
3689
unsaved_scenes += "\n " + editor_data.get_edited_scene_root(i)->get_scene_file_path();
3690
i = _next_unsaved_scene(true, ++i);
3691
}
3692
if (p_option == PROJECT_RELOAD_CURRENT_PROJECT) {
3693
save_confirmation->set_ok_button_text(TTR("Save & Reload"));
3694
save_confirmation->set_text(TTR("Save changes to the following scene(s) before reloading?") + unsaved_scenes);
3695
} else {
3696
save_confirmation->set_ok_button_text(TTR("Save & Quit"));
3697
save_confirmation->set_text((p_option == SCENE_QUIT ? TTR("Save changes to the following scene(s) before quitting?") : TTR("Save changes to the following scene(s) before opening Project Manager?")) + unsaved_scenes);
3698
}
3699
save_confirmation->reset_size();
3700
save_confirmation->popup_centered();
3701
}
3702
3703
DisplayServer::get_singleton()->window_request_attention();
3704
break;
3705
}
3706
_save_external_resources();
3707
_discard_changes();
3708
} break;
3709
case SPINNER_UPDATE_CONTINUOUSLY: {
3710
EditorSettings::get_singleton()->set("interface/editor/update_continuously", true);
3711
_update_update_spinner();
3712
show_accept(TTR("This option is deprecated. Situations where refresh must be forced are now considered a bug. Please report."), TTR("OK"));
3713
} break;
3714
case SPINNER_UPDATE_WHEN_CHANGED: {
3715
EditorSettings::get_singleton()->set("interface/editor/update_continuously", false);
3716
_update_update_spinner();
3717
} break;
3718
case SPINNER_UPDATE_SPINNER_HIDE: {
3719
EditorSettings::get_singleton()->set("interface/editor/show_update_spinner", 2); // Disabled
3720
_update_update_spinner();
3721
} break;
3722
case EDITOR_OPEN_SETTINGS: {
3723
editor_settings_dialog->popup_edit_settings();
3724
} break;
3725
case EDITOR_OPEN_DATA_FOLDER: {
3726
OS::get_singleton()->shell_show_in_file_manager(EditorPaths::get_singleton()->get_data_dir(), true);
3727
} break;
3728
case EDITOR_OPEN_CONFIG_FOLDER: {
3729
OS::get_singleton()->shell_show_in_file_manager(EditorPaths::get_singleton()->get_config_dir(), true);
3730
} break;
3731
case EDITOR_MANAGE_EXPORT_TEMPLATES: {
3732
export_template_manager->popup_manager();
3733
} break;
3734
case EDITOR_CONFIGURE_FBX_IMPORTER: {
3735
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
3736
fbx_importer_manager->show_dialog();
3737
#endif
3738
} break;
3739
case EDITOR_MANAGE_FEATURE_PROFILES: {
3740
feature_profile_manager->popup_centered_clamped(Size2(900, 800) * EDSCALE, 0.8);
3741
} break;
3742
case EDITOR_TOGGLE_FULLSCREEN: {
3743
DisplayServer::WindowMode mode = DisplayServer::get_singleton()->window_get_mode();
3744
if (mode == DisplayServer::WINDOW_MODE_FULLSCREEN || mode == DisplayServer::WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
3745
DisplayServer::get_singleton()->window_set_mode(prev_mode);
3746
#ifdef ANDROID_ENABLED
3747
EditorSettings::get_singleton()->set("_is_editor_fullscreen", false);
3748
EditorSettings::get_singleton()->save();
3749
#endif
3750
} else {
3751
prev_mode = mode;
3752
DisplayServer::get_singleton()->window_set_mode(DisplayServer::WINDOW_MODE_FULLSCREEN);
3753
#ifdef ANDROID_ENABLED
3754
EditorSettings::get_singleton()->set("_is_editor_fullscreen", true);
3755
EditorSettings::get_singleton()->save();
3756
#endif
3757
}
3758
} break;
3759
case EDITOR_TAKE_SCREENSHOT: {
3760
screenshot_timer->start();
3761
} break;
3762
case SETTINGS_PICK_MAIN_SCENE: {
3763
file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
3764
List<String> extensions;
3765
ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);
3766
file->clear_filters();
3767
for (const String &extension : extensions) {
3768
file->add_filter("*." + extension, extension.to_upper());
3769
}
3770
3771
Node *scene = editor_data.get_edited_scene_root();
3772
if (scene) {
3773
file->set_current_path(scene->get_scene_file_path());
3774
}
3775
file->set_title(TTR("Pick a Main Scene"));
3776
file->popup_file_dialog();
3777
3778
} break;
3779
case HELP_SEARCH: {
3780
emit_signal(SNAME("request_help_search"), "");
3781
} break;
3782
case EDITOR_COMMAND_PALETTE: {
3783
command_palette->open_popup();
3784
} break;
3785
case HELP_DOCS: {
3786
OS::get_singleton()->shell_open(GODOT_VERSION_DOCS_URL "/");
3787
} break;
3788
case HELP_FORUM: {
3789
OS::get_singleton()->shell_open("https://forum.godotengine.org/");
3790
} break;
3791
case HELP_REPORT_A_BUG: {
3792
OS::get_singleton()->shell_open("https://github.com/godotengine/godot/issues");
3793
} break;
3794
case HELP_COPY_SYSTEM_INFO: {
3795
String info = _get_system_info();
3796
DisplayServer::get_singleton()->clipboard_set(info);
3797
} break;
3798
case HELP_SUGGEST_A_FEATURE: {
3799
OS::get_singleton()->shell_open("https://github.com/godotengine/godot-proposals#readme");
3800
} break;
3801
case HELP_SEND_DOCS_FEEDBACK: {
3802
OS::get_singleton()->shell_open("https://github.com/godotengine/godot-docs/issues");
3803
} break;
3804
case HELP_COMMUNITY: {
3805
OS::get_singleton()->shell_open("https://godotengine.org/community");
3806
} break;
3807
case HELP_ABOUT: {
3808
about->popup_centered(Size2(780, 500) * EDSCALE);
3809
} break;
3810
case HELP_SUPPORT_GODOT_DEVELOPMENT: {
3811
OS::get_singleton()->shell_open("https://fund.godotengine.org/?ref=help_menu");
3812
} break;
3813
}
3814
}
3815
3816
String EditorNode::adjust_scene_name_casing(const String &p_root_name) {
3817
switch (GLOBAL_GET("editor/naming/scene_name_casing").operator int()) {
3818
case SCENE_NAME_CASING_AUTO:
3819
// Use casing of the root node.
3820
break;
3821
case SCENE_NAME_CASING_PASCAL_CASE:
3822
return p_root_name.to_pascal_case();
3823
case SCENE_NAME_CASING_SNAKE_CASE:
3824
return p_root_name.to_snake_case();
3825
case SCENE_NAME_CASING_KEBAB_CASE:
3826
return p_root_name.to_kebab_case();
3827
case SCENE_NAME_CASING_CAMEL_CASE:
3828
return p_root_name.to_camel_case();
3829
}
3830
return p_root_name;
3831
}
3832
3833
String EditorNode::adjust_script_name_casing(const String &p_file_name, ScriptLanguage::ScriptNameCasing p_auto_casing) {
3834
int editor_casing = GLOBAL_GET("editor/naming/script_name_casing");
3835
if (editor_casing == ScriptLanguage::SCRIPT_NAME_CASING_AUTO) {
3836
// Use the script language's preferred casing.
3837
editor_casing = p_auto_casing;
3838
}
3839
3840
switch (editor_casing) {
3841
case ScriptLanguage::SCRIPT_NAME_CASING_AUTO:
3842
// Script language has no preference, so do not adjust.
3843
break;
3844
case ScriptLanguage::SCRIPT_NAME_CASING_PASCAL_CASE:
3845
return p_file_name.to_pascal_case();
3846
case ScriptLanguage::SCRIPT_NAME_CASING_SNAKE_CASE:
3847
return p_file_name.to_snake_case();
3848
case ScriptLanguage::SCRIPT_NAME_CASING_KEBAB_CASE:
3849
return p_file_name.to_kebab_case();
3850
case ScriptLanguage::SCRIPT_NAME_CASING_CAMEL_CASE:
3851
return p_file_name.to_camel_case();
3852
}
3853
return p_file_name;
3854
}
3855
3856
void EditorNode::_request_screenshot() {
3857
_screenshot();
3858
}
3859
3860
void EditorNode::_screenshot(bool p_use_utc) {
3861
String name = "editor_screenshot_" + Time::get_singleton()->get_datetime_string_from_system(p_use_utc).remove_char(':') + ".png";
3862
String path = String("user://") + name;
3863
3864
if (!EditorRun::request_screenshot(callable_mp(this, &EditorNode::_save_screenshot_with_embedded_process).bind(path))) {
3865
_save_screenshot(path);
3866
}
3867
}
3868
3869
void EditorNode::_save_screenshot_with_embedded_process(int64_t p_w, int64_t p_h, const String &p_emb_path, const Rect2i &p_rect, const String &p_path) {
3870
Control *main_screen_control = editor_main_screen->get_control();
3871
ERR_FAIL_NULL_MSG(main_screen_control, "Cannot get the editor main screen control.");
3872
Viewport *viewport = main_screen_control->get_viewport();
3873
ERR_FAIL_NULL_MSG(viewport, "Cannot get a viewport from the editor main screen.");
3874
Ref<ViewportTexture> texture = viewport->get_texture();
3875
ERR_FAIL_COND_MSG(texture.is_null(), "Cannot get a viewport texture from the editor main screen.");
3876
Ref<Image> img = texture->get_image();
3877
ERR_FAIL_COND_MSG(img.is_null(), "Cannot get an image from a viewport texture of the editor main screen.");
3878
img->convert(Image::FORMAT_RGBA8);
3879
ERR_FAIL_COND(p_emb_path.is_empty());
3880
Ref<Image> overlay = Image::load_from_file(p_emb_path);
3881
DirAccess::remove_absolute(p_emb_path);
3882
ERR_FAIL_COND_MSG(overlay.is_null(), "Cannot get an image from a embedded process.");
3883
overlay->convert(Image::FORMAT_RGBA8);
3884
overlay->resize(p_rect.size.x, p_rect.size.y);
3885
img->blend_rect(overlay, Rect2i(0, 0, p_w, p_h), p_rect.position);
3886
Error error = img->save_png(p_path);
3887
ERR_FAIL_COND_MSG(error != OK, "Cannot save screenshot to file '" + p_path + "'.");
3888
3889
if (EDITOR_GET("interface/editor/automatically_open_screenshots")) {
3890
OS::get_singleton()->shell_show_in_file_manager(ProjectSettings::get_singleton()->globalize_path(p_path), true);
3891
}
3892
}
3893
3894
void EditorNode::_save_screenshot(const String &p_path) {
3895
Control *main_screen_control = editor_main_screen->get_control();
3896
ERR_FAIL_NULL_MSG(main_screen_control, "Cannot get the editor main screen control.");
3897
Viewport *viewport = main_screen_control->get_viewport();
3898
ERR_FAIL_NULL_MSG(viewport, "Cannot get a viewport from the editor main screen.");
3899
Ref<ViewportTexture> texture = viewport->get_texture();
3900
ERR_FAIL_COND_MSG(texture.is_null(), "Cannot get a viewport texture from the editor main screen.");
3901
Ref<Image> img = texture->get_image();
3902
ERR_FAIL_COND_MSG(img.is_null(), "Cannot get an image from a viewport texture of the editor main screen.");
3903
Error error = img->save_png(p_path);
3904
ERR_FAIL_COND_MSG(error != OK, "Cannot save screenshot to file '" + p_path + "'.");
3905
3906
if (EDITOR_GET("interface/editor/automatically_open_screenshots")) {
3907
OS::get_singleton()->shell_show_in_file_manager(ProjectSettings::get_singleton()->globalize_path(p_path), true);
3908
}
3909
}
3910
3911
void EditorNode::_check_system_theme_changed() {
3912
DisplayServer *display_server = DisplayServer::get_singleton();
3913
3914
bool system_theme_changed = false;
3915
3916
if (follow_system_theme) {
3917
if (display_server->get_base_color() != last_system_base_color) {
3918
system_theme_changed = true;
3919
last_system_base_color = display_server->get_base_color();
3920
}
3921
3922
if (display_server->is_dark_mode_supported() && display_server->is_dark_mode() != last_dark_mode_state) {
3923
system_theme_changed = true;
3924
last_dark_mode_state = display_server->is_dark_mode();
3925
}
3926
}
3927
3928
if (use_system_accent_color) {
3929
if (display_server->get_accent_color() != last_system_accent_color) {
3930
system_theme_changed = true;
3931
last_system_accent_color = display_server->get_accent_color();
3932
}
3933
}
3934
3935
if (system_theme_changed) {
3936
_update_theme();
3937
} else if (menu_type == MENU_TYPE_GLOBAL && display_server->is_dark_mode_supported() && display_server->is_dark_mode() != last_dark_mode_state) {
3938
last_dark_mode_state = display_server->is_dark_mode();
3939
3940
// Update system menus.
3941
bool dark_mode = DisplayServer::get_singleton()->is_dark_mode();
3942
3943
help_menu->set_item_icon(help_menu->get_item_index(HELP_SEARCH), get_editor_theme_native_menu_icon(SNAME("HelpSearch"), menu_type == MENU_TYPE_GLOBAL, dark_mode));
3944
help_menu->set_item_icon(help_menu->get_item_index(HELP_COPY_SYSTEM_INFO), get_editor_theme_native_menu_icon(SNAME("ActionCopy"), menu_type == MENU_TYPE_GLOBAL, dark_mode));
3945
help_menu->set_item_icon(help_menu->get_item_index(HELP_ABOUT), get_editor_theme_native_menu_icon(SNAME("Godot"), menu_type == MENU_TYPE_GLOBAL, dark_mode));
3946
help_menu->set_item_icon(help_menu->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), get_editor_theme_native_menu_icon(SNAME("Heart"), menu_type == MENU_TYPE_GLOBAL, dark_mode));
3947
editor_dock_manager->update_docks_menu();
3948
}
3949
}
3950
3951
void EditorNode::_tool_menu_option(int p_idx) {
3952
switch (tool_menu->get_item_id(p_idx)) {
3953
case TOOLS_ORPHAN_RESOURCES: {
3954
orphan_resources->show();
3955
} break;
3956
case TOOLS_BUILD_PROFILE_MANAGER: {
3957
build_profile_manager->popup_centered_clamped(Size2(700, 800) * EDSCALE, 0.8);
3958
} break;
3959
case TOOLS_PROJECT_UPGRADE: {
3960
project_upgrade_tool->popup_dialog();
3961
} break;
3962
case TOOLS_CUSTOM: {
3963
if (tool_menu->get_item_submenu(p_idx) == "") {
3964
Callable callback = tool_menu->get_item_metadata(p_idx);
3965
Callable::CallError ce;
3966
Variant result;
3967
callback.callp(nullptr, 0, result, ce);
3968
3969
if (ce.error != Callable::CallError::CALL_OK) {
3970
String err = Variant::get_callable_error_text(callback, nullptr, 0, ce);
3971
ERR_PRINT("Error calling function from tool menu: " + err);
3972
}
3973
} // Else it's a submenu so don't do anything.
3974
} break;
3975
}
3976
}
3977
3978
void EditorNode::_export_as_menu_option(int p_idx) {
3979
if (p_idx == 0) { // MeshLibrary
3980
current_menu_option = FILE_EXPORT_MESH_LIBRARY;
3981
3982
if (!editor_data.get_edited_scene_root()) {
3983
show_accept(TTR("This operation can't be done without a scene."), TTR("OK"));
3984
return;
3985
}
3986
3987
List<String> extensions;
3988
Ref<MeshLibrary> ml(memnew(MeshLibrary));
3989
ResourceSaver::get_recognized_extensions(ml, &extensions);
3990
file_export_lib->clear_filters();
3991
for (const String &E : extensions) {
3992
file_export_lib->add_filter("*." + E);
3993
}
3994
3995
file_export_lib->set_title(TTR("Export Mesh Library"));
3996
file_export_lib->popup_file_dialog();
3997
} else { // Custom menu options added by plugins
3998
if (export_as_menu->get_item_submenu(p_idx).is_empty()) { // If not a submenu
3999
Callable callback = export_as_menu->get_item_metadata(p_idx);
4000
Callable::CallError ce;
4001
Variant result;
4002
callback.callp(nullptr, 0, result, ce);
4003
4004
if (ce.error != Callable::CallError::CALL_OK) {
4005
String err = Variant::get_callable_error_text(callback, nullptr, 0, ce);
4006
ERR_PRINT("Error calling function from export_as menu: " + err);
4007
}
4008
}
4009
}
4010
}
4011
4012
int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) {
4013
for (int i = p_start; i < editor_data.get_edited_scene_count(); i++) {
4014
if (!editor_data.get_edited_scene_root(i)) {
4015
continue;
4016
}
4017
4018
String scene_filename = editor_data.get_edited_scene_root(i)->get_scene_file_path();
4019
if (p_valid_filename && scene_filename.is_empty()) {
4020
continue;
4021
}
4022
4023
bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(i));
4024
if (unsaved) {
4025
return i;
4026
} else {
4027
for (int j = 0; j < editor_data.get_editor_plugin_count(); j++) {
4028
if (!editor_data.get_editor_plugin(j)->get_unsaved_status(scene_filename).is_empty()) {
4029
return i;
4030
}
4031
}
4032
}
4033
}
4034
return -1;
4035
}
4036
4037
void EditorNode::_exit_editor(int p_exit_code) {
4038
exiting = true;
4039
waiting_for_first_scan = false;
4040
resource_preview->stop(); // Stop early to avoid crashes.
4041
_save_editor_layout();
4042
4043
// Dim the editor window while it's quitting to make it clearer that it's busy.
4044
dim_editor(true);
4045
4046
// Unload addons before quitting to allow cleanup.
4047
unload_editor_addons();
4048
4049
get_tree()->quit(p_exit_code);
4050
}
4051
4052
void EditorNode::unload_editor_addons() {
4053
for (const KeyValue<String, EditorPlugin *> &E : addon_name_to_plugin) {
4054
print_verbose(vformat("Unloading addon: %s", E.key));
4055
remove_editor_plugin(E.value, false);
4056
memdelete(E.value);
4057
}
4058
4059
addon_name_to_plugin.clear();
4060
}
4061
4062
void EditorNode::_discard_changes(const String &p_str) {
4063
switch (current_menu_option) {
4064
case SCENE_CLOSE:
4065
case SCENE_TAB_CLOSE: {
4066
const String path = editor_data.get_scene_path(tab_closing_idx);
4067
if (!path.is_empty()) {
4068
_update_prev_closed_scenes(path, true);
4069
}
4070
4071
// Don't close tabs when exiting the editor (required for "restore_scenes_on_load" setting).
4072
if (!_is_closing_editor()) {
4073
_remove_scene(tab_closing_idx);
4074
scene_tabs->update_scene_tabs();
4075
}
4076
_proceed_closing_scene_tabs();
4077
} break;
4078
case SCENE_RELOAD_SAVED_SCENE: {
4079
int cur_idx = editor_data.get_edited_scene();
4080
const String scene_filename = editor_data.get_scene_path(cur_idx);
4081
4082
_remove_edited_scene();
4083
4084
Error err = load_scene(scene_filename);
4085
if (err != OK) {
4086
ERR_PRINT("Failed to load scene");
4087
}
4088
editor_data.move_edited_scene_to_index(cur_idx);
4089
EditorUndoRedoManager::get_singleton()->clear_history(editor_data.get_current_edited_scene_history_id(), false);
4090
scene_tabs->set_current_tab(cur_idx);
4091
4092
confirmation->hide();
4093
} break;
4094
case SCENE_QUIT: {
4095
project_run_bar->stop_playing();
4096
_exit_editor(EXIT_SUCCESS);
4097
4098
} break;
4099
case PROJECT_QUIT_TO_PROJECT_MANAGER: {
4100
_restart_editor(true);
4101
} break;
4102
case PROJECT_RELOAD_CURRENT_PROJECT: {
4103
_restart_editor();
4104
} break;
4105
}
4106
}
4107
4108
void EditorNode::_update_file_menu_opened() {
4109
bool has_unsaved = false;
4110
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
4111
if (_is_scene_unsaved(i)) {
4112
has_unsaved = true;
4113
break;
4114
}
4115
}
4116
if (has_unsaved) {
4117
file_menu->set_item_disabled(file_menu->get_item_index(SCENE_SAVE_ALL_SCENES), false);
4118
file_menu->set_item_tooltip(file_menu->get_item_index(SCENE_SAVE_ALL_SCENES), String());
4119
} else {
4120
file_menu->set_item_disabled(file_menu->get_item_index(SCENE_SAVE_ALL_SCENES), true);
4121
file_menu->set_item_tooltip(file_menu->get_item_index(SCENE_SAVE_ALL_SCENES), TTR("All scenes are already saved."));
4122
}
4123
_update_undo_redo_allowed();
4124
}
4125
4126
void EditorNode::_palette_quick_open_dialog() {
4127
quick_open_color_palette->popup_dialog({ "ColorPalette" }, palette_file_selected_callback);
4128
quick_open_color_palette->set_title(TTRC("Quick Open Color Palette..."));
4129
}
4130
4131
void EditorNode::replace_resources_in_object(Object *p_object, const Vector<Ref<Resource>> &p_source_resources, const Vector<Ref<Resource>> &p_target_resource) {
4132
List<PropertyInfo> pi;
4133
p_object->get_property_list(&pi);
4134
4135
for (const PropertyInfo &E : pi) {
4136
if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
4137
continue;
4138
}
4139
4140
switch (E.type) {
4141
case Variant::OBJECT: {
4142
if (E.hint == PROPERTY_HINT_RESOURCE_TYPE) {
4143
const Variant &v = p_object->get(E.name);
4144
Ref<Resource> res = v;
4145
4146
if (res.is_valid()) {
4147
int res_idx = p_source_resources.find(res);
4148
if (res_idx != -1) {
4149
p_object->set(E.name, p_target_resource.get(res_idx));
4150
} else {
4151
replace_resources_in_object(v, p_source_resources, p_target_resource);
4152
}
4153
}
4154
}
4155
} break;
4156
case Variant::ARRAY: {
4157
Array varray = p_object->get(E.name);
4158
int len = varray.size();
4159
bool array_requires_updating = false;
4160
for (int i = 0; i < len; i++) {
4161
const Variant &v = varray.get(i);
4162
Ref<Resource> res = v;
4163
4164
if (res.is_valid()) {
4165
int res_idx = p_source_resources.find(res);
4166
if (res_idx != -1) {
4167
varray.set(i, p_target_resource.get(res_idx));
4168
array_requires_updating = true;
4169
} else {
4170
replace_resources_in_object(v, p_source_resources, p_target_resource);
4171
}
4172
}
4173
}
4174
if (array_requires_updating) {
4175
p_object->set(E.name, varray);
4176
}
4177
} break;
4178
case Variant::DICTIONARY: {
4179
Dictionary d = p_object->get(E.name);
4180
bool dictionary_requires_updating = false;
4181
for (const Variant &F : d.get_key_list()) {
4182
Variant v = d[F];
4183
Ref<Resource> res = v;
4184
4185
if (res.is_valid()) {
4186
int res_idx = p_source_resources.find(res);
4187
if (res_idx != -1) {
4188
d[F] = p_target_resource.get(res_idx);
4189
dictionary_requires_updating = true;
4190
} else {
4191
replace_resources_in_object(v, p_source_resources, p_target_resource);
4192
}
4193
}
4194
}
4195
if (dictionary_requires_updating) {
4196
p_object->set(E.name, d);
4197
}
4198
} break;
4199
default: {
4200
}
4201
}
4202
}
4203
4204
Node *n = Object::cast_to<Node>(p_object);
4205
if (n) {
4206
for (int i = 0; i < n->get_child_count(); i++) {
4207
replace_resources_in_object(n->get_child(i), p_source_resources, p_target_resource);
4208
}
4209
}
4210
}
4211
4212
void EditorNode::replace_resources_in_scenes(const Vector<Ref<Resource>> &p_source_resources, const Vector<Ref<Resource>> &p_target_resource) {
4213
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
4214
Node *edited_scene_root = editor_data.get_edited_scene_root(i);
4215
if (edited_scene_root) {
4216
replace_resources_in_object(edited_scene_root, p_source_resources, p_target_resource);
4217
}
4218
}
4219
}
4220
4221
void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed) {
4222
if (p_editor->has_main_screen()) {
4223
singleton->editor_main_screen->add_main_plugin(p_editor);
4224
}
4225
singleton->editor_data.add_editor_plugin(p_editor);
4226
singleton->add_child(p_editor);
4227
if (p_config_changed) {
4228
p_editor->enable_plugin();
4229
}
4230
}
4231
4232
void EditorNode::remove_editor_plugin(EditorPlugin *p_editor, bool p_config_changed) {
4233
if (p_editor->has_main_screen()) {
4234
singleton->editor_main_screen->remove_main_plugin(p_editor);
4235
}
4236
p_editor->make_visible(false);
4237
p_editor->clear();
4238
if (p_config_changed) {
4239
p_editor->disable_plugin();
4240
}
4241
singleton->editor_plugins_over->remove_plugin(p_editor);
4242
singleton->editor_plugins_force_over->remove_plugin(p_editor);
4243
singleton->editor_plugins_force_input_forwarding->remove_plugin(p_editor);
4244
singleton->remove_child(p_editor);
4245
singleton->editor_data.remove_editor_plugin(p_editor);
4246
4247
for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : singleton->active_plugins) {
4248
kv.value.erase(p_editor);
4249
}
4250
}
4251
4252
void EditorNode::add_extension_editor_plugin(const StringName &p_class_name) {
4253
ERR_FAIL_COND_MSG(!ClassDB::class_exists(p_class_name), vformat("No such editor plugin registered: %s", p_class_name));
4254
ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_class_name, SNAME("EditorPlugin")), vformat("Class is not an editor plugin: %s", p_class_name));
4255
ERR_FAIL_COND_MSG(singleton->editor_data.has_extension_editor_plugin(p_class_name), vformat("Editor plugin already added for class: %s", p_class_name));
4256
4257
EditorPlugin *plugin = Object::cast_to<EditorPlugin>(ClassDB::_instantiate_allow_unexposed(p_class_name));
4258
singleton->editor_data.add_extension_editor_plugin(p_class_name, plugin);
4259
add_editor_plugin(plugin);
4260
}
4261
4262
void EditorNode::remove_extension_editor_plugin(const StringName &p_class_name) {
4263
// If we're exiting, the editor plugins will get cleaned up anyway, so don't do anything.
4264
if (!singleton || singleton->exiting) {
4265
return;
4266
}
4267
4268
ERR_FAIL_COND_MSG(!singleton->editor_data.has_extension_editor_plugin(p_class_name), vformat("No editor plugin added for class: %s", p_class_name));
4269
4270
EditorPlugin *plugin = singleton->editor_data.get_extension_editor_plugin(p_class_name);
4271
remove_editor_plugin(plugin);
4272
memdelete(plugin);
4273
singleton->editor_data.remove_extension_editor_plugin(p_class_name);
4274
}
4275
4276
void EditorNode::_update_addon_config() {
4277
if (_initializing_plugins) {
4278
return;
4279
}
4280
4281
Vector<String> enabled_addons;
4282
4283
for (const KeyValue<String, EditorPlugin *> &E : addon_name_to_plugin) {
4284
enabled_addons.push_back(E.key);
4285
}
4286
4287
if (enabled_addons.is_empty()) {
4288
ProjectSettings::get_singleton()->set("editor_plugins/enabled", Variant());
4289
} else {
4290
enabled_addons.sort();
4291
ProjectSettings::get_singleton()->set("editor_plugins/enabled", enabled_addons);
4292
}
4293
4294
project_settings_editor->queue_save();
4295
}
4296
4297
void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, bool p_config_changed) {
4298
String addon_path = p_addon;
4299
4300
if (!addon_path.begins_with("res://")) {
4301
addon_path = "res://addons/" + addon_path + "/plugin.cfg";
4302
}
4303
4304
ERR_FAIL_COND(p_enabled && addon_name_to_plugin.has(addon_path));
4305
ERR_FAIL_COND(!p_enabled && !addon_name_to_plugin.has(addon_path));
4306
4307
if (!p_enabled) {
4308
EditorPlugin *addon = addon_name_to_plugin[addon_path];
4309
remove_editor_plugin(addon, p_config_changed);
4310
memdelete(addon);
4311
addon_name_to_plugin.erase(addon_path);
4312
_update_addon_config();
4313
return;
4314
}
4315
4316
Ref<ConfigFile> cf;
4317
cf.instantiate();
4318
if (!DirAccess::exists(addon_path.get_base_dir())) {
4319
_remove_plugin_from_enabled(addon_path);
4320
WARN_PRINT("Addon '" + addon_path + "' failed to load. No directory found. Removing from enabled plugins.");
4321
return;
4322
}
4323
Error err = cf->load(addon_path);
4324
if (err != OK) {
4325
show_warning(vformat(TTR("Unable to enable addon plugin at: '%s' parsing of config failed."), addon_path));
4326
return;
4327
}
4328
4329
String plugin_version;
4330
if (cf->has_section_key("plugin", "version")) {
4331
plugin_version = cf->get_value("plugin", "version");
4332
}
4333
4334
if (!cf->has_section_key("plugin", "script")) {
4335
show_warning(vformat(TTR("Unable to find script field for addon plugin at: '%s'."), addon_path));
4336
return;
4337
}
4338
4339
String script_path = cf->get_value("plugin", "script");
4340
Ref<Script> scr; // We need to save it for creating "ep" below.
4341
4342
// Only try to load the script if it has a name. Else, the plugin has no init script.
4343
EditorPlugin *ep = nullptr;
4344
if (script_path.length() > 0) {
4345
script_path = addon_path.get_base_dir().path_join(script_path);
4346
// We should not use the cached version on startup to prevent a script reload
4347
// if it is already loaded and potentially running from autoloads. See GH-100750.
4348
scr = ResourceLoader::load(script_path, "Script", EditorFileSystem::get_singleton()->doing_first_scan() ? ResourceFormatLoader::CACHE_MODE_REUSE : ResourceFormatLoader::CACHE_MODE_IGNORE);
4349
4350
if (scr.is_null()) {
4351
show_warning(vformat(TTR("Unable to load addon script from path: '%s'."), script_path));
4352
return;
4353
}
4354
4355
// Errors in the script cause the base_type to be an empty StringName.
4356
if (scr->get_instance_base_type() == StringName()) {
4357
if (_initializing_plugins) {
4358
// However, if it happens during initialization, waiting for file scan might help.
4359
pending_addons.push_back(p_addon);
4360
return;
4361
}
4362
4363
show_warning(vformat(TTR("Unable to load addon script from path: '%s'. This might be due to a code error in that script.\nDisabling the addon at '%s' to prevent further errors."), script_path, addon_path));
4364
_remove_plugin_from_enabled(addon_path);
4365
return;
4366
}
4367
4368
// Plugin init scripts must inherit from EditorPlugin and be tools.
4369
if (!ClassDB::is_parent_class(scr->get_instance_base_type(), "EditorPlugin")) {
4370
show_warning(vformat(TTR("Unable to load addon script from path: '%s'. Base type is not 'EditorPlugin'."), script_path));
4371
return;
4372
}
4373
4374
if (!scr->is_tool()) {
4375
show_warning(vformat(TTR("Unable to load addon script from path: '%s'. Script is not in tool mode."), script_path));
4376
return;
4377
}
4378
4379
Object *obj = ClassDB::instantiate(scr->get_instance_base_type());
4380
ep = Object::cast_to<EditorPlugin>(obj);
4381
ERR_FAIL_NULL(ep);
4382
ep->set_script(scr);
4383
} else {
4384
ep = memnew(EditorPlugin);
4385
}
4386
4387
ep->set_plugin_version(plugin_version);
4388
addon_name_to_plugin[addon_path] = ep;
4389
add_editor_plugin(ep, p_config_changed);
4390
4391
_update_addon_config();
4392
}
4393
4394
bool EditorNode::is_addon_plugin_enabled(const String &p_addon) const {
4395
if (p_addon.begins_with("res://")) {
4396
return addon_name_to_plugin.has(p_addon);
4397
}
4398
4399
return addon_name_to_plugin.has("res://addons/" + p_addon + "/plugin.cfg");
4400
}
4401
4402
void EditorNode::_remove_edited_scene(bool p_change_tab) {
4403
// When scene gets closed no node is edited anymore, so make sure the editors are notified before nodes are freed.
4404
hide_unused_editors(SceneTreeDock::get_singleton());
4405
SceneTreeDock::get_singleton()->clear_previous_node_selection();
4406
4407
int new_index = editor_data.get_edited_scene();
4408
int old_index = new_index;
4409
4410
if (new_index > 0) {
4411
new_index = new_index - 1;
4412
} else if (editor_data.get_edited_scene_count() > 1) {
4413
new_index = 1;
4414
} else {
4415
editor_data.add_edited_scene(-1);
4416
new_index = 1;
4417
}
4418
4419
if (p_change_tab) {
4420
_set_current_scene(new_index);
4421
}
4422
editor_data.remove_scene(old_index);
4423
_update_title();
4424
scene_tabs->update_scene_tabs();
4425
}
4426
4427
void EditorNode::_remove_scene(int index, bool p_change_tab) {
4428
// Clear icon cache in case some scripts are no longer needed or class icons are outdated.
4429
// FIXME: Ideally the cache should never be cleared and only updated on per-script basis, when an icon changes.
4430
editor_data.clear_script_icon_cache();
4431
class_icon_cache.clear();
4432
4433
if (editor_data.get_edited_scene() == index) {
4434
// Scene to remove is current scene.
4435
_remove_edited_scene(p_change_tab);
4436
} else {
4437
// Scene to remove is not active scene.
4438
editor_data.remove_scene(index);
4439
}
4440
}
4441
4442
void EditorNode::set_edited_scene(Node *p_scene) {
4443
set_edited_scene_root(p_scene, true);
4444
}
4445
4446
void EditorNode::set_edited_scene_root(Node *p_scene, bool p_auto_add) {
4447
Node *old_edited_scene_root = get_editor_data().get_edited_scene_root();
4448
ERR_FAIL_COND_MSG(p_scene && p_scene != old_edited_scene_root && p_scene->get_parent(), "Non-null nodes that are set as edited scene should not have a parent node.");
4449
4450
if (p_auto_add && old_edited_scene_root && old_edited_scene_root->get_parent() == scene_root) {
4451
scene_root->remove_child(old_edited_scene_root);
4452
}
4453
get_editor_data().set_edited_scene_root(p_scene);
4454
4455
if (Object::cast_to<Popup>(p_scene)) {
4456
Object::cast_to<Popup>(p_scene)->show();
4457
}
4458
SceneTreeDock::get_singleton()->set_edited_scene(p_scene);
4459
if (get_tree()) {
4460
get_tree()->set_edited_scene_root(p_scene);
4461
}
4462
4463
if (p_auto_add && p_scene) {
4464
scene_root->add_child(p_scene, true);
4465
}
4466
}
4467
4468
String EditorNode::get_preview_locale() const {
4469
const Ref<TranslationDomain> &main_domain = TranslationServer::get_singleton()->get_main_domain();
4470
return main_domain->is_enabled() ? main_domain->get_locale_override() : String();
4471
}
4472
4473
void EditorNode::set_preview_locale(const String &p_locale) {
4474
const String &prev_locale = get_preview_locale();
4475
if (prev_locale == p_locale) {
4476
return;
4477
}
4478
4479
// Texts set in the editor could be identifiers that should never be translated.
4480
// So we need to disable translation entirely.
4481
Ref<TranslationDomain> main_domain = TranslationServer::get_singleton()->get_main_domain();
4482
if (p_locale.is_empty()) {
4483
// Disable preview. Use the fallback locale.
4484
main_domain->set_enabled(false);
4485
main_domain->set_locale_override(TranslationServer::get_singleton()->get_fallback_locale());
4486
} else {
4487
// Preview a specific locale.
4488
main_domain->set_enabled(true);
4489
main_domain->set_locale_override(p_locale);
4490
}
4491
4492
EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "preview_locale", p_locale);
4493
4494
_translation_resources_changed();
4495
}
4496
4497
Dictionary EditorNode::_get_main_scene_state() {
4498
Dictionary state;
4499
state["scene_tree_offset"] = SceneTreeDock::get_singleton()->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value();
4500
state["property_edit_offset"] = InspectorDock::get_inspector_singleton()->get_scroll_offset();
4501
state["node_filter"] = SceneTreeDock::get_singleton()->get_filter();
4502
return state;
4503
}
4504
4505
void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
4506
if (get_edited_scene() != p_for_scene && p_for_scene != nullptr) {
4507
return; // Not for this scene.
4508
}
4509
4510
changing_scene = false;
4511
4512
if (get_edited_scene()) {
4513
if (editor_main_screen->can_auto_switch_screens()) {
4514
// Switch between 2D and 3D if currently in 2D or 3D.
4515
Node *selected_node = SceneTreeDock::get_singleton()->get_tree_editor()->get_selected();
4516
if (!selected_node) {
4517
selected_node = get_edited_scene();
4518
}
4519
const int plugin_index = editor_main_screen->get_plugin_index(editor_data.get_handling_main_editor(selected_node));
4520
if (plugin_index >= 0) {
4521
editor_main_screen->select(plugin_index);
4522
}
4523
}
4524
}
4525
4526
if (p_state.has("scene_tree_offset")) {
4527
SceneTreeDock::get_singleton()->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->set_value(p_state["scene_tree_offset"]);
4528
}
4529
if (p_state.has("property_edit_offset")) {
4530
InspectorDock::get_inspector_singleton()->set_scroll_offset(p_state["property_edit_offset"]);
4531
}
4532
4533
if (p_state.has("node_filter")) {
4534
SceneTreeDock::get_singleton()->set_filter(p_state["node_filter"]);
4535
}
4536
4537
// This should only happen at the very end.
4538
4539
EditorDebuggerNode::get_singleton()->update_live_edit_root();
4540
ScriptEditor::get_singleton()->set_scene_root_script(editor_data.get_scene_root_script(editor_data.get_edited_scene()));
4541
editor_data.notify_edited_scene_changed();
4542
emit_signal(SNAME("scene_changed"));
4543
4544
// Reset SDFGI after everything else so that any last-second scene modifications will be processed.
4545
RenderingServer::get_singleton()->sdfgi_reset();
4546
}
4547
4548
bool EditorNode::is_changing_scene() const {
4549
return changing_scene;
4550
}
4551
4552
void EditorNode::_set_current_scene(int p_idx) {
4553
if (p_idx == editor_data.get_edited_scene()) {
4554
return; // Pointless.
4555
}
4556
4557
_set_current_scene_nocheck(p_idx);
4558
}
4559
4560
void EditorNode::_set_current_scene_nocheck(int p_idx) {
4561
// Save the folding in case the scene gets reloaded.
4562
if (editor_data.get_scene_path(p_idx) != "" && editor_data.get_edited_scene_root(p_idx)) {
4563
editor_folding.save_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx));
4564
}
4565
4566
changing_scene = true;
4567
editor_data.save_edited_scene_state(editor_selection, &editor_history, _get_main_scene_state());
4568
4569
Node *old_scene = get_editor_data().get_edited_scene_root();
4570
4571
resource_count.clear();
4572
editor_selection->clear();
4573
SceneTreeDock::get_singleton()->clear_previous_node_selection();
4574
editor_data.set_edited_scene(p_idx);
4575
4576
Node *new_scene = editor_data.get_edited_scene_root();
4577
4578
// Remove the scene only if it's a new scene, preventing performance issues when adding and removing scenes.
4579
if (old_scene && new_scene != old_scene && old_scene->get_parent() == scene_root) {
4580
scene_root->remove_child(old_scene);
4581
}
4582
4583
if (Popup *p = Object::cast_to<Popup>(new_scene)) {
4584
p->show();
4585
}
4586
4587
SceneTreeDock::get_singleton()->set_edited_scene(new_scene);
4588
if (get_tree()) {
4589
get_tree()->set_edited_scene_root(new_scene);
4590
}
4591
4592
if (new_scene) {
4593
if (new_scene->get_parent() != scene_root) {
4594
scene_root->add_child(new_scene, true);
4595
}
4596
}
4597
4598
if (editor_data.check_and_update_scene(p_idx)) {
4599
if (!editor_data.get_scene_path(p_idx).is_empty()) {
4600
editor_folding.load_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx));
4601
}
4602
4603
EditorUndoRedoManager::get_singleton()->clear_history(editor_data.get_scene_history_id(p_idx), false);
4604
}
4605
4606
Dictionary state = editor_data.restore_edited_scene_state(editor_selection, &editor_history);
4607
_edit_current(true);
4608
4609
_update_title();
4610
callable_mp(scene_tabs, &EditorSceneTabs::update_scene_tabs).call_deferred();
4611
4612
if (tabs_to_close.is_empty() && !restoring_scenes) {
4613
callable_mp(this, &EditorNode::_set_main_scene_state).call_deferred(state, get_edited_scene()); // Do after everything else is done setting up.
4614
}
4615
4616
if (!select_current_scene_file_requested && EDITOR_GET("interface/scene_tabs/auto_select_current_scene_file")) {
4617
select_current_scene_file_requested = true;
4618
callable_mp(this, &EditorNode::_nav_to_selected_scene).call_deferred();
4619
}
4620
4621
_update_undo_redo_allowed();
4622
_update_unsaved_cache();
4623
}
4624
4625
void EditorNode::_nav_to_selected_scene() {
4626
select_current_scene_file_requested = false;
4627
const String scene_path = editor_data.get_scene_path(scene_tabs->get_current_tab());
4628
if (!scene_path.is_empty()) {
4629
FileSystemDock::get_singleton()->navigate_to_path(scene_path);
4630
}
4631
}
4632
4633
void EditorNode::setup_color_picker(ColorPicker *p_picker) {
4634
p_picker->set_editor_settings(EditorSettings::get_singleton());
4635
int default_color_mode = EditorSettings::get_singleton()->get_project_metadata("color_picker", "color_mode", EDITOR_GET("interface/inspector/default_color_picker_mode"));
4636
int picker_shape = EditorSettings::get_singleton()->get_project_metadata("color_picker", "picker_shape", EDITOR_GET("interface/inspector/default_color_picker_shape"));
4637
bool show_intensity = EDITOR_GET("interface/inspector/color_picker_show_intensity");
4638
4639
p_picker->set_color_mode((ColorPicker::ColorModeType)default_color_mode);
4640
p_picker->set_picker_shape((ColorPicker::PickerShapeType)picker_shape);
4641
p_picker->set_edit_intensity(show_intensity);
4642
4643
p_picker->set_quick_open_callback(callable_mp(this, &EditorNode::_palette_quick_open_dialog));
4644
p_picker->set_palette_saved_callback(callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::update_file));
4645
palette_file_selected_callback = callable_mp(p_picker, &ColorPicker::_quick_open_palette_file_selected);
4646
}
4647
4648
bool EditorNode::is_scene_open(const String &p_path) {
4649
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
4650
if (editor_data.get_scene_path(i) == p_path) {
4651
return true;
4652
}
4653
}
4654
4655
return false;
4656
}
4657
4658
bool EditorNode::is_multi_window_enabled() const {
4659
return !SceneTree::get_singleton()->get_root()->is_embedding_subwindows() && !EDITOR_GET("interface/editor/single_window_mode") && EDITOR_GET("interface/multi_window/enable");
4660
}
4661
4662
int EditorNode::new_scene() {
4663
int idx = editor_data.add_edited_scene(-1);
4664
_set_current_scene(idx); // Before trying to remove an empty scene, set the current tab index to the newly added tab index.
4665
4666
// Remove placeholder empty scene.
4667
if (editor_data.get_edited_scene_count() > 1) {
4668
for (int i = 0; i < editor_data.get_edited_scene_count() - 1; i++) {
4669
bool unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(i));
4670
if (!unsaved && editor_data.get_scene_path(i).is_empty() && editor_data.get_edited_scene_root(i) == nullptr) {
4671
editor_data.remove_scene(i);
4672
idx--;
4673
}
4674
}
4675
}
4676
4677
editor_data.clear_editor_states();
4678
scene_tabs->update_scene_tabs();
4679
return idx;
4680
}
4681
4682
Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, bool p_set_inherited, bool p_force_open_imported, bool p_silent_change_tab) {
4683
if (!is_inside_tree()) {
4684
defer_load_scene = p_scene;
4685
return OK;
4686
}
4687
4688
String lpath = ProjectSettings::get_singleton()->localize_path(ResourceUID::ensure_path(p_scene));
4689
_update_prev_closed_scenes(lpath, false);
4690
4691
if (!p_set_inherited) {
4692
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
4693
if (editor_data.get_scene_path(i) == lpath) {
4694
_set_current_scene(i);
4695
return OK;
4696
}
4697
}
4698
4699
if (!p_force_open_imported && FileAccess::exists(lpath + ".import")) {
4700
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."), lpath.get_file()));
4701
open_imported->popup_centered();
4702
new_inherited_button->grab_focus();
4703
open_import_request = lpath;
4704
return OK;
4705
}
4706
}
4707
4708
if (!lpath.begins_with("res://")) {
4709
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"));
4710
return ERR_FILE_NOT_FOUND;
4711
}
4712
4713
int prev = editor_data.get_edited_scene();
4714
int idx = prev;
4715
4716
if (prev == -1 || editor_data.get_edited_scene_root() || !editor_data.get_scene_path(prev).is_empty()) {
4717
idx = editor_data.add_edited_scene(-1);
4718
4719
if (p_silent_change_tab) {
4720
_set_current_scene_nocheck(idx);
4721
} else {
4722
_set_current_scene(idx);
4723
}
4724
} else {
4725
EditorUndoRedoManager::get_singleton()->clear_history(editor_data.get_current_edited_scene_history_id(), false);
4726
4727
Dictionary state = editor_data.restore_edited_scene_state(editor_selection, &editor_history);
4728
callable_mp(this, &EditorNode::_set_main_scene_state).call_deferred(state, get_edited_scene()); // Do after everything else is done setting up.
4729
}
4730
4731
dependency_errors.clear();
4732
4733
Error err;
4734
Ref<PackedScene> sdata = ResourceLoader::load(lpath, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err);
4735
4736
if (!p_ignore_broken_deps && !dependency_errors.is_empty()) {
4737
current_menu_option = -1;
4738
dependency_error->show(lpath, dependency_errors);
4739
dependency_errors.clear();
4740
4741
if (prev != -1 && prev != idx) {
4742
_set_current_scene(prev);
4743
editor_data.remove_scene(idx);
4744
}
4745
return ERR_FILE_MISSING_DEPENDENCIES;
4746
}
4747
4748
if (sdata.is_null()) {
4749
_dialog_display_load_error(lpath, err);
4750
4751
if (prev != -1 && prev != idx) {
4752
_set_current_scene(prev);
4753
editor_data.remove_scene(idx);
4754
}
4755
return ERR_FILE_NOT_FOUND;
4756
}
4757
4758
dependency_errors.erase(lpath); // At least not self path.
4759
4760
for (KeyValue<String, HashSet<String>> &E : dependency_errors) {
4761
String txt = vformat(TTR("Scene '%s' has broken dependencies:"), E.key) + "\n";
4762
for (const String &F : E.value) {
4763
txt += "\t" + F + "\n";
4764
}
4765
add_io_error(txt);
4766
}
4767
4768
if (ResourceCache::has(lpath)) {
4769
// Used from somewhere else? No problem! Update state and replace sdata.
4770
Ref<PackedScene> ps = ResourceCache::get_ref(lpath);
4771
if (ps.is_valid()) {
4772
ps->replace_state(sdata->get_state());
4773
ps->set_last_modified_time(sdata->get_last_modified_time());
4774
sdata = ps;
4775
}
4776
4777
} else {
4778
sdata->set_path(lpath, true); // Take over path.
4779
}
4780
4781
Node *new_scene = sdata->instantiate(p_set_inherited ? PackedScene::GEN_EDIT_STATE_MAIN_INHERITED : PackedScene::GEN_EDIT_STATE_MAIN);
4782
if (!new_scene) {
4783
sdata.unref();
4784
_dialog_display_load_error(lpath, ERR_FILE_CORRUPT);
4785
if (prev != -1 && prev != idx) {
4786
_set_current_scene(prev);
4787
editor_data.remove_scene(idx);
4788
}
4789
return ERR_FILE_CORRUPT;
4790
}
4791
4792
if (p_set_inherited) {
4793
Ref<SceneState> state = sdata->get_state();
4794
state->set_path(lpath);
4795
new_scene->set_scene_inherited_state(state);
4796
new_scene->set_scene_file_path(String());
4797
}
4798
4799
new_scene->set_scene_instance_state(Ref<SceneState>());
4800
4801
set_edited_scene(new_scene);
4802
// When editor plugins load in, they might use node transforms during their own setup, so make sure they're up to date.
4803
get_tree()->flush_transform_notifications();
4804
4805
String config_file_path = EditorPaths::get_singleton()->get_project_settings_dir().path_join(lpath.get_file() + "-editstate-" + lpath.md5_text() + ".cfg");
4806
Ref<ConfigFile> editor_state_cf;
4807
editor_state_cf.instantiate();
4808
Error editor_state_cf_err = editor_state_cf->load(config_file_path);
4809
if (editor_state_cf_err == OK || editor_state_cf->has_section("editor_states")) {
4810
_load_editor_plugin_states_from_config(editor_state_cf);
4811
}
4812
4813
if (editor_folding.has_folding_data(lpath)) {
4814
editor_folding.load_scene_folding(new_scene, lpath);
4815
} else if (EDITOR_GET("interface/inspector/auto_unfold_foreign_scenes")) {
4816
editor_folding.unfold_scene(new_scene);
4817
editor_folding.save_scene_folding(new_scene, lpath);
4818
}
4819
4820
EditorDebuggerNode::get_singleton()->update_live_edit_root();
4821
4822
if (restoring_scenes) {
4823
// Initialize history for restored scenes.
4824
ObjectID id = new_scene->get_instance_id();
4825
if (id != editor_history.get_current()) {
4826
editor_history.add_object(id);
4827
}
4828
}
4829
4830
// Load the selected nodes.
4831
if (editor_state_cf->has_section_key("editor_states", "selected_nodes")) {
4832
TypedArray<NodePath> selected_node_list = editor_state_cf->get_value("editor_states", "selected_nodes", TypedArray<String>());
4833
4834
for (int i = 0; i < selected_node_list.size(); i++) {
4835
Node *selected_node = new_scene->get_node_or_null(selected_node_list[i]);
4836
if (selected_node) {
4837
editor_selection->add_node(selected_node);
4838
}
4839
}
4840
}
4841
4842
if (!restoring_scenes) {
4843
save_editor_layout_delayed();
4844
}
4845
4846
if (p_set_inherited) {
4847
EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(editor_data.get_current_edited_scene_history_id());
4848
}
4849
4850
_update_title();
4851
scene_tabs->update_scene_tabs();
4852
if (!restoring_scenes) {
4853
_add_to_recent_scenes(lpath);
4854
}
4855
4856
return OK;
4857
}
4858
4859
HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node *p_node, bool p_node_references_only) {
4860
HashMap<StringName, Variant> modified_property_map;
4861
4862
List<PropertyInfo> pinfo;
4863
p_node->get_property_list(&pinfo);
4864
for (const PropertyInfo &E : pinfo) {
4865
if (E.usage & PROPERTY_USAGE_STORAGE) {
4866
bool node_reference = (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_NODE_TYPE);
4867
if (p_node_references_only && !node_reference) {
4868
continue;
4869
}
4870
bool is_valid_revert = false;
4871
Variant revert_value = EditorPropertyRevert::get_property_revert_value(p_node, E.name, &is_valid_revert);
4872
Variant current_value = p_node->get(E.name);
4873
if (is_valid_revert) {
4874
if (PropertyUtils::is_property_value_different(p_node, current_value, revert_value)) {
4875
// If this property is a direct node reference, save a NodePath instead to prevent corrupted references.
4876
if (node_reference) {
4877
Node *target_node = Object::cast_to<Node>(current_value);
4878
if (target_node) {
4879
modified_property_map[E.name] = p_node->get_path_to(target_node);
4880
}
4881
} else {
4882
modified_property_map[E.name] = current_value;
4883
}
4884
}
4885
}
4886
}
4887
}
4888
4889
return modified_property_map;
4890
}
4891
4892
HashMap<StringName, Variant> EditorNode::get_modified_properties_reference_to_nodes(Node *p_node, List<Node *> &p_nodes_referenced_by) {
4893
HashMap<StringName, Variant> modified_property_map;
4894
4895
List<PropertyInfo> pinfo;
4896
p_node->get_property_list(&pinfo);
4897
for (const PropertyInfo &E : pinfo) {
4898
if (E.usage & PROPERTY_USAGE_STORAGE) {
4899
if (E.type != Variant::OBJECT || E.hint != PROPERTY_HINT_NODE_TYPE) {
4900
continue;
4901
}
4902
Variant current_value = p_node->get(E.name);
4903
Node *target_node = Object::cast_to<Node>(current_value);
4904
if (target_node && p_nodes_referenced_by.find(target_node)) {
4905
modified_property_map[E.name] = p_node->get_path_to(target_node);
4906
}
4907
}
4908
}
4909
4910
return modified_property_map;
4911
}
4912
4913
void EditorNode::update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification) {
4914
if (p_node) {
4915
// First, attempt to restore the script property since it may affect the get_property_list method.
4916
Variant *script_property_table_entry = p_node_modification.property_table.getptr(CoreStringName(script));
4917
if (script_property_table_entry) {
4918
p_node->set_script(*script_property_table_entry);
4919
}
4920
4921
// Get properties for this node.
4922
List<PropertyInfo> pinfo;
4923
p_node->get_property_list(&pinfo);
4924
4925
// Get names of all valid property names.
4926
HashMap<StringName, bool> property_node_reference_table;
4927
for (const PropertyInfo &E : pinfo) {
4928
if (E.usage & PROPERTY_USAGE_STORAGE) {
4929
if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_NODE_TYPE) {
4930
property_node_reference_table[E.name] = true;
4931
} else {
4932
property_node_reference_table[E.name] = false;
4933
}
4934
}
4935
}
4936
4937
// Restore the modified properties for this node.
4938
for (const KeyValue<StringName, Variant> &E : p_node_modification.property_table) {
4939
bool *property_node_reference_table_entry = property_node_reference_table.getptr(E.key);
4940
if (property_node_reference_table_entry) {
4941
// If the property is a node reference, attempt to restore from the node path instead.
4942
bool is_node_reference = *property_node_reference_table_entry;
4943
if (is_node_reference) {
4944
if (E.value.get_type() == Variant::NODE_PATH) {
4945
p_node->set(E.key, p_node->get_node_or_null(E.value));
4946
}
4947
} else {
4948
p_node->set(E.key, E.value);
4949
}
4950
}
4951
}
4952
4953
// Restore the connections to other nodes.
4954
for (const ConnectionWithNodePath &E : p_node_modification.connections_to) {
4955
Connection conn = E.connection;
4956
4957
// Get the node the callable is targeting.
4958
Node *target_node = Object::cast_to<Node>(conn.callable.get_object());
4959
4960
// If the callable object no longer exists or is marked for deletion,
4961
// attempt to reaccquire the closest match by using the node path
4962
// we saved earlier.
4963
if (!target_node || !target_node->is_queued_for_deletion()) {
4964
target_node = p_node->get_node_or_null(E.node_path);
4965
}
4966
4967
if (target_node) {
4968
// Reconstruct the callable.
4969
Callable new_callable = Callable(target_node, conn.callable.get_method());
4970
4971
if (!p_node->is_connected(conn.signal.get_name(), new_callable)) {
4972
ERR_FAIL_COND(p_node->connect(conn.signal.get_name(), new_callable, conn.flags) != OK);
4973
}
4974
}
4975
}
4976
4977
// Restore the connections from other nodes.
4978
for (const Connection &E : p_node_modification.connections_from) {
4979
Connection conn = E;
4980
4981
bool valid = p_node->has_method(conn.callable.get_method()) || Ref<Script>(p_node->get_script()).is_null() || Ref<Script>(p_node->get_script())->has_method(conn.callable.get_method());
4982
ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", conn.signal.get_object()->get_class(), conn.signal.get_name(), conn.callable.get_object()->get_class(), conn.callable.get_method()));
4983
4984
// Get the object which the signal is connected from.
4985
Object *source_object = conn.signal.get_object();
4986
4987
if (source_object) {
4988
ERR_FAIL_COND(source_object->connect(conn.signal.get_name(), Callable(p_node, conn.callable.get_method()), conn.flags) != OK);
4989
}
4990
}
4991
4992
// Re-add the groups.
4993
for (const Node::GroupInfo &E : p_node_modification.groups) {
4994
p_node->add_to_group(E.name, E.persistent);
4995
}
4996
}
4997
}
4998
4999
bool EditorNode::is_additional_node_in_scene(Node *p_edited_scene, Node *p_reimported_root, Node *p_node) {
5000
if (p_node == p_reimported_root) {
5001
return false;
5002
}
5003
5004
bool node_part_of_subscene = p_node != p_edited_scene &&
5005
p_edited_scene->get_scene_inherited_state().is_valid() &&
5006
p_edited_scene->get_scene_inherited_state()->find_node_by_path(p_edited_scene->get_path_to(p_node)) >= 0 &&
5007
// It's important to process added nodes from the base scene in the inherited scene as
5008
// additional nodes to ensure they do not disappear on reload.
5009
// When p_reimported_root == p_edited_scene that means the edited scene
5010
// is the reimported scene, in that case the node is in the root base scene,
5011
// so it's not an addition, otherwise, the node would be added twice on reload.
5012
(p_node->get_owner() != p_edited_scene || p_reimported_root == p_edited_scene);
5013
5014
if (node_part_of_subscene) {
5015
return false;
5016
}
5017
5018
// Loop through the owners until either we reach the root node or nullptr
5019
Node *valid_node_owner = p_node->get_owner();
5020
while (valid_node_owner) {
5021
if (valid_node_owner == p_reimported_root) {
5022
break;
5023
}
5024
valid_node_owner = valid_node_owner->get_owner();
5025
}
5026
5027
// When the owner is the imported scene and the owner is also the edited scene,
5028
// that means the node was added in the current edited scene.
5029
// We can be sure here because if the node that the node does not come from
5030
// the base scene because we checked just over with 'get_scene_inherited_state()->find_node_by_path'.
5031
if (valid_node_owner == p_reimported_root && p_reimported_root != p_edited_scene) {
5032
return false;
5033
}
5034
5035
return true;
5036
}
5037
5038
void EditorNode::get_scene_editor_data_for_node(Node *p_root, Node *p_node, HashMap<NodePath, SceneEditorDataEntry> &p_table) {
5039
SceneEditorDataEntry new_entry;
5040
new_entry.is_display_folded = p_node->is_displayed_folded();
5041
5042
if (p_root != p_node) {
5043
new_entry.is_editable = p_root->is_editable_instance(p_node);
5044
}
5045
5046
p_table.insert(p_root->get_path_to(p_node), new_entry);
5047
5048
for (int i = 0; i < p_node->get_child_count(); i++) {
5049
get_scene_editor_data_for_node(p_root, p_node->get_child(i), p_table);
5050
}
5051
}
5052
5053
void EditorNode::get_preload_scene_modification_table(
5054
Node *p_edited_scene,
5055
Node *p_reimported_root,
5056
Node *p_node, InstanceModificationsEntry &p_instance_modifications) {
5057
if (is_additional_node_in_scene(p_edited_scene, p_reimported_root, p_node)) {
5058
// Only save additional nodes which have an owner since this was causing issues transient ownerless nodes
5059
// which get recreated upon scene tree entry.
5060
// For now instead, assume all ownerless nodes are transient and will have to be recreated.
5061
if (p_node->get_owner()) {
5062
HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, true);
5063
if (p_node->get_owner() == p_edited_scene) {
5064
AdditiveNodeEntry new_additive_node_entry;
5065
new_additive_node_entry.node = p_node;
5066
new_additive_node_entry.parent = p_reimported_root->get_path_to(p_node->get_parent());
5067
new_additive_node_entry.owner = p_node->get_owner();
5068
new_additive_node_entry.index = p_node->get_index();
5069
5070
Node2D *node_2d = Object::cast_to<Node2D>(p_node);
5071
if (node_2d) {
5072
new_additive_node_entry.transform_2d = node_2d->get_transform();
5073
}
5074
Node3D *node_3d = Object::cast_to<Node3D>(p_node);
5075
if (node_3d) {
5076
new_additive_node_entry.transform_3d = node_3d->get_transform();
5077
}
5078
5079
p_instance_modifications.addition_list.push_back(new_additive_node_entry);
5080
}
5081
if (!modified_properties.is_empty()) {
5082
ModificationNodeEntry modification_node_entry;
5083
modification_node_entry.property_table = modified_properties;
5084
5085
p_instance_modifications.modifications[p_reimported_root->get_path_to(p_node)] = modification_node_entry;
5086
}
5087
}
5088
} else {
5089
HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node, false);
5090
5091
// Find all valid connections to other nodes.
5092
List<Connection> connections_to;
5093
p_node->get_all_signal_connections(&connections_to);
5094
5095
List<ConnectionWithNodePath> valid_connections_to;
5096
for (const Connection &c : connections_to) {
5097
Node *connection_target_node = Object::cast_to<Node>(c.callable.get_object());
5098
if (connection_target_node) {
5099
// TODO: add support for reinstating custom callables
5100
if (!c.callable.is_custom()) {
5101
ConnectionWithNodePath connection_to;
5102
connection_to.connection = c;
5103
connection_to.node_path = p_node->get_path_to(connection_target_node);
5104
valid_connections_to.push_back(connection_to);
5105
}
5106
}
5107
}
5108
5109
// Find all valid connections from other nodes.
5110
List<Connection> connections_from;
5111
p_node->get_signals_connected_to_this(&connections_from);
5112
5113
List<Connection> valid_connections_from;
5114
for (const Connection &c : connections_from) {
5115
Node *source_node = Object::cast_to<Node>(c.signal.get_object());
5116
5117
Node *valid_source_owner = nullptr;
5118
if (source_node) {
5119
valid_source_owner = source_node->get_owner();
5120
while (valid_source_owner) {
5121
if (valid_source_owner == p_reimported_root) {
5122
break;
5123
}
5124
valid_source_owner = valid_source_owner->get_owner();
5125
}
5126
}
5127
5128
if (!source_node || valid_source_owner == nullptr) {
5129
// TODO: add support for reinstating custom callables
5130
if (!c.callable.is_custom()) {
5131
valid_connections_from.push_back(c);
5132
}
5133
}
5134
}
5135
5136
// Find all node groups.
5137
List<Node::GroupInfo> groups;
5138
p_node->get_groups(&groups);
5139
5140
if (!modified_properties.is_empty() || !valid_connections_to.is_empty() || !valid_connections_from.is_empty() || !groups.is_empty()) {
5141
ModificationNodeEntry modification_node_entry;
5142
modification_node_entry.property_table = modified_properties;
5143
modification_node_entry.connections_to = valid_connections_to;
5144
modification_node_entry.connections_from = valid_connections_from;
5145
modification_node_entry.groups = groups;
5146
5147
p_instance_modifications.modifications[p_reimported_root->get_path_to(p_node)] = modification_node_entry;
5148
}
5149
}
5150
5151
for (int i = 0; i < p_node->get_child_count(); i++) {
5152
get_preload_scene_modification_table(p_edited_scene, p_reimported_root, p_node->get_child(i), p_instance_modifications);
5153
}
5154
}
5155
5156
void EditorNode::get_preload_modifications_reference_to_nodes(
5157
Node *p_root,
5158
Node *p_node,
5159
HashSet<Node *> &p_excluded_nodes,
5160
List<Node *> &p_instance_list_with_children,
5161
HashMap<NodePath, ModificationNodeEntry> &p_modification_table) {
5162
if (!p_excluded_nodes.find(p_node)) {
5163
HashMap<StringName, Variant> modified_properties = get_modified_properties_reference_to_nodes(p_node, p_instance_list_with_children);
5164
5165
if (!modified_properties.is_empty()) {
5166
ModificationNodeEntry modification_node_entry;
5167
modification_node_entry.property_table = modified_properties;
5168
5169
p_modification_table[p_root->get_path_to(p_node)] = modification_node_entry;
5170
}
5171
5172
for (int i = 0; i < p_node->get_child_count(); i++) {
5173
get_preload_modifications_reference_to_nodes(p_root, p_node->get_child(i), p_excluded_nodes, p_instance_list_with_children, p_modification_table);
5174
}
5175
}
5176
}
5177
5178
void EditorNode::get_children_nodes(Node *p_node, List<Node *> &p_nodes) {
5179
for (int i = 0; i < p_node->get_child_count(); i++) {
5180
Node *child = p_node->get_child(i);
5181
p_nodes.push_back(child);
5182
get_children_nodes(child, p_nodes);
5183
}
5184
}
5185
5186
void EditorNode::replace_history_reimported_nodes(Node *p_original_root_node, Node *p_new_root_node, Node *p_node) {
5187
NodePath scene_path_to_node = p_original_root_node->get_path_to(p_node);
5188
Node *new_node = p_new_root_node->get_node_or_null(scene_path_to_node);
5189
if (new_node) {
5190
editor_history.replace_object(p_node->get_instance_id(), new_node->get_instance_id());
5191
} else {
5192
editor_history.replace_object(p_node->get_instance_id(), ObjectID());
5193
}
5194
5195
for (int i = 0; i < p_node->get_child_count(); i++) {
5196
replace_history_reimported_nodes(p_original_root_node, p_new_root_node, p_node->get_child(i));
5197
}
5198
}
5199
5200
bool EditorNode::has_previous_closed_scenes() const {
5201
return !prev_closed_scenes.is_empty();
5202
}
5203
5204
void EditorNode::edit_foreign_resource(Ref<Resource> p_resource) {
5205
load_scene(p_resource->get_path().get_slice("::", 0));
5206
callable_mp(InspectorDock::get_singleton(), &InspectorDock::edit_resource).call_deferred(p_resource);
5207
}
5208
5209
bool EditorNode::is_resource_read_only(Ref<Resource> p_resource, bool p_foreign_resources_are_writable) {
5210
ERR_FAIL_COND_V(p_resource.is_null(), false);
5211
5212
String path = p_resource->get_path();
5213
if (!path.is_resource_file()) {
5214
// If the resource name contains '::', that means it is a subresource embedded in another resource.
5215
int srpos = path.find("::");
5216
if (srpos != -1) {
5217
String base = path.substr(0, srpos);
5218
// If the base resource is a packed scene, we treat it as read-only if it is not the currently edited scene.
5219
if (ResourceLoader::get_resource_type(base) == "PackedScene") {
5220
if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
5221
// If we have not flagged foreign resources as writable or the base scene the resource is
5222
// part was imported, it can be considered read-only.
5223
if (!p_foreign_resources_are_writable || FileAccess::exists(base + ".import")) {
5224
return true;
5225
}
5226
}
5227
} else {
5228
// If a corresponding .import file exists for the base file, we assume it to be imported and should therefore treated as read-only.
5229
if (FileAccess::exists(base + ".import")) {
5230
return true;
5231
}
5232
}
5233
}
5234
} else if (FileAccess::exists(path + ".import")) {
5235
// The resource is not a subresource, but if it has an .import file, it's imported so treat it as read only.
5236
return true;
5237
}
5238
5239
return false;
5240
}
5241
5242
void EditorNode::request_instantiate_scene(const String &p_path) {
5243
SceneTreeDock::get_singleton()->instantiate(p_path);
5244
}
5245
5246
void EditorNode::request_instantiate_scenes(const Vector<String> &p_files) {
5247
SceneTreeDock::get_singleton()->instantiate_scenes(p_files);
5248
}
5249
5250
String EditorNode::get_multiwindow_support_tooltip_text() const {
5251
if (SceneTree::get_singleton()->get_root()->is_embedding_subwindows()) {
5252
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_SUBWINDOWS)) {
5253
return TTR("Multi-window support is not available because the `--single-window` command line argument was used to start the editor.");
5254
} else {
5255
return TTR("Multi-window support is not available because the current platform doesn't support multiple windows.");
5256
}
5257
} else if (EDITOR_GET("interface/editor/single_window_mode")) {
5258
return TTR("Multi-window support is not available because Interface > Editor > Single Window Mode is enabled in the editor settings.");
5259
}
5260
5261
return TTR("Multi-window support is not available because Interface > Multi Window > Enable is disabled in the editor settings.");
5262
}
5263
5264
void EditorNode::_inherit_request(String p_file) {
5265
current_menu_option = SCENE_NEW_INHERITED_SCENE;
5266
_dialog_action(p_file);
5267
}
5268
5269
void EditorNode::_instantiate_request(const Vector<String> &p_files) {
5270
request_instantiate_scenes(p_files);
5271
}
5272
5273
void EditorNode::_close_messages() {
5274
old_split_ofs = center_split->get_split_offset();
5275
center_split->set_split_offset(0);
5276
}
5277
5278
void EditorNode::_show_messages() {
5279
center_split->set_split_offset(old_split_ofs);
5280
}
5281
5282
void EditorNode::_update_prev_closed_scenes(const String &p_scene_path, bool p_add_scene) {
5283
if (!p_scene_path.is_empty()) {
5284
if (p_add_scene) {
5285
prev_closed_scenes.push_back(p_scene_path);
5286
} else {
5287
prev_closed_scenes.erase(p_scene_path);
5288
}
5289
file_menu->set_item_disabled(file_menu->get_item_index(SCENE_OPEN_PREV), prev_closed_scenes.is_empty());
5290
}
5291
}
5292
5293
void EditorNode::_add_to_recent_scenes(const String &p_scene) {
5294
Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scenes", Array());
5295
if (rc.has(p_scene)) {
5296
rc.erase(p_scene);
5297
}
5298
rc.push_front(p_scene);
5299
if (rc.size() > 10) {
5300
rc.resize(10);
5301
}
5302
5303
EditorSettings::get_singleton()->set_project_metadata("recent_files", "scenes", rc);
5304
_update_recent_scenes();
5305
}
5306
5307
void EditorNode::_open_recent_scene(int p_idx) {
5308
if (p_idx == recent_scenes->get_item_count() - 1) {
5309
EditorSettings::get_singleton()->set_project_metadata("recent_files", "scenes", Array());
5310
callable_mp(this, &EditorNode::_update_recent_scenes).call_deferred();
5311
} else {
5312
Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scenes", Array());
5313
ERR_FAIL_INDEX(p_idx, rc.size());
5314
5315
if (load_scene(rc[p_idx]) != OK) {
5316
rc.remove_at(p_idx);
5317
EditorSettings::get_singleton()->set_project_metadata("recent_files", "scenes", rc);
5318
_update_recent_scenes();
5319
}
5320
}
5321
}
5322
5323
void EditorNode::_update_recent_scenes() {
5324
Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scenes", Array());
5325
recent_scenes->clear();
5326
5327
if (rc.size() == 0) {
5328
recent_scenes->add_item(TTRC("No Recent Scenes"), -1);
5329
recent_scenes->set_item_disabled(-1, true);
5330
} else {
5331
String path;
5332
for (int i = 0; i < rc.size(); i++) {
5333
path = rc[i];
5334
recent_scenes->add_item(path.replace("res://", ""), i);
5335
}
5336
5337
recent_scenes->add_separator();
5338
recent_scenes->add_shortcut(ED_SHORTCUT("editor/clear_recent", TTRC("Clear Recent Scenes")));
5339
}
5340
recent_scenes->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_ALWAYS);
5341
recent_scenes->reset_size();
5342
}
5343
5344
void EditorNode::_quick_opened(const String &p_file_path) {
5345
load_scene_or_resource(p_file_path);
5346
}
5347
5348
void EditorNode::_project_run_started() {
5349
if (bool(EDITOR_GET("run/output/always_clear_output_on_play"))) {
5350
log->clear();
5351
}
5352
5353
int action_on_play = EDITOR_GET("run/bottom_panel/action_on_play");
5354
if (action_on_play == ACTION_ON_PLAY_OPEN_OUTPUT) {
5355
editor_dock_manager->open_dock(log, true);
5356
} else if (action_on_play == ACTION_ON_PLAY_OPEN_DEBUGGER) {
5357
editor_dock_manager->open_dock(EditorDebuggerNode::get_singleton(), true);
5358
}
5359
}
5360
5361
void EditorNode::_project_run_stopped() {
5362
int action_on_stop = EDITOR_GET("run/bottom_panel/action_on_stop");
5363
if (action_on_stop == ACTION_ON_STOP_CLOSE_BUTTOM_PANEL) {
5364
bottom_panel->hide_bottom_panel();
5365
}
5366
}
5367
5368
void EditorNode::notify_all_debug_sessions_exited() {
5369
project_run_bar->stop_playing();
5370
}
5371
5372
void EditorNode::add_io_error(const String &p_error) {
5373
DEV_ASSERT(Thread::get_caller_id() == Thread::get_main_id());
5374
singleton->load_errors->add_image(singleton->theme->get_icon(SNAME("Error"), EditorStringName(EditorIcons)));
5375
singleton->load_errors->add_text(p_error + "\n");
5376
// When a progress dialog is displayed, we will wait for it ot close before displaying
5377
// the io errors to prevent the io popup to set it's parent to the progress dialog.
5378
if (singleton->progress_dialog->is_visible()) {
5379
singleton->load_errors_queued_to_display = true;
5380
} else {
5381
EditorInterface::get_singleton()->popup_dialog_centered_ratio(singleton->load_error_dialog, 0.5);
5382
}
5383
}
5384
5385
void EditorNode::add_io_warning(const String &p_warning) {
5386
DEV_ASSERT(Thread::get_caller_id() == Thread::get_main_id());
5387
singleton->load_errors->add_image(singleton->theme->get_icon(SNAME("Warning"), EditorStringName(EditorIcons)));
5388
singleton->load_errors->add_text(p_warning + "\n");
5389
// When a progress dialog is displayed, we will wait for it ot close before displaying
5390
// the io errors to prevent the io popup to set it's parent to the progress dialog.
5391
if (singleton->progress_dialog->is_visible()) {
5392
singleton->load_errors_queued_to_display = true;
5393
} else {
5394
EditorInterface::get_singleton()->popup_dialog_centered_ratio(singleton->load_error_dialog, 0.5);
5395
}
5396
}
5397
5398
bool EditorNode::find_recursive_resources(const Variant &p_variant, HashSet<Resource *> &r_resources_found) {
5399
switch (p_variant.get_type()) {
5400
case Variant::ARRAY: {
5401
Array a = p_variant;
5402
for (int i = 0; i < a.size(); i++) {
5403
Variant v2 = a[i];
5404
if (v2.get_type() != Variant::ARRAY && v2.get_type() != Variant::DICTIONARY && v2.get_type() != Variant::OBJECT) {
5405
continue;
5406
}
5407
if (find_recursive_resources(v2, r_resources_found)) {
5408
return true;
5409
}
5410
}
5411
} break;
5412
case Variant::DICTIONARY: {
5413
Dictionary d = p_variant;
5414
for (const KeyValue<Variant, Variant> &kv : d) {
5415
const Variant &k = kv.key;
5416
const Variant &v2 = kv.value;
5417
if (k.get_type() == Variant::ARRAY || k.get_type() == Variant::DICTIONARY || k.get_type() == Variant::OBJECT) {
5418
if (find_recursive_resources(k, r_resources_found)) {
5419
return true;
5420
}
5421
}
5422
if (v2.get_type() == Variant::ARRAY || v2.get_type() == Variant::DICTIONARY || v2.get_type() == Variant::OBJECT) {
5423
if (find_recursive_resources(v2, r_resources_found)) {
5424
return true;
5425
}
5426
}
5427
}
5428
} break;
5429
case Variant::OBJECT: {
5430
Ref<Resource> r = p_variant;
5431
5432
if (r.is_null()) {
5433
return false;
5434
}
5435
5436
if (r_resources_found.has(r.ptr())) {
5437
return true;
5438
}
5439
5440
r_resources_found.insert(r.ptr());
5441
5442
List<PropertyInfo> plist;
5443
r->get_property_list(&plist);
5444
for (const PropertyInfo &pinfo : plist) {
5445
if (!(pinfo.usage & PROPERTY_USAGE_STORAGE)) {
5446
continue;
5447
}
5448
5449
if (pinfo.type != Variant::ARRAY && pinfo.type != Variant::DICTIONARY && pinfo.type != Variant::OBJECT) {
5450
continue;
5451
}
5452
if (find_recursive_resources(r->get(pinfo.name), r_resources_found)) {
5453
return true;
5454
}
5455
}
5456
5457
r_resources_found.erase(r.ptr());
5458
} break;
5459
default: {
5460
}
5461
}
5462
return false;
5463
}
5464
5465
bool EditorNode::_find_scene_in_use(Node *p_node, const String &p_path) const {
5466
if (p_node->get_scene_file_path() == p_path) {
5467
return true;
5468
}
5469
5470
for (int i = 0; i < p_node->get_child_count(); i++) {
5471
if (_find_scene_in_use(p_node->get_child(i), p_path)) {
5472
return true;
5473
}
5474
}
5475
5476
return false;
5477
}
5478
5479
bool EditorNode::close_scene() {
5480
int tab_index = editor_data.get_edited_scene();
5481
if (tab_index == 0 && get_edited_scene() == nullptr && editor_data.get_scene_path(tab_index).is_empty()) {
5482
return false;
5483
}
5484
5485
tab_closing_idx = tab_index;
5486
current_menu_option = SCENE_CLOSE;
5487
_discard_changes();
5488
changing_scene = false;
5489
return true;
5490
}
5491
5492
bool EditorNode::is_scene_in_use(const String &p_path) {
5493
Node *es = get_edited_scene();
5494
if (es) {
5495
return _find_scene_in_use(es, p_path);
5496
}
5497
return false;
5498
}
5499
5500
OS::ProcessID EditorNode::has_child_process(OS::ProcessID p_pid) const {
5501
return project_run_bar->has_child_process(p_pid);
5502
}
5503
5504
void EditorNode::stop_child_process(OS::ProcessID p_pid) {
5505
project_run_bar->stop_child_process(p_pid);
5506
}
5507
5508
Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) const {
5509
ERR_FAIL_NULL_V(p_object, nullptr);
5510
5511
const Node *node = Object::cast_to<const Node>(p_object);
5512
if (node && node->has_meta(SceneStringName(_custom_type_script))) {
5513
return PropertyUtils::get_custom_type_script(node);
5514
}
5515
5516
Ref<Script> scr = p_object->get_script();
5517
5518
if (scr.is_valid()) {
5519
// Uncommenting would break things! Consider adding a parameter if you need it.
5520
// StringName name = EditorNode::get_editor_data().script_class_get_name(base_script->get_path());
5521
// if (name != StringName()) {
5522
// return name;
5523
// }
5524
5525
// TODO: Should probably be deprecated in 4.x
5526
StringName base = scr->get_instance_base_type();
5527
if (base != StringName() && EditorNode::get_editor_data().get_custom_types().has(base)) {
5528
const Vector<EditorData::CustomType> &types = EditorNode::get_editor_data().get_custom_types()[base];
5529
5530
Ref<Script> base_scr = scr;
5531
while (base_scr.is_valid()) {
5532
for (int i = 0; i < types.size(); ++i) {
5533
if (types[i].script == base_scr) {
5534
return types[i].script;
5535
}
5536
}
5537
base_scr = base_scr->get_base_script();
5538
}
5539
}
5540
}
5541
5542
return nullptr;
5543
}
5544
5545
StringName EditorNode::get_object_custom_type_name(const Object *p_object) const {
5546
ERR_FAIL_NULL_V(p_object, StringName());
5547
5548
Ref<Script> scr = p_object->get_script();
5549
if (scr.is_null() && Object::cast_to<Script>(p_object)) {
5550
scr = p_object;
5551
}
5552
5553
if (scr.is_valid()) {
5554
Ref<Script> base_scr = scr;
5555
while (base_scr.is_valid()) {
5556
StringName name = EditorNode::get_editor_data().script_class_get_name(base_scr->get_path());
5557
if (name != StringName()) {
5558
return name;
5559
}
5560
5561
// TODO: Should probably be deprecated in 4.x.
5562
StringName base = base_scr->get_instance_base_type();
5563
if (base != StringName() && EditorNode::get_editor_data().get_custom_types().has(base)) {
5564
const Vector<EditorData::CustomType> &types = EditorNode::get_editor_data().get_custom_types()[base];
5565
for (int i = 0; i < types.size(); ++i) {
5566
if (types[i].script == base_scr) {
5567
return types[i].name;
5568
}
5569
}
5570
}
5571
base_scr = base_scr->get_base_script();
5572
}
5573
}
5574
5575
return StringName();
5576
}
5577
5578
void EditorNode::_pick_main_scene_custom_action(const String &p_custom_action_name) {
5579
if (p_custom_action_name == "select_current") {
5580
Node *scene = editor_data.get_edited_scene_root();
5581
5582
if (!scene) {
5583
show_accept(TTR("There is no defined scene to run."), TTR("OK"));
5584
return;
5585
}
5586
5587
pick_main_scene->hide();
5588
5589
if (!FileAccess::exists(scene->get_scene_file_path())) {
5590
current_menu_option = SAVE_AND_RUN_MAIN_SCENE;
5591
_menu_option_confirm(SCENE_SAVE_AS_SCENE, true);
5592
file->set_title(TTR("Save scene before running..."));
5593
} else {
5594
current_menu_option = SETTINGS_PICK_MAIN_SCENE;
5595
_dialog_action(scene->get_scene_file_path());
5596
}
5597
}
5598
}
5599
5600
Ref<Texture2D> EditorNode::_get_class_or_script_icon(const String &p_class, const String &p_script_path, const String &p_fallback, bool p_fallback_script_to_theme, bool p_skip_fallback_virtual) {
5601
ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty.");
5602
EditorData &ed = EditorNode::get_editor_data();
5603
5604
// Check for a script icon first.
5605
if (!p_script_path.is_empty()) {
5606
Ref<Texture2D> script_icon = ed.get_script_icon(p_script_path);
5607
if (script_icon.is_valid()) {
5608
return script_icon;
5609
}
5610
5611
if (p_fallback_script_to_theme) {
5612
// Look for the native base type in the editor theme. This is relevant for
5613
// scripts extending other scripts and for built-in classes.
5614
String base_type;
5615
if (ScriptServer::is_global_class(p_class)) {
5616
base_type = ScriptServer::get_global_class_native_base(p_class);
5617
} else {
5618
Ref<Script> scr = ResourceLoader::load(p_script_path, "Script");
5619
if (scr.is_valid()) {
5620
base_type = scr->get_instance_base_type();
5621
}
5622
}
5623
if (theme.is_valid()) {
5624
bool instantiable = false;
5625
5626
// If the class doesn't exist or isn't global, then it's not instantiable
5627
if (ClassDB::class_exists(p_class) || ScriptServer::is_global_class(p_class)) {
5628
instantiable = !ClassDB::is_virtual(p_class) && ClassDB::can_instantiate(p_class);
5629
}
5630
5631
return _get_class_or_script_icon(base_type, "", "", false, p_skip_fallback_virtual || instantiable);
5632
}
5633
}
5634
}
5635
5636
// Script was not valid or didn't yield any useful values, try the class name
5637
// directly.
5638
5639
// Check if the class name is an extension-defined type.
5640
Ref<Texture2D> ext_icon = ed.extension_class_get_icon(p_class);
5641
if (ext_icon.is_valid()) {
5642
return ext_icon;
5643
}
5644
5645
// Check if the class name is a custom type.
5646
// TODO: Should probably be deprecated in 4.x
5647
const EditorData::CustomType *ctype = ed.get_custom_type_by_name(p_class);
5648
if (ctype && ctype->icon.is_valid()) {
5649
return ctype->icon;
5650
}
5651
5652
// Look up the class name or the fallback name in the editor theme.
5653
// This is only relevant for built-in classes.
5654
if (theme.is_valid()) {
5655
if (theme->has_icon(p_class, EditorStringName(EditorIcons))) {
5656
return theme->get_icon(p_class, EditorStringName(EditorIcons));
5657
}
5658
5659
if (!p_fallback.is_empty() && theme->has_icon(p_fallback, EditorStringName(EditorIcons))) {
5660
return theme->get_icon(p_fallback, EditorStringName(EditorIcons));
5661
}
5662
5663
// If the fallback is empty or wasn't found, use the default fallback.
5664
if (ClassDB::class_exists(p_class)) {
5665
if (!p_skip_fallback_virtual) {
5666
bool instantiable = !ClassDB::is_virtual(p_class) && ClassDB::can_instantiate(p_class);
5667
if (!instantiable) {
5668
if (ClassDB::is_parent_class(p_class, SNAME("Node"))) {
5669
return theme->get_icon("NodeDisabled", EditorStringName(EditorIcons));
5670
} else {
5671
return theme->get_icon("ObjectDisabled", EditorStringName(EditorIcons));
5672
}
5673
}
5674
}
5675
StringName parent = ClassDB::get_parent_class_nocheck(p_class);
5676
if (parent) {
5677
// Skip virtual class if `p_skip_fallback_virtual` is true or `p_class` is instantiable.
5678
return _get_class_or_script_icon(parent, "", "", false, true);
5679
}
5680
}
5681
}
5682
5683
return nullptr;
5684
}
5685
5686
Ref<Texture2D> EditorNode::get_object_icon(const Object *p_object, const String &p_fallback) {
5687
ERR_FAIL_NULL_V_MSG(p_object, nullptr, "Object cannot be null.");
5688
5689
Ref<Script> scr = p_object->get_script();
5690
5691
const EditorDebuggerRemoteObjects *robjs = Object::cast_to<EditorDebuggerRemoteObjects>(p_object);
5692
if (robjs) {
5693
String class_name;
5694
if (scr.is_valid()) {
5695
class_name = scr->get_global_name();
5696
5697
if (class_name.is_empty()) {
5698
// If there is no class_name in this script we just take the script path.
5699
class_name = scr->get_path();
5700
}
5701
}
5702
5703
if (class_name.is_empty()) {
5704
return get_class_icon(robjs->type_name, p_fallback);
5705
}
5706
5707
return get_class_icon(class_name, p_fallback);
5708
}
5709
5710
if (scr.is_null() && p_object->is_class("Script")) {
5711
scr = p_object;
5712
}
5713
5714
if (Object::cast_to<MultiNodeEdit>(p_object)) {
5715
return get_class_icon(Object::cast_to<MultiNodeEdit>(p_object)->get_edited_class_name(), p_fallback);
5716
} else {
5717
return _get_class_or_script_icon(p_object->get_class(), scr.is_valid() ? scr->get_path() : String(), p_fallback);
5718
}
5719
}
5720
5721
Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) {
5722
ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty.");
5723
const Pair<String, String> key(p_class, p_fallback);
5724
5725
// Take from the local cache, if available.
5726
{
5727
Ref<Texture2D> *icon = class_icon_cache.getptr(key);
5728
if (icon) {
5729
return *icon;
5730
}
5731
}
5732
5733
String script_path;
5734
if (ScriptServer::is_global_class(p_class)) {
5735
script_path = ScriptServer::get_global_class_path(p_class);
5736
} else if (!p_class.get_extension().is_empty() && ResourceLoader::exists(p_class)) { // If the script is not a class_name we check if the script resource exists.
5737
script_path = p_class;
5738
}
5739
5740
Ref<Texture2D> icon = _get_class_or_script_icon(p_class, script_path, p_fallback, true);
5741
class_icon_cache[key] = icon;
5742
return icon;
5743
}
5744
5745
bool EditorNode::is_object_of_custom_type(const Object *p_object, const StringName &p_class) {
5746
ERR_FAIL_NULL_V(p_object, false);
5747
5748
Ref<Script> scr = p_object->get_script();
5749
if (scr.is_null() && Object::cast_to<Script>(p_object)) {
5750
scr = p_object;
5751
}
5752
5753
if (scr.is_valid()) {
5754
Ref<Script> base_script = scr;
5755
while (base_script.is_valid()) {
5756
StringName name = EditorNode::get_editor_data().script_class_get_name(base_script->get_path());
5757
if (name == p_class) {
5758
return true;
5759
}
5760
base_script = base_script->get_base_script();
5761
}
5762
}
5763
return false;
5764
}
5765
5766
// Used to track the progress of tasks in the CLI output (since we don't have any other frame of reference).
5767
static HashMap<String, int> progress_total_steps;
5768
5769
static String last_progress_task;
5770
static String last_progress_state;
5771
static int last_progress_step = 0;
5772
static double last_progress_time = 0;
5773
5774
void EditorNode::progress_add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
5775
if (!singleton) {
5776
return;
5777
} else if (singleton->cmdline_mode) {
5778
print_line_rich(vformat("[ 0%% ] [color=gray][b]%s[/b] | Started %s (%d steps)[/color]", p_task, p_label, p_steps));
5779
progress_total_steps[p_task] = p_steps;
5780
} else if (singleton->progress_dialog) {
5781
singleton->progress_dialog->add_task(p_task, p_label, p_steps, p_can_cancel);
5782
}
5783
}
5784
5785
bool EditorNode::progress_task_step(const String &p_task, const String &p_state, int p_step, bool p_force_refresh) {
5786
if (!singleton) {
5787
return false;
5788
} else if (singleton->cmdline_mode) {
5789
double current_time = USEC_TO_SEC(OS::get_singleton()->get_ticks_usec());
5790
double elapsed_time = current_time - last_progress_time;
5791
if (p_task != last_progress_task || p_state != last_progress_state || p_step != last_progress_step || elapsed_time >= 1.0) {
5792
// Only print the progress if it's changed since the last print, or if one second has passed.
5793
// This prevents multithreaded import from printing the same progress too often, which would bloat the log file.
5794
const int percent = (p_step / float(progress_total_steps[p_task] + 1)) * 100;
5795
print_line_rich(vformat("[%4d%% ] [color=gray][b]%s[/b] | %s[/color]", percent, p_task, p_state));
5796
last_progress_task = p_task;
5797
last_progress_state = p_state;
5798
last_progress_step = p_step;
5799
last_progress_time = current_time;
5800
}
5801
return false;
5802
} else if (singleton->progress_dialog) {
5803
return singleton->progress_dialog->task_step(p_task, p_state, p_step, p_force_refresh);
5804
} else {
5805
return false;
5806
}
5807
}
5808
5809
void EditorNode::progress_end_task(const String &p_task) {
5810
if (!singleton) {
5811
return;
5812
} else if (singleton->cmdline_mode) {
5813
progress_total_steps.erase(p_task);
5814
print_line_rich(vformat("[color=green][ DONE ][/color] [b]%s[/b]\n", p_task));
5815
} else if (singleton->progress_dialog) {
5816
singleton->progress_dialog->end_task(p_task);
5817
}
5818
}
5819
5820
void EditorNode::progress_add_task_bg(const String &p_task, const String &p_label, int p_steps) {
5821
singleton->progress_hb->add_task(p_task, p_label, p_steps);
5822
}
5823
5824
void EditorNode::progress_task_step_bg(const String &p_task, int p_step) {
5825
singleton->progress_hb->task_step(p_task, p_step);
5826
}
5827
5828
void EditorNode::progress_end_task_bg(const String &p_task) {
5829
singleton->progress_hb->end_task(p_task);
5830
}
5831
5832
void EditorNode::_progress_dialog_visibility_changed() {
5833
// Open the io errors after the progress dialog is closed.
5834
if (load_errors_queued_to_display && !progress_dialog->is_visible()) {
5835
EditorInterface::get_singleton()->popup_dialog_centered_ratio(singleton->load_error_dialog, 0.5);
5836
load_errors_queued_to_display = false;
5837
}
5838
}
5839
5840
void EditorNode::_load_error_dialog_visibility_changed() {
5841
if (!load_error_dialog->is_visible()) {
5842
load_errors->clear();
5843
}
5844
}
5845
5846
String EditorNode::_get_system_info() const {
5847
String distribution_name = OS::get_singleton()->get_distribution_name();
5848
if (distribution_name.is_empty()) {
5849
distribution_name = OS::get_singleton()->get_name();
5850
}
5851
if (distribution_name.is_empty()) {
5852
distribution_name = "Other";
5853
}
5854
const String distribution_version = OS::get_singleton()->get_version_alias();
5855
5856
String godot_version = "Godot v" + String(GODOT_VERSION_FULL_CONFIG);
5857
if (String(GODOT_VERSION_BUILD) != "official") {
5858
String hash = String(GODOT_VERSION_HASH);
5859
hash = hash.is_empty() ? String("unknown") : vformat("(%s)", hash.left(9));
5860
godot_version += " " + hash;
5861
}
5862
5863
String display_session_type;
5864
#ifdef LINUXBSD_ENABLED
5865
// `remove_char` is necessary, because `capitalize` introduces a whitespace between "x" and "11".
5866
display_session_type = OS::get_singleton()->get_environment("XDG_SESSION_TYPE").capitalize().remove_char(' ');
5867
#endif // LINUXBSD_ENABLED
5868
String driver_name = OS::get_singleton()->get_current_rendering_driver_name().to_lower();
5869
String rendering_method = OS::get_singleton()->get_current_rendering_method().to_lower();
5870
5871
const String rendering_device_name = RenderingServer::get_singleton()->get_video_adapter_name();
5872
5873
RenderingDevice::DeviceType device_type = RenderingServer::get_singleton()->get_video_adapter_type();
5874
String device_type_string;
5875
switch (device_type) {
5876
case RenderingDevice::DeviceType::DEVICE_TYPE_INTEGRATED_GPU:
5877
device_type_string = "integrated";
5878
break;
5879
case RenderingDevice::DeviceType::DEVICE_TYPE_DISCRETE_GPU:
5880
device_type_string = "dedicated";
5881
break;
5882
case RenderingDevice::DeviceType::DEVICE_TYPE_VIRTUAL_GPU:
5883
device_type_string = "virtual";
5884
break;
5885
case RenderingDevice::DeviceType::DEVICE_TYPE_CPU:
5886
device_type_string = "(software emulation on CPU)";
5887
break;
5888
case RenderingDevice::DeviceType::DEVICE_TYPE_OTHER:
5889
case RenderingDevice::DeviceType::DEVICE_TYPE_MAX:
5890
break; // Can't happen, but silences warning for DEVICE_TYPE_MAX
5891
}
5892
5893
const Vector<String> video_adapter_driver_info = OS::get_singleton()->get_video_adapter_driver_info();
5894
5895
const String processor_name = OS::get_singleton()->get_processor_name();
5896
const int processor_count = OS::get_singleton()->get_processor_count();
5897
5898
// Prettify
5899
if (rendering_method == "forward_plus") {
5900
rendering_method = "Forward+";
5901
} else if (rendering_method == "mobile") {
5902
rendering_method = "Mobile";
5903
} else if (rendering_method == "gl_compatibility") {
5904
rendering_method = "Compatibility";
5905
}
5906
if (driver_name == "vulkan") {
5907
driver_name = "Vulkan";
5908
} else if (driver_name == "d3d12") {
5909
driver_name = "Direct3D 12";
5910
} else if (driver_name == "opengl3_angle") {
5911
driver_name = "OpenGL ES 3/ANGLE";
5912
} else if (driver_name == "opengl3_es") {
5913
driver_name = "OpenGL ES 3";
5914
} else if (driver_name == "opengl3") {
5915
if (OS::get_singleton()->get_gles_over_gl()) {
5916
driver_name = "OpenGL 3";
5917
} else {
5918
driver_name = "OpenGL ES 3";
5919
}
5920
} else if (driver_name == "metal") {
5921
driver_name = "Metal";
5922
}
5923
5924
// Join info.
5925
Vector<String> info;
5926
info.push_back(godot_version);
5927
String distribution_display_session_type = distribution_name;
5928
if (!distribution_version.is_empty()) {
5929
distribution_display_session_type += " " + distribution_version;
5930
}
5931
if (!display_session_type.is_empty()) {
5932
distribution_display_session_type += " on " + display_session_type;
5933
}
5934
info.push_back(distribution_display_session_type);
5935
5936
String display_driver_window_mode;
5937
#ifdef LINUXBSD_ENABLED
5938
// `remove_char` is necessary, because `capitalize` introduces a whitespace between "x" and "11".
5939
display_driver_window_mode = DisplayServer::get_singleton()->get_name().capitalize().remove_char(' ') + " display driver";
5940
#endif // LINUXBSD_ENABLED
5941
if (!display_driver_window_mode.is_empty()) {
5942
display_driver_window_mode += ", ";
5943
}
5944
display_driver_window_mode += get_viewport()->is_embedding_subwindows() ? "Single-window" : "Multi-window";
5945
5946
if (DisplayServer::get_singleton()->get_screen_count() == 1) {
5947
display_driver_window_mode += ", " + itos(DisplayServer::get_singleton()->get_screen_count()) + " monitor";
5948
} else {
5949
display_driver_window_mode += ", " + itos(DisplayServer::get_singleton()->get_screen_count()) + " monitors";
5950
}
5951
5952
info.push_back(display_driver_window_mode);
5953
5954
info.push_back(vformat("%s (%s)", driver_name, rendering_method));
5955
5956
String graphics;
5957
if (!device_type_string.is_empty()) {
5958
graphics = device_type_string + " ";
5959
}
5960
graphics += rendering_device_name;
5961
if (video_adapter_driver_info.size() == 2) { // This vector is always either of length 0 or 2.
5962
const String &vad_name = video_adapter_driver_info[0];
5963
const String &vad_version = video_adapter_driver_info[1]; // Version could be potentially empty on Linux/BSD.
5964
if (!vad_version.is_empty()) {
5965
graphics += vformat(" (%s; %s)", vad_name, vad_version);
5966
} else if (!vad_name.is_empty()) {
5967
graphics += vformat(" (%s)", vad_name);
5968
}
5969
}
5970
info.push_back(graphics);
5971
5972
info.push_back(vformat("%s (%d threads)", processor_name, processor_count));
5973
5974
const int64_t system_ram = OS::get_singleton()->get_memory_info()["physical"];
5975
if (system_ram > 0) {
5976
// If the memory info is available, display it.
5977
info.push_back(vformat("%s memory", String::humanize_size(system_ram)));
5978
}
5979
5980
return String(" - ").join(info);
5981
}
5982
5983
bool EditorNode::_should_display_update_spinner() const {
5984
#ifdef DEV_ENABLED
5985
const bool in_dev = true;
5986
#else
5987
const bool in_dev = false;
5988
#endif
5989
const int show_update_spinner_setting = EDITOR_GET("interface/editor/show_update_spinner");
5990
return (show_update_spinner_setting == 0 && in_dev) || show_update_spinner_setting == 1;
5991
}
5992
5993
Ref<Texture2D> EditorNode::_file_dialog_get_icon(const String &p_path) {
5994
EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->get_filesystem_path(p_path.get_base_dir());
5995
if (efsd) {
5996
String file = p_path.get_file();
5997
for (int i = 0; i < efsd->get_file_count(); i++) {
5998
if (efsd->get_file(i) == file) {
5999
String type = efsd->get_file_type(i);
6000
6001
if (singleton->icon_type_cache.has(type)) {
6002
return singleton->icon_type_cache[type];
6003
} else {
6004
return singleton->icon_type_cache["Object"];
6005
}
6006
}
6007
}
6008
}
6009
6010
return singleton->icon_type_cache["Object"];
6011
}
6012
6013
Ref<Texture2D> EditorNode::_file_dialog_get_thumbnail(const String &p_path) {
6014
Ref<ImageTexture> texture = singleton->default_thumbnail->duplicate();
6015
EditorResourcePreview::get_singleton()->queue_resource_preview(p_path, callable_mp_static(EditorNode::_file_dialog_thumbnail_callback).bind(texture));
6016
return texture;
6017
}
6018
6019
void EditorNode::_file_dialog_thumbnail_callback(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Ref<ImageTexture> p_texture) {
6020
ERR_FAIL_COND(p_texture.is_null());
6021
if (p_preview.is_valid()) {
6022
p_texture->set_image(p_preview->get_image());
6023
}
6024
}
6025
6026
void EditorNode::_build_icon_type_cache() {
6027
List<StringName> tl;
6028
theme->get_icon_list(EditorStringName(EditorIcons), &tl);
6029
for (const StringName &E : tl) {
6030
if (!ClassDB::class_exists(E)) {
6031
continue;
6032
}
6033
icon_type_cache[E] = theme->get_icon(E, EditorStringName(EditorIcons));
6034
}
6035
}
6036
6037
void EditorNode::_enable_pending_addons() {
6038
for (uint32_t i = 0; i < pending_addons.size(); i++) {
6039
set_addon_plugin_enabled(pending_addons[i], true);
6040
}
6041
pending_addons.clear();
6042
}
6043
6044
void EditorNode::_file_dialog_register(FileDialog *p_dialog) {
6045
singleton->file_dialogs.insert(p_dialog);
6046
}
6047
6048
void EditorNode::_file_dialog_unregister(FileDialog *p_dialog) {
6049
singleton->file_dialogs.erase(p_dialog);
6050
}
6051
6052
Vector<EditorNodeInitCallback> EditorNode::_init_callbacks;
6053
6054
void EditorNode::_begin_first_scan() {
6055
if (!waiting_for_first_scan) {
6056
return;
6057
}
6058
requested_first_scan = true;
6059
}
6060
6061
Error EditorNode::export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only, bool p_android_build_template, bool p_patch, const Vector<String> &p_patches) {
6062
export_defer.preset = p_preset;
6063
export_defer.path = p_path;
6064
export_defer.debug = p_debug;
6065
export_defer.pack_only = p_pack_only;
6066
export_defer.android_build_template = p_android_build_template;
6067
export_defer.patch = p_patch;
6068
export_defer.patches = p_patches;
6069
cmdline_mode = true;
6070
return OK;
6071
}
6072
6073
bool EditorNode::is_project_exporting() const {
6074
return project_export && project_export->is_exporting();
6075
}
6076
6077
void EditorNode::show_accept(const String &p_text, const String &p_title) {
6078
current_menu_option = -1;
6079
if (accept) {
6080
_close_save_scene_progress();
6081
accept->set_ok_button_text(p_title);
6082
accept->set_text(p_text);
6083
accept->reset_size();
6084
EditorInterface::get_singleton()->popup_dialog_centered_clamped(accept, Size2i(), 0.0);
6085
}
6086
}
6087
6088
void EditorNode::show_save_accept(const String &p_text, const String &p_title) {
6089
current_menu_option = -1;
6090
if (save_accept) {
6091
_close_save_scene_progress();
6092
save_accept->set_ok_button_text(p_title);
6093
save_accept->set_text(p_text);
6094
save_accept->reset_size();
6095
EditorInterface::get_singleton()->popup_dialog_centered_clamped(save_accept, Size2i(), 0.0);
6096
}
6097
}
6098
6099
void EditorNode::show_warning(const String &p_text, const String &p_title) {
6100
if (warning) {
6101
_close_save_scene_progress();
6102
warning->set_text(p_text);
6103
warning->set_title(p_title);
6104
warning->reset_size();
6105
EditorInterface::get_singleton()->popup_dialog_centered_clamped(warning, Size2i(), 0.0);
6106
} else {
6107
WARN_PRINT(p_title + " " + p_text);
6108
}
6109
}
6110
6111
void EditorNode::_copy_warning(const String &p_str) {
6112
DisplayServer::get_singleton()->clipboard_set(warning->get_text());
6113
}
6114
6115
void EditorNode::_save_editor_layout() {
6116
if (!load_editor_layout_done) {
6117
return;
6118
}
6119
Ref<ConfigFile> config;
6120
config.instantiate();
6121
// Load and amend existing config if it exists.
6122
config->load(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg"));
6123
6124
editor_dock_manager->save_docks_to_config(config, "docks");
6125
_save_open_scenes_to_config(config);
6126
_save_central_editor_layout_to_config(config);
6127
_save_window_settings_to_config(config, "EditorWindow");
6128
editor_data.get_plugin_window_layout(config);
6129
6130
config->save(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg"));
6131
}
6132
6133
void EditorNode::_save_open_scenes_to_config(Ref<ConfigFile> p_layout) {
6134
PackedStringArray scenes;
6135
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
6136
String path = editor_data.get_scene_path(i);
6137
if (path.is_empty()) {
6138
continue;
6139
}
6140
scenes.push_back(path);
6141
}
6142
p_layout->set_value(EDITOR_NODE_CONFIG_SECTION, "open_scenes", scenes);
6143
6144
String currently_edited_scene_path = editor_data.get_scene_path(editor_data.get_edited_scene());
6145
p_layout->set_value(EDITOR_NODE_CONFIG_SECTION, "current_scene", currently_edited_scene_path);
6146
}
6147
6148
void EditorNode::save_editor_layout_delayed() {
6149
editor_layout_save_delay_timer->start();
6150
}
6151
6152
void EditorNode::_load_editor_layout() {
6153
EditorProgress ep("loading_editor_layout", TTR("Loading editor"), 5);
6154
ep.step(TTR("Loading editor layout..."), 0, true);
6155
Ref<ConfigFile> config;
6156
config.instantiate();
6157
Error err = config->load(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg"));
6158
if (err != OK) { // No config.
6159
// If config is not found, expand the res:// folder and favorites by default.
6160
TreeItem *root = FileSystemDock::get_singleton()->get_tree_control()->get_item_with_metadata("res://", 0);
6161
if (root) {
6162
root->set_collapsed(false);
6163
}
6164
6165
TreeItem *favorites = FileSystemDock::get_singleton()->get_tree_control()->get_item_with_metadata("Favorites", 0);
6166
if (favorites) {
6167
favorites->set_collapsed(false);
6168
}
6169
6170
if (overridden_default_layout >= 0) {
6171
_layout_menu_option(overridden_default_layout);
6172
} else {
6173
ep.step(TTR("Loading docks..."), 1, true);
6174
// Initialize some default values.
6175
bottom_panel->load_layout_from_config(default_layout, EDITOR_NODE_CONFIG_SECTION);
6176
}
6177
} else {
6178
ep.step(TTR("Loading docks..."), 1, true);
6179
editor_dock_manager->load_docks_from_config(config, "docks", true);
6180
6181
ep.step(TTR("Reopening scenes..."), 2, true);
6182
_load_open_scenes_from_config(config);
6183
6184
ep.step(TTR("Loading central editor layout..."), 3, true);
6185
_load_central_editor_layout_from_config(config);
6186
6187
ep.step(TTR("Loading plugin window layout..."), 4, true);
6188
editor_data.set_plugin_window_layout(config);
6189
6190
ep.step(TTR("Editor layout ready."), 5, true);
6191
}
6192
load_editor_layout_done = true;
6193
}
6194
6195
void EditorNode::_save_central_editor_layout_to_config(Ref<ConfigFile> p_config_file) {
6196
// Bottom panel.
6197
6198
bottom_panel->save_layout_to_config(p_config_file, EDITOR_NODE_CONFIG_SECTION);
6199
6200
// Debugger tab.
6201
6202
int selected_default_debugger_tab_idx = EditorDebuggerNode::get_singleton()->get_default_debugger()->get_current_debugger_tab();
6203
p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "selected_default_debugger_tab_idx", selected_default_debugger_tab_idx);
6204
6205
// Main editor (plugin).
6206
6207
editor_main_screen->save_layout_to_config(p_config_file, EDITOR_NODE_CONFIG_SECTION);
6208
}
6209
6210
void EditorNode::_load_central_editor_layout_from_config(Ref<ConfigFile> p_config_file) {
6211
// Bottom panel.
6212
6213
bottom_panel->load_layout_from_config(p_config_file, EDITOR_NODE_CONFIG_SECTION);
6214
6215
// Debugger tab.
6216
6217
if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "selected_default_debugger_tab_idx")) {
6218
int selected_default_debugger_tab_idx = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "selected_default_debugger_tab_idx");
6219
EditorDebuggerNode::get_singleton()->get_default_debugger()->switch_to_debugger(selected_default_debugger_tab_idx);
6220
}
6221
6222
// Main editor (plugin).
6223
6224
editor_main_screen->load_layout_from_config(p_config_file, EDITOR_NODE_CONFIG_SECTION);
6225
}
6226
6227
void EditorNode::_save_window_settings_to_config(Ref<ConfigFile> p_layout, const String &p_section) {
6228
Window *w = get_window();
6229
if (w) {
6230
p_layout->set_value(p_section, "screen", w->get_current_screen());
6231
6232
Window::Mode mode = w->get_mode();
6233
switch (mode) {
6234
case Window::MODE_WINDOWED:
6235
p_layout->set_value(p_section, "mode", "windowed");
6236
p_layout->set_value(p_section, "size", w->get_size());
6237
break;
6238
case Window::MODE_FULLSCREEN:
6239
case Window::MODE_EXCLUSIVE_FULLSCREEN:
6240
p_layout->set_value(p_section, "mode", "fullscreen");
6241
break;
6242
case Window::MODE_MINIMIZED:
6243
if (was_window_windowed_last) {
6244
p_layout->set_value(p_section, "mode", "windowed");
6245
p_layout->set_value(p_section, "size", w->get_size());
6246
} else {
6247
p_layout->set_value(p_section, "mode", "maximized");
6248
}
6249
break;
6250
default:
6251
p_layout->set_value(p_section, "mode", "maximized");
6252
break;
6253
}
6254
6255
p_layout->set_value(p_section, "position", w->get_position());
6256
}
6257
}
6258
6259
void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout) {
6260
if (Engine::get_singleton()->is_recovery_mode_hint()) {
6261
return;
6262
}
6263
6264
if (!bool(EDITOR_GET("interface/scene_tabs/restore_scenes_on_load"))) {
6265
return;
6266
}
6267
6268
if (!p_layout->has_section(EDITOR_NODE_CONFIG_SECTION) ||
6269
!p_layout->has_section_key(EDITOR_NODE_CONFIG_SECTION, "open_scenes")) {
6270
return;
6271
}
6272
6273
restoring_scenes = true;
6274
6275
PackedStringArray scenes = p_layout->get_value(EDITOR_NODE_CONFIG_SECTION, "open_scenes");
6276
for (int i = 0; i < scenes.size(); i++) {
6277
if (FileAccess::exists(scenes[i])) {
6278
load_scene(scenes[i]);
6279
}
6280
}
6281
6282
if (p_layout->has_section_key(EDITOR_NODE_CONFIG_SECTION, "current_scene")) {
6283
String current_scene = p_layout->get_value(EDITOR_NODE_CONFIG_SECTION, "current_scene");
6284
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
6285
if (editor_data.get_scene_path(i) == current_scene) {
6286
_set_current_scene(i);
6287
break;
6288
}
6289
}
6290
}
6291
6292
save_editor_layout_delayed();
6293
6294
restoring_scenes = false;
6295
}
6296
6297
bool EditorNode::has_scenes_in_session() {
6298
if (!bool(EDITOR_GET("interface/scene_tabs/restore_scenes_on_load"))) {
6299
return false;
6300
}
6301
Ref<ConfigFile> config;
6302
config.instantiate();
6303
Error err = config->load(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg"));
6304
if (err != OK) {
6305
return false;
6306
}
6307
if (!config->has_section(EDITOR_NODE_CONFIG_SECTION) || !config->has_section_key(EDITOR_NODE_CONFIG_SECTION, "open_scenes")) {
6308
return false;
6309
}
6310
Array scenes = config->get_value(EDITOR_NODE_CONFIG_SECTION, "open_scenes");
6311
return !scenes.is_empty();
6312
}
6313
6314
void EditorNode::undo() {
6315
_menu_option_confirm(SCENE_UNDO, true);
6316
}
6317
6318
void EditorNode::redo() {
6319
_menu_option_confirm(SCENE_REDO, true);
6320
}
6321
6322
bool EditorNode::ensure_main_scene(bool p_from_native) {
6323
pick_main_scene->set_meta("from_native", p_from_native); // Whether from play button or native run.
6324
String main_scene = GLOBAL_GET("application/run/main_scene");
6325
6326
if (main_scene.is_empty()) {
6327
current_menu_option = -1;
6328
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."));
6329
pick_main_scene->popup_centered();
6330
6331
if (editor_data.get_edited_scene_root()) {
6332
select_current_scene_button->set_disabled(false);
6333
select_current_scene_button->grab_focus();
6334
} else {
6335
select_current_scene_button->set_disabled(true);
6336
}
6337
6338
return false;
6339
}
6340
6341
if (!FileAccess::exists(main_scene)) {
6342
current_menu_option = -1;
6343
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));
6344
pick_main_scene->popup_centered();
6345
return false;
6346
}
6347
6348
if (ResourceLoader::get_resource_type(main_scene) != "PackedScene") {
6349
current_menu_option = -1;
6350
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));
6351
pick_main_scene->popup_centered();
6352
return false;
6353
}
6354
6355
return true;
6356
}
6357
6358
bool EditorNode::validate_custom_directory() {
6359
bool use_custom_dir = GLOBAL_GET("application/config/use_custom_user_dir");
6360
6361
if (use_custom_dir) {
6362
String data_dir = OS::get_singleton()->get_user_data_dir();
6363
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_USERDATA);
6364
if (dir->change_dir(data_dir) != OK) {
6365
dir->make_dir_recursive(data_dir);
6366
if (dir->change_dir(data_dir) != OK) {
6367
open_project_settings->set_text(vformat(TTR("User data dir '%s' is not valid. Change to a valid one?"), data_dir));
6368
open_project_settings->popup_centered();
6369
return false;
6370
}
6371
}
6372
}
6373
6374
return true;
6375
}
6376
6377
void EditorNode::run_editor_script(const Ref<Script> &p_script) {
6378
Error err = p_script->reload(true); // Always hard reload the script before running.
6379
if (err != OK || !p_script->is_valid()) {
6380
EditorToaster::get_singleton()->popup_str(TTR("Cannot run the script because it contains errors, check the output log."), EditorToaster::SEVERITY_WARNING);
6381
return;
6382
}
6383
6384
// Perform additional checks on the script to evaluate if it's runnable.
6385
6386
bool is_runnable = true;
6387
if (!ClassDB::is_parent_class(p_script->get_instance_base_type(), "EditorScript")) {
6388
is_runnable = false;
6389
6390
EditorToaster::get_singleton()->popup_str(TTR("Cannot run the script because it doesn't extend EditorScript."), EditorToaster::SEVERITY_WARNING);
6391
}
6392
if (!p_script->is_tool()) {
6393
is_runnable = false;
6394
6395
if (p_script->get_class() == "GDScript") {
6396
EditorToaster::get_singleton()->popup_str(TTR("Cannot run the script because it's not a tool script (add the @tool annotation at the top)."), EditorToaster::SEVERITY_WARNING);
6397
} else if (p_script->get_class() == "CSharpScript") {
6398
EditorToaster::get_singleton()->popup_str(TTR("Cannot run the script because it's not a tool script (add the [Tool] attribute above the class definition)."), EditorToaster::SEVERITY_WARNING);
6399
} else {
6400
EditorToaster::get_singleton()->popup_str(TTR("Cannot run the script because it's not a tool script."), EditorToaster::SEVERITY_WARNING);
6401
}
6402
}
6403
if (!is_runnable) {
6404
return;
6405
}
6406
6407
Ref<EditorScript> es = memnew(EditorScript);
6408
es->set_script(p_script);
6409
es->run();
6410
}
6411
6412
void EditorNode::_immediate_dialog_confirmed() {
6413
immediate_dialog_confirmed = true;
6414
}
6415
bool EditorNode::immediate_confirmation_dialog(const String &p_text, const String &p_ok_text, const String &p_cancel_text, uint32_t p_wrap_width) {
6416
ConfirmationDialog *cd = memnew(ConfirmationDialog);
6417
cd->set_text(p_text);
6418
cd->set_ok_button_text(p_ok_text);
6419
cd->set_cancel_button_text(p_cancel_text);
6420
if (p_wrap_width > 0) {
6421
cd->set_autowrap(true);
6422
cd->get_label()->set_custom_minimum_size(Size2(p_wrap_width, 0) * EDSCALE);
6423
}
6424
6425
cd->connect(SceneStringName(confirmed), callable_mp(singleton, &EditorNode::_immediate_dialog_confirmed));
6426
singleton->gui_base->add_child(cd);
6427
6428
cd->popup_centered();
6429
6430
while (true) {
6431
DisplayServer::get_singleton()->process_events();
6432
Main::iteration();
6433
if (singleton->immediate_dialog_confirmed || !cd->is_visible()) {
6434
break;
6435
}
6436
}
6437
6438
memdelete(cd);
6439
return singleton->immediate_dialog_confirmed;
6440
}
6441
6442
bool EditorNode::is_cmdline_mode() {
6443
ERR_FAIL_NULL_V(singleton, false);
6444
return singleton->cmdline_mode;
6445
}
6446
6447
void EditorNode::cleanup() {
6448
_init_callbacks.clear();
6449
}
6450
6451
void EditorNode::_update_layouts_menu() {
6452
editor_layouts->clear();
6453
overridden_default_layout = -1;
6454
6455
editor_layouts->reset_size();
6456
editor_layouts->add_shortcut(ED_SHORTCUT("layout/save", TTRC("Save Layout...")), LAYOUT_SAVE);
6457
editor_layouts->add_shortcut(ED_SHORTCUT("layout/delete", TTRC("Delete Layout...")), LAYOUT_DELETE);
6458
editor_layouts->add_separator();
6459
editor_layouts->add_shortcut(ED_SHORTCUT("layout/default", TTRC("Default")), LAYOUT_DEFAULT);
6460
6461
Ref<ConfigFile> config;
6462
config.instantiate();
6463
Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config());
6464
if (err != OK) {
6465
return; // No config.
6466
}
6467
6468
Vector<String> layouts = config->get_sections();
6469
const String default_layout_name = TTR("Default");
6470
6471
for (const String &layout : layouts) {
6472
if (layout.contains_char('/')) {
6473
continue;
6474
}
6475
6476
if (layout == default_layout_name) {
6477
editor_layouts->remove_item(editor_layouts->get_item_index(LAYOUT_DEFAULT));
6478
overridden_default_layout = editor_layouts->get_item_count();
6479
}
6480
6481
editor_layouts->add_item(layout);
6482
editor_layouts->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_DISABLED);
6483
}
6484
}
6485
6486
void EditorNode::_layout_menu_option(int p_id) {
6487
switch (p_id) {
6488
case LAYOUT_SAVE: {
6489
current_menu_option = p_id;
6490
layout_dialog->set_title(TTR("Save Layout"));
6491
layout_dialog->set_ok_button_text(TTR("Save"));
6492
layout_dialog->set_name_line_enabled(true);
6493
layout_dialog->popup_centered();
6494
} break;
6495
case LAYOUT_DELETE: {
6496
current_menu_option = p_id;
6497
layout_dialog->set_title(TTR("Delete Layout"));
6498
layout_dialog->set_ok_button_text(TTR("Delete"));
6499
layout_dialog->set_name_line_enabled(false);
6500
layout_dialog->popup_centered();
6501
} break;
6502
case LAYOUT_DEFAULT: {
6503
editor_dock_manager->load_docks_from_config(default_layout, "docks");
6504
_save_editor_layout();
6505
} break;
6506
default: {
6507
Ref<ConfigFile> config;
6508
config.instantiate();
6509
Error err = config->load(EditorSettings::get_singleton()->get_editor_layouts_config());
6510
if (err != OK) {
6511
return; // No config.
6512
}
6513
6514
editor_dock_manager->load_docks_from_config(config, editor_layouts->get_item_text(p_id));
6515
_save_editor_layout();
6516
}
6517
}
6518
}
6519
6520
void EditorNode::_proceed_closing_scene_tabs() {
6521
List<String>::Element *E = tabs_to_close.front();
6522
if (!E) {
6523
if (_is_closing_editor()) {
6524
current_menu_option = tab_closing_menu_option;
6525
_menu_option_confirm(tab_closing_menu_option, true);
6526
} else {
6527
current_menu_option = -1;
6528
save_confirmation->hide();
6529
}
6530
return;
6531
}
6532
String scene_to_close = E->get();
6533
tabs_to_close.pop_front();
6534
6535
int tab_idx = -1;
6536
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
6537
if (editor_data.get_scene_path(i) == scene_to_close) {
6538
tab_idx = i;
6539
break;
6540
}
6541
}
6542
ERR_FAIL_COND(tab_idx < 0);
6543
6544
_scene_tab_closed(tab_idx);
6545
}
6546
6547
void EditorNode::_proceed_save_asing_scene_tabs() {
6548
if (scenes_to_save_as.is_empty()) {
6549
return;
6550
}
6551
int scene_idx = scenes_to_save_as.front()->get();
6552
scenes_to_save_as.pop_front();
6553
_set_current_scene(scene_idx);
6554
_menu_option_confirm(SCENE_MULTI_SAVE_AS_SCENE, false);
6555
}
6556
6557
bool EditorNode::_is_closing_editor() const {
6558
return tab_closing_menu_option == SCENE_QUIT || tab_closing_menu_option == PROJECT_QUIT_TO_PROJECT_MANAGER || tab_closing_menu_option == PROJECT_RELOAD_CURRENT_PROJECT;
6559
}
6560
6561
void EditorNode::_restart_editor(bool p_goto_project_manager) {
6562
exiting = true;
6563
6564
if (project_run_bar->is_playing()) {
6565
project_run_bar->stop_playing();
6566
}
6567
6568
String to_reopen;
6569
if (!p_goto_project_manager && get_tree()->get_edited_scene_root()) {
6570
to_reopen = get_tree()->get_edited_scene_root()->get_scene_file_path();
6571
}
6572
6573
_exit_editor(EXIT_SUCCESS);
6574
6575
List<String> args;
6576
for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_TOOL)) {
6577
args.push_back(a);
6578
}
6579
6580
if (p_goto_project_manager) {
6581
args.push_back("--project-manager");
6582
6583
// Setup working directory.
6584
const String exec_dir = OS::get_singleton()->get_executable_path().get_base_dir();
6585
if (!exec_dir.is_empty()) {
6586
args.push_back("--path");
6587
args.push_back(exec_dir);
6588
}
6589
} else {
6590
args.push_back("--path");
6591
args.push_back(ProjectSettings::get_singleton()->get_resource_path());
6592
6593
args.push_back("-e");
6594
}
6595
6596
if (!to_reopen.is_empty()) {
6597
args.push_back(to_reopen);
6598
}
6599
6600
OS::get_singleton()->set_restart_on_exit(true, args);
6601
}
6602
6603
void EditorNode::_scene_tab_closed(int p_tab) {
6604
current_menu_option = SCENE_TAB_CLOSE;
6605
tab_closing_idx = p_tab;
6606
Node *scene = editor_data.get_edited_scene_root(p_tab);
6607
if (!scene) {
6608
_discard_changes();
6609
return;
6610
}
6611
6612
String scene_filename = scene->get_scene_file_path();
6613
String unsaved_message;
6614
6615
if (EditorUndoRedoManager::get_singleton()->is_history_unsaved(editor_data.get_scene_history_id(p_tab))) {
6616
if (scene_filename.is_empty()) {
6617
unsaved_message = TTR("This scene was never saved.");
6618
} else {
6619
unsaved_message = _get_unsaved_scene_dialog_text(scene_filename, started_timestamp);
6620
}
6621
} else {
6622
// Check if any plugin has unsaved changes in that scene.
6623
for (int i = 0; i < editor_data.get_editor_plugin_count(); i++) {
6624
unsaved_message = editor_data.get_editor_plugin(i)->get_unsaved_status(scene_filename);
6625
if (!unsaved_message.is_empty()) {
6626
break;
6627
}
6628
}
6629
}
6630
6631
if (!unsaved_message.is_empty()) {
6632
if (scene_tabs->get_current_tab() != p_tab) {
6633
_set_current_scene(p_tab);
6634
}
6635
6636
save_confirmation->set_ok_button_text(TTR("Save & Close"));
6637
save_confirmation->set_text(unsaved_message + "\n\n" + TTR("Save before closing?"));
6638
save_confirmation->reset_size();
6639
save_confirmation->popup_centered();
6640
} else {
6641
_discard_changes();
6642
}
6643
6644
save_editor_layout_delayed();
6645
scene_tabs->update_scene_tabs();
6646
}
6647
6648
void EditorNode::_cancel_close_scene_tab() {
6649
if (_is_closing_editor()) {
6650
tab_closing_menu_option = -1;
6651
}
6652
changing_scene = false;
6653
tabs_to_close.clear();
6654
}
6655
6656
void EditorNode::_cancel_confirmation() {
6657
stop_project_confirmation = false;
6658
}
6659
6660
void EditorNode::_prepare_save_confirmation_popup() {
6661
if (save_confirmation->get_window() != get_last_exclusive_window()) {
6662
save_confirmation->reparent(get_last_exclusive_window());
6663
}
6664
}
6665
6666
void EditorNode::_toggle_distraction_free_mode() {
6667
if (EDITOR_GET("interface/editor/separate_distraction_mode")) {
6668
int screen = editor_main_screen->get_selected_index();
6669
6670
if (screen == EditorMainScreen::EDITOR_SCRIPT) {
6671
script_distraction_free = !script_distraction_free;
6672
set_distraction_free_mode(script_distraction_free);
6673
} else {
6674
scene_distraction_free = !scene_distraction_free;
6675
set_distraction_free_mode(scene_distraction_free);
6676
}
6677
} else {
6678
set_distraction_free_mode(distraction_free->is_pressed());
6679
}
6680
}
6681
6682
void EditorNode::update_distraction_free_mode() {
6683
if (!EDITOR_GET("interface/editor/separate_distraction_mode")) {
6684
return;
6685
}
6686
int screen = editor_main_screen->get_selected_index();
6687
if (screen == EditorMainScreen::EDITOR_SCRIPT) {
6688
set_distraction_free_mode(script_distraction_free);
6689
} else {
6690
set_distraction_free_mode(scene_distraction_free);
6691
}
6692
}
6693
6694
void EditorNode::set_distraction_free_mode(bool p_enter) {
6695
distraction_free->set_pressed(p_enter);
6696
6697
if (p_enter) {
6698
if (editor_dock_manager->are_docks_visible()) {
6699
editor_dock_manager->set_docks_visible(false);
6700
}
6701
} else {
6702
editor_dock_manager->set_docks_visible(true);
6703
}
6704
}
6705
6706
bool EditorNode::is_distraction_free_mode_enabled() const {
6707
return distraction_free->is_pressed();
6708
}
6709
6710
void EditorNode::update_distraction_free_button_theme() {
6711
if (distraction_free->get_meta("_scene_tabs_owned", true)) {
6712
distraction_free->set_theme_type_variation("FlatMenuButton");
6713
distraction_free->add_theme_style_override(SceneStringName(pressed), theme->get_stylebox(CoreStringName(normal), "FlatMenuButton"));
6714
} else {
6715
distraction_free->set_theme_type_variation("BottomPanelButton");
6716
distraction_free->remove_theme_style_override(SceneStringName(pressed));
6717
}
6718
}
6719
6720
void EditorNode::set_center_split_offset(int p_offset) {
6721
center_split->set_split_offset(p_offset);
6722
}
6723
6724
Dictionary EditorNode::drag_resource(const Ref<Resource> &p_res, Control *p_from) {
6725
Control *drag_control = memnew(Control);
6726
TextureRect *drag_preview = memnew(TextureRect);
6727
Label *label = memnew(Label);
6728
label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
6729
label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
6730
6731
Ref<Texture2D> preview;
6732
6733
{
6734
// TODO: make proper previews
6735
Ref<Texture2D> texture = theme->get_icon(SNAME("FileBigThumb"), EditorStringName(EditorIcons));
6736
if (texture.is_valid()) {
6737
Ref<Image> img = texture->get_image();
6738
img = img->duplicate();
6739
img->resize(48, 48); // meh
6740
preview = ImageTexture::create_from_image(img);
6741
}
6742
}
6743
6744
drag_preview->set_texture(preview);
6745
drag_control->add_child(drag_preview);
6746
if (p_res->get_path().is_resource_file()) {
6747
label->set_text(p_res->get_path().get_file());
6748
} else if (!p_res->get_name().is_empty()) {
6749
label->set_text(p_res->get_name());
6750
} else {
6751
label->set_text(p_res->get_class());
6752
}
6753
6754
drag_control->add_child(label);
6755
6756
p_from->set_drag_preview(drag_control); // Wait until it enters scene.
6757
6758
label->set_position(Point2((preview->get_width() - label->get_minimum_size().width) / 2, preview->get_height()));
6759
6760
Dictionary drag_data;
6761
drag_data["type"] = "resource";
6762
drag_data["resource"] = p_res;
6763
drag_data["from"] = p_from;
6764
6765
return drag_data;
6766
}
6767
6768
Dictionary EditorNode::drag_files_and_dirs(const Vector<String> &p_paths, Control *p_from) {
6769
bool has_folder = false;
6770
bool has_file = false;
6771
for (int i = 0; i < p_paths.size(); i++) {
6772
bool is_folder = p_paths[i].ends_with("/");
6773
has_folder |= is_folder;
6774
has_file |= !is_folder;
6775
}
6776
6777
int max_rows = 6;
6778
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.
6779
VBoxContainer *vbox = memnew(VBoxContainer);
6780
for (int i = 0; i < num_rows; i++) {
6781
HBoxContainer *hbox = memnew(HBoxContainer);
6782
TextureRect *icon = memnew(TextureRect);
6783
Label *label = memnew(Label);
6784
label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
6785
label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
6786
6787
if (p_paths[i].ends_with("/")) {
6788
label->set_text(p_paths[i].substr(0, p_paths[i].length() - 1).get_file());
6789
icon->set_texture(theme->get_icon(SNAME("Folder"), EditorStringName(EditorIcons)));
6790
} else {
6791
label->set_text(p_paths[i].get_file());
6792
icon->set_texture(theme->get_icon(SNAME("File"), EditorStringName(EditorIcons)));
6793
}
6794
icon->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
6795
icon->set_size(Size2(16, 16));
6796
hbox->add_child(icon);
6797
hbox->add_child(label);
6798
vbox->add_child(hbox);
6799
}
6800
6801
if (p_paths.size() > num_rows) {
6802
Label *label = memnew(Label);
6803
label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
6804
if (has_file && has_folder) {
6805
label->set_text(vformat(TTR("%d more files or folders"), p_paths.size() - num_rows));
6806
} else if (has_folder) {
6807
label->set_text(vformat(TTR("%d more folders"), p_paths.size() - num_rows));
6808
} else {
6809
label->set_text(vformat(TTR("%d more files"), p_paths.size() - num_rows));
6810
}
6811
vbox->add_child(label);
6812
}
6813
p_from->set_drag_preview(vbox); // Wait until it enters scene.
6814
6815
Dictionary drag_data;
6816
drag_data["type"] = has_folder ? "files_and_dirs" : "files";
6817
drag_data["files"] = p_paths;
6818
drag_data["from"] = p_from;
6819
return drag_data;
6820
}
6821
6822
void EditorNode::add_tool_menu_item(const String &p_name, const Callable &p_callback) {
6823
int idx = tool_menu->get_item_count();
6824
tool_menu->add_item(p_name, TOOLS_CUSTOM);
6825
tool_menu->set_item_metadata(idx, p_callback);
6826
}
6827
6828
void EditorNode::add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu) {
6829
ERR_FAIL_NULL(p_submenu);
6830
ERR_FAIL_COND(p_submenu->get_parent() != nullptr);
6831
tool_menu->add_submenu_node_item(p_name, p_submenu, TOOLS_CUSTOM);
6832
}
6833
6834
void EditorNode::remove_tool_menu_item(const String &p_name) {
6835
for (int i = 0; i < tool_menu->get_item_count(); i++) {
6836
if (tool_menu->get_item_id(i) != TOOLS_CUSTOM) {
6837
continue;
6838
}
6839
6840
if (tool_menu->get_item_text(i) == p_name) {
6841
if (tool_menu->get_item_submenu(i) != "") {
6842
Node *n = tool_menu->get_node(tool_menu->get_item_submenu(i));
6843
tool_menu->remove_child(n);
6844
memdelete(n);
6845
}
6846
tool_menu->remove_item(i);
6847
tool_menu->reset_size();
6848
return;
6849
}
6850
}
6851
}
6852
6853
PopupMenu *EditorNode::get_export_as_menu() {
6854
return export_as_menu;
6855
}
6856
6857
void EditorNode::_dropped_files(const Vector<String> &p_files) {
6858
String to_path = FileSystemDock::get_singleton()->get_folder_path_at_mouse_position();
6859
if (to_path.is_empty()) {
6860
to_path = FileSystemDock::get_singleton()->get_current_directory();
6861
}
6862
to_path = ProjectSettings::get_singleton()->globalize_path(to_path);
6863
6864
_add_dropped_files_recursive(p_files, to_path);
6865
6866
EditorFileSystem::get_singleton()->scan_changes();
6867
}
6868
6869
void EditorNode::_add_dropped_files_recursive(const Vector<String> &p_files, String to_path) {
6870
Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
6871
ERR_FAIL_COND(dir.is_null());
6872
6873
for (int i = 0; i < p_files.size(); i++) {
6874
const String &from = p_files[i];
6875
String to = to_path.path_join(from.get_file());
6876
6877
if (dir->dir_exists(from)) {
6878
Vector<String> sub_files;
6879
6880
Ref<DirAccess> sub_dir = DirAccess::open(from);
6881
ERR_FAIL_COND(sub_dir.is_null());
6882
6883
sub_dir->list_dir_begin();
6884
6885
String next_file = sub_dir->get_next();
6886
while (!next_file.is_empty()) {
6887
if (next_file == "." || next_file == "..") {
6888
next_file = sub_dir->get_next();
6889
continue;
6890
}
6891
6892
sub_files.push_back(from.path_join(next_file));
6893
next_file = sub_dir->get_next();
6894
}
6895
6896
if (!sub_files.is_empty()) {
6897
dir->make_dir(to);
6898
_add_dropped_files_recursive(sub_files, to);
6899
}
6900
6901
continue;
6902
}
6903
6904
dir->copy(from, to);
6905
}
6906
}
6907
6908
void EditorNode::_file_access_close_error_notify(const String &p_str) {
6909
callable_mp_static(&EditorNode::_file_access_close_error_notify_impl).call_deferred(p_str);
6910
}
6911
6912
void EditorNode::_file_access_close_error_notify_impl(const String &p_str) {
6913
add_io_error(vformat(TTR("Unable to write to file '%s', file in use, locked or lacking permissions."), p_str));
6914
}
6915
6916
// Recursive function to inform nodes that an array of nodes have had their scene reimported.
6917
// It will attempt to call a method named '_nodes_scene_reimported' on every node in the
6918
// tree so that editor scripts which create transient nodes will have the opportunity
6919
// to recreate them.
6920
void EditorNode::_notify_nodes_scene_reimported(Node *p_node, Array p_reimported_nodes) {
6921
Skeleton3D *skel_3d = Object::cast_to<Skeleton3D>(p_node);
6922
if (skel_3d) {
6923
skel_3d->reset_bone_poses();
6924
} else {
6925
BoneAttachment3D *attachment = Object::cast_to<BoneAttachment3D>(p_node);
6926
if (attachment) {
6927
attachment->notify_rebind_required();
6928
}
6929
}
6930
6931
if (p_node->has_method("_nodes_scene_reimported")) {
6932
p_node->call("_nodes_scene_reimported", p_reimported_nodes);
6933
}
6934
6935
for (int i = 0; i < p_node->get_child_count(); i++) {
6936
_notify_nodes_scene_reimported(p_node->get_child(i), p_reimported_nodes);
6937
}
6938
}
6939
6940
void EditorNode::reload_scene(const String &p_path) {
6941
int scene_idx = -1;
6942
6943
String lpath = ProjectSettings::get_singleton()->localize_path(p_path);
6944
6945
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
6946
if (editor_data.get_scene_path(i) == lpath) {
6947
scene_idx = i;
6948
break;
6949
}
6950
}
6951
6952
int current_tab = editor_data.get_edited_scene();
6953
6954
if (scene_idx == -1) {
6955
if (get_edited_scene()) {
6956
int current_history_id = editor_data.get_current_edited_scene_history_id();
6957
bool is_unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(current_history_id);
6958
6959
// Scene is not open, so at it might be instantiated. We'll refresh the whole scene later.
6960
EditorUndoRedoManager::get_singleton()->clear_history(current_history_id, false);
6961
if (is_unsaved) {
6962
EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(current_history_id);
6963
}
6964
}
6965
return;
6966
}
6967
6968
if (current_tab == scene_idx) {
6969
editor_data.apply_changes_in_editors();
6970
_save_editor_states(p_path);
6971
}
6972
6973
// Reload scene.
6974
_remove_scene(scene_idx, false);
6975
load_scene(p_path, true, false, true);
6976
6977
// Adjust index so tab is back a the previous position.
6978
editor_data.move_edited_scene_to_index(scene_idx);
6979
EditorUndoRedoManager::get_singleton()->clear_history(editor_data.get_scene_history_id(scene_idx), false);
6980
6981
// Recover the tab.
6982
scene_tabs->set_current_tab(current_tab);
6983
}
6984
6985
void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, HashSet<Node *> &p_instance_list) {
6986
String scene_file_path = p_node->get_scene_file_path();
6987
6988
bool valid_instance_found = false;
6989
6990
// Attempt to find all the instances matching path we're going to reload.
6991
if (p_node->get_scene_file_path() == p_instance_path) {
6992
valid_instance_found = true;
6993
} else {
6994
Node *current_node = p_node;
6995
6996
Ref<SceneState> inherited_state = current_node->get_scene_inherited_state();
6997
while (inherited_state.is_valid()) {
6998
String inherited_path = inherited_state->get_path();
6999
if (inherited_path == p_instance_path) {
7000
valid_instance_found = true;
7001
break;
7002
}
7003
7004
inherited_state = inherited_state->get_base_scene_state();
7005
}
7006
}
7007
7008
// Instead of adding this instance directly, if its not owned by the scene, walk its ancestors
7009
// and find the first node still owned by the scene. This is what we will reloading instead.
7010
if (valid_instance_found) {
7011
Node *current_node = p_node;
7012
while (true) {
7013
if (current_node->get_owner() == p_root || current_node->get_owner() == nullptr) {
7014
p_instance_list.insert(current_node);
7015
break;
7016
}
7017
current_node = current_node->get_parent();
7018
}
7019
}
7020
7021
for (int i = 0; i < p_node->get_child_count(); i++) {
7022
find_all_instances_inheriting_path_in_node(p_root, p_node->get_child(i), p_instance_path, p_instance_list);
7023
}
7024
}
7025
7026
void EditorNode::preload_reimporting_with_path_in_edited_scenes(const List<String> &p_scenes) {
7027
EditorProgress progress("preload_reimporting_scene", TTR("Preparing scenes for reload"), editor_data.get_edited_scene_count());
7028
7029
int original_edited_scene_idx = editor_data.get_edited_scene();
7030
7031
// Walk through each opened scene to get a global list of all instances which match
7032
// the current reimported scenes.
7033
for (int current_scene_idx = 0; current_scene_idx < editor_data.get_edited_scene_count(); current_scene_idx++) {
7034
progress.step(vformat(TTR("Analyzing scene %s"), editor_data.get_scene_title(current_scene_idx)), current_scene_idx);
7035
7036
Node *edited_scene_root = editor_data.get_edited_scene_root(current_scene_idx);
7037
7038
if (edited_scene_root) {
7039
SceneModificationsEntry scene_modifications;
7040
7041
for (const String &instance_path : p_scenes) {
7042
if (editor_data.get_scene_path(current_scene_idx) == instance_path) {
7043
continue;
7044
}
7045
7046
HashSet<Node *> instances_to_reimport;
7047
find_all_instances_inheriting_path_in_node(edited_scene_root, edited_scene_root, instance_path, instances_to_reimport);
7048
if (instances_to_reimport.size() > 0) {
7049
editor_data.set_edited_scene(current_scene_idx);
7050
7051
List<Node *> instance_list_with_children;
7052
for (Node *original_node : instances_to_reimport) {
7053
InstanceModificationsEntry instance_modifications;
7054
7055
// Fetching all the modified properties of the nodes reimported scene.
7056
get_preload_scene_modification_table(edited_scene_root, original_node, original_node, instance_modifications);
7057
7058
instance_modifications.original_node = original_node;
7059
instance_modifications.instance_path = instance_path;
7060
scene_modifications.instance_list.push_back(instance_modifications);
7061
7062
instance_list_with_children.push_back(original_node);
7063
get_children_nodes(original_node, instance_list_with_children);
7064
}
7065
7066
// Search the scene to find nodes that references the nodes will be recreated.
7067
get_preload_modifications_reference_to_nodes(edited_scene_root, edited_scene_root, instances_to_reimport, instance_list_with_children, scene_modifications.other_instances_modifications);
7068
}
7069
}
7070
7071
if (scene_modifications.instance_list.size() > 0) {
7072
scenes_modification_table[current_scene_idx] = scene_modifications;
7073
}
7074
}
7075
}
7076
7077
editor_data.set_edited_scene(original_edited_scene_idx);
7078
7079
progress.step(TTR("Preparation done."), editor_data.get_edited_scene_count());
7080
}
7081
7082
void EditorNode::reload_instances_with_path_in_edited_scenes() {
7083
if (scenes_modification_table.is_empty()) {
7084
return;
7085
}
7086
EditorProgress progress("reloading_scene", TTR("Scenes reloading"), editor_data.get_edited_scene_count());
7087
progress.step(TTR("Reloading..."), 0, true);
7088
7089
Error err;
7090
Array replaced_nodes;
7091
HashMap<String, Ref<PackedScene>> local_scene_cache;
7092
7093
// Reload the new instances.
7094
for (KeyValue<int, SceneModificationsEntry> &scene_modifications_elem : scenes_modification_table) {
7095
for (InstanceModificationsEntry instance_modifications : scene_modifications_elem.value.instance_list) {
7096
if (!local_scene_cache.has(instance_modifications.instance_path)) {
7097
Ref<PackedScene> instance_scene_packed_scene = ResourceLoader::load(instance_modifications.instance_path, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err);
7098
7099
ERR_FAIL_COND(err != OK);
7100
ERR_FAIL_COND(instance_scene_packed_scene.is_null());
7101
7102
local_scene_cache[instance_modifications.instance_path] = instance_scene_packed_scene;
7103
}
7104
}
7105
}
7106
7107
// Save the current scene state/selection in case of lost.
7108
Dictionary editor_state = _get_main_scene_state();
7109
editor_data.save_edited_scene_state(editor_selection, &editor_history, editor_state);
7110
editor_selection->clear();
7111
7112
int original_edited_scene_idx = editor_data.get_edited_scene();
7113
7114
for (KeyValue<int, SceneModificationsEntry> &scene_modifications_elem : scenes_modification_table) {
7115
// Set the current scene.
7116
int current_scene_idx = scene_modifications_elem.key;
7117
SceneModificationsEntry *scene_modifications = &scene_modifications_elem.value;
7118
7119
editor_data.set_edited_scene(current_scene_idx);
7120
Node *current_edited_scene = editor_data.get_edited_scene_root(current_scene_idx);
7121
7122
// Make sure the node is in the tree so that editor_selection can add node smoothly.
7123
if (original_edited_scene_idx != current_scene_idx) {
7124
// Prevent scene roots with the same name from being in the tree at the same time.
7125
Node *original_edited_scene_root = editor_data.get_edited_scene_root(original_edited_scene_idx);
7126
if (original_edited_scene_root && original_edited_scene_root->get_name() == current_edited_scene->get_name()) {
7127
scene_root->remove_child(original_edited_scene_root);
7128
}
7129
scene_root->add_child(current_edited_scene);
7130
}
7131
7132
// Restore the state so that the selection can be updated.
7133
editor_state = editor_data.restore_edited_scene_state(editor_selection, &editor_history);
7134
7135
int current_history_id = editor_data.get_current_edited_scene_history_id();
7136
bool is_unsaved = EditorUndoRedoManager::get_singleton()->is_history_unsaved(current_history_id);
7137
7138
// Clear the history for this affected tab.
7139
EditorUndoRedoManager::get_singleton()->clear_history(current_history_id, false);
7140
7141
// Update the version
7142
editor_data.is_scene_changed(current_scene_idx);
7143
7144
for (InstanceModificationsEntry instance_modifications : scene_modifications->instance_list) {
7145
Node *original_node = instance_modifications.original_node;
7146
String original_node_file_path = original_node->get_scene_file_path();
7147
Ref<PackedScene> instance_scene_packed_scene = local_scene_cache[instance_modifications.instance_path];
7148
7149
// Load a replacement scene for the node.
7150
Ref<PackedScene> current_packed_scene;
7151
Ref<PackedScene> base_packed_scene;
7152
if (original_node_file_path == instance_modifications.instance_path) {
7153
// If the node file name directly matches the scene we're replacing,
7154
// just load it since we already cached it.
7155
current_packed_scene = instance_scene_packed_scene;
7156
} else {
7157
// Otherwise, check the inheritance chain, reloading and caching any scenes
7158
// we require along the way.
7159
List<String> required_load_paths;
7160
7161
// Do we need to check if the paths are empty?
7162
if (!original_node_file_path.is_empty()) {
7163
required_load_paths.push_front(original_node_file_path);
7164
}
7165
Ref<SceneState> inherited_state = original_node->get_scene_inherited_state();
7166
while (inherited_state.is_valid()) {
7167
String inherited_path = inherited_state->get_path();
7168
// Do we need to check if the paths are empty?
7169
if (!inherited_path.is_empty()) {
7170
required_load_paths.push_front(inherited_path);
7171
}
7172
inherited_state = inherited_state->get_base_scene_state();
7173
}
7174
7175
// Ensure the inheritance chain is loaded in the correct order so that cache can
7176
// be properly updated.
7177
for (String path : required_load_paths) {
7178
if (current_packed_scene.is_valid()) {
7179
base_packed_scene = current_packed_scene;
7180
}
7181
if (!local_scene_cache.find(path)) {
7182
current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err);
7183
local_scene_cache[path] = current_packed_scene;
7184
} else {
7185
current_packed_scene = local_scene_cache[path];
7186
}
7187
}
7188
}
7189
7190
ERR_FAIL_COND(current_packed_scene.is_null());
7191
7192
// Instantiate early so that caches cleared on load in SceneState can be rebuilt early.
7193
Node *instantiated_node = nullptr;
7194
7195
// If we are in a inherited scene, it's easier to create a new base scene and
7196
// grab the node from there.
7197
// When scene_path_to_node is '.' and we have scene_inherited_state, it's because
7198
// it's a multi-level inheritance scene. We should use
7199
NodePath scene_path_to_node = current_edited_scene->get_path_to(original_node);
7200
Ref<SceneState> scene_state = current_edited_scene->get_scene_inherited_state();
7201
if (String(scene_path_to_node) != "." && scene_state.is_valid() && scene_state->get_path() != instance_modifications.instance_path && scene_state->find_node_by_path(scene_path_to_node) >= 0) {
7202
Node *root_node = scene_state->instantiate(SceneState::GenEditState::GEN_EDIT_STATE_INSTANCE);
7203
instantiated_node = root_node->get_node(scene_path_to_node);
7204
7205
if (instantiated_node) {
7206
if (instantiated_node->get_parent()) {
7207
// Remove from the root so we can delete it from memory.
7208
instantiated_node->get_parent()->remove_child(instantiated_node);
7209
// No need of the additional children that could have been added to the node
7210
// in the base scene. That will be managed by the 'addition_list' later.
7211
_remove_all_not_owned_children(instantiated_node, instantiated_node);
7212
memdelete(root_node);
7213
}
7214
} else {
7215
// Should not happen because we checked with find_node_by_path before, just in case.
7216
memdelete(root_node);
7217
}
7218
}
7219
7220
if (!instantiated_node) {
7221
// If no base scene was found to create the node, we will use the reimported packed scene directly.
7222
// But, when the current edited scene is the reimported scene, it's because it's an inherited scene
7223
// derived from the reimported scene. In that case, we will not instantiate current_packed_scene, because
7224
// we would reinstantiate ourself. Using the base scene is better.
7225
if (current_edited_scene == original_node) {
7226
if (base_packed_scene.is_valid()) {
7227
instantiated_node = base_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
7228
} else {
7229
instantiated_node = instance_scene_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
7230
}
7231
} else {
7232
instantiated_node = current_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
7233
}
7234
}
7235
ERR_FAIL_NULL(instantiated_node);
7236
7237
// Disconnect all relevant connections, all connections from and persistent connections to.
7238
for (const KeyValue<NodePath, ModificationNodeEntry> &modification_table_entry : instance_modifications.modifications) {
7239
for (Connection conn : modification_table_entry.value.connections_from) {
7240
conn.signal.get_object()->disconnect(conn.signal.get_name(), conn.callable);
7241
}
7242
for (ConnectionWithNodePath cwnp : modification_table_entry.value.connections_to) {
7243
Connection conn = cwnp.connection;
7244
if (conn.flags & CONNECT_PERSIST) {
7245
conn.signal.get_object()->disconnect(conn.signal.get_name(), conn.callable);
7246
}
7247
}
7248
}
7249
7250
// Store all the paths for any selected nodes which are ancestors of the node we're replacing.
7251
List<NodePath> selected_node_paths;
7252
for (Node *selected_node : editor_selection->get_top_selected_node_list()) {
7253
if (selected_node == original_node || original_node->is_ancestor_of(selected_node)) {
7254
selected_node_paths.push_back(original_node->get_path_to(selected_node));
7255
editor_selection->remove_node(selected_node);
7256
}
7257
}
7258
7259
// Remove all nodes which were added as additional elements (they will be restored later).
7260
for (AdditiveNodeEntry additive_node_entry : instance_modifications.addition_list) {
7261
Node *addition_node = additive_node_entry.node;
7262
addition_node->get_parent()->remove_child(addition_node);
7263
}
7264
7265
// Clear ownership of the nodes (kind of hack to workaround an issue with
7266
// replace_by when called on nodes in other tabs).
7267
List<Node *> nodes_owned_by_original_node;
7268
original_node->get_owned_by(original_node, &nodes_owned_by_original_node);
7269
for (Node *owned_node : nodes_owned_by_original_node) {
7270
owned_node->set_owner(nullptr);
7271
}
7272
7273
// Replace the old nodes in the history with the new ones.
7274
// Otherwise, the history will contain old nodes, and some could still be
7275
// instantiated if used elsewhere, causing the "current edited item" to be
7276
// linked to a node that will be destroyed later. This caused the editor to
7277
// crash when reimporting scenes with animations when "Editable children" was enabled.
7278
replace_history_reimported_nodes(original_node, instantiated_node, original_node);
7279
7280
// Reset the editable instance state.
7281
HashMap<NodePath, SceneEditorDataEntry> scene_editor_data_table;
7282
Node *owner = original_node->get_owner();
7283
if (!owner) {
7284
owner = original_node;
7285
}
7286
7287
get_scene_editor_data_for_node(owner, original_node, scene_editor_data_table);
7288
7289
// The current node being reloaded may also be an additional node for another node
7290
// that is in the process of being reloaded.
7291
// Replacing the additional node with the new one prevents a crash where nodes
7292
// in 'addition_list' are removed from the scene tree and queued for deletion.
7293
for (InstanceModificationsEntry &im : scene_modifications->instance_list) {
7294
for (AdditiveNodeEntry &additive_node_entry : im.addition_list) {
7295
if (additive_node_entry.node == original_node) {
7296
additive_node_entry.node = instantiated_node;
7297
}
7298
}
7299
}
7300
7301
bool original_node_scene_instance_load_placeholder = original_node->get_scene_instance_load_placeholder();
7302
7303
// Delete all the remaining node children.
7304
while (original_node->get_child_count()) {
7305
Node *child = original_node->get_child(0);
7306
7307
original_node->remove_child(child);
7308
child->queue_free();
7309
}
7310
7311
// Update the name to match
7312
instantiated_node->set_name(original_node->get_name());
7313
7314
// Is this replacing the edited root node?
7315
7316
if (current_edited_scene == original_node) {
7317
// Set the instance as un inherited scene of itself.
7318
instantiated_node->set_scene_inherited_state(instantiated_node->get_scene_instance_state());
7319
instantiated_node->set_scene_instance_state(nullptr);
7320
instantiated_node->set_scene_file_path(original_node_file_path);
7321
current_edited_scene = instantiated_node;
7322
editor_data.set_edited_scene_root(current_edited_scene);
7323
7324
if (original_edited_scene_idx == current_scene_idx) {
7325
// How that the editor executes a redraw while destroying or progressing the EditorProgress,
7326
// it crashes when the root scene has been replaced because the edited scene
7327
// was freed and no longer in the scene tree.
7328
SceneTreeDock::get_singleton()->set_edited_scene(current_edited_scene);
7329
if (get_tree()) {
7330
get_tree()->set_edited_scene_root(current_edited_scene);
7331
}
7332
}
7333
}
7334
7335
// Replace the original node with the instantiated version.
7336
original_node->replace_by(instantiated_node, false);
7337
7338
// Mark the old node for deletion.
7339
original_node->queue_free();
7340
7341
// Restore the placeholder state from the original node.
7342
instantiated_node->set_scene_instance_load_placeholder(original_node_scene_instance_load_placeholder);
7343
7344
// Attempt to re-add all the additional nodes.
7345
for (AdditiveNodeEntry additive_node_entry : instance_modifications.addition_list) {
7346
Node *parent_node = instantiated_node->get_node_or_null(additive_node_entry.parent);
7347
7348
if (!parent_node) {
7349
parent_node = current_edited_scene;
7350
}
7351
7352
parent_node->add_child(additive_node_entry.node);
7353
parent_node->move_child(additive_node_entry.node, additive_node_entry.index);
7354
// If the additive node's owner was the node which got replaced, update it.
7355
if (additive_node_entry.owner == original_node) {
7356
additive_node_entry.owner = instantiated_node;
7357
}
7358
7359
additive_node_entry.node->set_owner(additive_node_entry.owner);
7360
7361
// If the parent node was lost, attempt to restore the original global transform.
7362
{
7363
Node2D *node_2d = Object::cast_to<Node2D>(additive_node_entry.node);
7364
if (node_2d) {
7365
node_2d->set_transform(additive_node_entry.transform_2d);
7366
}
7367
7368
Node3D *node_3d = Object::cast_to<Node3D>(additive_node_entry.node);
7369
if (node_3d) {
7370
node_3d->set_transform(additive_node_entry.transform_3d);
7371
}
7372
}
7373
}
7374
7375
// Restore the scene's editable instance and folded states.
7376
for (HashMap<NodePath, SceneEditorDataEntry>::Iterator I = scene_editor_data_table.begin(); I; ++I) {
7377
Node *node = owner->get_node_or_null(I->key);
7378
if (node) {
7379
if (owner != node) {
7380
owner->set_editable_instance(node, I->value.is_editable);
7381
}
7382
node->set_display_folded(I->value.is_display_folded);
7383
}
7384
}
7385
7386
// Restore the selection.
7387
if (selected_node_paths.size()) {
7388
for (NodePath selected_node_path : selected_node_paths) {
7389
Node *selected_node = instantiated_node->get_node_or_null(selected_node_path);
7390
if (selected_node) {
7391
editor_selection->add_node(selected_node);
7392
}
7393
}
7394
editor_selection->update();
7395
}
7396
7397
// Attempt to restore the modified properties and signals for the instantitated node and all its owned children.
7398
for (KeyValue<NodePath, ModificationNodeEntry> &E : instance_modifications.modifications) {
7399
NodePath new_current_path = E.key;
7400
Node *modifiable_node = instantiated_node->get_node_or_null(new_current_path);
7401
7402
update_node_from_node_modification_entry(modifiable_node, E.value);
7403
}
7404
// Add the newly instantiated node to the edited scene's replaced node list.
7405
replaced_nodes.push_back(instantiated_node);
7406
}
7407
7408
// Attempt to restore the modified properties and signals for the instantitated node and all its owned children.
7409
for (KeyValue<NodePath, ModificationNodeEntry> &E : scene_modifications->other_instances_modifications) {
7410
NodePath new_current_path = E.key;
7411
Node *modifiable_node = current_edited_scene->get_node_or_null(new_current_path);
7412
7413
if (modifiable_node) {
7414
update_node_from_node_modification_entry(modifiable_node, E.value);
7415
}
7416
}
7417
7418
if (is_unsaved) {
7419
EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(current_history_id);
7420
}
7421
7422
// Save the current handled scene state.
7423
editor_data.save_edited_scene_state(editor_selection, &editor_history, editor_state);
7424
editor_selection->clear();
7425
7426
// Cleanup the history of the changes.
7427
editor_history.cleanup_history();
7428
7429
if (original_edited_scene_idx != current_scene_idx) {
7430
scene_root->remove_child(current_edited_scene);
7431
7432
// Ensure the current edited scene is re-added if removed earlier because it has the same name
7433
// as the reimported scene. The editor could crash when reloading SceneTreeDock if the current
7434
// edited scene is not in the scene tree.
7435
Node *original_edited_scene_root = editor_data.get_edited_scene_root(original_edited_scene_idx);
7436
if (original_edited_scene_root && !original_edited_scene_root->get_parent()) {
7437
scene_root->add_child(original_edited_scene_root);
7438
}
7439
}
7440
}
7441
7442
// For the whole editor, call the _notify_nodes_scene_reimported with a list of replaced nodes.
7443
// To inform anything that depends on them that they should update as appropriate.
7444
_notify_nodes_scene_reimported(this, replaced_nodes);
7445
7446
editor_data.set_edited_scene(original_edited_scene_idx);
7447
7448
editor_data.restore_edited_scene_state(editor_selection, &editor_history);
7449
7450
progress.step(TTR("Reloading done."), editor_data.get_edited_scene_count());
7451
}
7452
7453
void EditorNode::_remove_all_not_owned_children(Node *p_node, Node *p_owner) {
7454
Vector<Node *> nodes_to_remove;
7455
if (p_node != p_owner && p_node->get_owner() != p_owner) {
7456
nodes_to_remove.push_back(p_node);
7457
}
7458
for (int i = 0; i < p_node->get_child_count(); i++) {
7459
Node *child_node = p_node->get_child(i);
7460
_remove_all_not_owned_children(child_node, p_owner);
7461
}
7462
7463
for (Node *node : nodes_to_remove) {
7464
node->get_parent()->remove_child(node);
7465
node->queue_free();
7466
}
7467
}
7468
7469
int EditorNode::plugin_init_callback_count = 0;
7470
7471
void EditorNode::add_plugin_init_callback(EditorPluginInitializeCallback p_callback) {
7472
ERR_FAIL_COND(plugin_init_callback_count == MAX_INIT_CALLBACKS);
7473
7474
plugin_init_callbacks[plugin_init_callback_count++] = p_callback;
7475
}
7476
7477
EditorPluginInitializeCallback EditorNode::plugin_init_callbacks[EditorNode::MAX_INIT_CALLBACKS];
7478
7479
int EditorNode::build_callback_count = 0;
7480
7481
void EditorNode::add_build_callback(EditorBuildCallback p_callback) {
7482
ERR_FAIL_COND(build_callback_count == MAX_INIT_CALLBACKS);
7483
7484
build_callbacks[build_callback_count++] = p_callback;
7485
}
7486
7487
EditorBuildCallback EditorNode::build_callbacks[EditorNode::MAX_BUILD_CALLBACKS];
7488
7489
bool EditorNode::call_build() {
7490
bool builds_successful = true;
7491
7492
for (int i = 0; i < build_callback_count && builds_successful; i++) {
7493
if (!build_callbacks[i]()) {
7494
ERR_PRINT("A Godot Engine build callback failed.");
7495
builds_successful = false;
7496
}
7497
}
7498
7499
if (builds_successful && !editor_data.call_build()) {
7500
ERR_PRINT("An EditorPlugin build callback failed.");
7501
builds_successful = false;
7502
}
7503
7504
return builds_successful;
7505
}
7506
7507
void EditorNode::call_run_scene(const String &p_scene, Vector<String> &r_args) {
7508
for (int i = 0; i < editor_data.get_editor_plugin_count(); i++) {
7509
EditorPlugin *plugin = editor_data.get_editor_plugin(i);
7510
plugin->run_scene(p_scene, r_args);
7511
}
7512
}
7513
7514
void EditorNode::_inherit_imported(const String &p_action) {
7515
open_imported->hide();
7516
load_scene(open_import_request, true, true);
7517
}
7518
7519
void EditorNode::_open_imported() {
7520
load_scene(open_import_request, true, false, true);
7521
}
7522
7523
void EditorNode::dim_editor(bool p_dimming) {
7524
dimmed = p_dimming;
7525
gui_base->set_modulate(p_dimming ? Color(0.5, 0.5, 0.5) : Color(1, 1, 1));
7526
}
7527
7528
bool EditorNode::is_editor_dimmed() const {
7529
return dimmed;
7530
}
7531
7532
void EditorNode::open_export_template_manager() {
7533
export_template_manager->popup_manager();
7534
}
7535
7536
void EditorNode::add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin) {
7537
resource_conversion_plugins.push_back(p_plugin);
7538
}
7539
7540
void EditorNode::remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin) {
7541
resource_conversion_plugins.erase(p_plugin);
7542
}
7543
7544
Vector<Ref<EditorResourceConversionPlugin>> EditorNode::find_resource_conversion_plugin_for_resource(const Ref<Resource> &p_for_resource) {
7545
if (p_for_resource.is_null()) {
7546
return Vector<Ref<EditorResourceConversionPlugin>>();
7547
}
7548
7549
Vector<Ref<EditorResourceConversionPlugin>> ret;
7550
for (Ref<EditorResourceConversionPlugin> resource_conversion_plugin : resource_conversion_plugins) {
7551
if (resource_conversion_plugin.is_valid() && resource_conversion_plugin->handles(p_for_resource)) {
7552
ret.push_back(resource_conversion_plugin);
7553
}
7554
}
7555
7556
return ret;
7557
}
7558
7559
Vector<Ref<EditorResourceConversionPlugin>> EditorNode::find_resource_conversion_plugin_for_type_name(const String &p_type) {
7560
Vector<Ref<EditorResourceConversionPlugin>> ret;
7561
7562
if (ClassDB::class_exists(p_type) && ClassDB::can_instantiate(p_type)) {
7563
Ref<Resource> temp = Object::cast_to<Resource>(ClassDB::instantiate(p_type));
7564
if (temp.is_valid()) {
7565
for (Ref<EditorResourceConversionPlugin> resource_conversion_plugin : resource_conversion_plugins) {
7566
if (resource_conversion_plugin.is_valid() && resource_conversion_plugin->handles(temp)) {
7567
ret.push_back(resource_conversion_plugin);
7568
}
7569
}
7570
}
7571
}
7572
7573
return ret;
7574
}
7575
7576
void EditorNode::_update_renderer_color() {
7577
String rendering_method = renderer->get_selected_metadata();
7578
7579
const Color renderer_normal_color = theme->get_color(rendering_method + "_color", EditorStringName(Editor));
7580
const Color mono_color = theme->get_color(SNAME("mono_color"), EditorStringName(Editor));
7581
7582
renderer->add_theme_color_override(SceneStringName(font_color), renderer_normal_color);
7583
renderer->add_theme_color_override("font_hover_color", renderer_normal_color.lerp(mono_color, 0.3));
7584
renderer->add_theme_color_override("font_pressed_color", renderer_normal_color.lerp(mono_color, 0.4));
7585
renderer->add_theme_color_override("font_hover_pressed_color", renderer_normal_color.lerp(mono_color, 0.5));
7586
}
7587
7588
void EditorNode::_renderer_selected(int p_index) {
7589
const String rendering_method = renderer->get_item_metadata(p_index);
7590
const String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method");
7591
if (rendering_method == current_renderer) {
7592
return;
7593
}
7594
7595
// Don't change selection.
7596
for (int i = 0; i < renderer->get_item_count(); i++) {
7597
if (renderer->get_item_metadata(i) == current_renderer) {
7598
renderer->select(i);
7599
break;
7600
}
7601
}
7602
7603
if (video_restart_dialog == nullptr) {
7604
video_restart_dialog = memnew(ConfirmationDialog);
7605
video_restart_dialog->set_ok_button_text(TTRC("Save & Restart"));
7606
video_restart_dialog->get_label()->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
7607
gui_base->add_child(video_restart_dialog);
7608
} else {
7609
video_restart_dialog->disconnect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_set_renderer_name_save_and_restart));
7610
}
7611
7612
const String mobile_rendering_method = rendering_method == "forward_plus" ? "mobile" : rendering_method;
7613
const String web_rendering_method = "gl_compatibility";
7614
video_restart_dialog->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_set_renderer_name_save_and_restart).bind(rendering_method));
7615
video_restart_dialog->set_text(
7616
vformat(TTR("Changing the renderer requires restarting the editor.\n\nChoosing Save & Restart will change the renderer to:\n- Desktop platforms: %s\n- Mobile platforms: %s\n- Web platform: %s"),
7617
_to_rendering_method_display_name(rendering_method), _to_rendering_method_display_name(mobile_rendering_method), _to_rendering_method_display_name(web_rendering_method)));
7618
video_restart_dialog->popup_centered();
7619
7620
_update_renderer_color();
7621
}
7622
7623
String EditorNode::_to_rendering_method_display_name(const String &p_rendering_method) const {
7624
if (p_rendering_method == "forward_plus") {
7625
return TTR("Forward+");
7626
}
7627
if (p_rendering_method == "mobile") {
7628
return TTR("Mobile");
7629
}
7630
if (p_rendering_method == "gl_compatibility") {
7631
return TTR("Compatibility");
7632
}
7633
return p_rendering_method;
7634
}
7635
7636
void EditorNode::_set_renderer_name_save_and_restart(const String &p_rendering_method) {
7637
ProjectSettings::get_singleton()->set("rendering/renderer/rendering_method", p_rendering_method);
7638
7639
if (p_rendering_method == "mobile" || p_rendering_method == "gl_compatibility") {
7640
// Also change the mobile override if changing to a compatible renderer.
7641
// This prevents visual discrepancies between desktop and mobile platforms.
7642
ProjectSettings::get_singleton()->set("rendering/renderer/rendering_method.mobile", p_rendering_method);
7643
} else if (p_rendering_method == "forward_plus") {
7644
// Use the equivalent mobile renderer. This prevents the renderer from staying
7645
// on its old choice if moving from `gl_compatibility` to `forward_plus`.
7646
ProjectSettings::get_singleton()->set("rendering/renderer/rendering_method.mobile", "mobile");
7647
}
7648
7649
ProjectSettings::get_singleton()->save();
7650
7651
save_all_scenes();
7652
restart_editor();
7653
}
7654
7655
void EditorNode::_resource_saved(Ref<Resource> p_resource, const String &p_path) {
7656
if (singleton->saving_resources_in_path.has(p_resource)) {
7657
// This is going to be handled by save_resource_in_path when the time is right.
7658
return;
7659
}
7660
7661
if (EditorFileSystem::get_singleton()) {
7662
EditorFileSystem::get_singleton()->update_file(p_path);
7663
}
7664
7665
singleton->editor_folding.save_resource_folding(p_resource, p_path);
7666
}
7667
7668
void EditorNode::_resource_loaded(Ref<Resource> p_resource, const String &p_path) {
7669
singleton->editor_folding.load_resource_folding(p_resource, p_path);
7670
}
7671
7672
void EditorNode::_feature_profile_changed() {
7673
Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile();
7674
if (profile.is_valid()) {
7675
editor_dock_manager->set_dock_enabled(SignalsDock::get_singleton(), !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SIGNALS_DOCK));
7676
editor_dock_manager->set_dock_enabled(GroupsDock::get_singleton(), !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_GROUPS_DOCK));
7677
// The Import dock is useless without the FileSystem dock. Ensure the configuration is valid.
7678
bool fs_dock_disabled = profile->is_feature_disabled(EditorFeatureProfile::FEATURE_FILESYSTEM_DOCK);
7679
editor_dock_manager->set_dock_enabled(FileSystemDock::get_singleton(), !fs_dock_disabled);
7680
editor_dock_manager->set_dock_enabled(ImportDock::get_singleton(), !fs_dock_disabled && !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_IMPORT_DOCK));
7681
editor_dock_manager->set_dock_enabled(history_dock, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_HISTORY_DOCK));
7682
7683
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_3D, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D));
7684
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_SCRIPT, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT));
7685
if (!Engine::get_singleton()->is_recovery_mode_hint()) {
7686
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_GAME, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_GAME));
7687
}
7688
if (AssetLibraryEditorPlugin::is_available()) {
7689
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_ASSETLIB, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB));
7690
}
7691
} else {
7692
editor_dock_manager->set_dock_enabled(ImportDock::get_singleton(), true);
7693
editor_dock_manager->set_dock_enabled(SignalsDock::get_singleton(), true);
7694
editor_dock_manager->set_dock_enabled(GroupsDock::get_singleton(), true);
7695
editor_dock_manager->set_dock_enabled(FileSystemDock::get_singleton(), true);
7696
editor_dock_manager->set_dock_enabled(history_dock, true);
7697
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_3D, true);
7698
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_SCRIPT, true);
7699
if (!Engine::get_singleton()->is_recovery_mode_hint()) {
7700
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_GAME, true);
7701
}
7702
if (AssetLibraryEditorPlugin::is_available()) {
7703
editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_ASSETLIB, true);
7704
}
7705
}
7706
}
7707
7708
void EditorNode::_bind_methods() {
7709
ClassDB::bind_method(D_METHOD("push_item", "object", "property", "inspector_only"), &EditorNode::push_item, DEFVAL(""), DEFVAL(false));
7710
7711
ClassDB::bind_method("set_edited_scene", &EditorNode::set_edited_scene);
7712
7713
ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process);
7714
7715
ClassDB::bind_method(D_METHOD("update_node_reference", "value", "node", "remove"), &EditorNode::update_node_reference, DEFVAL(false));
7716
7717
ADD_SIGNAL(MethodInfo("request_help_search"));
7718
ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "args")));
7719
ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj")));
7720
ADD_SIGNAL(MethodInfo("scene_saved", PropertyInfo(Variant::STRING, "path")));
7721
ADD_SIGNAL(MethodInfo("scene_changed"));
7722
ADD_SIGNAL(MethodInfo("scene_closed", PropertyInfo(Variant::STRING, "path")));
7723
ADD_SIGNAL(MethodInfo("preview_locale_changed"));
7724
ADD_SIGNAL(MethodInfo("resource_counter_changed"));
7725
}
7726
7727
static Node *_resource_get_edited_scene() {
7728
return EditorNode::get_singleton()->get_edited_scene();
7729
}
7730
7731
void EditorNode::_print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich) {
7732
if (!Thread::is_main_thread()) {
7733
callable_mp_static(&EditorNode::_print_handler_impl).call_deferred(p_string, p_error, p_rich);
7734
} else {
7735
_print_handler_impl(p_string, p_error, p_rich);
7736
}
7737
}
7738
7739
void EditorNode::_print_handler_impl(const String &p_string, bool p_error, bool p_rich) {
7740
if (!singleton) {
7741
return;
7742
}
7743
if (p_error) {
7744
singleton->log->add_message(p_string, EditorLog::MSG_TYPE_ERROR);
7745
} else if (p_rich) {
7746
singleton->log->add_message(p_string, EditorLog::MSG_TYPE_STD_RICH);
7747
} else {
7748
singleton->log->add_message(p_string, EditorLog::MSG_TYPE_STD);
7749
}
7750
}
7751
7752
static void _execute_thread(void *p_ud) {
7753
EditorNode::ExecuteThreadArgs *eta = (EditorNode::ExecuteThreadArgs *)p_ud;
7754
Error err = OS::get_singleton()->execute(eta->path, eta->args, &eta->output, &eta->exitcode, true, &eta->execute_output_mutex);
7755
print_verbose("Thread exit status: " + itos(eta->exitcode));
7756
if (err != OK) {
7757
eta->exitcode = err;
7758
}
7759
7760
eta->done.set();
7761
}
7762
7763
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, String *r_output) {
7764
if (execute_output_dialog) {
7765
execute_output_dialog->set_title(p_title);
7766
execute_output_dialog->get_ok_button()->set_disabled(true);
7767
execute_outputs->clear();
7768
execute_outputs->set_scroll_follow(true);
7769
EditorInterface::get_singleton()->popup_dialog_centered_ratio(execute_output_dialog);
7770
}
7771
7772
ExecuteThreadArgs eta;
7773
eta.path = p_path;
7774
eta.args = p_arguments;
7775
eta.exitcode = 255;
7776
7777
int prev_len = 0;
7778
7779
eta.execute_output_thread.start(_execute_thread, &eta);
7780
7781
while (!eta.done.is_set()) {
7782
{
7783
MutexLock lock(eta.execute_output_mutex);
7784
if (prev_len != eta.output.length()) {
7785
String to_add = eta.output.substr(prev_len);
7786
prev_len = eta.output.length();
7787
execute_outputs->add_text(to_add);
7788
DisplayServer::get_singleton()->process_events(); // Get rid of pending events.
7789
Main::iteration();
7790
}
7791
}
7792
OS::get_singleton()->delay_usec(1000);
7793
}
7794
7795
eta.execute_output_thread.wait_to_finish();
7796
execute_outputs->add_text("\nExit Code: " + itos(eta.exitcode));
7797
7798
if (execute_output_dialog) {
7799
if (p_close_on_errors && eta.exitcode != 0) {
7800
execute_output_dialog->hide();
7801
}
7802
if (p_close_on_ok && eta.exitcode == 0) {
7803
execute_output_dialog->hide();
7804
}
7805
7806
execute_output_dialog->get_ok_button()->set_disabled(false);
7807
}
7808
7809
if (r_output) {
7810
*r_output = eta.output;
7811
}
7812
return eta.exitcode;
7813
}
7814
7815
void EditorNode::set_unfocused_low_processor_usage_mode_enabled(bool p_enabled) {
7816
unfocused_low_processor_usage_mode_enabled = p_enabled;
7817
}
7818
7819
void EditorNode::_build_file_menu() {
7820
if (!file_menu) {
7821
return;
7822
}
7823
file_menu->clear(false);
7824
7825
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/new_scene"), SCENE_NEW_SCENE);
7826
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/new_inherited_scene"), SCENE_NEW_INHERITED_SCENE);
7827
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/open_scene"), SCENE_OPEN_SCENE);
7828
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/reopen_closed_scene"), SCENE_OPEN_PREV);
7829
if (!recent_scenes) {
7830
recent_scenes = memnew(PopupMenu);
7831
recent_scenes->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
7832
recent_scenes->connect(SceneStringName(id_pressed), callable_mp(this, &EditorNode::_open_recent_scene));
7833
}
7834
file_menu->add_submenu_node_item(TTRC("Open Recent"), recent_scenes, SCENE_OPEN_RECENT);
7835
file_menu->add_separator();
7836
7837
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene"), SCENE_SAVE_SCENE);
7838
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene_as"), SCENE_SAVE_AS_SCENE);
7839
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), SCENE_SAVE_ALL_SCENES);
7840
file_menu->add_separator();
7841
7842
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/quick_open"), SCENE_QUICK_OPEN);
7843
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/quick_open_scene"), SCENE_QUICK_OPEN_SCENE);
7844
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/quick_open_script"), SCENE_QUICK_OPEN_SCRIPT);
7845
file_menu->add_separator();
7846
7847
if (!export_as_menu) {
7848
export_as_menu = memnew(PopupMenu);
7849
export_as_menu->add_shortcut(ED_GET_SHORTCUT("editor/export_as_mesh_library"), FILE_EXPORT_MESH_LIBRARY);
7850
export_as_menu->connect("index_pressed", callable_mp(this, &EditorNode::_export_as_menu_option));
7851
}
7852
file_menu->add_submenu_node_item(TTRC("Export As..."), export_as_menu, SCENE_EXPORT_AS);
7853
file_menu->add_separator();
7854
7855
file_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), SCENE_UNDO, false, true);
7856
file_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), SCENE_REDO, false, true);
7857
file_menu->add_separator();
7858
7859
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/reload_saved_scene"), SCENE_RELOAD_SAVED_SCENE);
7860
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/close_scene"), SCENE_CLOSE);
7861
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/close_all_scenes"), SCENE_CLOSE_ALL);
7862
#ifdef MACOS_ENABLED
7863
if (menu_type != MENU_TYPE_GLOBAL) {
7864
// On macOS "Quit" option is in the "app" menu.
7865
file_menu->add_separator();
7866
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/file_quit"), SCENE_QUIT, true);
7867
}
7868
#else
7869
file_menu->add_separator();
7870
file_menu->add_shortcut(ED_GET_SHORTCUT("editor/file_quit"), SCENE_QUIT, true);
7871
#endif
7872
}
7873
7874
void EditorNode::_build_project_menu() {
7875
if (!project_menu) {
7876
return;
7877
}
7878
project_menu->clear(false);
7879
7880
project_menu->add_shortcut(ED_GET_SHORTCUT("editor/project_settings"), PROJECT_OPEN_SETTINGS);
7881
project_menu->add_shortcut(ED_GET_SHORTCUT("editor/find_in_files"), PROJECT_FIND_IN_FILES);
7882
project_menu->add_separator();
7883
7884
project_menu->add_item(TTRC("Version Control"), PROJECT_VERSION_CONTROL);
7885
if (!vcs_actions_menu) {
7886
vcs_actions_menu = VersionControlEditorPlugin::get_singleton()->get_version_control_actions_panel();
7887
vcs_actions_menu->connect("index_pressed", callable_mp(this, &EditorNode::_version_control_menu_option));
7888
vcs_actions_menu->add_item(TTRC("Create/Override Version Control Metadata..."), VCS_METADATA);
7889
vcs_actions_menu->add_item(TTRC("Version Control Settings..."), VCS_SETTINGS);
7890
}
7891
project_menu->set_item_submenu_node(project_menu->get_item_index(PROJECT_VERSION_CONTROL), vcs_actions_menu);
7892
7893
project_menu->add_separator();
7894
project_menu->add_shortcut(ED_GET_SHORTCUT("editor/export"), PROJECT_EXPORT);
7895
project_menu->add_item(TTRC("Pack Project as ZIP..."), PROJECT_PACK_AS_ZIP);
7896
project_menu->add_item(TTRC("Install Android Build Template..."), PROJECT_INSTALL_ANDROID_SOURCE);
7897
#ifndef ANDROID_ENABLED
7898
project_menu->add_item(TTRC("Open User Data Folder"), PROJECT_OPEN_USER_DATA_FOLDER);
7899
#endif
7900
project_menu->add_separator();
7901
7902
if (!tool_menu) {
7903
tool_menu = memnew(PopupMenu);
7904
tool_menu->connect("index_pressed", callable_mp(this, &EditorNode::_tool_menu_option));
7905
tool_menu->add_shortcut(ED_GET_SHORTCUT("editor/orphan_resource_explorer"), TOOLS_ORPHAN_RESOURCES);
7906
tool_menu->add_shortcut(ED_GET_SHORTCUT("editor/engine_compilation_configuration_editor"), TOOLS_BUILD_PROFILE_MANAGER);
7907
tool_menu->add_shortcut(ED_GET_SHORTCUT("editor/upgrade_project"), TOOLS_PROJECT_UPGRADE);
7908
}
7909
project_menu->add_submenu_node_item(TTRC("Tools"), tool_menu);
7910
7911
project_menu->add_separator();
7912
project_menu->add_shortcut(ED_GET_SHORTCUT("editor/reload_current_project"), PROJECT_RELOAD_CURRENT_PROJECT);
7913
project_menu->add_shortcut(ED_GET_SHORTCUT("editor/quit_to_project_list"), PROJECT_QUIT_TO_PROJECT_MANAGER, true);
7914
}
7915
7916
void EditorNode::_build_settings_menu() {
7917
if (!settings_menu) {
7918
return;
7919
}
7920
settings_menu->clear(false);
7921
7922
#ifdef MACOS_ENABLED
7923
if (menu_type != MENU_TYPE_GLOBAL) {
7924
// On macOS "Settings" option is in the "app" menu.
7925
settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/editor_settings"), EDITOR_OPEN_SETTINGS);
7926
}
7927
#else
7928
settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/editor_settings"), EDITOR_OPEN_SETTINGS);
7929
#endif
7930
settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/command_palette"), EDITOR_COMMAND_PALETTE);
7931
settings_menu->add_separator();
7932
7933
settings_menu->add_submenu_node_item(TTRC("Editor Docks"), editor_dock_manager->get_docks_menu());
7934
7935
if (!editor_layouts) {
7936
editor_layouts = memnew(PopupMenu);
7937
editor_layouts->connect(SceneStringName(id_pressed), callable_mp(this, &EditorNode::_layout_menu_option));
7938
}
7939
settings_menu->add_submenu_node_item(TTRC("Editor Layout"), editor_layouts);
7940
settings_menu->add_separator();
7941
7942
settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/take_screenshot"), EDITOR_TAKE_SCREENSHOT);
7943
settings_menu->set_item_tooltip(-1, TTRC("Screenshots are stored in the user data folder (\"user://\")."));
7944
7945
settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/fullscreen_mode"), EDITOR_TOGGLE_FULLSCREEN);
7946
settings_menu->add_separator();
7947
7948
#ifndef ANDROID_ENABLED
7949
if (EditorPaths::get_singleton()->get_data_dir() == EditorPaths::get_singleton()->get_config_dir()) {
7950
// Configuration and data folders are located in the same place.
7951
settings_menu->add_item(TTRC("Open Editor Data/Settings Folder"), EDITOR_OPEN_DATA_FOLDER);
7952
} else {
7953
// Separate configuration and data folders.
7954
settings_menu->add_item(TTRC("Open Editor Data Folder"), EDITOR_OPEN_DATA_FOLDER);
7955
settings_menu->add_item(TTRC("Open Editor Settings Folder"), EDITOR_OPEN_CONFIG_FOLDER);
7956
}
7957
settings_menu->add_separator();
7958
#endif
7959
7960
settings_menu->add_item(TTRC("Manage Editor Features..."), EDITOR_MANAGE_FEATURE_PROFILES);
7961
settings_menu->add_item(TTRC("Manage Export Templates..."), EDITOR_MANAGE_EXPORT_TEMPLATES);
7962
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
7963
settings_menu->add_item(TTRC("Configure FBX Importer..."), EDITOR_CONFIGURE_FBX_IMPORTER);
7964
#endif
7965
}
7966
7967
void EditorNode::_build_help_menu() {
7968
if (!help_menu) {
7969
return;
7970
}
7971
help_menu->clear(false);
7972
7973
if (menu_type == MENU_TYPE_GLOBAL && NativeMenu::get_singleton()->has_system_menu(NativeMenu::HELP_MENU_ID)) {
7974
help_menu->set_system_menu(NativeMenu::HELP_MENU_ID);
7975
} else {
7976
help_menu->set_system_menu(NativeMenu::INVALID_MENU_ID);
7977
}
7978
bool dark_mode = DisplayServer::get_singleton()->is_dark_mode_supported() && DisplayServer::get_singleton()->is_dark_mode();
7979
help_menu->add_icon_shortcut(get_editor_theme_native_menu_icon(SNAME("HelpSearch"), menu_type == MENU_TYPE_GLOBAL, dark_mode), ED_GET_SHORTCUT("editor/editor_help"), HELP_SEARCH);
7980
help_menu->add_separator();
7981
help_menu->add_shortcut(ED_GET_SHORTCUT("editor/online_docs"), HELP_DOCS);
7982
help_menu->add_shortcut(ED_GET_SHORTCUT("editor/forum"), HELP_FORUM);
7983
help_menu->add_shortcut(ED_GET_SHORTCUT("editor/community"), HELP_COMMUNITY);
7984
help_menu->add_separator();
7985
help_menu->add_icon_shortcut(get_editor_theme_native_menu_icon(SNAME("ActionCopy"), menu_type == MENU_TYPE_GLOBAL, dark_mode), ED_GET_SHORTCUT("editor/copy_system_info"), HELP_COPY_SYSTEM_INFO);
7986
help_menu->set_item_tooltip(-1, TTRC("Copies the system info as a single-line text into the clipboard."));
7987
help_menu->add_shortcut(ED_GET_SHORTCUT("editor/report_a_bug"), HELP_REPORT_A_BUG);
7988
help_menu->add_shortcut(ED_GET_SHORTCUT("editor/suggest_a_feature"), HELP_SUGGEST_A_FEATURE);
7989
help_menu->add_shortcut(ED_GET_SHORTCUT("editor/send_docs_feedback"), HELP_SEND_DOCS_FEEDBACK);
7990
help_menu->add_separator();
7991
#ifdef MACOS_ENABLED
7992
if (menu_type != MENU_TYPE_GLOBAL) {
7993
// On macOS "About" option is in the "app" menu.
7994
help_menu->add_icon_shortcut(get_editor_theme_native_menu_icon(SNAME("Godot"), menu_type == MENU_TYPE_GLOBAL, dark_mode), ED_GET_SHORTCUT("editor/about"), HELP_ABOUT);
7995
}
7996
#else
7997
help_menu->add_icon_shortcut(get_editor_theme_native_menu_icon(SNAME("Godot"), menu_type == MENU_TYPE_GLOBAL, dark_mode), ED_GET_SHORTCUT("editor/about"), HELP_ABOUT);
7998
#endif
7999
help_menu->add_icon_shortcut(get_editor_theme_native_menu_icon(SNAME("Heart"), menu_type == MENU_TYPE_GLOBAL, dark_mode), ED_GET_SHORTCUT("editor/support_development"), HELP_SUPPORT_GODOT_DEVELOPMENT);
8000
}
8001
8002
void EditorNode::_add_to_main_menu(const String &p_name, PopupMenu *p_menu) {
8003
p_menu->set_name(p_name);
8004
main_menu_items.push_back(p_menu);
8005
}
8006
8007
void EditorNode::_update_main_menu_type() {
8008
bool can_expand = bool(EDITOR_GET("interface/editor/expand_to_title")) && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EXTEND_TO_TITLE);
8009
bool use_menu_button = EDITOR_GET("interface/editor/collapse_main_menu");
8010
bool global_menu = !bool(EDITOR_GET("interface/editor/use_embedded_menu")) && NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU);
8011
MenuType new_menu_type;
8012
if (global_menu) {
8013
new_menu_type = MENU_TYPE_GLOBAL;
8014
} else if (use_menu_button) {
8015
new_menu_type = MENU_TYPE_COMPACT;
8016
} else {
8017
new_menu_type = MENU_TYPE_FULL;
8018
}
8019
8020
if (new_menu_type == menu_type) {
8021
return; // Nothing to do.
8022
}
8023
menu_type = new_menu_type;
8024
8025
// Update menu items.
8026
_build_file_menu();
8027
_build_project_menu();
8028
_build_settings_menu();
8029
_build_help_menu();
8030
8031
// Delete all menu.
8032
if (main_menu_bar) {
8033
for (PopupMenu *menu : main_menu_items) {
8034
if (menu->get_parent() == main_menu_bar) {
8035
main_menu_bar->remove_child(menu);
8036
}
8037
}
8038
memdelete(main_menu_bar);
8039
main_menu_bar = nullptr;
8040
}
8041
if (main_menu_button) {
8042
PopupMenu *popup = main_menu_button->get_popup();
8043
popup->clear(false);
8044
for (PopupMenu *menu : main_menu_items) {
8045
if (menu->get_parent() == popup) {
8046
popup->remove_child(menu);
8047
}
8048
}
8049
memdelete(main_menu_button);
8050
main_menu_button = nullptr;
8051
}
8052
memdelete_notnull(menu_btn_spacer);
8053
menu_btn_spacer = nullptr;
8054
8055
// Create new menu.
8056
if (new_menu_type == MENU_TYPE_COMPACT) {
8057
main_menu_button = memnew(MenuButton);
8058
main_menu_button->set_text(TTRC("Main Menu"));
8059
main_menu_button->set_theme_type_variation("MainScreenButton");
8060
main_menu_button->set_focus_mode(Control::FOCUS_NONE);
8061
if (is_inside_tree()) {
8062
main_menu_button->set_button_icon(theme->get_icon(SNAME("TripleBar"), EditorStringName(EditorIcons)));
8063
}
8064
main_menu_button->set_switch_on_hover(true);
8065
8066
for (PopupMenu *menu : main_menu_items) {
8067
if (menu != apple_menu) {
8068
main_menu_button->get_popup()->add_submenu_node_item(menu->get_name(), menu);
8069
}
8070
}
8071
8072
#ifdef ANDROID_ENABLED
8073
// Align main menu icon visually with TouchActionsPanel buttons.
8074
menu_btn_spacer = memnew(Control);
8075
menu_btn_spacer->set_custom_minimum_size(Vector2(8, 0) * EDSCALE);
8076
title_bar->add_child(menu_btn_spacer);
8077
title_bar->move_child(menu_btn_spacer, left_menu_spacer ? left_menu_spacer->get_index() + 1 : 0);
8078
#endif
8079
title_bar->add_child(main_menu_button);
8080
if (menu_btn_spacer == nullptr) {
8081
title_bar->move_child(main_menu_button, left_menu_spacer ? left_menu_spacer->get_index() + 1 : 0);
8082
} else {
8083
title_bar->move_child(main_menu_button, menu_btn_spacer->get_index() + 1);
8084
}
8085
} else {
8086
main_menu_bar = memnew(MenuBar);
8087
main_menu_bar->set_mouse_filter(Control::MOUSE_FILTER_STOP);
8088
main_menu_bar->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
8089
main_menu_bar->set_theme_type_variation("MainMenuBar");
8090
main_menu_bar->set_start_index(0); // Main menu, add to the start of global menu.
8091
main_menu_bar->set_prefer_global_menu(menu_type == MENU_TYPE_GLOBAL);
8092
main_menu_bar->set_switch_on_hover(true);
8093
8094
for (PopupMenu *menu : main_menu_items) {
8095
if (menu != apple_menu || menu_type == MENU_TYPE_GLOBAL) {
8096
main_menu_bar->add_child(menu);
8097
}
8098
}
8099
8100
title_bar->add_child(main_menu_bar);
8101
title_bar->move_child(main_menu_bar, left_menu_spacer ? left_menu_spacer->get_index() + 1 : 0);
8102
}
8103
8104
// Show/hide project title.
8105
if (project_title) {
8106
project_title->set_visible(can_expand && menu_type == MENU_TYPE_GLOBAL);
8107
}
8108
}
8109
8110
void EditorNode::_bottom_panel_resized() {
8111
bottom_panel->set_bottom_panel_offset(center_split->get_split_offset());
8112
}
8113
8114
#ifdef ANDROID_ENABLED
8115
void EditorNode::_touch_actions_panel_mode_changed() {
8116
int panel_mode = EDITOR_GET("interface/touchscreen/touch_actions_panel");
8117
switch (panel_mode) {
8118
case 1:
8119
if (touch_actions_panel != nullptr) {
8120
touch_actions_panel->queue_free();
8121
}
8122
touch_actions_panel = memnew(TouchActionsPanel);
8123
main_hbox->call_deferred("add_child", touch_actions_panel);
8124
break;
8125
case 2:
8126
if (touch_actions_panel != nullptr) {
8127
touch_actions_panel->queue_free();
8128
}
8129
touch_actions_panel = memnew(TouchActionsPanel);
8130
call_deferred("add_child", touch_actions_panel);
8131
break;
8132
case 0:
8133
if (touch_actions_panel != nullptr) {
8134
touch_actions_panel->queue_free();
8135
touch_actions_panel = nullptr;
8136
}
8137
break;
8138
}
8139
}
8140
#endif
8141
8142
#ifdef MACOS_ENABLED
8143
extern "C" GameViewPluginBase *get_game_view_plugin();
8144
#else
8145
GameViewPluginBase *get_game_view_plugin() {
8146
return memnew(GameViewPlugin);
8147
}
8148
#endif
8149
8150
void EditorNode::open_setting_override(const String &p_property) {
8151
editor_settings_dialog->hide();
8152
project_settings_editor->popup_for_override(p_property);
8153
}
8154
8155
void EditorNode::notify_settings_overrides_changed() {
8156
settings_overrides_changed = true;
8157
}
8158
8159
// Returns the list of project settings to add to new projects. This is used by the
8160
// project manager creation dialog, but also applies to empty `project.godot` files
8161
// to cover the command line workflow of creating projects using `touch project.godot`.
8162
//
8163
// This is used to set better defaults for new projects without affecting existing projects.
8164
HashMap<String, Variant> EditorNode::get_initial_settings() {
8165
HashMap<String, Variant> settings;
8166
settings["physics/3d/physics_engine"] = "Jolt Physics";
8167
settings["rendering/rendering_device/driver.windows"] = "d3d12";
8168
return settings;
8169
}
8170
8171
EditorNode::EditorNode() {
8172
DEV_ASSERT(!singleton);
8173
singleton = this;
8174
8175
// Detecting headless mode, that means the editor is running in command line.
8176
if (!DisplayServer::get_singleton()->window_can_draw()) {
8177
cmdline_mode = true;
8178
}
8179
8180
Resource::_get_local_scene_func = _resource_get_edited_scene;
8181
8182
{
8183
PortableCompressedTexture2D::set_keep_all_compressed_buffers(true);
8184
RenderingServer::get_singleton()->set_debug_generate_wireframes(true);
8185
8186
AudioServer::get_singleton()->set_enable_tagging_used_audio_streams(true);
8187
8188
// No navigation by default if in editor.
8189
if (NavigationServer3D::get_singleton()->get_debug_enabled()) {
8190
NavigationServer3D::get_singleton()->set_active(true);
8191
} else {
8192
NavigationServer3D::get_singleton()->set_active(false);
8193
}
8194
8195
// No physics by default if in editor.
8196
#ifndef PHYSICS_3D_DISABLED
8197
PhysicsServer3D::get_singleton()->set_active(false);
8198
#endif // PHYSICS_3D_DISABLED
8199
#ifndef PHYSICS_2D_DISABLED
8200
PhysicsServer2D::get_singleton()->set_active(false);
8201
#endif // PHYSICS_2D_DISABLED
8202
8203
// No scripting by default if in editor (except for tool).
8204
ScriptServer::set_scripting_enabled(false);
8205
8206
if (!DisplayServer::get_singleton()->is_touchscreen_available()) {
8207
// Only if no touchscreen ui hint, disable emulation just in case.
8208
Input::get_singleton()->set_emulate_touch_from_mouse(false);
8209
}
8210
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CUSTOM_CURSOR_SHAPE)) {
8211
DisplayServer::get_singleton()->cursor_set_custom_image(Ref<Resource>());
8212
}
8213
}
8214
8215
SceneState::set_disable_placeholders(true);
8216
ResourceLoader::clear_translation_remaps(); // Using no remaps if in editor.
8217
ResourceLoader::set_create_missing_resources_if_class_unavailable(true);
8218
8219
EditorPropertyNameProcessor *epnp = memnew(EditorPropertyNameProcessor);
8220
add_child(epnp);
8221
8222
EditorUndoRedoManager::get_singleton()->connect("version_changed", callable_mp(this, &EditorNode::_update_undo_redo_allowed));
8223
EditorUndoRedoManager::get_singleton()->connect("version_changed", callable_mp(this, &EditorNode::_update_unsaved_cache));
8224
EditorUndoRedoManager::get_singleton()->connect("history_changed", callable_mp(this, &EditorNode::_update_undo_redo_allowed));
8225
EditorUndoRedoManager::get_singleton()->connect("history_changed", callable_mp(this, &EditorNode::_update_unsaved_cache));
8226
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EditorNode::_update_from_settings));
8227
GDExtensionManager::get_singleton()->connect("extensions_reloaded", callable_mp(this, &EditorNode::_gdextensions_reloaded));
8228
8229
Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_main_domain();
8230
domain->set_enabled(false);
8231
domain->set_locale_override(TranslationServer::get_singleton()->get_fallback_locale());
8232
8233
// Load settings.
8234
if (!EditorSettings::get_singleton()) {
8235
EditorSettings::create();
8236
}
8237
8238
ED_SHORTCUT("editor/lock_selected_nodes", TTRC("Lock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::L);
8239
ED_SHORTCUT("editor/unlock_selected_nodes", TTRC("Unlock Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::L);
8240
ED_SHORTCUT("editor/group_selected_nodes", TTRC("Group Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | Key::G);
8241
ED_SHORTCUT("editor/ungroup_selected_nodes", TTRC("Ungroup Selected Node(s)"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::G);
8242
8243
FileAccess::set_backup_save(EDITOR_GET("filesystem/on_save/safe_save_on_backup_then_rename"));
8244
8245
_update_vsync_mode();
8246
8247
// Warm up the project upgrade tool as early as possible.
8248
project_upgrade_tool = memnew(ProjectUpgradeTool);
8249
run_project_upgrade_tool = EditorSettings::get_singleton()->get_project_metadata(project_upgrade_tool->META_PROJECT_UPGRADE_TOOL, project_upgrade_tool->META_RUN_ON_RESTART, false);
8250
if (run_project_upgrade_tool) {
8251
project_upgrade_tool->begin_upgrade();
8252
}
8253
8254
{
8255
bool agile_input_event_flushing = EDITOR_GET("input/buffering/agile_event_flushing");
8256
bool use_accumulated_input = EDITOR_GET("input/buffering/use_accumulated_input");
8257
8258
Input::get_singleton()->set_agile_input_event_flushing(agile_input_event_flushing);
8259
Input::get_singleton()->set_use_accumulated_input(use_accumulated_input);
8260
}
8261
8262
{
8263
int display_scale = EDITOR_GET("interface/editor/display_scale");
8264
8265
switch (display_scale) {
8266
case 0:
8267
// Try applying a suitable display scale automatically.
8268
EditorScale::set_scale(EditorSettings::get_auto_display_scale());
8269
break;
8270
case 1:
8271
EditorScale::set_scale(0.75);
8272
break;
8273
case 2:
8274
EditorScale::set_scale(1.0);
8275
break;
8276
case 3:
8277
EditorScale::set_scale(1.25);
8278
break;
8279
case 4:
8280
EditorScale::set_scale(1.5);
8281
break;
8282
case 5:
8283
EditorScale::set_scale(1.75);
8284
break;
8285
case 6:
8286
EditorScale::set_scale(2.0);
8287
break;
8288
default:
8289
EditorScale::set_scale(EDITOR_GET("interface/editor/custom_display_scale"));
8290
break;
8291
}
8292
}
8293
8294
// Define a minimum window size to prevent UI elements from overlapping or being cut off.
8295
Window *w = Object::cast_to<Window>(SceneTree::get_singleton()->get_root());
8296
if (w) {
8297
const Size2 minimum_size = Size2(1024, 600) * EDSCALE;
8298
w->set_min_size(minimum_size); // Calling it this early doesn't sync the property with DS.
8299
DisplayServer::get_singleton()->window_set_min_size(minimum_size);
8300
}
8301
8302
FileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files"));
8303
FileDialog::set_default_display_mode(EDITOR_GET("filesystem/file_dialog/display_mode"));
8304
8305
int swap_cancel_ok = EDITOR_GET("interface/editor/accept_dialog_cancel_ok_buttons");
8306
if (swap_cancel_ok != 0) { // 0 is auto, set in register_scene based on DisplayServer.
8307
// Swap on means OK first.
8308
AcceptDialog::set_swap_cancel_ok(swap_cancel_ok == 2);
8309
}
8310
8311
int ed_root_dir = EDITOR_GET("interface/editor/ui_layout_direction");
8312
Control::set_root_layout_direction(ed_root_dir);
8313
Window::set_root_layout_direction(ed_root_dir);
8314
8315
ResourceLoader::set_abort_on_missing_resources(false);
8316
ResourceLoader::set_error_notify_func(&EditorNode::add_io_error);
8317
ResourceLoader::set_dependency_error_notify_func(&EditorNode::_dependency_error_report);
8318
8319
SceneState::set_instantiation_warning_notify_func([](const String &p_warning) {
8320
add_io_warning(p_warning);
8321
callable_mp(EditorInterface::get_singleton(), &EditorInterface::mark_scene_as_unsaved).call_deferred();
8322
});
8323
8324
{
8325
// Register importers at the beginning, so dialogs are created with the right extensions.
8326
Ref<ResourceImporterTexture> import_texture = memnew(ResourceImporterTexture(true));
8327
ResourceFormatImporter::get_singleton()->add_importer(import_texture);
8328
8329
Ref<ResourceImporterLayeredTexture> import_cubemap;
8330
import_cubemap.instantiate();
8331
import_cubemap->set_mode(ResourceImporterLayeredTexture::MODE_CUBEMAP);
8332
ResourceFormatImporter::get_singleton()->add_importer(import_cubemap);
8333
8334
Ref<ResourceImporterLayeredTexture> import_array;
8335
import_array.instantiate();
8336
import_array->set_mode(ResourceImporterLayeredTexture::MODE_2D_ARRAY);
8337
ResourceFormatImporter::get_singleton()->add_importer(import_array);
8338
8339
Ref<ResourceImporterLayeredTexture> import_cubemap_array;
8340
import_cubemap_array.instantiate();
8341
import_cubemap_array->set_mode(ResourceImporterLayeredTexture::MODE_CUBEMAP_ARRAY);
8342
ResourceFormatImporter::get_singleton()->add_importer(import_cubemap_array);
8343
8344
Ref<ResourceImporterLayeredTexture> import_3d = memnew(ResourceImporterLayeredTexture(true));
8345
import_3d->set_mode(ResourceImporterLayeredTexture::MODE_3D);
8346
ResourceFormatImporter::get_singleton()->add_importer(import_3d);
8347
8348
Ref<ResourceImporterImage> import_image;
8349
import_image.instantiate();
8350
ResourceFormatImporter::get_singleton()->add_importer(import_image);
8351
8352
Ref<ResourceImporterSVG> import_svg;
8353
import_svg.instantiate();
8354
ResourceFormatImporter::get_singleton()->add_importer(import_svg);
8355
8356
Ref<ResourceImporterTextureAtlas> import_texture_atlas;
8357
import_texture_atlas.instantiate();
8358
ResourceFormatImporter::get_singleton()->add_importer(import_texture_atlas);
8359
8360
Ref<ResourceImporterDynamicFont> import_font_data_dynamic;
8361
import_font_data_dynamic.instantiate();
8362
ResourceFormatImporter::get_singleton()->add_importer(import_font_data_dynamic);
8363
8364
Ref<ResourceImporterBMFont> import_font_data_bmfont;
8365
import_font_data_bmfont.instantiate();
8366
ResourceFormatImporter::get_singleton()->add_importer(import_font_data_bmfont);
8367
8368
Ref<ResourceImporterImageFont> import_font_data_image;
8369
import_font_data_image.instantiate();
8370
ResourceFormatImporter::get_singleton()->add_importer(import_font_data_image);
8371
8372
Ref<ResourceImporterCSVTranslation> import_csv_translation;
8373
import_csv_translation.instantiate();
8374
ResourceFormatImporter::get_singleton()->add_importer(import_csv_translation);
8375
8376
Ref<ResourceImporterWAV> import_wav;
8377
import_wav.instantiate();
8378
ResourceFormatImporter::get_singleton()->add_importer(import_wav);
8379
8380
Ref<ResourceImporterOBJ> import_obj;
8381
import_obj.instantiate();
8382
ResourceFormatImporter::get_singleton()->add_importer(import_obj);
8383
8384
Ref<ResourceImporterShaderFile> import_shader_file;
8385
import_shader_file.instantiate();
8386
ResourceFormatImporter::get_singleton()->add_importer(import_shader_file);
8387
8388
Ref<ResourceImporterScene> import_model_as_scene;
8389
import_model_as_scene.instantiate("PackedScene");
8390
ResourceFormatImporter::get_singleton()->add_importer(import_model_as_scene);
8391
8392
Ref<ResourceImporterScene> import_model_as_animation;
8393
import_model_as_animation.instantiate("AnimationLibrary");
8394
ResourceFormatImporter::get_singleton()->add_importer(import_model_as_animation);
8395
8396
{
8397
Ref<EditorSceneFormatImporterCollada> import_collada;
8398
import_collada.instantiate();
8399
ResourceImporterScene::add_scene_importer(import_collada);
8400
8401
Ref<EditorOBJImporter> import_obj2;
8402
import_obj2.instantiate();
8403
ResourceImporterScene::add_scene_importer(import_obj2);
8404
8405
Ref<EditorSceneFormatImporterESCN> import_escn;
8406
import_escn.instantiate();
8407
ResourceImporterScene::add_scene_importer(import_escn);
8408
}
8409
8410
Ref<ResourceImporterBitMap> import_bitmap;
8411
import_bitmap.instantiate();
8412
ResourceFormatImporter::get_singleton()->add_importer(import_bitmap);
8413
}
8414
8415
{
8416
Ref<EditorInspectorDefaultPlugin> eidp;
8417
eidp.instantiate();
8418
EditorInspector::add_inspector_plugin(eidp);
8419
8420
Ref<EditorInspectorRootMotionPlugin> rmp;
8421
rmp.instantiate();
8422
EditorInspector::add_inspector_plugin(rmp);
8423
8424
Ref<EditorInspectorVisualShaderModePlugin> smp;
8425
smp.instantiate();
8426
EditorInspector::add_inspector_plugin(smp);
8427
8428
Ref<EditorInspectorParticleProcessMaterialPlugin> ppm;
8429
ppm.instantiate();
8430
EditorInspector::add_inspector_plugin(ppm);
8431
}
8432
8433
editor_selection = memnew(EditorSelection);
8434
8435
EditorFileSystem *efs = memnew(EditorFileSystem);
8436
add_child(efs);
8437
8438
EditorContextMenuPluginManager::create();
8439
8440
// Used for previews.
8441
FileDialog::set_get_icon_callback(callable_mp_static(_file_dialog_get_icon));
8442
FileDialog::set_get_thumbnail_callback(callable_mp_static(_file_dialog_get_thumbnail));
8443
FileDialog::register_func = _file_dialog_register;
8444
FileDialog::unregister_func = _file_dialog_unregister;
8445
8446
editor_export = memnew(EditorExport);
8447
add_child(editor_export);
8448
8449
// Exporters might need the theme.
8450
EditorThemeManager::initialize();
8451
theme = EditorThemeManager::generate_theme();
8452
DisplayServer::set_early_window_clear_color_override(true, theme->get_color(SNAME("background"), EditorStringName(Editor)));
8453
8454
EDITOR_DEF("_export_preset_advanced_mode", false); // Could be accessed in EditorExportPreset.
8455
8456
register_exporters();
8457
8458
ED_SHORTCUT("canvas_item_editor/pan_view", TTRC("Pan View"), Key::SPACE);
8459
8460
const Vector<String> textfile_ext = ((String)(EDITOR_GET("docks/filesystem/textfile_extensions"))).split(",", false);
8461
for (const String &E : textfile_ext) {
8462
textfile_extensions.insert(E);
8463
}
8464
const Vector<String> other_file_ext = ((String)(EDITOR_GET("docks/filesystem/other_file_extensions"))).split(",", false);
8465
for (const String &E : other_file_ext) {
8466
other_file_extensions.insert(E);
8467
}
8468
8469
force_textfile_extensions.insert("csv"); // CSV translation source, has `Translation` resource type, but not loadable as resource.
8470
8471
resource_preview = memnew(EditorResourcePreview);
8472
add_child(resource_preview);
8473
progress_dialog = memnew(ProgressDialog);
8474
add_child(progress_dialog);
8475
progress_dialog->connect(SceneStringName(visibility_changed), callable_mp(this, &EditorNode::_progress_dialog_visibility_changed));
8476
8477
gui_base = memnew(Panel);
8478
add_child(gui_base);
8479
8480
// Take up all screen.
8481
gui_base->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
8482
gui_base->set_anchor(SIDE_RIGHT, Control::ANCHOR_END);
8483
gui_base->set_anchor(SIDE_BOTTOM, Control::ANCHOR_END);
8484
gui_base->set_end(Point2(0, 0));
8485
8486
main_vbox = memnew(VBoxContainer);
8487
8488
#ifdef ANDROID_ENABLED
8489
base_vbox = memnew(VBoxContainer);
8490
base_vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, theme->get_constant(SNAME("window_border_margin"), EditorStringName(Editor)));
8491
8492
title_bar = memnew(EditorTitleBar);
8493
base_vbox->add_child(title_bar);
8494
8495
main_hbox = memnew(HBoxContainer);
8496
main_hbox->add_child(main_vbox);
8497
main_vbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
8498
main_hbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
8499
base_vbox->add_child(main_hbox);
8500
8501
_touch_actions_panel_mode_changed();
8502
8503
gui_base->add_child(base_vbox);
8504
#else
8505
gui_base->add_child(main_vbox);
8506
8507
title_bar = memnew(EditorTitleBar);
8508
main_vbox->add_child(title_bar);
8509
#endif
8510
8511
main_hsplit = memnew(DockSplitContainer);
8512
main_hsplit->set_name("DockHSplitMain");
8513
main_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
8514
main_vbox->add_child(main_hsplit);
8515
8516
left_l_vsplit = memnew(DockSplitContainer);
8517
left_l_vsplit->set_name("DockVSplitLeftL");
8518
left_l_vsplit->set_vertical(true);
8519
main_hsplit->add_child(left_l_vsplit);
8520
8521
DockTabContainer *dock_slots[EditorDock::DOCK_SLOT_MAX];
8522
{
8523
DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_LEFT_UL));
8524
dock_container->set_name("DockSlotLeftUL");
8525
left_l_vsplit->add_child(dock_container);
8526
dock_slots[dock_container->dock_slot] = dock_container;
8527
}
8528
{
8529
DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_LEFT_BL));
8530
dock_container->set_name("DockSlotLeftBL");
8531
left_l_vsplit->add_child(dock_container);
8532
dock_slots[dock_container->dock_slot] = dock_container;
8533
}
8534
8535
left_r_vsplit = memnew(DockSplitContainer);
8536
left_r_vsplit->set_name("DockVSplitLeftR");
8537
left_r_vsplit->set_vertical(true);
8538
main_hsplit->add_child(left_r_vsplit);
8539
{
8540
DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_LEFT_UR));
8541
dock_container->set_name("DockSlotLeftUR");
8542
left_r_vsplit->add_child(dock_container);
8543
dock_slots[dock_container->dock_slot] = dock_container;
8544
}
8545
{
8546
DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_LEFT_BR));
8547
dock_container->set_name("DockSlotLeftBR");
8548
left_r_vsplit->add_child(dock_container);
8549
dock_slots[dock_container->dock_slot] = dock_container;
8550
}
8551
8552
VBoxContainer *center_vb = memnew(VBoxContainer);
8553
center_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
8554
main_hsplit->add_child(center_vb);
8555
8556
center_split = memnew(DockSplitContainer);
8557
center_split->set_name("DockVSplitCenter");
8558
center_split->set_vertical(true);
8559
center_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
8560
center_split->set_collapsed(true);
8561
center_vb->add_child(center_split);
8562
center_split->connect("drag_ended", callable_mp(this, &EditorNode::_bottom_panel_resized));
8563
8564
right_l_vsplit = memnew(DockSplitContainer);
8565
right_l_vsplit->set_name("DockVSplitRightL");
8566
right_l_vsplit->set_vertical(true);
8567
main_hsplit->add_child(right_l_vsplit);
8568
{
8569
DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_RIGHT_UL));
8570
dock_container->set_name("DockSlotRightUL");
8571
right_l_vsplit->add_child(dock_container);
8572
dock_slots[dock_container->dock_slot] = dock_container;
8573
}
8574
{
8575
DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_RIGHT_BL));
8576
dock_container->set_name("DockSlotRightBL");
8577
right_l_vsplit->add_child(dock_container);
8578
dock_slots[dock_container->dock_slot] = dock_container;
8579
}
8580
8581
right_r_vsplit = memnew(DockSplitContainer);
8582
right_r_vsplit->set_name("DockVSplitRightR");
8583
right_r_vsplit->set_vertical(true);
8584
main_hsplit->add_child(right_r_vsplit);
8585
{
8586
DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_RIGHT_UR));
8587
dock_container->set_name("DockSlotRightUR");
8588
right_r_vsplit->add_child(dock_container);
8589
dock_slots[dock_container->dock_slot] = dock_container;
8590
}
8591
{
8592
DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_RIGHT_BR));
8593
dock_container->set_name("DockSlotRightBR");
8594
right_r_vsplit->add_child(dock_container);
8595
dock_slots[dock_container->dock_slot] = dock_container;
8596
}
8597
8598
editor_dock_manager = memnew(EditorDockManager);
8599
8600
// Save the splits for easier access.
8601
editor_dock_manager->add_vsplit(left_l_vsplit);
8602
editor_dock_manager->add_vsplit(left_r_vsplit);
8603
editor_dock_manager->add_vsplit(right_l_vsplit);
8604
editor_dock_manager->add_vsplit(right_r_vsplit);
8605
8606
editor_dock_manager->set_hsplit(main_hsplit);
8607
8608
for (int i = 0; i < EditorDock::DOCK_SLOT_BOTTOM; i++) {
8609
editor_dock_manager->register_dock_slot(dock_slots[i]);
8610
}
8611
8612
editor_layout_save_delay_timer = memnew(Timer);
8613
add_child(editor_layout_save_delay_timer);
8614
editor_layout_save_delay_timer->set_wait_time(0.5);
8615
editor_layout_save_delay_timer->set_one_shot(true);
8616
editor_layout_save_delay_timer->connect("timeout", callable_mp(this, &EditorNode::_save_editor_layout));
8617
8618
scan_changes_timer = memnew(Timer);
8619
scan_changes_timer->set_wait_time(0.5);
8620
scan_changes_timer->set_autostart(EDITOR_GET("interface/editor/import_resources_when_unfocused"));
8621
scan_changes_timer->connect("timeout", callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan_changes));
8622
add_child(scan_changes_timer);
8623
8624
top_split = memnew(VSplitContainer);
8625
center_split->add_child(top_split);
8626
top_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
8627
top_split->set_collapsed(true);
8628
8629
VBoxContainer *srt = memnew(VBoxContainer);
8630
srt->set_v_size_flags(Control::SIZE_EXPAND_FILL);
8631
srt->add_theme_constant_override("separation", 0);
8632
top_split->add_child(srt);
8633
8634
scene_tabs = memnew(EditorSceneTabs);
8635
srt->add_child(scene_tabs);
8636
scene_tabs->connect("tab_changed", callable_mp(this, &EditorNode::_set_current_scene));
8637
scene_tabs->connect("tab_closed", callable_mp(this, &EditorNode::_scene_tab_closed));
8638
8639
distraction_free = memnew(Button);
8640
distraction_free->set_theme_type_variation("FlatMenuButton");
8641
ED_SHORTCUT_AND_COMMAND("editor/distraction_free_mode", TTRC("Distraction Free Mode"), KeyModifierMask::CTRL | KeyModifierMask::SHIFT | Key::F11);
8642
ED_SHORTCUT_OVERRIDE("editor/distraction_free_mode", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::D);
8643
ED_SHORTCUT_AND_COMMAND("editor/toggle_last_opened_bottom_panel", TTRC("Toggle Last Opened Bottom Panel"), KeyModifierMask::CMD_OR_CTRL | Key::J);
8644
distraction_free->set_shortcut(ED_GET_SHORTCUT("editor/distraction_free_mode"));
8645
distraction_free->set_tooltip_text(TTRC("Toggle distraction-free mode."));
8646
distraction_free->set_toggle_mode(true);
8647
scene_tabs->add_extra_button(distraction_free);
8648
distraction_free->connect(SceneStringName(pressed), callable_mp(this, &EditorNode::_toggle_distraction_free_mode));
8649
8650
editor_main_screen = memnew(EditorMainScreen);
8651
editor_main_screen->set_custom_minimum_size(Size2(0, 80) * EDSCALE);
8652
editor_main_screen->set_draw_behind_parent(true);
8653
srt->add_child(editor_main_screen);
8654
editor_main_screen->set_v_size_flags(Control::SIZE_EXPAND_FILL);
8655
8656
scene_root = memnew(SubViewport);
8657
scene_root->set_auto_translate_mode(AUTO_TRANSLATE_MODE_ALWAYS);
8658
scene_root->set_translation_domain(StringName());
8659
scene_root->set_embedding_subwindows(true);
8660
scene_root->set_disable_3d(true);
8661
scene_root->set_disable_input(true);
8662
scene_root->set_as_audio_listener_2d(true);
8663
8664
accept = memnew(AcceptDialog);
8665
accept->set_autowrap(true);
8666
accept->set_min_size(Vector2i(600, 0));
8667
accept->set_unparent_when_invisible(true);
8668
8669
save_accept = memnew(AcceptDialog);
8670
save_accept->set_unparent_when_invisible(true);
8671
save_accept->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_menu_option).bind((int)MenuOptions::SCENE_SAVE_AS_SCENE));
8672
8673
project_export = memnew(ProjectExportDialog);
8674
gui_base->add_child(project_export);
8675
8676
dependency_error = memnew(DependencyErrorDialog);
8677
gui_base->add_child(dependency_error);
8678
8679
editor_settings_dialog = memnew(EditorSettingsDialog);
8680
gui_base->add_child(editor_settings_dialog);
8681
editor_settings_dialog->connect("restart_requested", callable_mp(this, &EditorNode::_restart_editor).bind(false));
8682
8683
project_settings_editor = memnew(ProjectSettingsEditor(&editor_data));
8684
gui_base->add_child(project_settings_editor);
8685
8686
scene_import_settings = memnew(SceneImportSettingsDialog);
8687
gui_base->add_child(scene_import_settings);
8688
8689
audio_stream_import_settings = memnew(AudioStreamImportSettingsDialog);
8690
gui_base->add_child(audio_stream_import_settings);
8691
8692
fontdata_import_settings = memnew(DynamicFontImportSettingsDialog);
8693
gui_base->add_child(fontdata_import_settings);
8694
8695
export_template_manager = memnew(ExportTemplateManager);
8696
gui_base->add_child(export_template_manager);
8697
8698
feature_profile_manager = memnew(EditorFeatureProfileManager);
8699
gui_base->add_child(feature_profile_manager);
8700
8701
build_profile_manager = memnew(EditorBuildProfileManager);
8702
gui_base->add_child(build_profile_manager);
8703
8704
about = memnew(EditorAbout);
8705
gui_base->add_child(about);
8706
feature_profile_manager->connect("current_feature_profile_changed", callable_mp(this, &EditorNode::_feature_profile_changed));
8707
8708
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
8709
fbx_importer_manager = memnew(FBXImporterManager);
8710
gui_base->add_child(fbx_importer_manager);
8711
#endif
8712
8713
warning = memnew(AcceptDialog);
8714
warning->set_unparent_when_invisible(true);
8715
warning->add_button(TTRC("Copy Text"), true, "copy");
8716
warning->connect("custom_action", callable_mp(this, &EditorNode::_copy_warning));
8717
8718
// Command palette and editor shortcuts.
8719
command_palette = EditorCommandPalette::get_singleton();
8720
command_palette->set_title(TTR("Command Palette"));
8721
gui_base->add_child(command_palette);
8722
8723
ED_SHORTCUT("editor/next_tab", TTRC("Next Scene Tab"), KeyModifierMask::CTRL + Key::TAB);
8724
ED_SHORTCUT("editor/prev_tab", TTRC("Previous Scene Tab"), KeyModifierMask::CTRL + KeyModifierMask::SHIFT + Key::TAB);
8725
ED_SHORTCUT("editor/filter_files", TTRC("Focus FileSystem Filter"), KeyModifierMask::CMD_OR_CTRL + KeyModifierMask::ALT + Key::P);
8726
8727
ED_SHORTCUT_AND_COMMAND("editor/new_scene", TTRC("New Scene"), KeyModifierMask::CMD_OR_CTRL + Key::N);
8728
ED_SHORTCUT_AND_COMMAND("editor/new_inherited_scene", TTRC("New Inherited Scene..."), KeyModifierMask::CMD_OR_CTRL + KeyModifierMask::SHIFT + Key::N);
8729
ED_SHORTCUT_AND_COMMAND("editor/open_scene", TTRC("Open Scene..."), KeyModifierMask::CMD_OR_CTRL + Key::O);
8730
ED_SHORTCUT_AND_COMMAND("editor/reopen_closed_scene", TTRC("Reopen Closed Scene"), KeyModifierMask::CMD_OR_CTRL + KeyModifierMask::SHIFT + Key::T);
8731
8732
ED_SHORTCUT_AND_COMMAND("editor/save_scene", TTRC("Save Scene"), KeyModifierMask::CMD_OR_CTRL + Key::S);
8733
ED_SHORTCUT_AND_COMMAND("editor/save_scene_as", TTRC("Save Scene As..."), KeyModifierMask::CMD_OR_CTRL + KeyModifierMask::SHIFT + Key::S);
8734
ED_SHORTCUT_AND_COMMAND("editor/save_all_scenes", TTRC("Save All Scenes"), KeyModifierMask::CMD_OR_CTRL + KeyModifierMask::SHIFT + KeyModifierMask::ALT + Key::S);
8735
8736
ED_SHORTCUT_ARRAY_AND_COMMAND("editor/quick_open", TTRC("Quick Open..."), { int32_t(KeyModifierMask::SHIFT + KeyModifierMask::ALT + Key::O), int32_t(KeyModifierMask::CMD_OR_CTRL + Key::P) });
8737
ED_SHORTCUT_OVERRIDE_ARRAY("editor/quick_open", "macos", { int32_t(KeyModifierMask::META + KeyModifierMask::CTRL + Key::O), int32_t(KeyModifierMask::CMD_OR_CTRL + Key::P) });
8738
ED_SHORTCUT_AND_COMMAND("editor/quick_open_scene", TTRC("Quick Open Scene..."), KeyModifierMask::CMD_OR_CTRL + KeyModifierMask::SHIFT + Key::O);
8739
ED_SHORTCUT_AND_COMMAND("editor/quick_open_script", TTRC("Quick Open Script..."), KeyModifierMask::CMD_OR_CTRL + KeyModifierMask::ALT + Key::O);
8740
8741
ED_SHORTCUT("editor/export_as_mesh_library", TTRC("MeshLibrary..."));
8742
8743
ED_SHORTCUT_AND_COMMAND("editor/reload_saved_scene", TTRC("Reload Saved Scene"));
8744
ED_SHORTCUT_AND_COMMAND("editor/close_scene", TTRC("Close Scene"), KeyModifierMask::CMD_OR_CTRL + KeyModifierMask::SHIFT + Key::W);
8745
ED_SHORTCUT_AND_COMMAND("editor/close_all_scenes", TTRC("Close All Scenes"));
8746
ED_SHORTCUT_OVERRIDE("editor/close_scene", "macos", KeyModifierMask::CMD_OR_CTRL + Key::W);
8747
8748
ED_SHORTCUT_AND_COMMAND("editor/editor_settings", TTRC("Editor Settings..."));
8749
ED_SHORTCUT_OVERRIDE("editor/editor_settings", "macos", KeyModifierMask::META + Key::COMMA);
8750
8751
ED_SHORTCUT_AND_COMMAND("editor/file_quit", TTRC("Quit"), KeyModifierMask::CMD_OR_CTRL + Key::Q);
8752
8753
ED_SHORTCUT_AND_COMMAND("editor/project_settings", TTRC("Project Settings..."), Key::NONE, TTRC("Project Settings"));
8754
ED_SHORTCUT_AND_COMMAND("editor/find_in_files", TTRC("Find in Files..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F);
8755
8756
ED_SHORTCUT_AND_COMMAND("editor/export", TTRC("Export..."), Key::NONE, TTRC("Export"));
8757
8758
ED_SHORTCUT_AND_COMMAND("editor/orphan_resource_explorer", TTRC("Orphan Resource Explorer..."));
8759
ED_SHORTCUT_AND_COMMAND("editor/engine_compilation_configuration_editor", TTRC("Engine Compilation Configuration Editor..."));
8760
ED_SHORTCUT_AND_COMMAND("editor/upgrade_project", TTRC("Upgrade Project Files..."));
8761
8762
ED_SHORTCUT("editor/reload_current_project", TTRC("Reload Current Project"));
8763
ED_SHORTCUT_AND_COMMAND("editor/quit_to_project_list", TTRC("Quit to Project List"), KeyModifierMask::CTRL + KeyModifierMask::SHIFT + Key::Q);
8764
ED_SHORTCUT_OVERRIDE("editor/quit_to_project_list", "macos", KeyModifierMask::META + KeyModifierMask::CTRL + KeyModifierMask::ALT + Key::Q);
8765
8766
ED_SHORTCUT("editor/command_palette", TTRC("Command Palette..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::P);
8767
8768
ED_SHORTCUT_AND_COMMAND("editor/take_screenshot", TTRC("Take Screenshot"), KeyModifierMask::CTRL | Key::F12);
8769
ED_SHORTCUT_OVERRIDE("editor/take_screenshot", "macos", KeyModifierMask::META | Key::F12);
8770
8771
ED_SHORTCUT_AND_COMMAND("editor/fullscreen_mode", TTRC("Toggle Fullscreen"), KeyModifierMask::SHIFT | Key::F11);
8772
ED_SHORTCUT_OVERRIDE("editor/fullscreen_mode", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::F);
8773
8774
ED_SHORTCUT_AND_COMMAND("editor/editor_help", TTRC("Search Help..."), Key::F1);
8775
ED_SHORTCUT_OVERRIDE("editor/editor_help", "macos", KeyModifierMask::ALT | Key::SPACE);
8776
ED_SHORTCUT_AND_COMMAND("editor/online_docs", TTRC("Online Documentation"));
8777
ED_SHORTCUT_AND_COMMAND("editor/forum", TTRC("Forum"));
8778
ED_SHORTCUT_AND_COMMAND("editor/community", TTRC("Community"));
8779
8780
ED_SHORTCUT_AND_COMMAND("editor/copy_system_info", TTRC("Copy System Info"));
8781
ED_SHORTCUT_AND_COMMAND("editor/report_a_bug", TTRC("Report a Bug"));
8782
ED_SHORTCUT_AND_COMMAND("editor/suggest_a_feature", TTRC("Suggest a Feature"));
8783
ED_SHORTCUT_AND_COMMAND("editor/send_docs_feedback", TTRC("Send Docs Feedback"));
8784
ED_SHORTCUT_AND_COMMAND("editor/about", TTRC("About Godot..."));
8785
ED_SHORTCUT_AND_COMMAND("editor/support_development", TTRC("Support Godot Development"));
8786
8787
// Use the Ctrl modifier so F2 can be used to rename nodes in the scene tree dock.
8788
ED_SHORTCUT_AND_COMMAND("editor/editor_2d", TTRC("Open 2D Workspace"), KeyModifierMask::CTRL | Key::F1);
8789
ED_SHORTCUT_AND_COMMAND("editor/editor_3d", TTRC("Open 3D Workspace"), KeyModifierMask::CTRL | Key::F2);
8790
ED_SHORTCUT_AND_COMMAND("editor/editor_script", TTRC("Open Script Editor"), KeyModifierMask::CTRL | Key::F3);
8791
ED_SHORTCUT_AND_COMMAND("editor/editor_game", TTRC("Open Game View"), KeyModifierMask::CTRL | Key::F4);
8792
ED_SHORTCUT_AND_COMMAND("editor/editor_assetlib", TTRC("Open Asset Library"), KeyModifierMask::CTRL | Key::F5);
8793
8794
ED_SHORTCUT_OVERRIDE("editor/editor_2d", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_1);
8795
ED_SHORTCUT_OVERRIDE("editor/editor_3d", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_2);
8796
ED_SHORTCUT_OVERRIDE("editor/editor_script", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_3);
8797
ED_SHORTCUT_OVERRIDE("editor/editor_game", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_4);
8798
ED_SHORTCUT_OVERRIDE("editor/editor_assetlib", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_5);
8799
8800
ED_SHORTCUT_AND_COMMAND("editor/editor_next", TTRC("Open the next Editor"));
8801
ED_SHORTCUT_AND_COMMAND("editor/editor_prev", TTRC("Open the previous Editor"));
8802
8803
// Editor menu and toolbar.
8804
bool can_expand = bool(EDITOR_GET("interface/editor/expand_to_title")) && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EXTEND_TO_TITLE);
8805
8806
#ifdef MACOS_ENABLED
8807
if (NativeMenu::get_singleton()->has_system_menu(NativeMenu::APPLICATION_MENU_ID)) {
8808
apple_menu = memnew(PopupMenu);
8809
apple_menu->set_system_menu(NativeMenu::APPLICATION_MENU_ID);
8810
_add_to_main_menu("Apple", apple_menu);
8811
8812
apple_menu->add_shortcut(ED_GET_SHORTCUT("editor/editor_settings"), EDITOR_OPEN_SETTINGS);
8813
apple_menu->add_separator();
8814
apple_menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorNode::_menu_option));
8815
}
8816
#endif
8817
8818
if (can_expand) {
8819
// Add spacer to avoid other controls under window minimize/maximize/close buttons (left side).
8820
left_menu_spacer = memnew(Control);
8821
left_menu_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS);
8822
title_bar->add_child(left_menu_spacer);
8823
}
8824
8825
file_menu = memnew(PopupMenu);
8826
file_menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorNode::_menu_option));
8827
file_menu->connect("about_to_popup", callable_mp(this, &EditorNode::_update_file_menu_opened));
8828
_add_to_main_menu(TTRC("Scene"), file_menu);
8829
8830
project_menu = memnew(PopupMenu);
8831
project_menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorNode::_menu_option));
8832
_add_to_main_menu(TTRC("Project"), project_menu);
8833
8834
debug_menu = memnew(PopupMenu);
8835
// Options are added and handled by DebuggerEditorPlugin, do not rebuild.
8836
_add_to_main_menu(TTRC("Debug"), debug_menu);
8837
8838
settings_menu = memnew(PopupMenu);
8839
settings_menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorNode::_menu_option));
8840
_add_to_main_menu(TTRC("Editor"), settings_menu);
8841
8842
help_menu = memnew(PopupMenu);
8843
help_menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorNode::_menu_option));
8844
_add_to_main_menu(TTRC("Help"), help_menu);
8845
8846
_update_main_menu_type();
8847
8848
// Spacer to center 2D / 3D / Script buttons.
8849
left_spacer = memnew(HBoxContainer);
8850
left_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS);
8851
left_spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL);
8852
title_bar->add_child(left_spacer);
8853
8854
project_title = memnew(Label);
8855
project_title->add_theme_font_override(SceneStringName(font), theme->get_font(SNAME("bold"), EditorStringName(EditorFonts)));
8856
project_title->add_theme_font_size_override(SceneStringName(font_size), theme->get_font_size(SNAME("bold_size"), EditorStringName(EditorFonts)));
8857
project_title->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
8858
project_title->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
8859
project_title->set_h_size_flags(Control::SIZE_EXPAND_FILL);
8860
project_title->set_mouse_filter(Control::MOUSE_FILTER_PASS);
8861
project_title->set_visible(can_expand && menu_type == MENU_TYPE_GLOBAL);
8862
left_spacer->add_child(project_title);
8863
8864
HBoxContainer *main_editor_button_hb = memnew(HBoxContainer);
8865
main_editor_button_hb->set_mouse_filter(Control::MOUSE_FILTER_STOP);
8866
main_editor_button_hb->set_name("EditorMainScreenButtons");
8867
editor_main_screen->set_button_container(main_editor_button_hb);
8868
title_bar->add_child(main_editor_button_hb);
8869
title_bar->set_center_control(main_editor_button_hb);
8870
8871
// Spacer to center 2D / 3D / Script buttons.
8872
right_spacer = memnew(Control);
8873
right_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS);
8874
right_spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL);
8875
title_bar->add_child(right_spacer);
8876
8877
project_run_bar = memnew(EditorRunBar);
8878
project_run_bar->set_mouse_filter(Control::MOUSE_FILTER_STOP);
8879
title_bar->add_child(project_run_bar);
8880
project_run_bar->connect("play_pressed", callable_mp(this, &EditorNode::_project_run_started));
8881
project_run_bar->connect("stop_pressed", callable_mp(this, &EditorNode::_project_run_stopped));
8882
8883
right_menu_hb = memnew(HBoxContainer);
8884
right_menu_hb->set_mouse_filter(Control::MOUSE_FILTER_STOP);
8885
title_bar->add_child(right_menu_hb);
8886
8887
renderer = memnew(OptionButton);
8888
renderer->set_visible(true);
8889
renderer->set_flat(true);
8890
renderer->set_theme_type_variation("TopBarOptionButton");
8891
renderer->set_fit_to_longest_item(false);
8892
renderer->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
8893
renderer->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
8894
renderer->set_tooltip_auto_translate_mode(AUTO_TRANSLATE_MODE_ALWAYS);
8895
renderer->set_tooltip_text(TTRC("Choose a renderer.\n\nNotes:\n- On mobile platforms, the Mobile renderer is used if Forward+ is selected here.\n- On the web platform, the Compatibility renderer is always used."));
8896
renderer->set_accessibility_name(TTRC("Renderer"));
8897
8898
right_menu_hb->add_child(renderer);
8899
8900
if (can_expand) {
8901
// Add spacer to avoid other controls under the window minimize/maximize/close buttons (right side).
8902
right_menu_spacer = memnew(Control);
8903
right_menu_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS);
8904
title_bar->add_child(right_menu_spacer);
8905
}
8906
8907
const String current_renderer_ps = String(GLOBAL_GET("rendering/renderer/rendering_method")).to_lower();
8908
const String current_renderer_os = OS::get_singleton()->get_current_rendering_method().to_lower();
8909
8910
// Add the renderers name to the UI.
8911
if (current_renderer_ps == current_renderer_os) {
8912
renderer->connect(SceneStringName(item_selected), callable_mp(this, &EditorNode::_renderer_selected));
8913
// As we are doing string comparisons, keep in standard case to prevent problems with capitals
8914
// "vulkan" in particular uses lowercase "v" in the code, and uppercase in the UI.
8915
PackedStringArray renderers = ProjectSettings::get_singleton()->get_custom_property_info().get(StringName("rendering/renderer/rendering_method")).hint_string.split(",", false);
8916
for (int i = 0; i < renderers.size(); i++) {
8917
const String rendering_method = renderers[i].to_lower();
8918
if (rendering_method == "dummy") {
8919
continue;
8920
}
8921
renderer->add_item(String()); // Set in NOTIFICATION_TRANSLATION_CHANGED.
8922
renderer->set_item_metadata(-1, rendering_method);
8923
if (current_renderer_ps == rendering_method) {
8924
renderer->select(i);
8925
}
8926
}
8927
} else {
8928
// It's an CLI-overridden rendering method.
8929
renderer->add_item(String()); // Set in NOTIFICATION_TRANSLATION_CHANGED.
8930
renderer->set_item_metadata(-1, current_renderer_os);
8931
}
8932
_update_renderer_color();
8933
8934
progress_hb = memnew(BackgroundProgress);
8935
8936
layout_dialog = memnew(EditorLayoutsDialog);
8937
gui_base->add_child(layout_dialog);
8938
layout_dialog->set_hide_on_ok(false);
8939
layout_dialog->set_size(Size2(225, 270) * EDSCALE);
8940
layout_dialog->connect("name_confirmed", callable_mp(this, &EditorNode::_dialog_action));
8941
8942
update_spinner = memnew(MenuButton);
8943
right_menu_hb->add_child(update_spinner);
8944
update_spinner->set_button_icon(theme->get_icon(SNAME("Progress1"), EditorStringName(EditorIcons)));
8945
update_spinner->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &EditorNode::_menu_option));
8946
update_spinner->set_accessibility_name(TTRC("Update Mode"));
8947
PopupMenu *p = update_spinner->get_popup();
8948
p->add_radio_check_item(TTRC("Update Continuously"), SPINNER_UPDATE_CONTINUOUSLY);
8949
p->add_radio_check_item(TTRC("Update When Changed"), SPINNER_UPDATE_WHEN_CHANGED);
8950
p->add_separator();
8951
p->add_item(TTRC("Hide Update Spinner"), SPINNER_UPDATE_SPINNER_HIDE);
8952
_update_update_spinner();
8953
8954
// Instantiate and place editor docks.
8955
8956
memnew(SceneTreeDock(scene_root, editor_selection, editor_data));
8957
editor_dock_manager->add_dock(SceneTreeDock::get_singleton());
8958
8959
memnew(ImportDock);
8960
editor_dock_manager->add_dock(ImportDock::get_singleton());
8961
8962
FileSystemDock *filesystem_dock = memnew(FileSystemDock);
8963
filesystem_dock->connect("inherit", callable_mp(this, &EditorNode::_inherit_request));
8964
filesystem_dock->connect("instantiate", callable_mp(this, &EditorNode::_instantiate_request));
8965
filesystem_dock->connect("display_mode_changed", callable_mp(this, &EditorNode::_save_editor_layout));
8966
get_project_settings()->connect_filesystem_dock_signals(filesystem_dock);
8967
editor_dock_manager->add_dock(filesystem_dock);
8968
8969
memnew(InspectorDock(editor_data));
8970
editor_dock_manager->add_dock(InspectorDock::get_singleton());
8971
8972
memnew(SignalsDock);
8973
editor_dock_manager->add_dock(SignalsDock::get_singleton());
8974
8975
memnew(GroupsDock);
8976
editor_dock_manager->add_dock(GroupsDock::get_singleton());
8977
8978
history_dock = memnew(HistoryDock);
8979
editor_dock_manager->add_dock(history_dock);
8980
8981
// Add some offsets to make LEFT_R and RIGHT_L docks wider than minsize.
8982
const int dock_hsize = 280;
8983
// By default there is only 3 visible, so set 2 split offsets for them.
8984
const int dock_hsize_scaled = dock_hsize * EDSCALE;
8985
main_hsplit->set_split_offsets({ dock_hsize_scaled, -dock_hsize_scaled });
8986
8987
// Define corresponding default layout.
8988
8989
const String docks_section = "docks";
8990
default_layout.instantiate();
8991
// Dock numbers are based on DockSlot enum value + 1.
8992
default_layout->set_value(docks_section, "dock_3", "Scene,Import");
8993
default_layout->set_value(docks_section, "dock_4", "FileSystem,History");
8994
default_layout->set_value(docks_section, "dock_5", "Inspector,Signals,Groups");
8995
8996
int hsplits[] = { 0, dock_hsize, -dock_hsize, 0 };
8997
for (int i = 0; i < (int)std_size(hsplits); i++) {
8998
default_layout->set_value(docks_section, "dock_hsplit_" + itos(i + 1), hsplits[i]);
8999
}
9000
for (int i = 0; i < editor_dock_manager->get_vsplit_count(); i++) {
9001
default_layout->set_value(docks_section, "dock_split_" + itos(i + 1), 0);
9002
}
9003
9004
{
9005
Dictionary offsets;
9006
offsets["Audio"] = -450;
9007
default_layout->set_value(EDITOR_NODE_CONFIG_SECTION, "bottom_panel_offsets", offsets);
9008
}
9009
9010
_update_layouts_menu();
9011
9012
// Bottom panels.
9013
9014
bottom_panel = memnew(EditorBottomPanel);
9015
editor_dock_manager->register_dock_slot(bottom_panel);
9016
center_split->add_child(bottom_panel);
9017
center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
9018
9019
log = memnew(EditorLog);
9020
editor_dock_manager->add_dock(log);
9021
9022
center_split->connect(SceneStringName(resized), callable_mp(this, &EditorNode::_vp_resized));
9023
9024
native_shader_source_visualizer = memnew(EditorNativeShaderSourceVisualizer);
9025
gui_base->add_child(native_shader_source_visualizer);
9026
9027
orphan_resources = memnew(OrphanResourcesDialog);
9028
gui_base->add_child(orphan_resources);
9029
9030
confirmation = memnew(ConfirmationDialog);
9031
confirmation_button = confirmation->add_button(TTRC("Don't Save"), DisplayServer::get_singleton()->get_swap_cancel_ok(), "discard");
9032
gui_base->add_child(confirmation);
9033
confirmation->set_min_size(Vector2(450.0 * EDSCALE, 0));
9034
confirmation->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_menu_confirm_current));
9035
confirmation->connect("custom_action", callable_mp(this, &EditorNode::_discard_changes));
9036
confirmation->connect("canceled", callable_mp(this, &EditorNode::_cancel_confirmation));
9037
9038
save_confirmation = memnew(ConfirmationDialog);
9039
save_confirmation->add_button(TTRC("Don't Save"), DisplayServer::get_singleton()->get_swap_cancel_ok(), "discard");
9040
gui_base->add_child(save_confirmation);
9041
save_confirmation->set_min_size(Vector2(450.0 * EDSCALE, 0));
9042
save_confirmation->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_menu_confirm_current));
9043
save_confirmation->connect("custom_action", callable_mp(this, &EditorNode::_discard_changes));
9044
save_confirmation->connect("canceled", callable_mp(this, &EditorNode::_cancel_close_scene_tab));
9045
save_confirmation->connect("about_to_popup", callable_mp(this, &EditorNode::_prepare_save_confirmation_popup));
9046
9047
gradle_build_manage_templates = memnew(ConfirmationDialog);
9048
gradle_build_manage_templates->set_text(TTR("Android build template is missing, please install relevant templates."));
9049
gradle_build_manage_templates->set_ok_button_text(TTR("Manage Templates"));
9050
gradle_build_manage_templates->add_button(TTR("Install from file"))->connect(SceneStringName(pressed), callable_mp(this, &EditorNode::_android_install_build_template));
9051
gradle_build_manage_templates->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_menu_option).bind(EDITOR_MANAGE_EXPORT_TEMPLATES));
9052
gui_base->add_child(gradle_build_manage_templates);
9053
9054
file_android_build_source = memnew(EditorFileDialog);
9055
file_android_build_source->set_title(TTR("Select Android sources file"));
9056
file_android_build_source->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
9057
file_android_build_source->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
9058
file_android_build_source->add_filter("*.zip");
9059
file_android_build_source->connect("file_selected", callable_mp(this, &EditorNode::_android_build_source_selected));
9060
gui_base->add_child(file_android_build_source);
9061
9062
{
9063
VBoxContainer *vbox = memnew(VBoxContainer);
9064
install_android_build_template_message = memnew(Label);
9065
install_android_build_template_message->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
9066
install_android_build_template_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
9067
install_android_build_template_message->set_custom_minimum_size(Size2(300 * EDSCALE, 1));
9068
vbox->add_child(install_android_build_template_message);
9069
9070
choose_android_export_profile = memnew(OptionButton);
9071
choose_android_export_profile->connect(SceneStringName(item_selected), callable_mp(this, &EditorNode::_android_export_preset_selected));
9072
vbox->add_child(choose_android_export_profile);
9073
9074
install_android_build_template = memnew(ConfirmationDialog);
9075
install_android_build_template->set_ok_button_text(TTR("Install"));
9076
install_android_build_template->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_menu_confirm_current));
9077
install_android_build_template->add_child(vbox);
9078
install_android_build_template->set_min_size(Vector2(500.0 * EDSCALE, 0));
9079
gui_base->add_child(install_android_build_template);
9080
}
9081
9082
remove_android_build_template = memnew(ConfirmationDialog);
9083
remove_android_build_template->set_ok_button_text(TTR("Show in File Manager"));
9084
remove_android_build_template->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_android_explore_build_templates));
9085
gui_base->add_child(remove_android_build_template);
9086
9087
file_templates = memnew(EditorFileDialog);
9088
file_templates->set_title(TTR("Import Templates From ZIP File"));
9089
9090
gui_base->add_child(file_templates);
9091
file_templates->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
9092
file_templates->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
9093
file_templates->clear_filters();
9094
file_templates->add_filter("*.tpz", TTR("Template Package"));
9095
9096
file = memnew(EditorFileDialog);
9097
gui_base->add_child(file);
9098
file->set_current_dir("res://");
9099
file->set_transient_to_focused(true);
9100
9101
file_export_lib = memnew(EditorFileDialog);
9102
file_export_lib->set_title(TTR("Export Library"));
9103
file_export_lib->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
9104
file_export_lib->connect("file_selected", callable_mp(this, &EditorNode::_dialog_action));
9105
file_export_lib->add_option(TTR("Merge With Existing"), Vector<String>(), true);
9106
file_export_lib->add_option(TTR("Apply MeshInstance Transforms"), Vector<String>(), false);
9107
gui_base->add_child(file_export_lib);
9108
9109
file_pack_zip = memnew(EditorFileDialog);
9110
file_pack_zip->connect("file_selected", callable_mp(this, &EditorNode::_dialog_action));
9111
file_pack_zip->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
9112
file_pack_zip->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
9113
file_pack_zip->add_filter("*.zip", "ZIP Archive");
9114
file_pack_zip->set_title(TTR("Pack Project as ZIP..."));
9115
gui_base->add_child(file_pack_zip);
9116
9117
file->connect("file_selected", callable_mp(this, &EditorNode::_dialog_action));
9118
file_templates->connect("file_selected", callable_mp(this, &EditorNode::_dialog_action));
9119
9120
audio_preview_gen = memnew(AudioStreamPreviewGenerator);
9121
add_child(audio_preview_gen);
9122
9123
add_editor_plugin(memnew(DebuggerEditorPlugin(debug_menu)));
9124
9125
disk_changed = memnew(ConfirmationDialog);
9126
{
9127
disk_changed->set_title(TTR("Files have been modified outside Godot"));
9128
9129
VBoxContainer *vbc = memnew(VBoxContainer);
9130
disk_changed->add_child(vbc);
9131
9132
Label *dl = memnew(Label);
9133
dl->set_text(TTR("The following files are newer on disk:"));
9134
vbc->add_child(dl);
9135
9136
disk_changed_list = memnew(Tree);
9137
disk_changed_list->set_accessibility_name(TTRC("The following files are newer on disk:"));
9138
vbc->add_child(disk_changed_list);
9139
disk_changed_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
9140
9141
Label *what_action_label = memnew(Label);
9142
what_action_label->set_text(TTR("What action should be taken?"));
9143
vbc->add_child(what_action_label);
9144
9145
disk_changed->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_reload_modified_scenes));
9146
disk_changed->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_reload_project_settings));
9147
disk_changed->set_ok_button_text(TTR("Reload from disk"));
9148
9149
disk_changed->add_button(TTR("Ignore external changes"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave");
9150
disk_changed->connect("custom_action", callable_mp(this, &EditorNode::_resave_externally_modified_scenes));
9151
}
9152
9153
gui_base->add_child(disk_changed);
9154
9155
project_data_missing = memnew(ConfirmationDialog);
9156
project_data_missing->set_text(TTRC("Project data folder (.godot) is missing. Please restart editor."));
9157
project_data_missing->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::restart_editor).bind(false));
9158
project_data_missing->set_ok_button_text(TTRC("Restart"));
9159
9160
gui_base->add_child(project_data_missing);
9161
9162
add_editor_plugin(memnew(CanvasItemEditorPlugin));
9163
add_editor_plugin(memnew(Node3DEditorPlugin));
9164
add_editor_plugin(memnew(ScriptEditorPlugin));
9165
9166
if (!Engine::get_singleton()->is_recovery_mode_hint()) {
9167
add_editor_plugin(get_game_view_plugin());
9168
}
9169
9170
EditorAudioBuses *audio_bus_editor = EditorAudioBuses::register_editor();
9171
9172
ScriptTextEditor::register_editor(); // Register one for text scripts.
9173
TextEditor::register_editor();
9174
9175
if (AssetLibraryEditorPlugin::is_available()) {
9176
add_editor_plugin(memnew(AssetLibraryEditorPlugin));
9177
} else {
9178
print_verbose("Asset Library not available (due to using Web editor, or SSL support disabled).");
9179
}
9180
9181
// More visually meaningful to have this later.
9182
add_editor_plugin(memnew(AnimationPlayerEditorPlugin));
9183
add_editor_plugin(memnew(AnimationTrackKeyEditEditorPlugin));
9184
add_editor_plugin(memnew(AnimationMarkerKeyEditEditorPlugin));
9185
9186
add_editor_plugin(VersionControlEditorPlugin::get_singleton());
9187
9188
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
9189
9190
for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) {
9191
add_editor_plugin(EditorPlugins::create(i));
9192
}
9193
9194
for (const StringName &extension_class_name : GDExtensionEditorPlugins::get_extension_classes()) {
9195
add_extension_editor_plugin(extension_class_name);
9196
}
9197
GDExtensionEditorPlugins::editor_node_add_plugin = &EditorNode::add_extension_editor_plugin;
9198
GDExtensionEditorPlugins::editor_node_remove_plugin = &EditorNode::remove_extension_editor_plugin;
9199
9200
for (int i = 0; i < plugin_init_callback_count; i++) {
9201
plugin_init_callbacks[i]();
9202
}
9203
9204
resource_preview->add_preview_generator(Ref<EditorTexturePreviewPlugin>(memnew(EditorTexturePreviewPlugin)));
9205
resource_preview->add_preview_generator(Ref<EditorImagePreviewPlugin>(memnew(EditorImagePreviewPlugin)));
9206
resource_preview->add_preview_generator(Ref<EditorPackedScenePreviewPlugin>(memnew(EditorPackedScenePreviewPlugin)));
9207
resource_preview->add_preview_generator(Ref<EditorMaterialPreviewPlugin>(memnew(EditorMaterialPreviewPlugin)));
9208
resource_preview->add_preview_generator(Ref<EditorScriptPreviewPlugin>(memnew(EditorScriptPreviewPlugin)));
9209
resource_preview->add_preview_generator(Ref<EditorAudioStreamPreviewPlugin>(memnew(EditorAudioStreamPreviewPlugin)));
9210
resource_preview->add_preview_generator(Ref<EditorMeshPreviewPlugin>(memnew(EditorMeshPreviewPlugin)));
9211
resource_preview->add_preview_generator(Ref<EditorBitmapPreviewPlugin>(memnew(EditorBitmapPreviewPlugin)));
9212
resource_preview->add_preview_generator(Ref<EditorFontPreviewPlugin>(memnew(EditorFontPreviewPlugin)));
9213
resource_preview->add_preview_generator(Ref<EditorGradientPreviewPlugin>(memnew(EditorGradientPreviewPlugin)));
9214
9215
{
9216
Ref<StandardMaterial3DConversionPlugin> spatial_mat_convert;
9217
spatial_mat_convert.instantiate();
9218
resource_conversion_plugins.push_back(spatial_mat_convert);
9219
9220
Ref<ORMMaterial3DConversionPlugin> orm_mat_convert;
9221
orm_mat_convert.instantiate();
9222
resource_conversion_plugins.push_back(orm_mat_convert);
9223
9224
Ref<CanvasItemMaterialConversionPlugin> canvas_item_mat_convert;
9225
canvas_item_mat_convert.instantiate();
9226
resource_conversion_plugins.push_back(canvas_item_mat_convert);
9227
9228
Ref<BlitMaterialConversionPlugin> blit_mat_convert;
9229
blit_mat_convert.instantiate();
9230
resource_conversion_plugins.push_back(blit_mat_convert);
9231
9232
Ref<ParticleProcessMaterialConversionPlugin> particles_mat_convert;
9233
particles_mat_convert.instantiate();
9234
resource_conversion_plugins.push_back(particles_mat_convert);
9235
9236
Ref<ProceduralSkyMaterialConversionPlugin> procedural_sky_mat_convert;
9237
procedural_sky_mat_convert.instantiate();
9238
resource_conversion_plugins.push_back(procedural_sky_mat_convert);
9239
9240
Ref<PanoramaSkyMaterialConversionPlugin> panorama_sky_mat_convert;
9241
panorama_sky_mat_convert.instantiate();
9242
resource_conversion_plugins.push_back(panorama_sky_mat_convert);
9243
9244
Ref<PhysicalSkyMaterialConversionPlugin> physical_sky_mat_convert;
9245
physical_sky_mat_convert.instantiate();
9246
resource_conversion_plugins.push_back(physical_sky_mat_convert);
9247
9248
Ref<FogMaterialConversionPlugin> fog_mat_convert;
9249
fog_mat_convert.instantiate();
9250
resource_conversion_plugins.push_back(fog_mat_convert);
9251
9252
Ref<VisualShaderConversionPlugin> vshader_convert;
9253
vshader_convert.instantiate();
9254
resource_conversion_plugins.push_back(vshader_convert);
9255
}
9256
9257
update_spinner_step_msec = OS::get_singleton()->get_ticks_msec();
9258
update_spinner_step_frame = Engine::get_singleton()->get_frames_drawn();
9259
9260
editor_plugins_over = memnew(EditorPluginList);
9261
editor_plugins_force_over = memnew(EditorPluginList);
9262
editor_plugins_force_input_forwarding = memnew(EditorPluginList);
9263
9264
Ref<GDExtensionExportPlugin> gdextension_export_plugin;
9265
gdextension_export_plugin.instantiate();
9266
9267
EditorExport::get_singleton()->add_export_plugin(gdextension_export_plugin);
9268
9269
Ref<DedicatedServerExportPlugin> dedicated_server_export_plugin;
9270
dedicated_server_export_plugin.instantiate();
9271
9272
EditorExport::get_singleton()->add_export_plugin(dedicated_server_export_plugin);
9273
9274
Ref<ShaderBakerExportPlugin> shader_baker_export_plugin;
9275
shader_baker_export_plugin.instantiate();
9276
9277
#ifdef VULKAN_ENABLED
9278
Ref<ShaderBakerExportPluginPlatformVulkan> shader_baker_export_plugin_platform_vulkan;
9279
shader_baker_export_plugin_platform_vulkan.instantiate();
9280
shader_baker_export_plugin->add_platform(shader_baker_export_plugin_platform_vulkan);
9281
#endif
9282
9283
#ifdef D3D12_ENABLED
9284
Ref<ShaderBakerExportPluginPlatformD3D12> shader_baker_export_plugin_platform_d3d12;
9285
shader_baker_export_plugin_platform_d3d12.instantiate();
9286
shader_baker_export_plugin->add_platform(shader_baker_export_plugin_platform_d3d12);
9287
#endif
9288
9289
#ifdef METAL_ENABLED
9290
Ref<ShaderBakerExportPluginPlatformMetal> shader_baker_export_plugin_platform_metal;
9291
shader_baker_export_plugin_platform_metal.instantiate();
9292
shader_baker_export_plugin->add_platform(shader_baker_export_plugin_platform_metal);
9293
#endif
9294
9295
EditorExport::get_singleton()->add_export_plugin(shader_baker_export_plugin);
9296
9297
Ref<PackedSceneEditorTranslationParserPlugin> packed_scene_translation_parser_plugin;
9298
packed_scene_translation_parser_plugin.instantiate();
9299
EditorTranslationParser::get_singleton()->add_parser(packed_scene_translation_parser_plugin, EditorTranslationParser::STANDARD);
9300
9301
_edit_current();
9302
saving_resource = Ref<Resource>();
9303
9304
set_process(true);
9305
9306
open_imported = memnew(ConfirmationDialog);
9307
open_imported->set_ok_button_text(TTR("Open Anyway"));
9308
new_inherited_button = open_imported->add_button(TTR("New Inherited"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "inherit");
9309
open_imported->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_open_imported));
9310
open_imported->connect("custom_action", callable_mp(this, &EditorNode::_inherit_imported));
9311
gui_base->add_child(open_imported);
9312
9313
quick_open_dialog = memnew(EditorQuickOpenDialog);
9314
gui_base->add_child(quick_open_dialog);
9315
9316
quick_open_color_palette = memnew(EditorQuickOpenDialog);
9317
gui_base->add_child(quick_open_color_palette);
9318
9319
_update_recent_scenes();
9320
9321
set_process_shortcut_input(true);
9322
9323
load_errors = memnew(RichTextLabel);
9324
load_error_dialog = memnew(AcceptDialog);
9325
load_error_dialog->set_unparent_when_invisible(true);
9326
load_error_dialog->add_child(load_errors);
9327
load_error_dialog->set_title(TTR("Load Errors"));
9328
load_error_dialog->connect(SceneStringName(visibility_changed), callable_mp(this, &EditorNode::_load_error_dialog_visibility_changed));
9329
9330
execute_outputs = memnew(RichTextLabel);
9331
execute_outputs->set_selection_enabled(true);
9332
execute_outputs->set_context_menu_enabled(true);
9333
execute_output_dialog = memnew(AcceptDialog);
9334
execute_output_dialog->set_unparent_when_invisible(true);
9335
execute_output_dialog->add_child(execute_outputs);
9336
execute_output_dialog->set_title("");
9337
9338
EditorFileSystem::get_singleton()->connect("sources_changed", callable_mp(this, &EditorNode::_sources_changed));
9339
EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &EditorNode::_fs_changed));
9340
EditorFileSystem::get_singleton()->connect("resources_reimporting", callable_mp(this, &EditorNode::_resources_reimporting));
9341
EditorFileSystem::get_singleton()->connect("resources_reimported", callable_mp(this, &EditorNode::_resources_reimported));
9342
EditorFileSystem::get_singleton()->connect("resources_reload", callable_mp(this, &EditorNode::_resources_changed));
9343
9344
_build_icon_type_cache();
9345
9346
pick_main_scene = memnew(ConfirmationDialog);
9347
gui_base->add_child(pick_main_scene);
9348
pick_main_scene->set_ok_button_text(TTR("Select"));
9349
pick_main_scene->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_menu_option).bind(SETTINGS_PICK_MAIN_SCENE));
9350
select_current_scene_button = pick_main_scene->add_button(TTR("Select Current"), true, "select_current");
9351
pick_main_scene->connect("custom_action", callable_mp(this, &EditorNode::_pick_main_scene_custom_action));
9352
9353
open_project_settings = memnew(ConfirmationDialog);
9354
gui_base->add_child(open_project_settings);
9355
open_project_settings->set_ok_button_text(TTRC("Open Project Settings"));
9356
open_project_settings->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_menu_option).bind(PROJECT_OPEN_SETTINGS));
9357
9358
for (int i = 0; i < _init_callbacks.size(); i++) {
9359
_init_callbacks[i]();
9360
}
9361
9362
editor_data.add_edited_scene(-1);
9363
editor_data.set_edited_scene(0);
9364
scene_tabs->update_scene_tabs();
9365
9366
ImportDock::get_singleton()->initialize_import_options();
9367
9368
FileAccess::set_file_close_fail_notify_callback(_file_access_close_error_notify);
9369
9370
print_handler.printfunc = _print_handler;
9371
print_handler.userdata = this;
9372
add_print_handler(&print_handler);
9373
9374
ResourceSaver::set_save_callback(_resource_saved);
9375
ResourceLoader::set_load_callback(_resource_loaded);
9376
9377
// Apply setting presets in case the editor_settings file is missing values.
9378
EditorSettingsDialog::update_navigation_preset();
9379
9380
screenshot_timer = memnew(Timer);
9381
screenshot_timer->set_one_shot(true);
9382
screenshot_timer->set_wait_time(settings_menu->get_submenu_popup_delay() + 0.1f);
9383
screenshot_timer->connect("timeout", callable_mp(this, &EditorNode::_request_screenshot));
9384
add_child(screenshot_timer);
9385
screenshot_timer->set_owner(get_owner());
9386
9387
// Extend menu bar to window title.
9388
if (can_expand) {
9389
DisplayServer::get_singleton()->process_events();
9390
DisplayServer::get_singleton()->window_set_flag(DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE, true, DisplayServer::MAIN_WINDOW_ID);
9391
title_bar->set_can_move_window(true);
9392
}
9393
9394
{
9395
const String exec = OS::get_singleton()->get_executable_path();
9396
const String old_exec = EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "executable_path", "");
9397
// Save editor executable path for third-party tools.
9398
if (exec != old_exec) {
9399
EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "executable_path", exec);
9400
}
9401
}
9402
9403
follow_system_theme = EDITOR_GET("interface/theme/follow_system_theme");
9404
use_system_accent_color = EDITOR_GET("interface/theme/use_system_accent_color");
9405
}
9406
9407
EditorNode::~EditorNode() {
9408
EditorInspector::cleanup_plugins();
9409
EditorTranslationParser::get_singleton()->clean_parsers();
9410
ResourceImporterScene::clean_up_importer_plugins();
9411
EditorContextMenuPluginManager::cleanup();
9412
9413
remove_print_handler(&print_handler);
9414
EditorHelp::cleanup_doc();
9415
#if defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED)
9416
EditorHelpHighlighter::free_singleton();
9417
#endif
9418
memdelete(editor_selection);
9419
memdelete(editor_plugins_over);
9420
memdelete(editor_plugins_force_over);
9421
memdelete(editor_plugins_force_input_forwarding);
9422
memdelete(progress_hb);
9423
memdelete(project_upgrade_tool);
9424
memdelete(editor_dock_manager);
9425
9426
EditorSettings::destroy();
9427
EditorThemeManager::finalize();
9428
9429
GDExtensionEditorPlugins::editor_node_add_plugin = nullptr;
9430
GDExtensionEditorPlugins::editor_node_remove_plugin = nullptr;
9431
9432
FileDialog::register_func = nullptr;
9433
FileDialog::unregister_func = nullptr;
9434
9435
file_dialogs.clear();
9436
9437
singleton = nullptr;
9438
}
9439
9440