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