diff options
Diffstat (limited to 'editor/editor_node.cpp')
-rw-r--r-- | editor/editor_node.cpp | 860 |
1 files changed, 514 insertions, 346 deletions
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index f3086d416b..9c77f9d25c 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -46,19 +46,23 @@ #include "core/string/print_string.h" #include "core/string/translation.h" #include "core/version.h" +#include "core/version_hash.gen.h" #include "main/main.h" #include "scene/gui/center_container.h" #include "scene/gui/control.h" #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" +#include "scene/gui/link_button.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" #include "scene/gui/panel_container.h" #include "scene/gui/split_container.h" #include "scene/gui/tab_container.h" #include "scene/gui/tabs.h" -#include "scene/gui/texture_progress.h" +#include "scene/gui/texture_progress_bar.h" +#include "scene/main/window.h" #include "scene/resources/packed_scene.h" +#include "servers/display_server.h" #include "servers/navigation_server_2d.h" #include "servers/navigation_server_3d.h" #include "servers/physics_server_2d.h" @@ -76,8 +80,10 @@ #include "editor/editor_inspector.h" #include "editor/editor_layouts_dialog.h" #include "editor/editor_log.h" +#include "editor/editor_paths.h" #include "editor/editor_plugin.h" #include "editor/editor_properties.h" +#include "editor/editor_resource_picker.h" #include "editor/editor_resource_preview.h" #include "editor/editor_run_native.h" #include "editor/editor_run_script.h" @@ -89,9 +95,7 @@ #include "editor/export_template_manager.h" #include "editor/filesystem_dock.h" #include "editor/import/editor_import_collada.h" -#include "editor/import/editor_scene_importer_gltf.h" #include "editor/import/resource_importer_bitmask.h" -#include "editor/import/resource_importer_csv.h" #include "editor/import/resource_importer_csv_translation.h" #include "editor/import/resource_importer_image.h" #include "editor/import/resource_importer_layered_texture.h" @@ -101,6 +105,8 @@ #include "editor/import/resource_importer_texture.h" #include "editor/import/resource_importer_texture_atlas.h" #include "editor/import/resource_importer_wav.h" +#include "editor/import/scene_import_settings.h" +#include "editor/import/scene_importer_mesh_node_3d.h" #include "editor/import_dock.h" #include "editor/multi_node_edit.h" #include "editor/node_dock.h" @@ -141,6 +147,7 @@ #include "editor/plugins/multimesh_editor_plugin.h" #include "editor/plugins/navigation_polygon_editor_plugin.h" #include "editor/plugins/node_3d_editor_plugin.h" +#include "editor/plugins/occluder_instance_3d_editor_plugin.h" #include "editor/plugins/ot_features_plugin.h" #include "editor/plugins/packed_scene_translation_parser_plugin.h" #include "editor/plugins/path_2d_editor_plugin.h" @@ -165,24 +172,24 @@ #include "editor/plugins/texture_layered_editor_plugin.h" #include "editor/plugins/texture_region_editor_plugin.h" #include "editor/plugins/theme_editor_plugin.h" -#include "editor/plugins/tile_map_editor_plugin.h" -#include "editor/plugins/tile_set_editor_plugin.h" +#include "editor/plugins/tiles/tiles_editor_plugin.h" #include "editor/plugins/version_control_editor_plugin.h" #include "editor/plugins/visual_shader_editor_plugin.h" #include "editor/progress_dialog.h" #include "editor/project_export.h" #include "editor/project_settings_editor.h" -#include "editor/pvrtc_compress.h" #include "editor/quick_open.h" #include "editor/register_exporters.h" #include "editor/settings_config_dialog.h" -#include "scene/main/window.h" -#include "servers/display_server.h" + #include <stdio.h> #include <stdlib.h> EditorNode *EditorNode::singleton = nullptr; +// The metadata key used to store and retrieve the version text to copy to the clipboard. +static const String META_TEXT_TO_COPY = "text_to_copy"; + void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames) { // Keep track of a list of "index sets," i.e. sets of indices // within disambiguated_scene_names which contain the same name. @@ -377,9 +384,9 @@ void EditorNode::_version_control_menu_option(int p_idx) { void EditorNode::_update_title() { String appname = ProjectSettings::get_singleton()->get("application/config/name"); - String title = appname.empty() ? String(VERSION_FULL_NAME) : String(VERSION_NAME + String(" - ") + appname); + String title = appname.is_empty() ? String(VERSION_FULL_NAME) : String(VERSION_NAME + String(" - ") + appname); String edited = editor_data.get_edited_scene_root() ? editor_data.get_edited_scene_root()->get_filename() : String(); - if (!edited.empty()) { + if (!edited.is_empty()) { title += " - " + String(edited.get_file()); } if (unsaved_cache) { @@ -390,6 +397,8 @@ void EditorNode::_update_title() { } void EditorNode::_unhandled_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + Ref<InputEventKey> k = p_event; if (k.is_valid() && k->is_pressed() && !k->is_echo()) { EditorPlugin *old_editor = editor_plugin_screen; @@ -430,6 +439,74 @@ void EditorNode::_unhandled_input(const Ref<InputEvent> &p_event) { } } +void EditorNode::_update_from_settings() { + int current_filter = GLOBAL_GET("rendering/textures/canvas_textures/default_texture_filter"); + if (current_filter != scene_root->get_default_canvas_item_texture_filter()) { + Viewport::DefaultCanvasItemTextureFilter tf = (Viewport::DefaultCanvasItemTextureFilter)current_filter; + scene_root->set_default_canvas_item_texture_filter(tf); + } + int current_repeat = GLOBAL_GET("rendering/textures/canvas_textures/default_texture_repeat"); + if (current_repeat != scene_root->get_default_canvas_item_texture_repeat()) { + Viewport::DefaultCanvasItemTextureRepeat tr = (Viewport::DefaultCanvasItemTextureRepeat)current_repeat; + scene_root->set_default_canvas_item_texture_repeat(tr); + } + + RS::DOFBokehShape dof_shape = RS::DOFBokehShape(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_shape"))); + RS::get_singleton()->camera_effects_set_dof_blur_bokeh_shape(dof_shape); + RS::DOFBlurQuality dof_quality = RS::DOFBlurQuality(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_quality"))); + bool dof_jitter = GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_use_jitter"); + RS::get_singleton()->camera_effects_set_dof_blur_quality(dof_quality, dof_jitter); + 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")); + 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")); + bool glow_bicubic = int(GLOBAL_GET("rendering/environment/glow/upscale_mode")) > 0; + RS::get_singleton()->environment_glow_set_use_bicubic_upscale(glow_bicubic); + bool glow_high_quality = GLOBAL_GET("rendering/environment/glow/use_high_quality"); + RS::get_singleton()->environment_glow_set_use_high_quality(glow_high_quality); + RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::EnvironmentSSRRoughnessQuality(int(GLOBAL_GET("rendering/environment/screen_space_reflection/roughness_quality"))); + RS::get_singleton()->environment_set_ssr_roughness_quality(ssr_roughness_quality); + RS::SubSurfaceScatteringQuality sss_quality = RS::SubSurfaceScatteringQuality(int(GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_quality"))); + RS::get_singleton()->sub_surface_scattering_set_quality(sss_quality); + float sss_scale = GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_scale"); + float sss_depth_scale = GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale"); + RS::get_singleton()->sub_surface_scattering_set_scale(sss_scale, sss_depth_scale); + + uint32_t directional_shadow_size = GLOBAL_GET("rendering/shadows/directional_shadow/size"); + uint32_t directional_shadow_16_bits = GLOBAL_GET("rendering/shadows/directional_shadow/16_bits"); + RS::get_singleton()->directional_shadow_atlas_set_size(directional_shadow_size, directional_shadow_16_bits); + + RS::ShadowQuality shadows_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/shadows/shadows/soft_shadow_quality"))); + RS::get_singleton()->shadows_quality_set(shadows_quality); + RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/shadows/directional_shadow/soft_shadow_quality"))); + RS::get_singleton()->directional_shadow_quality_set(directional_shadow_quality); + float probe_update_speed = GLOBAL_GET("rendering/lightmapping/probe_capture/update_speed"); + RS::get_singleton()->lightmap_set_probe_capture_update_speed(probe_update_speed); + RS::EnvironmentSDFGIFramesToConverge frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(int(GLOBAL_GET("rendering/global_illumination/sdfgi/frames_to_converge"))); + RS::get_singleton()->environment_set_sdfgi_frames_to_converge(frames_to_converge); + RS::EnvironmentSDFGIRayCount ray_count = RS::EnvironmentSDFGIRayCount(int(GLOBAL_GET("rendering/global_illumination/sdfgi/probe_ray_count"))); + RS::get_singleton()->environment_set_sdfgi_ray_count(ray_count); + RS::GIProbeQuality gi_probe_quality = RS::GIProbeQuality(int(GLOBAL_GET("rendering/global_illumination/gi_probes/quality"))); + RS::get_singleton()->gi_probe_set_quality(gi_probe_quality); + 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")); + RS::get_singleton()->environment_set_volumetric_fog_filter_active(bool(GLOBAL_GET("rendering/environment/volumetric_fog/use_filter"))); + RS::get_singleton()->canvas_set_shadow_texture_size(GLOBAL_GET("rendering/2d/shadow_atlas/size")); + + bool use_half_res_gi = GLOBAL_DEF("rendering/global_illumination/gi/use_half_resolution", false); + RS::get_singleton()->gi_set_use_half_resolution(use_half_res_gi); + + bool snap_2d_transforms = GLOBAL_GET("rendering/2d/snap/snap_2d_transforms_to_pixel"); + scene_root->set_snap_2d_transforms_to_pixel(snap_2d_transforms); + bool snap_2d_vertices = GLOBAL_GET("rendering/2d/snap/snap_2d_vertices_to_pixel"); + scene_root->set_snap_2d_vertices_to_pixel(snap_2d_vertices); + + Viewport::SDFOversize sdf_oversize = Viewport::SDFOversize(int(GLOBAL_GET("rendering/2d/sdf/oversize"))); + scene_root->set_sdf_oversize(sdf_oversize); + Viewport::SDFScale sdf_scale = Viewport::SDFScale(int(GLOBAL_GET("rendering/2d/sdf/scale"))); + scene_root->set_sdf_scale(sdf_scale); + + float lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels"); + scene_root->set_lod_threshold(lod_threshold); +} + void EditorNode::_notification(int p_what) { switch (p_what) { case NOTIFICATION_PROCESS: { @@ -468,66 +545,13 @@ void EditorNode::_notification(int p_what) { editor_selection->update(); - { //TODO should only happen on settings changed - int current_filter = GLOBAL_GET("rendering/canvas_textures/default_texture_filter"); - if (current_filter != scene_root->get_default_canvas_item_texture_filter()) { - Viewport::DefaultCanvasItemTextureFilter tf = (Viewport::DefaultCanvasItemTextureFilter)current_filter; - scene_root->set_default_canvas_item_texture_filter(tf); - } - int current_repeat = GLOBAL_GET("rendering/canvas_textures/default_texture_repeat"); - if (current_repeat != scene_root->get_default_canvas_item_texture_repeat()) { - Viewport::DefaultCanvasItemTextureRepeat tr = (Viewport::DefaultCanvasItemTextureRepeat)current_repeat; - scene_root->set_default_canvas_item_texture_repeat(tr); - } + ResourceImporterTexture::get_singleton()->update_imports(); - RS::DOFBokehShape dof_shape = RS::DOFBokehShape(int(GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_bokeh_shape"))); - RS::get_singleton()->camera_effects_set_dof_blur_bokeh_shape(dof_shape); - RS::DOFBlurQuality dof_quality = RS::DOFBlurQuality(int(GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_bokeh_quality"))); - bool dof_jitter = GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_use_jitter"); - RS::get_singleton()->camera_effects_set_dof_blur_quality(dof_quality, dof_jitter); - RS::get_singleton()->environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/quality/ssao/quality"))), GLOBAL_GET("rendering/quality/ssao/half_size")); - RS::get_singleton()->screen_space_roughness_limiter_set_active(GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_enabled"), GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_amount"), GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_limit")); - bool glow_bicubic = int(GLOBAL_GET("rendering/quality/glow/upscale_mode")) > 0; - RS::get_singleton()->environment_glow_set_use_bicubic_upscale(glow_bicubic); - bool glow_high_quality = GLOBAL_GET("rendering/quality/glow/use_high_quality"); - RS::get_singleton()->environment_glow_set_use_high_quality(glow_high_quality); - RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::EnvironmentSSRRoughnessQuality(int(GLOBAL_GET("rendering/quality/screen_space_reflection/roughness_quality"))); - RS::get_singleton()->environment_set_ssr_roughness_quality(ssr_roughness_quality); - RS::SubSurfaceScatteringQuality sss_quality = RS::SubSurfaceScatteringQuality(int(GLOBAL_GET("rendering/quality/subsurface_scattering/subsurface_scattering_quality"))); - RS::get_singleton()->sub_surface_scattering_set_quality(sss_quality); - float sss_scale = GLOBAL_GET("rendering/quality/subsurface_scattering/subsurface_scattering_scale"); - float sss_depth_scale = GLOBAL_GET("rendering/quality/subsurface_scattering/subsurface_scattering_depth_scale"); - RS::get_singleton()->sub_surface_scattering_set_scale(sss_scale, sss_depth_scale); - RS::ShadowQuality shadows_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/quality/shadows/soft_shadow_quality"))); - RS::get_singleton()->shadows_quality_set(shadows_quality); - RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/quality/directional_shadow/soft_shadow_quality"))); - RS::get_singleton()->directional_shadow_quality_set(directional_shadow_quality); - float probe_update_speed = GLOBAL_GET("rendering/lightmapper/probe_capture_update_speed"); - RS::get_singleton()->lightmap_set_probe_capture_update_speed(probe_update_speed); - RS::EnvironmentSDFGIFramesToConverge frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(int(GLOBAL_GET("rendering/sdfgi/frames_to_converge"))); - RS::get_singleton()->environment_set_sdfgi_frames_to_converge(frames_to_converge); - RS::EnvironmentSDFGIRayCount ray_count = RS::EnvironmentSDFGIRayCount(int(GLOBAL_GET("rendering/sdfgi/probe_ray_count"))); - RS::get_singleton()->environment_set_sdfgi_ray_count(ray_count); - RS::GIProbeQuality gi_probe_quality = RS::GIProbeQuality(int(GLOBAL_GET("rendering/quality/gi_probes/quality"))); - RS::get_singleton()->gi_probe_set_quality(gi_probe_quality); - RS::get_singleton()->environment_set_volumetric_fog_volume_size(GLOBAL_GET("rendering/volumetric_fog/volume_size"), GLOBAL_GET("rendering/volumetric_fog/volume_depth")); - RS::get_singleton()->environment_set_volumetric_fog_filter_active(bool(GLOBAL_GET("rendering/volumetric_fog/use_filter"))); - RS::get_singleton()->environment_set_volumetric_fog_directional_shadow_shrink_size(GLOBAL_GET("rendering/volumetric_fog/directional_shadow_shrink")); - RS::get_singleton()->environment_set_volumetric_fog_positional_shadow_shrink_size(GLOBAL_GET("rendering/volumetric_fog/positional_shadow_shrink")); - RS::get_singleton()->canvas_set_shadow_texture_size(GLOBAL_GET("rendering/quality/2d_shadow_atlas/size")); - - bool snap_2d_transforms = GLOBAL_GET("rendering/quality/2d/snap_2d_transforms_to_pixel"); - scene_root->set_snap_2d_transforms_to_pixel(snap_2d_transforms); - bool snap_2d_vertices = GLOBAL_GET("rendering/quality/2d/snap_2d_vertices_to_pixel"); - scene_root->set_snap_2d_vertices_to_pixel(snap_2d_vertices); - - Viewport::SDFOversize sdf_oversize = Viewport::SDFOversize(int(GLOBAL_GET("rendering/quality/2d_sdf/oversize"))); - scene_root->set_sdf_oversize(sdf_oversize); - Viewport::SDFScale sdf_scale = Viewport::SDFScale(int(GLOBAL_GET("rendering/quality/2d_sdf/scale"))); - scene_root->set_sdf_scale(sdf_scale); + if (settings_changed) { + _update_from_settings(); + settings_changed = false; + emit_signal("project_settings_changed"); } - - ResourceImporterTexture::get_singleton()->update_imports(); } break; case NOTIFICATION_ENTER_TREE: { @@ -577,6 +601,9 @@ void EditorNode::_notification(int p_what) { _editor_select(EDITOR_3D); } + // Save the project after opening to mark it as last modified. + ProjectSettings::get_singleton()->save(); + /* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */ } break; @@ -585,6 +612,7 @@ void EditorNode::_notification(int p_what) { OS::get_singleton()->set_low_processor_usage_mode_sleep_usec(int(EDITOR_GET("interface/editor/low_processor_mode_sleep_usec"))); EditorFileSystem::get_singleton()->scan_changes(); + _scan_external_changes(); } break; case NOTIFICATION_APPLICATION_FOCUS_OUT: { @@ -602,7 +630,7 @@ void EditorNode::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { scene_tabs->set_tab_close_display_policy((bool(EDITOR_GET("interface/scene_tabs/always_show_close_button")) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY)); - theme = create_editor_theme(theme_base->get_theme()); + theme = create_custom_theme(theme_base->get_theme()); theme_base->set_theme(theme); gui_base->set_theme(theme); @@ -610,8 +638,8 @@ void EditorNode::_notification(int p_what) { gui_base->add_theme_style_override("panel", gui_base->get_theme_stylebox("Background", "EditorStyles")); scene_root_parent->add_theme_style_override("panel", gui_base->get_theme_stylebox("Content", "EditorStyles")); bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox("panel", "TabContainer")); - scene_tabs->add_theme_style_override("tab_fg", gui_base->get_theme_stylebox("SceneTabFG", "EditorStyles")); - scene_tabs->add_theme_style_override("tab_bg", gui_base->get_theme_stylebox("SceneTabBG", "EditorStyles")); + scene_tabs->add_theme_style_override("tab_selected", gui_base->get_theme_stylebox("SceneTabFG", "EditorStyles")); + scene_tabs->add_theme_style_override("tab_unselected", gui_base->get_theme_stylebox("SceneTabBG", "EditorStyles")); file_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox("MenuHover", "EditorStyles")); project_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox("MenuHover", "EditorStyles")); @@ -673,11 +701,16 @@ void EditorNode::_notification(int p_what) { p->set_item_icon(p->get_item_index(HELP_SEARCH), gui_base->get_theme_icon("HelpSearch", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_theme_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_theme_icon("Instance", "EditorIcons")); - p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_theme_icon("Godot", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_REPORT_A_BUG), gui_base->get_theme_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_theme_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_COMMUNITY), gui_base->get_theme_icon("Instance", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_theme_icon("Godot", "EditorIcons")); + p->set_item_icon(p->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), gui_base->get_theme_icon("Heart", "EditorIcons")); + + for (int i = 0; i < main_editor_buttons.size(); i++) { + main_editor_buttons.write[i]->add_theme_font_override("font", gui_base->get_theme_font("main_button_font", "EditorFonts")); + main_editor_buttons.write[i]->add_theme_font_size_override("font_size", gui_base->get_theme_font_size("main_button_font_size", "EditorFonts")); + } _update_update_spinner(); } break; @@ -712,6 +745,18 @@ void EditorNode::_on_plugin_ready(Object *p_script, const String &p_activate_nam push_item(script.operator->()); } +void EditorNode::_remove_plugin_from_enabled(const String &p_name) { + ProjectSettings *ps = ProjectSettings::get_singleton(); + PackedStringArray enabled_plugins = ps->get("editor_plugins/enabled"); + for (int i = 0; i < enabled_plugins.size(); ++i) { + if (enabled_plugins.get(i) == p_name) { + enabled_plugins.remove(i); + break; + } + } + ps->set("editor_plugins/enabled", enabled_plugins); +} + void EditorNode::_resources_changed(const Vector<String> &p_resources) { List<Ref<Resource>> changed; @@ -773,17 +818,27 @@ void EditorNode::_fs_changed() { } preset.unref(); } + if (preset.is_null()) { - export_error = vformat( - "Invalid export preset name: %s. Make sure `export_presets.cfg` is present in the current directory.", - preset_name); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (da->file_exists("res://export_presets.cfg")) { + export_error = vformat( + "Invalid export preset name: %s.\nThe following presets were detected in this project's `export_presets.cfg`:\n\n", + preset_name); + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); ++i) { + // Write the preset name between double quotes since it needs to be written between quotes on the command line if it contains spaces. + export_error += vformat(" \"%s\"\n", EditorExport::get_singleton()->get_export_preset(i)->get_name()); + } + } else { + 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."; + } } else { Ref<EditorExportPlatform> platform = preset->get_platform(); - const String export_path = export_defer.path.empty() ? preset->get_export_path() : export_defer.path; - if (export_path.empty()) { - export_error = vformat("Export preset '%s' doesn't have a default export path, and none was specified.", preset_name); + const String export_path = export_defer.path.is_empty() ? preset->get_export_path() : export_defer.path; + if (export_path.is_empty()) { + export_error = vformat("Export preset \"%s\" doesn't have a default export path, and none was specified.", preset_name); } else if (platform.is_null()) { - export_error = vformat("Export preset '%s' doesn't have a matching platform.", preset_name); + export_error = vformat("Export preset \"%s\" doesn't have a matching platform.", preset_name); } else { Error err = OK; if (export_defer.pack_only) { // Only export .pck or .zip data pack. @@ -796,7 +851,7 @@ void EditorNode::_fs_changed() { String config_error; bool missing_templates; if (!platform->can_export(preset, config_error, missing_templates)) { - ERR_PRINT(vformat("Cannot export project with preset '%s' due to configuration errors:\n%s", preset_name, config_error)); + ERR_PRINT(vformat("Cannot export project with preset \"%s\" due to configuration errors:\n%s", preset_name, config_error)); err = missing_templates ? ERR_FILE_NOT_FOUND : ERR_UNCONFIGURED; } else { err = platform->export_project(preset, export_defer.debug, export_path); @@ -806,19 +861,19 @@ void EditorNode::_fs_changed() { case OK: break; case ERR_FILE_NOT_FOUND: - export_error = vformat("Project export failed for preset '%s', the export template appears to be missing.", preset_name); + export_error = vformat("Project export failed for preset \"%s\". The export template appears to be missing.", preset_name); break; case ERR_FILE_BAD_PATH: - export_error = vformat("Project export failed for preset '%s', the target path '%s' appears to be invalid.", preset_name, export_path); + export_error = vformat("Project export failed for preset \"%s\". The target path \"%s\" appears to be invalid.", preset_name, export_path); break; default: - export_error = vformat("Project export failed with error code %d for preset '%s'.", (int)err, preset_name); + export_error = vformat("Project export failed with error code %d for preset \"%s\".", (int)err, preset_name); break; } } } - if (!export_error.empty()) { + if (!export_error.is_empty()) { ERR_PRINT(export_error); OS::get_singleton()->set_exit_code(EXIT_FAILURE); } @@ -877,9 +932,90 @@ void EditorNode::_sources_changed(bool p_exist) { } } +void EditorNode::_scan_external_changes() { + disk_changed_list->clear(); + TreeItem *r = disk_changed_list->create_item(); + disk_changed_list->set_hide_root(true); + bool need_reload = false; + + // Check if any edited scene has changed. + + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (editor_data.get_scene_path(i) == "" || !da->file_exists(editor_data.get_scene_path(i))) { + continue; + } + + uint64_t last_date = editor_data.get_scene_modified_time(i); + uint64_t date = FileAccess::get_modified_time(editor_data.get_scene_path(i)); + + if (date > last_date) { + TreeItem *ti = disk_changed_list->create_item(r); + ti->set_text(0, editor_data.get_scene_path(i).get_file()); + need_reload = true; + } + } + + String project_settings_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("project.godot"); + if (FileAccess::get_modified_time(project_settings_path) > ProjectSettings::get_singleton()->get_last_saved_time()) { + TreeItem *ti = disk_changed_list->create_item(r); + ti->set_text(0, "project.godot"); + need_reload = true; + } + + if (need_reload) { + disk_changed->call_deferred("popup_centered_ratio", 0.5); + } +} + +void EditorNode::_resave_scenes(String p_str) { + save_all_scenes(); + ProjectSettings::get_singleton()->save(); + disk_changed->hide(); +} + +void EditorNode::_reload_modified_scenes() { + int current_idx = editor_data.get_edited_scene(); + + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + if (editor_data.get_scene_path(i) == "") { + continue; + } + + uint64_t last_date = editor_data.get_scene_modified_time(i); + uint64_t date = FileAccess::get_modified_time(editor_data.get_scene_path(i)); + + if (date > last_date) { + String filename = editor_data.get_scene_path(i); + editor_data.set_edited_scene(i); + _remove_edited_scene(false); + + Error err = load_scene(filename, false, false, true, false, true); + if (err != OK) { + ERR_PRINT(vformat("Failed to load scene: %s", filename)); + } + editor_data.move_edited_scene_to_index(i); + } + } + + get_undo_redo()->clear_history(false); + set_current_scene(current_idx); + _update_scene_tabs(); + disk_changed->hide(); +} + +void EditorNode::_reload_project_settings() { + ProjectSettings::get_singleton()->setup(ProjectSettings::get_singleton()->get_resource_path(), String(), true); + settings_changed = true; +} + void EditorNode::_vp_resized() { } +void EditorNode::_version_button_pressed() { + DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY)); +} + void EditorNode::_node_renamed() { if (get_inspector()) { get_inspector()->update_tree(); @@ -918,7 +1054,7 @@ Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_d dependency_errors.clear(); Error err; - RES res = ResourceLoader::load(p_resource, "", false, &err); + RES res = ResourceLoader::load(p_resource, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); ERR_FAIL_COND_V(!res.is_valid(), ERR_CANT_OPEN); if (!p_ignore_broken_deps && dependency_errors.has(p_resource)) { @@ -996,13 +1132,23 @@ void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String file->clear_filters(); List<String> preferred; - for (int i = 0; i < extensions.size(); i++) { - if (p_resource->is_class("Script") && (extensions[i] == "tres" || extensions[i] == "res" || extensions[i] == "xml")) { + for (List<String>::Element *E = extensions.front(); E; E = E->next()) { + if (p_resource->is_class("Script") && (E->get() == "tres" || E->get() == "res")) { //this serves no purpose and confused people continue; } - file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); - preferred.push_back(extensions[i]); + file->add_filter("*." + E->get() + " ; " + E->get().to_upper()); + preferred.push_back(E->get()); + } + // Lowest priority extension + List<String>::Element *res_element = preferred.find("res"); + if (res_element) { + preferred.move_to_back(res_element); + } + // Highest priority extension + List<String>::Element *tres_element = preferred.find("tres"); + if (tres_element) { + preferred.move_to_front(tres_element); } if (p_at_path != String()) { @@ -1271,18 +1417,19 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { // which would result in an invalid texture. if (c3d == 0 && c2d == 0) { img.instance(); - img->create(1, 1, 0, Image::FORMAT_RGB8); + img->create(1, 1, false, Image::FORMAT_RGB8); } else if (c3d < c2d) { Ref<ViewportTexture> viewport_texture = scene_root->get_texture(); if (viewport_texture->get_width() > 0 && viewport_texture->get_height() > 0) { - img = viewport_texture->get_data(); + img = viewport_texture->get_image(); } } else { // The 3D editor may be disabled as a feature, but scenes can still be opened. // This check prevents the preview from regenerating in case those scenes are then saved. + // The preview will be generated if no feature profile is set (as the 3D editor is enabled by default). Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile(); - if (profile.is_valid() && !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)) { - img = Node3DEditor::get_singleton()->get_editor_viewport(0)->get_viewport_node()->get_texture()->get_data(); + if (!profile.is_valid() || !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)) { + img = Node3DEditor::get_singleton()->get_editor_viewport(0)->get_viewport_node()->get_texture()->get_image(); } } @@ -1316,7 +1463,7 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { img->convert(Image::FORMAT_RGB8); //save thumbnail directly, as thumbnailer may not update due to actual scene not changing md5 - String temp_path = EditorSettings::get_singleton()->get_cache_dir(); + String temp_path = EditorPaths::get_singleton()->get_cache_dir(); String cache_base = ProjectSettings::get_singleton()->globalize_path(p_file).md5_text(); cache_base = temp_path.plus_file("resthumb-" + cache_base); @@ -1416,6 +1563,17 @@ int EditorNode::_save_external_resources() { return saved; } +static void _reset_animation_players(Node *p_node, List<Ref<AnimatedValuesBackup>> *r_anim_backups) { + for (int i = 0; i < p_node->get_child_count(); i++) { + AnimationPlayer *player = Object::cast_to<AnimationPlayer>(p_node->get_child(i)); + if (player && player->is_reset_on_save_enabled() && player->can_apply_reset()) { + Ref<AnimatedValuesBackup> old_values = player->apply_reset(); + r_anim_backups->push_back(old_values); + } + _reset_animation_players(p_node->get_child(i), r_anim_backups); + } +} + void EditorNode::_save_scene(String p_file, int idx) { Node *scene = editor_data.get_edited_scene_root(idx); @@ -1430,6 +1588,8 @@ void EditorNode::_save_scene(String p_file, int idx) { } editor_data.apply_changes_in_editors(); + List<Ref<AnimatedValuesBackup>> anim_backups; + _reset_animation_players(scene, &anim_backups); _save_default_environment(); _set_scene_metadata(p_file, idx); @@ -1477,6 +1637,11 @@ void EditorNode::_save_scene(String p_file, int idx) { _save_external_resources(); editor_data.save_editor_external_data(); + + for (List<Ref<AnimatedValuesBackup>>::Element *E = anim_backups.front(); E; E = E->next()) { + E->get()->restore(); + } + if (err == OK) { scene->set_filename(ProjectSettings::get_singleton()->localize_path(p_file)); if (idx < 0 || idx == editor_data.get_edited_scene()) { @@ -1484,6 +1649,7 @@ void EditorNode::_save_scene(String p_file, int idx) { } else { editor_data.set_edited_scene_version(0, idx); } + editor_data.set_scene_modified_time(idx, FileAccess::get_modified_time(p_file)); editor_folding.save_scene_folding(scene, p_file); @@ -1656,28 +1822,6 @@ void EditorNode::_dialog_action(String p_file) { } } break; - case FILE_EXPORT_TILESET: { - Ref<TileSet> tileset; - if (FileAccess::exists(p_file) && file_export_lib_merge->is_pressed()) { - tileset = ResourceLoader::load(p_file, "TileSet"); - - if (tileset.is_null()) { - show_accept(TTR("Can't load TileSet for merging!"), TTR("OK")); - return; - } - - } else { - tileset = Ref<TileSet>(memnew(TileSet)); - } - - TileSetEditor::update_library_file(editor_data.get_edited_scene_root(), tileset, true); - - Error err = ResourceSaver::save(p_file, tileset); - if (err) { - show_accept(TTR("Error saving TileSet!"), TTR("OK")); - return; - } - } break; case RESOURCE_SAVE: case RESOURCE_SAVE_AS: { @@ -1687,10 +1831,10 @@ void EditorNode::_dialog_action(String p_file) { ObjectID current = editor_history.get_current(); Object *current_obj = current.is_valid() ? ObjectDB::get_instance(current) : nullptr; ERR_FAIL_COND(!current_obj); - current_obj->_change_notify(); + current_obj->notify_property_list_changed(); } break; case SETTINGS_LAYOUT_SAVE: { - if (p_file.empty()) { + if (p_file.is_empty()) { return; } @@ -1718,7 +1862,7 @@ void EditorNode::_dialog_action(String p_file) { } break; case SETTINGS_LAYOUT_DELETE: { - if (p_file.empty()) { + if (p_file.is_empty()) { return; } @@ -1801,7 +1945,7 @@ void EditorNode::edit_item(Object *p_object) { sub_plugins = editor_data.get_subeditors(p_object); } - if (!sub_plugins.empty()) { + if (!sub_plugins.is_empty()) { bool same = true; if (sub_plugins.size() == editor_plugins_over->get_plugins_list().size()) { for (int i = 0; i < sub_plugins.size(); i++) { @@ -1829,6 +1973,7 @@ void EditorNode::push_item(Object *p_object, const String &p_property, bool p_in node_dock->set_node(nullptr); scene_tree_dock->set_selected(nullptr); inspector_dock->update(nullptr); + _display_top_editors(false); return; } @@ -1977,7 +2122,7 @@ void EditorNode::_edit_current() { multi_nodes.push_back(node); } } - if (!multi_nodes.empty()) { + if (!multi_nodes.is_empty()) { // Pick the top-most node multi_nodes.sort_custom<Node::Comparator>(); selected_node = multi_nodes.front()->get(); @@ -2059,13 +2204,13 @@ void EditorNode::_edit_current() { sub_plugins = editor_data.get_subeditors(current_obj); } - if (!sub_plugins.empty()) { + if (!sub_plugins.is_empty()) { _display_top_editors(false); _set_top_editors(sub_plugins); _set_editing_top_editors(current_obj); _display_top_editors(true); - } else if (!editor_plugins_over->get_plugins_list().empty()) { + } else if (!editor_plugins_over->get_plugins_list().is_empty()) { hide_top_editors(); } } @@ -2102,7 +2247,10 @@ void EditorNode::_run(bool p_current, const String &p_custom) { if (scene->get_filename() == "") { current_option = -1; - _menu_option_confirm(FILE_SAVE_BEFORE_RUN, false); + _menu_option(FILE_SAVE_AS_SCENE); + // Set the option to save and run so when the dialog is accepted, the scene runs. + current_option = FILE_SAVE_AND_RUN; + file->set_title(TTR("Save scene before running...")); return; } @@ -2145,7 +2293,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) { List<String> breakpoints; editor_data.get_editor_breakpoints(&breakpoints); - args = ProjectSettings::get_singleton()->get("editor/main_run_args"); + args = ProjectSettings::get_singleton()->get("editor/run/main_run_args"); skip_breakpoints = EditorDebuggerNode::get_singleton()->is_skip_breakpoints(); EditorDebuggerNode::get_singleton()->start(); @@ -2235,7 +2383,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; case FILE_OPEN_PREV: { - if (previous_scenes.empty()) { + if (previous_scenes.is_empty()) { break; } opening_prev = true; @@ -2268,13 +2416,17 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { case FILE_CLOSE: { if (!p_confirmed) { tab_closing = p_option == FILE_CLOSE ? editor_data.get_edited_scene() : _next_unsaved_scene(false); + _scene_tab_changed(tab_closing); if (unsaved_cache || p_option == FILE_CLOSE_ALL_AND_QUIT || p_option == FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER) { - String scene_filename = editor_data.get_edited_scene_root(tab_closing)->get_filename(); - save_confirmation->get_ok()->set_text(TTR("Save & Close")); - save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), scene_filename != "" ? scene_filename : "unsaved scene")); - save_confirmation->popup_centered(); - break; + Node *scene_root = editor_data.get_edited_scene_root(tab_closing); + if (scene_root) { + String scene_filename = scene_root->get_filename(); + save_confirmation->get_ok_button()->set_text(TTR("Save & Close")); + save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), scene_filename != "" ? scene_filename : "unsaved scene")); + save_confirmation->popup_centered(); + break; + } } } else if (p_option == FILE_CLOSE) { tab_closing = editor_data.get_edited_scene(); @@ -2314,15 +2466,24 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { Node *scene = editor_data.get_edited_scene_root(scene_idx); if (!scene) { - int saved = _save_external_resources(); - String err_text; + if (p_option == FILE_SAVE_SCENE) { + // Pressing Ctrl + S saves the current script if a scene is currently open, but it won't if the scene has no root node. + // Work around this by explicitly saving the script in this case (similar to pressing Ctrl + Alt + S). + ScriptEditor::get_singleton()->save_current_script(); + } + + const int saved = _save_external_resources(); if (saved > 0) { - err_text = vformat(TTR("Saved %s modified resource(s)."), itos(saved)); - } else { - err_text = TTR("A root node is required to save the scene."); + show_accept( + vformat(TTR("The current scene has no root node, but %d modified external resource(s) were saved anyway."), saved), + TTR("OK")); + } else if (p_option == FILE_SAVE_AS_SCENE) { + // Don't show this dialog when pressing Ctrl + S to avoid interfering with script saving. + show_accept( + TTR("A root node is required to save the scene. You can add a root node using the Scene tree dock."), + TTR("OK")); } - show_accept(err_text, TTR("OK")); break; } @@ -2337,11 +2498,12 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } if (scene->get_filename() != "") { - file->set_current_path(scene->get_filename()); + String path = scene->get_filename(); + file->set_current_path(path); if (extensions.size()) { - String ext = scene->get_filename().get_extension().to_lower(); + String ext = path.get_extension().to_lower(); if (extensions.find(ext) == nullptr) { - file->set_current_path(scene->get_filename().replacen("." + ext, "." + extensions.front()->get())); + file->set_current_path(path.replacen("." + ext, "." + extensions.front()->get())); } } } else { @@ -2360,18 +2522,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { case FILE_SAVE_ALL_SCENES: { _save_all_scenes(); } break; - case FILE_SAVE_BEFORE_RUN: { - if (!p_confirmed) { - confirmation->get_cancel()->set_text(TTR("No")); - confirmation->get_ok()->set_text(TTR("Yes")); - confirmation->set_text(TTR("This scene has never been saved. Save before running?")); - confirmation->popup_centered(); - break; - } - - _menu_option(FILE_SAVE_AS_SCENE); - _menu_option_confirm(FILE_SAVE_AND_RUN, false); - } break; case FILE_EXPORT_PROJECT: { project_export->popup_export(); @@ -2395,39 +2545,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { file_export_lib->set_title(TTR("Export Mesh Library")); } break; - case FILE_EXPORT_TILESET: { - //Make sure that the scene has a root before trying to convert to tileset - if (!editor_data.get_edited_scene_root()) { - show_accept(TTR("This operation can't be done without a root node."), TTR("OK")); - break; - } - - List<String> extensions; - Ref<TileSet> ml(memnew(TileSet)); - ResourceSaver::get_recognized_extensions(ml, &extensions); - file_export_lib->clear_filters(); - for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - file_export_lib->add_filter("*." + E->get()); - } - - file_export_lib->popup_file_dialog(); - file_export_lib->set_title(TTR("Export Tile Set")); - - } break; - - case FILE_IMPORT_SUBSCENE: { - if (!editor_data.get_edited_scene_root()) { - show_accept(TTR("This operation can't be done without a selected node."), TTR("OK")); - break; - } - - scene_tree_dock->import_subscene(); - - } break; case FILE_EXTERNAL_OPEN_SCENE: { if (unsaved_cache && !p_confirmed) { - confirmation->get_ok()->set_text(TTR("Open")); + confirmation->get_ok_button()->set_text(TTR("Open")); confirmation->set_text(TTR("Current scene not saved. Open anyway?")); confirmation->popup_centered(); break; @@ -2483,7 +2604,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } if (unsaved_cache && !p_confirmed) { - confirmation->get_ok()->set_text(TTR("Reload Saved Scene")); + confirmation->get_ok_button()->set_text(TTR("Reload Saved Scene")); confirmation->set_text( TTR("The current scene has unsaved changes.\nReload the saved scene anyway? This action cannot be undone.")); confirmation->popup_centered(); @@ -2506,7 +2627,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; case RUN_PLAY_CUSTOM_SCENE: { - if (run_custom_filename.empty() || editor_run.get_status() == EditorRun::STATUS_STOP) { + if (run_custom_filename.is_empty() || editor_run.get_status() == EditorRun::STATUS_STOP) { _menu_option_confirm(RUN_STOP, true); quick_run->popup_dialog("PackedScene", true); quick_run->set_title(TTR("Quick Run Scene...")); @@ -2585,15 +2706,8 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { if (!p_confirmed) { bool save_each = EDITOR_GET("interface/editor/save_each_scene_on_quit"); if (_next_unsaved_scene(!save_each) == -1) { - bool confirm = EDITOR_GET("interface/editor/quit_confirmation"); - if (confirm) { - confirmation->get_ok()->set_text(p_option == FILE_QUIT ? TTR("Quit") : TTR("Yes")); - confirmation->set_text(p_option == FILE_QUIT ? TTR("Exit the editor?") : TTR("Open Project Manager?")); - confirmation->popup_centered(); - } else { - _discard_changes(); - break; - } + _discard_changes(); + break; } else { if (save_each) { _menu_option_confirm(p_option == FILE_QUIT ? FILE_CLOSE_ALL_AND_QUIT : FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER, false); @@ -2605,7 +2719,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { i = _next_unsaved_scene(true, ++i); } - save_confirmation->get_ok()->set_text(TTR("Save & Quit")); + save_confirmation->get_ok_button()->set_text(TTR("Save & Quit")); save_confirmation->set_text((p_option == FILE_QUIT ? TTR("Save changes to the following scene(s) before quitting?") : TTR("Save changes the following scene(s) before opening Project Manager?")) + unsaved_scenes); save_confirmation->popup_centered(); } @@ -2637,10 +2751,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { settings_config_dialog->popup_edit_settings(); } break; case SETTINGS_EDITOR_DATA_FOLDER: { - OS::get_singleton()->shell_open(String("file://") + EditorSettings::get_singleton()->get_data_dir()); + OS::get_singleton()->shell_open(String("file://") + EditorPaths::get_singleton()->get_data_dir()); } break; case SETTINGS_EDITOR_CONFIG_FOLDER: { - OS::get_singleton()->shell_open(String("file://") + EditorSettings::get_singleton()->get_settings_dir()); + OS::get_singleton()->shell_open(String("file://") + EditorPaths::get_singleton()->get_settings_dir()); } break; case SETTINGS_MANAGE_EXPORT_TEMPLATES: { export_template_manager->popup_manager(); @@ -2699,9 +2813,12 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { case HELP_ABOUT: { about->popup_centered(Size2(780, 500) * EDSCALE); } break; + case HELP_SUPPORT_GODOT_DEVELOPMENT: { + OS::get_singleton()->shell_open("https://godotengine.org/donate"); + } break; case SET_VIDEO_DRIVER_SAVE_AND_RESTART: { - ProjectSettings::get_singleton()->set("rendering/quality/driver/driver_name", video_driver_request); + ProjectSettings::get_singleton()->set("rendering/driver/driver_name", video_driver_request); ProjectSettings::get_singleton()->save(); save_all_scenes(); @@ -2724,14 +2841,14 @@ void EditorNode::_screenshot(bool p_use_utc) { } void EditorNode::_save_screenshot(NodePath p_path) { - Control *editor_viewport = EditorInterface::get_singleton()->get_editor_viewport(); - ERR_FAIL_COND_MSG(!editor_viewport, "Cannot get editor viewport."); - Viewport *viewport = editor_viewport->get_viewport(); - ERR_FAIL_COND_MSG(!viewport, "Cannot get editor viewport."); + Control *editor_main_control = EditorInterface::get_singleton()->get_editor_main_control(); + ERR_FAIL_COND_MSG(!editor_main_control, "Cannot get editor main control."); + Viewport *viewport = editor_main_control->get_viewport(); + ERR_FAIL_COND_MSG(!viewport, "Cannot get editor main control viewport."); Ref<ViewportTexture> texture = viewport->get_texture(); - ERR_FAIL_COND_MSG(texture.is_null(), "Cannot get editor viewport texture."); - Ref<Image> img = texture->get_data(); - ERR_FAIL_COND_MSG(img.is_null(), "Cannot get editor viewport texture image."); + ERR_FAIL_COND_MSG(texture.is_null(), "Cannot get editor main control viewport texture."); + Ref<Image> img = texture->get_image(); + ERR_FAIL_COND_MSG(img.is_null(), "Cannot get editor main control viewport texture image."); Error error = img->save_png(p_path); ERR_FAIL_COND_MSG(error != OK, "Cannot save screenshot to file '" + p_path + "'."); } @@ -2743,16 +2860,13 @@ void EditorNode::_tool_menu_option(int p_idx) { } break; case TOOLS_CUSTOM: { if (tool_menu->get_item_submenu(p_idx) == "") { - Array params = tool_menu->get_item_metadata(p_idx); - - Object *handler = ObjectDB::get_instance(params[0]); - String callback = params[1]; - Variant *ud = ¶ms[2]; + Callable callback = tool_menu->get_item_metadata(p_idx); Callable::CallError ce; + Variant result; + callback.call(nullptr, 0, result, ce); - handler->call(callback, (const Variant **)&ud, 1, ce); if (ce.error != Callable::CallError::CALL_OK) { - String err = Variant::get_call_error_text(handler, callback, (const Variant **)&ud, 1, ce); + String err = Variant::get_callable_error_text(callback, nullptr, 0, ce); ERR_PRINT("Error calling function from tool menu: " + err); } } // else it's a submenu so don't do anything. @@ -2784,7 +2898,7 @@ void EditorNode::_exit_editor() { _save_docks(); // Dim the editor window while it's quitting to make it clearer that it's busy - dim_editor(true, true); + dim_editor(true); get_tree()->quit(); } @@ -2810,6 +2924,10 @@ void EditorNode::_discard_changes(const String &p_str) { _update_scene_tabs(); if (current_option == FILE_CLOSE_ALL_AND_QUIT || current_option == FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER) { + // If restore tabs is enabled, reopen the scene that has just been closed, so it's remembered properly. + if (bool(EDITOR_GET("interface/scene_tabs/restore_scenes_on_load"))) { + _menu_option_confirm(FILE_OPEN_PREV, true); + } if (_next_unsaved_scene(false) == -1) { current_option = current_option == FILE_CLOSE_ALL_AND_QUIT ? FILE_QUIT : RUN_PROJECT_MANAGER; _discard_changes(); @@ -2845,8 +2963,7 @@ void EditorNode::_discard_changes(const String &p_str) { args.push_back(exec.get_base_dir()); args.push_back("--project-manager"); - OS::ProcessID pid = 0; - Error err = OS::get_singleton()->execute(exec, args, false, &pid); + Error err = OS::get_singleton()->create_process(exec, args); ERR_FAIL_COND(err); } break; } @@ -2858,7 +2975,7 @@ void EditorNode::_update_file_menu_opened() { Ref<Shortcut> reopen_closed_scene_sc = ED_GET_SHORTCUT("editor/reopen_closed_scene"); reopen_closed_scene_sc->set_name(TTR("Reopen Closed Scene")); PopupMenu *pop = file_menu->get_popup(); - pop->set_item_disabled(pop->get_item_index(FILE_OPEN_PREV), previous_scenes.empty()); + pop->set_item_disabled(pop->get_item_index(FILE_OPEN_PREV), previous_scenes.is_empty()); } void EditorNode::_update_file_menu_closed() { @@ -2866,8 +2983,8 @@ void EditorNode::_update_file_menu_closed() { pop->set_item_disabled(pop->get_item_index(FILE_OPEN_PREV), false); } -Control *EditorNode::get_viewport() { - return viewport; +Control *EditorNode::get_main_control() { + return main_control; } void EditorNode::_editor_select(int p_which) { @@ -2947,6 +3064,9 @@ void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed tb->set_icon(singleton->gui_base->get_theme_icon(p_editor->get_name(), "EditorIcons")); } + tb->add_theme_font_override("font", singleton->gui_base->get_theme_font("main_button_font", "EditorFonts")); + tb->add_theme_font_size_override("font_size", singleton->gui_base->get_theme_font_size("main_button_font_size", "EditorFonts")); + tb->set_name(p_editor->get_name()); singleton->main_editor_buttons.push_back(tb); singleton->main_editor_button_vb->add_child(tb); @@ -2983,10 +3103,11 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor, bool p_config_chan if (p_config_changed) { p_editor->disable_plugin(); } - singleton->editor_plugins_over->get_plugins_list().erase(p_editor); + singleton->editor_plugins_over->remove_plugin(p_editor); + singleton->editor_plugins_force_over->remove_plugin(p_editor); + singleton->editor_plugins_force_input_forwarding->remove_plugin(p_editor); singleton->remove_child(p_editor); singleton->editor_data.remove_editor_plugin(p_editor); - singleton->get_editor_plugins_force_input_forwarding()->remove_plugin(p_editor); } void EditorNode::_update_addon_config() { @@ -3024,29 +3145,19 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, Ref<ConfigFile> cf; cf.instance(); - String addon_path = String("res://addons").plus_file(p_addon).plus_file("plugin.cfg"); - if (!DirAccess::exists(addon_path.get_base_dir())) { - ProjectSettings *ps = ProjectSettings::get_singleton(); - PackedStringArray enabled_plugins = ps->get("editor_plugins/enabled"); - for (int i = 0; i < enabled_plugins.size(); ++i) { - if (enabled_plugins.get(i) == p_addon) { - enabled_plugins.remove(i); - break; - } - } - ps->set("editor_plugins/enabled", enabled_plugins); - ps->save(); + if (!DirAccess::exists(p_addon.get_base_dir())) { + _remove_plugin_from_enabled(p_addon); WARN_PRINT("Addon '" + p_addon + "' failed to load. No directory found. Removing from enabled plugins."); return; } - Error err = cf->load(addon_path); + Error err = cf->load(p_addon); if (err != OK) { - show_warning(vformat(TTR("Unable to enable addon plugin at: '%s' parsing of config failed."), addon_path)); + show_warning(vformat(TTR("Unable to enable addon plugin at: '%s' parsing of config failed."), p_addon)); return; } if (!cf->has_section_key("plugin", "script")) { - show_warning(vformat(TTR("Unable to find script field for addon plugin at: 'res://addons/%s'."), p_addon)); + show_warning(vformat(TTR("Unable to find script field for addon plugin at: '%s'."), p_addon)); return; } @@ -3055,7 +3166,7 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, // Only try to load the script if it has a name. Else, the plugin has no init script. if (script_path.length() > 0) { - script_path = String("res://addons").plus_file(p_addon).plus_file(script_path); + script_path = p_addon.get_base_dir().plus_file(script_path); script = ResourceLoader::load(script_path); if (script.is_null()) { @@ -3065,7 +3176,8 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled, // Errors in the script cause the base_type to be an empty string. if (String(script->get_instance_base_type()) == "") { - show_warning(vformat(TTR("Unable to load addon script from path: '%s' There seems to be an error in the code, please check the syntax."), script_path)); + 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, p_addon)); + _remove_plugin_from_enabled(p_addon); return; } @@ -3313,7 +3425,7 @@ int EditorNode::new_scene() { return idx; } -Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, bool p_set_inherited, bool p_clear_errors, bool p_force_open_imported) { +Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, bool p_set_inherited, bool p_clear_errors, bool p_force_open_imported, bool p_silent_change_tab) { if (!is_inside_tree()) { defer_load_scene = p_scene; return OK; @@ -3353,14 +3465,16 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b if (!editor_data.get_edited_scene_root() && editor_data.get_edited_scene_count() == 2) { _remove_edited_scene(); - } else { + } else if (!p_silent_change_tab) { _scene_tab_changed(idx); + } else { + set_current_scene(idx); } dependency_errors.clear(); Error err; - Ref<PackedScene> sdata = ResourceLoader::load(lpath, "", true, &err); + Ref<PackedScene> sdata = ResourceLoader::load(lpath, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err); if (!sdata.is_valid()) { _dialog_display_load_error(lpath, err); opening_prev = false; @@ -3622,10 +3736,15 @@ bool EditorNode::is_scene_in_use(const String &p_path) { return false; } +void EditorNode::register_editor_paths(bool p_for_project_manager) { + EditorPaths::create(p_for_project_manager); +} + void EditorNode::register_editor_types() { ResourceLoader::set_timestamp_on_load(true); ResourceSaver::set_timestamp_on_save(true); + ClassDB::register_class<EditorPaths>(); ClassDB::register_class<EditorPlugin>(); ClassDB::register_class<EditorTranslationParserPlugin>(); ClassDB::register_class<EditorImportPlugin>(); @@ -3654,6 +3773,11 @@ void EditorNode::register_editor_types() { ClassDB::register_class<ScriptCreateDialog>(); ClassDB::register_class<EditorFeatureProfile>(); ClassDB::register_class<EditorSpinSlider>(); + ClassDB::register_class<EditorResourcePicker>(); + ClassDB::register_class<EditorScriptPicker>(); + ClassDB::register_class<EditorSceneImporterMesh>(); + ClassDB::register_class<EditorSceneImporterMeshNode3D>(); + ClassDB::register_virtual_class<FileSystemDock>(); // FIXME: Is this stuff obsolete, or should it be ported to new APIs? @@ -3664,6 +3788,9 @@ void EditorNode::register_editor_types() { void EditorNode::unregister_editor_types() { _init_callbacks.clear(); + if (EditorPaths::get_singleton()) { + EditorPaths::free(); + } } void EditorNode::stop_child_process(OS::ProcessID p_pid) { @@ -3802,7 +3929,7 @@ Ref<Texture2D> EditorNode::get_object_icon(const Object *p_object, const String } Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) const { - ERR_FAIL_COND_V_MSG(p_class.empty(), nullptr, "Class name cannot be empty."); + ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty."); if (ScriptServer::is_global_class(p_class)) { Ref<ImageTexture> icon; @@ -3945,7 +4072,7 @@ Error EditorNode::export_preset(const String &p_preset, const String &p_path, bo void EditorNode::show_accept(const String &p_text, const String &p_title) { current_option = -1; - accept->get_ok()->set_text(p_title); + accept->get_ok_button()->set_text(p_title); accept->set_text(p_text); accept->popup_centered(); } @@ -3997,16 +4124,16 @@ void EditorNode::_dock_make_float() { window->set_title(dock->get_name()); Panel *p = memnew(Panel); p->set_mode(Panel::MODE_FOREGROUND); - p->set_anchors_and_margins_preset(Control::PRESET_WIDE); + p->set_anchors_and_offsets_preset(Control::PRESET_WIDE); window->add_child(p); MarginContainer *margin = memnew(MarginContainer); - margin->set_anchors_and_margins_preset(Control::PRESET_WIDE); + margin->set_anchors_and_offsets_preset(Control::PRESET_WIDE); margin->add_theme_constant_override("margin_right", borders.width); margin->add_theme_constant_override("margin_top", borders.height); margin->add_theme_constant_override("margin_left", borders.width); margin->add_theme_constant_override("margin_bottom", borders.height); window->add_child(margin); - dock->set_anchors_and_margins_preset(Control::PRESET_WIDE); + dock->set_anchors_and_offsets_preset(Control::PRESET_WIDE); margin->add_child(dock); window->set_wrap_controls(true); window->set_size(dock_size); @@ -4230,6 +4357,8 @@ void EditorNode::_save_docks() { } Ref<ConfigFile> config; config.instance(); + // Load and amend existing config if it exists. + config->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("editor_layout.cfg")); _save_docks_to_config(config, "docks"); _save_open_scenes_to_config(config, "EditorNode"); @@ -4534,7 +4663,7 @@ bool EditorNode::has_scenes_in_session() { return false; } Array scenes = config->get_value("EditorNode", "open_scenes"); - return !scenes.empty(); + return !scenes.is_empty(); } bool EditorNode::ensure_main_scene(bool p_from_native) { @@ -4644,14 +4773,14 @@ void EditorNode::_layout_menu_option(int p_id) { case SETTINGS_LAYOUT_SAVE: { current_option = p_id; layout_dialog->set_title(TTR("Save Layout")); - layout_dialog->get_ok()->set_text(TTR("Save")); + layout_dialog->get_ok_button()->set_text(TTR("Save")); layout_dialog->popup_centered(); layout_dialog->set_name_line_enabled(true); } break; case SETTINGS_LAYOUT_DELETE: { current_option = p_id; layout_dialog->set_title(TTR("Delete Layout")); - layout_dialog->get_ok()->set_text(TTR("Delete")); + layout_dialog->get_ok_button()->set_text(TTR("Delete")); layout_dialog->popup_centered(); layout_dialog->set_name_line_enabled(false); } break; @@ -4690,10 +4819,10 @@ void EditorNode::_scene_tab_closed(int p_tab, int option) { } bool unsaved = (p_tab == editor_data.get_edited_scene()) ? - saved_version != editor_data.get_undo_redo().get_version() : - editor_data.get_scene_version(p_tab) != 0; + saved_version != editor_data.get_undo_redo().get_version() : + editor_data.get_scene_version(p_tab) != 0; if (unsaved) { - save_confirmation->get_ok()->set_text(TTR("Save & Close")); + save_confirmation->get_ok_button()->set_text(TTR("Save & Close")); save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), scene->get_filename() != "" ? scene->get_filename() : "unsaved scene")); save_confirmation->popup_centered(); } else { @@ -4729,15 +4858,15 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { if (mb.is_valid()) { if (scene_tabs->get_hovered_tab() >= 0) { - if (mb->get_button_index() == BUTTON_MIDDLE && mb->is_pressed()) { + if (mb->get_button_index() == MOUSE_BUTTON_MIDDLE && mb->is_pressed()) { _scene_tab_closed(scene_tabs->get_hovered_tab()); } } else { - if ((mb->get_button_index() == BUTTON_LEFT && mb->is_doubleclick()) || (mb->get_button_index() == BUTTON_MIDDLE && mb->is_pressed())) { + if ((mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_double_click()) || (mb->get_button_index() == MOUSE_BUTTON_MIDDLE && mb->is_pressed())) { _menu_option_confirm(FILE_NEW_SCENE, true); } } - if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) { + if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed()) { // context menu scene_tabs_context_menu->clear(); scene_tabs_context_menu->set_size(Size2(1, 1)); @@ -4760,7 +4889,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { Ref<Shortcut> undo_close_tab_sc = ED_GET_SHORTCUT("editor/reopen_closed_scene"); undo_close_tab_sc->set_name(TTR("Undo Close Tab")); scene_tabs_context_menu->add_shortcut(undo_close_tab_sc, FILE_OPEN_PREV); - if (previous_scenes.empty()) { + if (previous_scenes.is_empty()) { scene_tabs_context_menu->set_item_disabled(scene_tabs_context_menu->get_item_index(FILE_OPEN_PREV), true); } scene_tabs_context_menu->add_item(TTR("Close Other Tabs"), FILE_CLOSE_OTHERS); @@ -4999,8 +5128,8 @@ Variant EditorNode::drag_resource(const Ref<Resource> &p_res, Control *p_from) { { //todo make proper previews - Ref<ImageTexture> pic = gui_base->get_theme_icon("FileBigThumb", "EditorIcons"); - Ref<Image> img = pic->get_data(); + Ref<ImageTexture> texture = gui_base->get_theme_icon("FileBigThumb", "EditorIcons"); + Ref<Image> img = texture->get_image(); img = img->duplicate(); img->resize(48, 48); //meh Ref<ImageTexture> resized_pic = Ref<ImageTexture>(memnew(ImageTexture)); @@ -5083,17 +5212,10 @@ Variant EditorNode::drag_files_and_dirs(const Vector<String> &p_paths, Control * return drag_data; } -void EditorNode::add_tool_menu_item(const String &p_name, Object *p_handler, const String &p_callback, const Variant &p_ud) { - ERR_FAIL_NULL(p_handler); +void EditorNode::add_tool_menu_item(const String &p_name, const Callable &p_callback) { int idx = tool_menu->get_item_count(); tool_menu->add_item(p_name, TOOLS_CUSTOM); - - Array parameters; - parameters.push_back(p_handler->get_instance_id()); - parameters.push_back(p_callback); - parameters.push_back(p_ud); - - tool_menu->set_item_metadata(idx, parameters); + tool_menu->set_item_metadata(idx, p_callback); } void EditorNode::add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu) { @@ -5133,9 +5255,7 @@ void EditorNode::_global_menu_new_window(const Variant &p_tag) { List<String> args; args.push_back("-p"); String exec = OS::get_singleton()->get_executable_path(); - - OS::ProcessID pid = 0; - OS::get_singleton()->execute(exec, args, false, &pid); + OS::get_singleton()->create_process(exec, args); } } @@ -5171,7 +5291,7 @@ void EditorNode::_add_dropped_files_recursive(const Vector<String> &p_files, Str next_file = sub_dir->get_next(); } - if (!sub_files.empty()) { + if (!sub_files.is_empty()) { dir->make_dir(to); _add_dropped_files_recursive(sub_files, to); } @@ -5188,6 +5308,8 @@ void EditorNode::_file_access_close_error_notify(const String &p_str) { } void EditorNode::reload_scene(const String &p_path) { + /* + * No longer necessary since scenes now reset and reload their internal resource if needed. //first of all, reload internal textures, materials, meshes, etc. as they might have changed on disk List<Ref<Resource>> cached; @@ -5205,6 +5327,8 @@ void EditorNode::reload_scene(const String &p_path) { to_clear.pop_front(); } + */ + int scene_idx = -1; for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { if (editor_data.get_scene_path(i) == p_path) { @@ -5289,15 +5413,9 @@ void EditorNode::_open_imported() { load_scene(open_import_request, true, false, true, true); } -void EditorNode::dim_editor(bool p_dimming, bool p_force_dim) { - // Dimming can be forced regardless of the editor setting, which is useful when quitting the editor. - if ((p_force_dim || EditorSettings::get_singleton()->get("interface/editor/dim_editor_on_dialog_popup")) && p_dimming) { - dimmed = true; - gui_base->set_modulate(Color(0.5, 0.5, 0.5)); - } else { - dimmed = false; - gui_base->set_modulate(Color(1, 1, 1)); - } +void EditorNode::dim_editor(bool p_dimming) { + dimmed = p_dimming; + gui_base->set_modulate(p_dimming ? Color(0.5, 0.5, 0.5) : Color(1, 1, 1)); } bool EditorNode::is_editor_dimmed() const { @@ -5442,12 +5560,15 @@ void EditorNode::_bind_methods() { ClassDB::bind_method("_screenshot", &EditorNode::_screenshot); ClassDB::bind_method("_save_screenshot", &EditorNode::_save_screenshot); + ClassDB::bind_method("_version_button_pressed", &EditorNode::_version_button_pressed); + ADD_SIGNAL(MethodInfo("play_pressed")); ADD_SIGNAL(MethodInfo("pause_pressed")); ADD_SIGNAL(MethodInfo("stop_pressed")); ADD_SIGNAL(MethodInfo("request_help_search")); ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "args"))); ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj"))); + ADD_SIGNAL(MethodInfo("project_settings_changed")); } static Node *_resource_get_edited_scene() { @@ -5461,18 +5582,19 @@ void EditorNode::_print_handler(void *p_this, const String &p_string, bool p_err static void _execute_thread(void *p_ud) { EditorNode::ExecuteThreadArgs *eta = (EditorNode::ExecuteThreadArgs *)p_ud; - Error err = OS::get_singleton()->execute(eta->path, eta->args, true, nullptr, &eta->output, &eta->exitcode, true, &eta->execute_output_mutex); + Error err = OS::get_singleton()->execute(eta->path, eta->args, &eta->output, &eta->exitcode, true, &eta->execute_output_mutex); print_verbose("Thread exit status: " + itos(eta->exitcode)); if (err != OK) { eta->exitcode = err; } - eta->done = true; + eta->done.set(); + ; } int EditorNode::execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok, bool p_close_on_errors) { execute_output_dialog->set_title(p_title); - execute_output_dialog->get_ok()->set_disabled(true); + execute_output_dialog->get_ok_button()->set_disabled(true); execute_outputs->clear(); execute_outputs->set_scroll_follow(true); execute_output_dialog->popup_centered_ratio(); @@ -5481,15 +5603,12 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p eta.path = p_path; eta.args = p_arguments; eta.exitcode = 255; - eta.done = false; int prev_len = 0; - eta.execute_output_thread = Thread::create(_execute_thread, &eta); - - ERR_FAIL_COND_V(!eta.execute_output_thread, 0); + eta.execute_output_thread.start(_execute_thread, &eta); - while (!eta.done) { + while (!eta.done.is_set()) { { MutexLock lock(eta.execute_output_mutex); if (prev_len != eta.output.length()) { @@ -5502,8 +5621,7 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p OS::get_singleton()->delay_usec(1000); } - Thread::wait_to_finish(eta.execute_output_thread); - memdelete(eta.execute_output_thread); + eta.execute_output_thread.wait_to_finish(); execute_outputs->add_text("\nExit Code: " + itos(eta.exitcode)); if (p_close_on_errors && eta.exitcode != 0) { @@ -5513,11 +5631,15 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p execute_output_dialog->hide(); } - execute_output_dialog->get_ok()->set_disabled(false); + execute_output_dialog->get_ok_button()->set_disabled(false); return eta.exitcode; } +void EditorNode::notify_settings_changed() { + settings_changed = true; +} + EditorNode::EditorNode() { Input::get_singleton()->set_use_accumulated_input(true); Resource::_get_local_scene_func = _resource_get_edited_scene; @@ -5578,6 +5700,8 @@ EditorNode::EditorNode() { switch (display_scale) { case 0: { // Try applying a suitable display scale automatically. + // The code below is adapted in `editor/editor_settings.cpp` and `editor/project_manager.cpp`. + // Make sure to update those when modifying the code below. #ifdef OSX_ENABLED editor_set_scale(DisplayServer::get_singleton()->screen_get_max_scale()); #else @@ -5586,6 +5710,10 @@ EditorNode::EditorNode() { if (DisplayServer::get_singleton()->screen_get_dpi(screen) >= 192 && DisplayServer::get_singleton()->screen_get_size(screen).y >= 1400) { // hiDPI display. scale = 2.0; + } else if (DisplayServer::get_singleton()->screen_get_size(screen).y >= 1700) { + // Likely a hiDPI display, but we aren't certain due to the returned DPI. + // Use an intermediate scale to handle this situation. + scale = 1.5; } else if (DisplayServer::get_singleton()->screen_get_size(screen).y <= 800) { // Small loDPI display. Use a smaller display scale so that editor elements fit more easily. // Icons won't look great, but this is better than having editor elements overflow from its window. @@ -5669,10 +5797,6 @@ EditorNode::EditorNode() { import_csv_translation.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_csv_translation); - Ref<ResourceImporterCSV> import_csv; - import_csv.instance(); - ResourceFormatImporter::get_singleton()->add_importer(import_csv); - Ref<ResourceImporterWAV> import_wav; import_wav.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_wav); @@ -5698,10 +5822,6 @@ EditorNode::EditorNode() { import_obj2.instance(); import_scene->add_importer(import_obj2); - Ref<EditorSceneImporterGLTF> import_gltf; - import_gltf.instance(); - import_scene->add_importer(import_gltf); - Ref<EditorSceneImporterESCN> import_escn; import_escn.instance(); import_scene->add_importer(import_escn); @@ -5726,8 +5846,6 @@ EditorNode::EditorNode() { EditorInspector::add_inspector_plugin(smp); } - _pvrtc_register_compressors(); - editor_selection = memnew(EditorSelection); EditorFileSystem *efs = memnew(EditorFileSystem); @@ -5750,8 +5868,6 @@ EditorNode::EditorNode() { register_exporters(); - GLOBAL_DEF("editor/main_run_args", ""); - ClassDB::set_class_enabled("RootMotionView", true); //defs here, use EDITOR_GET in logic @@ -5763,7 +5879,6 @@ EditorNode::EditorNode() { EDITOR_DEF("run/output/always_close_output_on_stop", true); EDITOR_DEF("run/auto_save/save_before_running", true); EDITOR_DEF_RST("interface/editor/save_each_scene_on_quit", true); - EDITOR_DEF("interface/editor/quit_confirmation", true); EDITOR_DEF("interface/editor/show_update_spinner", false); EDITOR_DEF("interface/editor/update_continuously", false); EDITOR_DEF_RST("interface/scene_tabs/restore_scenes_on_load", false); @@ -5776,18 +5891,20 @@ EditorNode::EditorNode() { EDITOR_DEF("interface/inspector/horizontal_vector2_editing", false); EDITOR_DEF("interface/inspector/horizontal_vector_types_editing", true); EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true); - EDITOR_DEF("interface/inspector/resources_to_open_in_new_inspector", "StandardMaterial3D,ORMMaterial3D,Script,MeshLibrary,TileSet"); + EDITOR_DEF("interface/inspector/resources_to_open_in_new_inspector", "Script,MeshLibrary"); EDITOR_DEF("interface/inspector/default_color_picker_mode", 0); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/inspector/default_color_picker_mode", PROPERTY_HINT_ENUM, "RGB,HSV,RAW", PROPERTY_USAGE_DEFAULT)); + EDITOR_DEF("interface/inspector/default_color_picker_shape", (int32_t)ColorPicker::SHAPE_VHS_CIRCLE); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/inspector/default_color_picker_shape", PROPERTY_HINT_ENUM, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle", PROPERTY_USAGE_DEFAULT)); EDITOR_DEF("run/auto_save/save_before_running", true); theme_base = memnew(Control); add_child(theme_base); - theme_base->set_anchors_and_margins_preset(Control::PRESET_WIDE); + theme_base->set_anchors_and_offsets_preset(Control::PRESET_WIDE); gui_base = memnew(Panel); theme_base->add_child(gui_base); - gui_base->set_anchors_and_margins_preset(Control::PRESET_WIDE); + gui_base->set_anchors_and_offsets_preset(Control::PRESET_WIDE); theme_base->set_theme(theme); gui_base->set_theme(theme); @@ -5799,13 +5916,13 @@ EditorNode::EditorNode() { gui_base->add_child(progress_dialog); // take up all screen - gui_base->set_anchor(MARGIN_RIGHT, Control::ANCHOR_END); - gui_base->set_anchor(MARGIN_BOTTOM, Control::ANCHOR_END); + gui_base->set_anchor(SIDE_RIGHT, Control::ANCHOR_END); + gui_base->set_anchor(SIDE_BOTTOM, Control::ANCHOR_END); gui_base->set_end(Point2(0, 0)); main_vbox = memnew(VBoxContainer); gui_base->add_child(main_vbox); - main_vbox->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 8); + main_vbox->set_anchors_and_offsets_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 8); main_vbox->add_theme_constant_override("separation", 8 * EDSCALE); menu_hb = memnew(HBoxContainer); @@ -5972,8 +6089,8 @@ EditorNode::EditorNode() { tab_preview_panel->add_child(tab_preview); scene_tabs = memnew(Tabs); - scene_tabs->add_theme_style_override("tab_fg", gui_base->get_theme_stylebox("SceneTabFG", "EditorStyles")); - scene_tabs->add_theme_style_override("tab_bg", gui_base->get_theme_stylebox("SceneTabBG", "EditorStyles")); + scene_tabs->add_theme_style_override("tab_selected", gui_base->get_theme_stylebox("SceneTabFG", "EditorStyles")); + scene_tabs->add_theme_style_override("tab_unselected", gui_base->get_theme_stylebox("SceneTabBG", "EditorStyles")); scene_tabs->set_select_with_rmb(true); scene_tabs->add_tab("unsaved"); scene_tabs->set_tab_align(Tabs::ALIGN_LEFT); @@ -6016,7 +6133,7 @@ EditorNode::EditorNode() { tabbar_container->add_child(distraction_free); scene_tab_add->set_tooltip(TTR("Add a new scene.")); scene_tab_add->set_icon(gui_base->get_theme_icon("Add", "EditorIcons")); - scene_tab_add->add_theme_color_override("icon_color_normal", Color(0.6f, 0.6f, 0.6f, 0.8f)); + scene_tab_add->add_theme_color_override("icon_normal_color", Color(0.6f, 0.6f, 0.6f, 0.8f)); scene_tab_add->connect("pressed", callable_mp(this, &EditorNode::_menu_option), make_binds(FILE_NEW_SCENE)); scene_root_parent = memnew(PanelContainer); @@ -6033,10 +6150,10 @@ EditorNode::EditorNode() { scene_root->set_disable_input(true); scene_root->set_as_audio_listener_2d(true); - viewport = memnew(VBoxContainer); - viewport->set_v_size_flags(Control::SIZE_EXPAND_FILL); - viewport->add_theme_constant_override("separation", 0); - scene_root_parent->add_child(viewport); + main_control = memnew(VBoxContainer); + main_control->set_v_size_flags(Control::SIZE_EXPAND_FILL); + main_control->add_theme_constant_override("separation", 0); + scene_root_parent->add_child(main_control); HBoxContainer *left_menu_hb = memnew(HBoxContainer); menu_hb->add_child(left_menu_hb); @@ -6077,6 +6194,9 @@ EditorNode::EditorNode() { project_settings = memnew(ProjectSettingsEditor(&editor_data)); gui_base->add_child(project_settings); + scene_import_settings = memnew(SceneImportSettings); + gui_base->add_child(scene_import_settings); + export_template_manager = memnew(ExportTemplateManager); gui_base->add_child(export_template_manager); @@ -6100,8 +6220,8 @@ EditorNode::EditorNode() { p = file_menu->get_popup(); - p->add_shortcut(ED_SHORTCUT("editor/new_scene", TTR("New Scene")), FILE_NEW_SCENE); - p->add_shortcut(ED_SHORTCUT("editor/new_inherited_scene", TTR("New Inherited Scene...")), FILE_NEW_INHERITED_SCENE); + p->add_shortcut(ED_SHORTCUT("editor/new_scene", TTR("New Scene"), KEY_MASK_CMD + KEY_N), FILE_NEW_SCENE); + p->add_shortcut(ED_SHORTCUT("editor/new_inherited_scene", TTR("New Inherited Scene..."), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_N), FILE_NEW_INHERITED_SCENE); p->add_shortcut(ED_SHORTCUT("editor/open_scene", TTR("Open Scene..."), KEY_MASK_CMD + KEY_O), FILE_OPEN_SCENE); p->add_shortcut(ED_SHORTCUT("editor/reopen_closed_scene", TTR("Reopen Closed Scene"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_T), FILE_OPEN_PREV); p->add_submenu_item(TTR("Open Recent"), "RecentScenes", FILE_OPEN_RECENT); @@ -6123,12 +6243,11 @@ EditorNode::EditorNode() { p->add_child(pm_export); p->add_submenu_item(TTR("Convert To..."), "Export"); pm_export->add_shortcut(ED_SHORTCUT("editor/convert_to_MeshLibrary", TTR("MeshLibrary...")), FILE_EXPORT_MESH_LIBRARY); - pm_export->add_shortcut(ED_SHORTCUT("editor/convert_to_TileSet", TTR("TileSet...")), FILE_EXPORT_TILESET); pm_export->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); p->add_separator(); - p->add_shortcut(ED_SHORTCUT("editor/undo", TTR("Undo"), KEY_MASK_CMD + KEY_Z), EDIT_UNDO, true); - p->add_shortcut(ED_SHORTCUT("editor/redo", TTR("Redo"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_Z), EDIT_REDO, true); + p->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO, true); + p->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true); p->add_separator(); p->add_shortcut(ED_SHORTCUT("editor/reload_saved_scene", TTR("Reload Saved Scene")), EDIT_RELOAD_SAVED_SCENE); @@ -6264,15 +6383,20 @@ EditorNode::EditorNode() { p = help_menu->get_popup(); p->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); - p->add_icon_shortcut(gui_base->get_theme_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("editor/editor_help", TTR("Search"), KEY_MASK_SHIFT | KEY_F1), HELP_SEARCH); +#ifdef OSX_ENABLED + p->add_icon_shortcut(gui_base->get_theme_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("editor/editor_help", TTR("Search Help"), KEY_MASK_ALT | KEY_SPACE), HELP_SEARCH); +#else + p->add_icon_shortcut(gui_base->get_theme_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("editor/editor_help", TTR("Search Help"), KEY_F1), HELP_SEARCH); +#endif p->add_separator(); - p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/online_docs", TTR("Online Docs")), HELP_DOCS); - p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/q&a", TTR("Q&A")), HELP_QA); + p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/online_docs", TTR("Online Documentation")), HELP_DOCS); + p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/q&a", TTR("Questions & Answers")), HELP_QA); p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG); p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK); p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/community", TTR("Community")), HELP_COMMUNITY); p->add_separator(); - p->add_icon_shortcut(gui_base->get_theme_icon("Godot", "EditorIcons"), ED_SHORTCUT("editor/about", TTR("About")), HELP_ABOUT); + p->add_icon_shortcut(gui_base->get_theme_icon("Godot", "EditorIcons"), ED_SHORTCUT("editor/about", TTR("About Godot")), HELP_ABOUT); + p->add_icon_shortcut(gui_base->get_theme_icon("Heart", "EditorIcons"), ED_SHORTCUT("editor/support_development", TTR("Support Godot Development")), HELP_SUPPORT_GODOT_DEVELOPMENT); HBoxContainer *play_hb = memnew(HBoxContainer); menu_hb->add_child(play_hb); @@ -6356,20 +6480,19 @@ EditorNode::EditorNode() { // Toggle for video driver video_driver = memnew(OptionButton); - video_driver->set_flat(true); video_driver->set_focus_mode(Control::FOCUS_NONE); video_driver->connect("item_selected", callable_mp(this, &EditorNode::_video_driver_selected)); video_driver->add_theme_font_override("font", gui_base->get_theme_font("bold", "EditorFonts")); video_driver->add_theme_font_size_override("font_size", gui_base->get_theme_font_size("bold_size", "EditorFonts")); - // TODO re-enable when GLES2 is ported - video_driver->set_disabled(true); + // TODO: Show again when OpenGL is ported. + video_driver->set_visible(false); right_menu_hb->add_child(video_driver); #ifndef _MSC_VER #warning needs to be reimplemented #endif #if 0 - String video_drivers = ProjectSettings::get_singleton()->get_custom_property_info()["rendering/quality/driver/driver_name"].hint_string; + String video_drivers = ProjectSettings::get_singleton()->get_custom_property_info()["rendering/driver/driver_name"].hint_string; String current_video_driver = OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver()); video_driver_current = 0; for (int i = 0; i < video_drivers.get_slice_count(","); i++) { @@ -6387,7 +6510,7 @@ EditorNode::EditorNode() { #endif video_restart_dialog = memnew(ConfirmationDialog); video_restart_dialog->set_text(TTR("Changing the video driver requires restarting the editor.")); - video_restart_dialog->get_ok()->set_text(TTR("Save & Restart")); + video_restart_dialog->get_ok_button()->set_text(TTR("Save & Restart")); video_restart_dialog->connect("confirmed", callable_mp(this, &EditorNode::_menu_option), varray(SET_VIDEO_DRIVER_SAVE_AND_RESTART)); gui_base->add_child(video_restart_dialog); @@ -6494,11 +6617,31 @@ EditorNode::EditorNode() { bottom_panel_hb_editors->set_h_size_flags(Control::SIZE_EXPAND_FILL); bottom_panel_hb->add_child(bottom_panel_hb_editors); - version_label = memnew(Label); - version_label->set_text(VERSION_FULL_CONFIG); + VBoxContainer *version_info_vbc = memnew(VBoxContainer); + bottom_panel_hb->add_child(version_info_vbc); + + // Add a dummy control node for vertical spacing. + Control *v_spacer = memnew(Control); + version_info_vbc->add_child(v_spacer); + + version_btn = memnew(LinkButton); + version_btn->set_text(VERSION_FULL_CONFIG); + String hash = String(VERSION_HASH); + if (hash.length() != 0) { + hash = "." + hash.left(9); + } + // Set the text to copy in metadata as it slightly differs from the button's text. + version_btn->set_meta(META_TEXT_TO_COPY, "v" VERSION_FULL_BUILD + hash); // Fade out the version label to be less prominent, but still readable - version_label->set_self_modulate(Color(1, 1, 1, 0.6)); - bottom_panel_hb->add_child(version_label); + version_btn->set_self_modulate(Color(1, 1, 1, 0.65)); + version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); + version_btn->set_tooltip(TTR("Click to copy.")); + version_btn->connect("pressed", callable_mp(this, &EditorNode::_version_button_pressed)); + version_info_vbc->add_child(version_btn); + + // Add a dummy control node for horizontal spacing. + Control *h_spacer = memnew(Control); + bottom_panel_hb->add_child(h_spacer); bottom_panel_raise = memnew(Button); bottom_panel_raise->set_flat(true); @@ -6519,6 +6662,9 @@ EditorNode::EditorNode() { center_split->connect("resized", callable_mp(this, &EditorNode::_vp_resized)); + native_shader_source_visualizer = memnew(EditorNativeShaderSourceVisualizer); + gui_base->add_child(native_shader_source_visualizer); + orphan_resources = memnew(OrphanResourcesDialog); gui_base->add_child(orphan_resources); @@ -6534,19 +6680,19 @@ EditorNode::EditorNode() { custom_build_manage_templates = memnew(ConfirmationDialog); custom_build_manage_templates->set_text(TTR("Android build template is missing, please install relevant templates.")); - custom_build_manage_templates->get_ok()->set_text(TTR("Manage Templates")); + custom_build_manage_templates->get_ok_button()->set_text(TTR("Manage Templates")); custom_build_manage_templates->connect("confirmed", callable_mp(this, &EditorNode::_menu_option), varray(SETTINGS_MANAGE_EXPORT_TEMPLATES)); gui_base->add_child(custom_build_manage_templates); install_android_build_template = memnew(ConfirmationDialog); install_android_build_template->set_text(TTR("This will set up your project for custom Android builds by installing the source template to \"res://android/build\".\nYou can then apply modifications and build your own custom APK on export (adding modules, changing the AndroidManifest.xml, etc.).\nNote that in order to make custom builds instead of using pre-built APKs, the \"Use Custom Build\" option should be enabled in the Android export preset.")); - install_android_build_template->get_ok()->set_text(TTR("Install")); + install_android_build_template->get_ok_button()->set_text(TTR("Install")); install_android_build_template->connect("confirmed", callable_mp(this, &EditorNode::_menu_confirm_current)); gui_base->add_child(install_android_build_template); remove_android_build_template = memnew(ConfirmationDialog); remove_android_build_template->set_text(TTR("The Android build template is already installed in this project and it won't be overwritten.\nRemove the \"res://android/build\" directory manually before attempting this operation again.")); - remove_android_build_template->get_ok()->set_text(TTR("Show in File Manager")); + remove_android_build_template->get_ok_button()->set_text(TTR("Show in File Manager")); remove_android_build_template->connect("confirmed", callable_mp(this, &EditorNode::_menu_option), varray(FILE_EXPLORE_ANDROID_BUILD_TEMPLATES)); gui_base->add_child(remove_android_build_template); @@ -6599,6 +6745,30 @@ EditorNode::EditorNode() { //plugin stuff add_editor_plugin(memnew(DebuggerEditorPlugin(this, debug_menu))); + + disk_changed = memnew(ConfirmationDialog); + { + VBoxContainer *vbc = memnew(VBoxContainer); + disk_changed->add_child(vbc); + + Label *dl = memnew(Label); + dl->set_text(TTR("The following files are newer on disk.\nWhat action should be taken?")); + vbc->add_child(dl); + + disk_changed_list = memnew(Tree); + vbc->add_child(disk_changed_list); + disk_changed_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + + disk_changed->connect("confirmed", callable_mp(this, &EditorNode::_reload_modified_scenes)); + disk_changed->connect("confirmed", callable_mp(this, &EditorNode::_reload_project_settings)); + disk_changed->get_ok_button()->set_text(TTR("Reload")); + + disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); + disk_changed->connect("custom_action", callable_mp(this, &EditorNode::_resave_scenes)); + } + + gui_base->add_child(disk_changed); + add_editor_plugin(memnew(AnimationPlayerEditorPlugin(this))); add_editor_plugin(memnew(CanvasItemEditorPlugin(this))); add_editor_plugin(memnew(Node3DEditorPlugin(this))); @@ -6645,12 +6815,12 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(ItemListEditorPlugin(this))); add_editor_plugin(memnew(Polygon3DEditorPlugin(this))); add_editor_plugin(memnew(CollisionPolygon2DEditorPlugin(this))); - add_editor_plugin(memnew(TileSetEditorPlugin(this))); - add_editor_plugin(memnew(TileMapEditorPlugin(this))); + add_editor_plugin(memnew(TilesEditorPlugin(this))); add_editor_plugin(memnew(SpriteFramesEditorPlugin(this))); add_editor_plugin(memnew(TextureRegionEditorPlugin(this))); add_editor_plugin(memnew(GIProbeEditorPlugin(this))); add_editor_plugin(memnew(BakedLightmapEditorPlugin(this))); + add_editor_plugin(memnew(OccluderInstance3DEditorPlugin(this))); add_editor_plugin(memnew(Path2DEditorPlugin(this))); add_editor_plugin(memnew(Path3DEditorPlugin(this))); add_editor_plugin(memnew(Line2DEditorPlugin(this))); @@ -6749,7 +6919,7 @@ EditorNode::EditorNode() { set_process(true); open_imported = memnew(ConfirmationDialog); - open_imported->get_ok()->set_text(TTR("Open Anyway")); + open_imported->get_ok_button()->set_text(TTR("Open Anyway")); new_inherited_button = open_imported->add_button(TTR("New Inherited"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "inherit"); open_imported->connect("confirmed", callable_mp(this, &EditorNode::_open_imported)); open_imported->connect("custom_action", callable_mp(this, &EditorNode::_inherit_imported)); @@ -6799,7 +6969,7 @@ EditorNode::EditorNode() { pick_main_scene = memnew(ConfirmationDialog); gui_base->add_child(pick_main_scene); - pick_main_scene->get_ok()->set_text(TTR("Select")); + pick_main_scene->get_ok_button()->set_text(TTR("Select")); pick_main_scene->connect("confirmed", callable_mp(this, &EditorNode::_menu_option), varray(SETTINGS_PICK_MAIN_SCENE)); for (int i = 0; i < _init_callbacks.size(); i++) { @@ -6828,14 +6998,12 @@ EditorNode::EditorNode() { ED_SHORTCUT("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_ALT | KEY_2); ED_SHORTCUT("editor/editor_script", TTR("Open Script Editor"), KEY_MASK_ALT | KEY_3); ED_SHORTCUT("editor/editor_assetlib", TTR("Open Asset Library"), KEY_MASK_ALT | KEY_4); - ED_SHORTCUT("editor/editor_help", TTR("Search Help"), KEY_MASK_ALT | KEY_SPACE); #else // Use the Ctrl modifier so F2 can be used to rename nodes in the scene tree dock. ED_SHORTCUT("editor/editor_2d", TTR("Open 2D Editor"), KEY_MASK_CTRL | KEY_F1); ED_SHORTCUT("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_CTRL | KEY_F2); ED_SHORTCUT("editor/editor_script", TTR("Open Script Editor"), KEY_MASK_CTRL | KEY_F3); ED_SHORTCUT("editor/editor_assetlib", TTR("Open Asset Library"), KEY_MASK_CTRL | KEY_F4); - ED_SHORTCUT("editor/editor_help", TTR("Search Help"), KEY_F1); #endif ED_SHORTCUT("editor/editor_next", TTR("Open the next Editor")); ED_SHORTCUT("editor/editor_prev", TTR("Open the previous Editor")); @@ -6942,8 +7110,8 @@ void EditorPluginList::remove_plugin(EditorPlugin *p_plugin) { plugins_list.erase(p_plugin); } -bool EditorPluginList::empty() { - return plugins_list.empty(); +bool EditorPluginList::is_empty() { + return plugins_list.is_empty(); } void EditorPluginList::clear() { |