diff options
Diffstat (limited to 'editor/editor_node.cpp')
| -rw-r--r-- | editor/editor_node.cpp | 308 | 
1 files changed, 179 insertions, 129 deletions
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index f0e53e7ef5..9c77f9d25c 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -46,11 +46,13 @@  #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" @@ -78,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" @@ -92,7 +96,6 @@  #include "editor/filesystem_dock.h"  #include "editor/import/editor_import_collada.h"  #include "editor/import/resource_importer_bitmask.h" -#include "editor/import/resource_importer_csv.h"  #include "editor/import/resource_importer_csv_translation.h"  #include "editor/import/resource_importer_image.h"  #include "editor/import/resource_importer_layered_texture.h" @@ -102,6 +105,7 @@  #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" @@ -143,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" @@ -167,8 +172,7 @@  #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" @@ -183,6 +187,9 @@  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. @@ -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; @@ -592,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; @@ -618,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); @@ -689,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; @@ -728,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; @@ -789,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.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); +				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. @@ -812,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); @@ -822,13 +861,13 @@ 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;  				}  			} @@ -973,6 +1012,10 @@ void EditorNode::_reload_project_settings() {  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(); @@ -1089,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()) { @@ -1364,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();  			}  		} @@ -1409,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); @@ -1768,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: { @@ -2387,11 +2419,14 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {  				_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_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; +					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(); @@ -2431,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;  			} @@ -2501,25 +2545,6 @@ 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_EXTERNAL_OPEN_SCENE: {  			if (unsaved_cache && !p_confirmed) { @@ -2726,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(); @@ -2788,6 +2813,9 @@ 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/driver/driver_name", video_driver_request); @@ -2819,7 +2847,7 @@ void EditorNode::_save_screenshot(NodePath p_path) {  	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 main control viewport texture."); -	Ref<Image> img = texture->get_data(); +	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 + "'."); @@ -2870,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();  } @@ -3036,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); @@ -3072,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() { @@ -3114,16 +3146,7 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled,  	Ref<ConfigFile> cf;  	cf.instance();  	if (!DirAccess::exists(p_addon.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(); +		_remove_plugin_from_enabled(p_addon);  		WARN_PRINT("Addon '" + p_addon + "' failed to load. No directory found. Removing from enabled plugins.");  		return;  	} @@ -3153,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;  		} @@ -3712,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>(); @@ -3744,6 +3773,8 @@ 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>(); @@ -3757,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) { @@ -4323,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"); @@ -4822,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)); @@ -5092,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)); @@ -5273,7 +5309,7 @@ void EditorNode::_file_access_close_error_notify(const String &p_str) {  void EditorNode::reload_scene(const String &p_path) {  	/* -	 * No longer necesary since scenes now reset and reload their internal resource if needed. +	 * 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; @@ -5377,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 { @@ -5530,6 +5560,8 @@ 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")); @@ -5765,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); @@ -5840,8 +5868,6 @@ EditorNode::EditorNode() {  	register_exporters(); -	GLOBAL_DEF("editor/run/main_run_args", ""); -  	ClassDB::set_class_enabled("RootMotionView", true);  	//defs here, use EDITOR_GET in logic @@ -5865,9 +5891,11 @@ 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", "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); @@ -6166,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); @@ -6189,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); @@ -6212,7 +6243,6 @@ 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(); @@ -6359,13 +6389,14 @@ EditorNode::EditorNode() {  	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); @@ -6449,13 +6480,12 @@ 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 @@ -6587,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); @@ -6765,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)));  |