diff options
Diffstat (limited to 'editor')
41 files changed, 388 insertions, 112 deletions
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 919e335d21..c555ca2109 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -2695,18 +2695,25 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) { AnimationPlayer *ap = ape->get_player(); if (ap) { NodePath npath = animation->track_get_path(track); - Node *nd = ap->get_node(ap->get_root_node())->get_node(NodePath(npath.get_concatenated_names())); - StringName prop = npath.get_concatenated_subnames(); - PropertyInfo prop_info; - ClassDB::get_property_info(nd->get_class(), prop, &prop_info); + Node *a_ap_root_node = ap->get_node(ap->get_root_node()); + Node *nd = nullptr; + // We must test that we have a valid a_ap_root_node before trying to access its content to init the nd Node. + if (a_ap_root_node) { + nd = a_ap_root_node->get_node(NodePath(npath.get_concatenated_names())); + } + if (nd) { + StringName prop = npath.get_concatenated_subnames(); + PropertyInfo prop_info; + ClassDB::get_property_info(nd->get_class(), prop, &prop_info); #ifdef DISABLE_DEPRECATED - bool is_angle = prop_info.type == Variant::FLOAT && prop_info.hint_string.find("radians_as_degrees") != -1; + bool is_angle = prop_info.type == Variant::FLOAT && prop_info.hint_string.find("radians_as_degrees") != -1; #else - bool is_angle = prop_info.type == Variant::FLOAT && prop_info.hint_string.find("radians") != -1; + bool is_angle = prop_info.type == Variant::FLOAT && prop_info.hint_string.find("radians") != -1; #endif // DISABLE_DEPRECATED - if (is_angle) { - menu->add_icon_item(get_editor_theme_icon(SNAME("InterpLinearAngle")), TTR("Linear Angle"), MENU_INTERPOLATION_LINEAR_ANGLE); - menu->add_icon_item(get_editor_theme_icon(SNAME("InterpCubicAngle")), TTR("Cubic Angle"), MENU_INTERPOLATION_CUBIC_ANGLE); + if (is_angle) { + menu->add_icon_item(get_editor_theme_icon(SNAME("InterpLinearAngle")), TTR("Linear Angle"), MENU_INTERPOLATION_LINEAR_ANGLE); + menu->add_icon_item(get_editor_theme_icon(SNAME("InterpCubicAngle")), TTR("Cubic Angle"), MENU_INTERPOLATION_CUBIC_ANGLE); + } } } } @@ -3903,7 +3910,7 @@ void AnimationTrackEditor::insert_value_key(const String &p_property, const Vari ERR_FAIL_NULL(root); ERR_FAIL_COND(history->get_path_size() == 0); Object *obj = ObjectDB::get_instance(history->get_path_object(0)); - ERR_FAIL_COND(!Object::cast_to<Node>(obj)); + ERR_FAIL_NULL(Object::cast_to<Node>(obj)); // Let's build a node path. Node *node = Object::cast_to<Node>(obj); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 64467bc254..cd6f672b4b 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -951,6 +951,8 @@ void CodeTextEditor::_complete_request() { font_color = get_theme_color(e.theme_color_name, SNAME("Editor")); } else if (e.insert_text.begins_with("\"") || e.insert_text.begins_with("\'")) { font_color = completion_string_color; + } else if (e.insert_text.begins_with("##") || e.insert_text.begins_with("///")) { + font_color = completion_doc_comment_color; } else if (e.insert_text.begins_with("#") || e.insert_text.begins_with("//")) { font_color = completion_comment_color; } @@ -1026,6 +1028,7 @@ void CodeTextEditor::update_editor_settings() { completion_font_color = EDITOR_GET("text_editor/theme/highlighting/completion_font_color"); completion_string_color = EDITOR_GET("text_editor/theme/highlighting/string_color"); completion_comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color"); + completion_doc_comment_color = EDITOR_GET("text_editor/theme/highlighting/doc_comment_color"); // Appearance: Caret text_editor->set_caret_type((TextEdit::CaretType)EDITOR_GET("text_editor/appearance/caret/type").operator int()); diff --git a/editor/code_editor.h b/editor/code_editor.h index c18154a1ef..43173bd475 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -188,6 +188,7 @@ class CodeTextEditor : public VBoxContainer { Color completion_font_color; Color completion_string_color; Color completion_comment_color; + Color completion_doc_comment_color; CodeTextEditorCodeCompleteFunc code_complete_func; void *code_complete_ud = nullptr; diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 662356378d..fcaec600b5 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -32,7 +32,6 @@ #include "core/config/project_settings.h" #include "core/templates/hash_set.h" -#include "editor/doc_tools.h" #include "editor/editor_help.h" #include "editor/editor_inspector.h" #include "editor/editor_node.h" diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index 7316a770ec..2fd4778389 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -187,8 +187,7 @@ public: ////////////////////////////////////////// -// Custom Tree needed to use a RichTextLabel as tooltip control -// when display signal documentation. +// Custom `Tree` needed to use `EditorHelpTooltip` to display signal documentation. class ConnectionsDockTree : public Tree { virtual Control *make_custom_tooltip(const String &p_text) const; }; diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 6cdcf1a6d0..ab8df824e8 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -1605,7 +1605,7 @@ void ScriptEditorDebugger::_breakpoints_item_rmb_selected(const Vector2 &p_pos, breakpoints_menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Delete All Breakpoints in:") + " " + file, ACTION_DELETE_BREAKPOINTS_IN_FILE); breakpoints_menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Delete All Breakpoints"), ACTION_DELETE_ALL_BREAKPOINTS); - breakpoints_menu->set_position(breakpoints_tree->get_global_position() + p_pos); + breakpoints_menu->set_position(get_screen_position() + get_local_mouse_position()); breakpoints_menu->popup(); } diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 41616d50cd..db54179633 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -2510,17 +2510,13 @@ bool EditorFileSystem::_scan_extensions() { bool needs_restart = false; for (int i = 0; i < extensions_added.size(); i++) { GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->load_extension(extensions_added[i]); - if (st == GDExtensionManager::LOAD_STATUS_FAILED) { - EditorNode::get_singleton()->add_io_error("Error loading extension: " + extensions_added[i]); - } else if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) { + if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) { needs_restart = true; } } for (int i = 0; i < extensions_removed.size(); i++) { GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->unload_extension(extensions_removed[i]); - if (st == GDExtensionManager::LOAD_STATUS_FAILED) { - EditorNode::get_singleton()->add_io_error("Error removing extension: " + extensions_added[i]); - } else if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) { + if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) { needs_restart = true; } } diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 6004591bb2..6e3b5b7b9e 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -2264,7 +2264,12 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, Control p_rt->push_strikethrough(); pos = brk_end + 1; tag_stack.push_front(tag); - + } else if (tag == "lb") { + p_rt->add_text("["); + pos = brk_end + 1; + } else if (tag == "rb") { + p_rt->add_text("]"); + pos = brk_end + 1; } else if (tag == "url") { int end = bbcode.find("[", brk_end); if (end == -1) { @@ -2850,7 +2855,7 @@ void EditorHelpTooltip::_notification(int p_what) { // `p_text` is expected to be something like these: // - `class|Control||`; // - `property|Control|size|`; -// - `signal|Control|gui_input|(event: InputEvent)` +// - `signal|Control|gui_input|(event: InputEvent)`. void EditorHelpTooltip::parse_tooltip(const String &p_text) { tooltip_text = p_text; @@ -2875,7 +2880,11 @@ void EditorHelpTooltip::parse_tooltip(const String &p_text) { if (type == "property") { description = get_property_description(class_name, property_name); - formatted_text = TTR("Property:"); + if (property_name.begins_with("metadata/")) { + formatted_text = TTR("Metadata:"); + } else { + formatted_text = TTR("Property:"); + } } else if (type == "method") { description = get_method_description(class_name, property_name); formatted_text = TTR("Method:"); @@ -2890,7 +2899,8 @@ void EditorHelpTooltip::parse_tooltip(const String &p_text) { } } - formatted_text += " [u][b]" + title + "[/b][/u]" + property_args + "\n"; + // Metadata special handling replaces "Property:" with "Metadata": above. + formatted_text += " [u][b]" + title.trim_prefix("metadata/") + "[/b][/u]" + property_args.replace("[", "[lb]") + "\n"; formatted_text += description.is_empty() ? "[i]" + TTR("No description available.") + "[/i]" : description; set_text(formatted_text); } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 395f4faa39..e1a7d8f111 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -906,12 +906,24 @@ void EditorProperty::_update_pin_flags() { } Control *EditorProperty::make_custom_tooltip(const String &p_text) const { - EditorHelpTooltip *tooltip = memnew(EditorHelpTooltip(p_text)); + EditorHelpBit *tooltip = nullptr; + + if (has_doc_tooltip) { + tooltip = memnew(EditorHelpTooltip(p_text)); + } if (object->has_method("_get_property_warning")) { String warn = object->call("_get_property_warning", property); if (!warn.is_empty()) { - tooltip->set_text(tooltip->get_rich_text()->get_text() + "\n[b][color=" + get_theme_color(SNAME("warning_color")).to_html(false) + "]" + warn + "[/color][/b]"); + String prev_text; + if (tooltip == nullptr) { + tooltip = memnew(EditorHelpBit()); + tooltip->set_text(p_text); + tooltip->get_rich_text()->set_custom_minimum_size(Size2(360 * EDSCALE, 0)); + } else { + prev_text = tooltip->get_rich_text()->get_text() + "\n"; + } + tooltip->set_text(prev_text + "[b][color=" + get_theme_color(SNAME("warning_color")).to_html(false) + "]" + warn + "[/color][/b]"); } } @@ -1148,8 +1160,7 @@ void EditorInspectorCategory::_notification(int p_what) { } Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) const { - // Far from perfect solution, as there's nothing that prevents a category from having a name that starts with that. - return p_text.begins_with("class|") ? memnew(EditorHelpTooltip(p_text)) : nullptr; + return doc_class_name.is_empty() ? nullptr : memnew(EditorHelpTooltip(p_text)); } Size2 EditorInspectorCategory::get_minimum_size() const { @@ -3316,6 +3327,7 @@ void EditorInspector::update_tree() { } else { ep->set_tooltip_text("theme_item|" + classname + "|" + theme_item_name + "|"); } + ep->has_doc_tooltip = true; } ep->set_doc_path(doc_path); @@ -3391,11 +3403,16 @@ Object *EditorInspector::get_edited_object() { return object; } +Object *EditorInspector::get_next_edited_object() { + return next_object; +} + void EditorInspector::edit(Object *p_object) { if (object == p_object) { return; } + next_object = p_object; // Some plugins need to know the next edited object when clearing the inspector. if (object) { _clear(); object->disconnect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback)); @@ -3412,6 +3429,10 @@ void EditorInspector::edit(Object *p_object) { object->connect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback)); update_tree(); } + + // Keep it available until the end so it works with both main and sub inspectors. + next_object = nullptr; + emit_signal(SNAME("edited_object_changed")); } diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index b5f0cec80b..e36606c080 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -74,6 +74,7 @@ private: StringName property; String property_path; String doc_path; + bool has_doc_tooltip = false; int property_usage; @@ -473,6 +474,7 @@ class EditorInspector : public ScrollContainer { void _clear(bool p_hide_plugins = true); Object *object = nullptr; + Object *next_object = nullptr; // @@ -576,6 +578,7 @@ public: void update_property(const String &p_prop); void edit(Object *p_object); Object *get_edited_object(); + Object *get_next_edited_object(); void set_keying(bool p_active); void set_read_only(bool p_read_only); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 0037b356d0..665e609cb0 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -790,7 +790,7 @@ void EditorNode::_notification(int p_what) { } void EditorNode::_update_update_spinner() { - update_spinner->set_visible(EDITOR_GET("interface/editor/show_update_spinner")); + update_spinner->set_visible(!RenderingServer::get_singleton()->canvas_item_get_debug_redraw() && EDITOR_GET("interface/editor/show_update_spinner")); const bool update_continuously = EDITOR_GET("interface/editor/update_continuously"); PopupMenu *update_popup = update_spinner->get_popup(); @@ -851,6 +851,10 @@ void EditorNode::_plugin_over_edit(EditorPlugin *p_plugin, Object *p_object) { } } +void EditorNode::_plugin_over_self_own(EditorPlugin *p_plugin) { + active_plugins[p_plugin->get_instance_id()].insert(p_plugin); +} + void EditorNode::_resources_changed(const Vector<String> &p_resources) { List<Ref<Resource>> changed; @@ -2135,7 +2139,12 @@ void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) { for (EditorPlugin *plugin : active_plugins[owner_id]) { if (!available_plugins.has(plugin)) { to_remove.push_back(plugin); - _plugin_over_edit(plugin, nullptr); + if (plugin->can_auto_hide()) { + _plugin_over_edit(plugin, nullptr); + } else { + // If plugin can't be hidden, make it own itself and become responsible for closing. + _plugin_over_self_own(plugin); + } } } @@ -2151,6 +2160,12 @@ void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) { continue; } + if (active_plugins.has(plugin->get_instance_id())) { + // Plugin is already active, but as self-owning, so it needs a separate check. + plugin->edit(p_object); + continue; + } + // If plugin is already associated with another owner, remove it from there first. for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) { if (kv.key != owner_id) { @@ -2214,7 +2229,11 @@ void EditorNode::hide_unused_editors(const Object *p_editing_owner) { if (p_editing_owner) { const ObjectID id = p_editing_owner->get_instance_id(); for (EditorPlugin *plugin : active_plugins[id]) { - _plugin_over_edit(plugin, nullptr); + if (plugin->can_auto_hide()) { + _plugin_over_edit(plugin, nullptr); + } else { + _plugin_over_self_own(plugin); + } } active_plugins.erase(id); } else { @@ -2222,10 +2241,23 @@ void EditorNode::hide_unused_editors(const Object *p_editing_owner) { // This is to sweep properties that were removed from the inspector. List<ObjectID> to_remove; for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) { - if (!ObjectDB::get_instance(kv.key)) { + const Object *context = ObjectDB::get_instance(kv.key); + if (context) { + // In case of self-owning plugins, they are disabled here if they can auto hide. + const EditorPlugin *self_owning = Object::cast_to<EditorPlugin>(context); + if (self_owning && self_owning->can_auto_hide()) { + context = nullptr; + } + } + + if (!context) { to_remove.push_back(kv.key); for (EditorPlugin *plugin : kv.value) { - _plugin_over_edit(plugin, nullptr); + if (plugin->can_auto_hide()) { + _plugin_over_edit(plugin, nullptr); + } else { + _plugin_over_self_own(plugin); + } } } } @@ -3700,16 +3732,6 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b Error 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; - - if (prev != -1) { - _set_current_scene(prev); - editor_data.remove_scene(idx); - } - return ERR_FILE_NOT_FOUND; - } if (!p_ignore_broken_deps && dependency_errors.has(lpath)) { current_menu_option = -1; @@ -3727,6 +3749,17 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b return ERR_FILE_MISSING_DEPENDENCIES; } + if (!sdata.is_valid()) { + _dialog_display_load_error(lpath, err); + opening_prev = false; + + if (prev != -1) { + _set_current_scene(prev); + editor_data.remove_scene(idx); + } + return ERR_FILE_NOT_FOUND; + } + dependency_errors.erase(lpath); // At least not self path. for (KeyValue<String, HashSet<String>> &E : dependency_errors) { diff --git a/editor/editor_node.h b/editor/editor_node.h index 72134e283b..4e36c19e96 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -550,6 +550,7 @@ private: void _remove_plugin_from_enabled(const String &p_name); void _plugin_over_edit(EditorPlugin *p_plugin, Object *p_object); + void _plugin_over_self_own(EditorPlugin *p_plugin); void _fs_changed(); void _resources_reimported(const Vector<String> &p_resources); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 2726a781b4..b43e7eba9c 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -335,6 +335,10 @@ bool EditorPlugin::handles(Object *p_object) const { return success; } +bool EditorPlugin::can_auto_hide() const { + return true; +} + Dictionary EditorPlugin::get_state() const { Dictionary state; GDVIRTUAL_CALL(_get_state, state); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 62ed432ecc..8870ef425e 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -178,6 +178,7 @@ public: virtual void selected_notify() {} //notify that it was raised by the user, not the editor virtual void edit(Object *p_object); virtual bool handles(Object *p_object) const; + virtual bool can_auto_hide() const; virtual Dictionary get_state() const; //save editor state so it can't be reloaded when reloading scene virtual void set_state(const Dictionary &p_state); //restore editor state (likely was saved with the scene) virtual void clear(); // clear any temporary data in the editor, reset it (likely new scene or load another scene) diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index 42cd858581..00db344b6a 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -110,6 +110,8 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) { bool debug_paths = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_paths", false); bool debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false); bool debug_avoidance = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_avoidance", false); + bool debug_canvas_redraw = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_canvas_redraw", false); + if (debug_collisions) { args.push_back("--debug-collisions"); } @@ -126,6 +128,10 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) { args.push_back("--debug-avoidance"); } + if (debug_canvas_redraw) { + args.push_back("--debug-canvas-item-redraw"); + } + if (p_write_movie != "") { args.push_back("--write-movie"); args.push_back(p_write_movie); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 89a94fe0da..1eaeee97a5 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -519,6 +519,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING_USAGE(Variant::FLOAT, PROPERTY_HINT_RANGE, "filesystem/import/blender/rpc_server_uptime", 5, "0,300,1,or_greater,suffix:s", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/import/fbx/fbx2gltf_path", "", "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + // Tools (denoise) + EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/tools/oidn/oidn_denoise_path", "", "", PROPERTY_USAGE_DEFAULT) + /* Docks */ // SceneTree @@ -821,6 +824,7 @@ void EditorSettings::_load_godot2_text_editor_theme() { _initial_set("text_editor/theme/highlighting/engine_type_color", Color(0.51, 0.83, 1.0)); _initial_set("text_editor/theme/highlighting/user_type_color", Color(0.42, 0.67, 0.93)); _initial_set("text_editor/theme/highlighting/comment_color", Color(0.4, 0.4, 0.4)); + _initial_set("text_editor/theme/highlighting/doc_comment_color", Color(0.5, 0.6, 0.7)); _initial_set("text_editor/theme/highlighting/string_color", Color(0.94, 0.43, 0.75)); _initial_set("text_editor/theme/highlighting/background_color", Color(0.13, 0.12, 0.15)); _initial_set("text_editor/theme/highlighting/completion_background_color", Color(0.17, 0.16, 0.2)); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 0e7525875d..f5be91c6de 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -2215,7 +2215,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // adaptive script theme constants // for comments and elements with lower relevance - const Color dim_color = Color(font_color.r, font_color.g, font_color.b, 0.5); + const Color dim_color = Color(font_color, 0.5); const float mono_value = mono_color.r; const Color alpha1 = Color(mono_value, mono_value, mono_value, 0.07); @@ -2229,6 +2229,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { const Color engine_type_color = dark_theme ? Color(0.56, 1, 0.86) : Color(0.11, 0.55, 0.4); const Color user_type_color = dark_theme ? Color(0.78, 1, 0.93) : Color(0.18, 0.45, 0.4); const Color comment_color = dark_theme ? dim_color : Color(0.08, 0.08, 0.08, 0.5); + const Color doc_comment_color = dark_theme ? Color(0.6, 0.7, 0.8, 0.8) : Color(0.15, 0.15, 0.4, 0.7); const Color string_color = dark_theme ? Color(1, 0.93, 0.63) : Color(0.6, 0.42, 0); // Use the brightest background color on a light theme (which generally uses a negative contrast rate). @@ -2272,6 +2273,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { setting->set_initial_value("text_editor/theme/highlighting/engine_type_color", engine_type_color, true); setting->set_initial_value("text_editor/theme/highlighting/user_type_color", user_type_color, true); setting->set_initial_value("text_editor/theme/highlighting/comment_color", comment_color, true); + setting->set_initial_value("text_editor/theme/highlighting/doc_comment_color", doc_comment_color, true); setting->set_initial_value("text_editor/theme/highlighting/string_color", string_color, true); setting->set_initial_value("text_editor/theme/highlighting/background_color", te_background_color, true); setting->set_initial_value("text_editor/theme/highlighting/completion_background_color", completion_background_color, true); diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp index 3a034c8dcc..26ed7c46fb 100644 --- a/editor/export/export_template_manager.cpp +++ b/editor/export/export_template_manager.cpp @@ -686,7 +686,7 @@ Error ExportTemplateManager::install_android_template_from_file(const String &p_ zlib_filefunc_def io = zipio_create_io(&io_fa); unzFile pkg = unzOpen2(p_file.utf8().get_data(), &io); - ERR_FAIL_COND_V_MSG(!pkg, ERR_CANT_OPEN, "Android sources not in ZIP format."); + ERR_FAIL_NULL_V_MSG(pkg, ERR_CANT_OPEN, "Android sources not in ZIP format."); int ret = unzGoToFirstFile(pkg); int total_files = 0; diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index be06a3932c..fb4f5efc25 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1729,12 +1729,12 @@ void FileSystemDock::_folder_removed(String p_folder) { void FileSystemDock::_rename_operation_confirm() { String new_name; - TreeItem *s = tree->get_selected(); - int col_index = tree->get_selected_column(); + TreeItem *ti = tree->get_edited(); + int col_index = tree->get_edited_column(); - if (tree->has_focus()) { - new_name = s->get_text(col_index).strip_edges(); - } else if (files->has_focus()) { + if (ti) { + new_name = ti->get_text(col_index).strip_edges(); + } else { new_name = files->get_edit_text().strip_edges(); } String old_name = to_rename.is_file ? to_rename.path.get_file() : to_rename.path.left(-1).get_file(); @@ -1757,10 +1757,10 @@ void FileSystemDock::_rename_operation_confirm() { } // Restore original name. - if (rename_error && tree->has_focus()) { - s->set_text(col_index, old_name); - return; - } else if (rename_error && files->has_focus()) { + if (rename_error) { + if (ti) { + ti->set_text(col_index, old_name); + } return; } @@ -1783,7 +1783,7 @@ void FileSystemDock::_rename_operation_confirm() { if (da->file_exists(new_path) || da->dir_exists(new_path)) { #endif EditorNode::get_singleton()->show_warning(TTR("A file or folder with this name already exists.")); - s->set_text(col_index, old_name); + ti->set_text(col_index, old_name); return; } @@ -1803,7 +1803,7 @@ void FileSystemDock::_rename_operation_confirm() { EditorSceneTabs::get_singleton()->set_current_tab(current_tab); - if (tree->has_focus()) { + if (ti) { current_path = new_path; current_path_line_edit->set_text(current_path); } @@ -2004,13 +2004,13 @@ void FileSystemDock::_before_move(HashMap<String, ResourceUID::ID> &r_uids, Vect EditorNode::get_singleton()->save_scene_list(r_file_owners); } -Vector<String> FileSystemDock::_tree_get_selected(bool remove_self_inclusion) const { +Vector<String> FileSystemDock::_tree_get_selected(bool remove_self_inclusion, bool p_include_unselected_cursor) const { // Build a list of selected items with the active one at the first position. Vector<String> selected_strings; TreeItem *favorites_item = tree->get_root()->get_first_child(); TreeItem *cursor_item = tree->get_selected(); - if (cursor_item && cursor_item->is_selected(0) && cursor_item != favorites_item) { + if (cursor_item && (p_include_unselected_cursor || cursor_item->is_selected(0)) && cursor_item != favorites_item) { selected_strings.push_back(cursor_item->get_metadata(0)); } @@ -2059,6 +2059,10 @@ void FileSystemDock::_tree_rmb_option(int p_option) { tree->get_selected()->set_collapsed_recursive(p_option == FOLDER_COLLAPSE_ALL); } } break; + case FILE_RENAME: { + selected_strings = _tree_get_selected(false, true); + [[fallthrough]]; + } default: { _file_option(p_option, selected_strings); } break; diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 1e04b6a4ff..104def71c8 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -351,7 +351,7 @@ private: void _update_display_mode(bool p_force = false); - Vector<String> _tree_get_selected(bool remove_self_inclusion = true) const; + Vector<String> _tree_get_selected(bool remove_self_inclusion = true, bool p_include_unselected_cursor = false) const; bool _is_file_type_disabled_by_feature_profile(const StringName &p_class); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 5506f4a6bc..a14df01858 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -604,6 +604,8 @@ FindInFilesPanel::FindInFilesPanel() { _results_display->set_select_mode(Tree::SELECT_ROW); _results_display->set_allow_rmb_select(true); _results_display->set_allow_reselect(true); + _results_display->add_theme_constant_override("inner_item_margin_left", 0); + _results_display->add_theme_constant_override("inner_item_margin_right", 0); _results_display->create_item(); // Root vbc->add_child(_results_display); diff --git a/editor/gui/editor_toaster.cpp b/editor/gui/editor_toaster.cpp index 0d468bc0a3..1abb591508 100644 --- a/editor/gui/editor_toaster.cpp +++ b/editor/gui/editor_toaster.cpp @@ -149,7 +149,7 @@ void EditorToaster::_notification(int p_what) { void EditorToaster::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) { // This may be called from a thread. Since we will deal with non-thread-safe elements, // we have to put it in the queue for safety. - callable_mp_static(&EditorToaster::_error_handler_impl).bind(p_file, p_line, p_error, p_errorexp, p_editor_notify, p_type).call_deferred(); + callable_mp_static(&EditorToaster::_error_handler_impl).bind(String::utf8(p_file), p_line, String::utf8(p_error), String::utf8(p_errorexp), p_editor_notify, p_type).call_deferred(); } void EditorToaster::_error_handler_impl(const String &p_file, int p_line, const String &p_error, const String &p_errorexp, bool p_editor_notify, int p_type) { diff --git a/editor/gui/editor_zoom_widget.cpp b/editor/gui/editor_zoom_widget.cpp index e292dc99ac..5c936a75f2 100644 --- a/editor/gui/editor_zoom_widget.cpp +++ b/editor/gui/editor_zoom_widget.cpp @@ -70,12 +70,36 @@ float EditorZoomWidget::get_zoom() { } void EditorZoomWidget::set_zoom(float p_zoom) { - if (p_zoom > 0 && p_zoom != zoom) { - zoom = p_zoom; + float new_zoom = CLAMP(p_zoom, min_zoom, max_zoom); + if (zoom != new_zoom) { + zoom = new_zoom; _update_zoom_label(); } } +float EditorZoomWidget::get_min_zoom() { + return min_zoom; +} + +float EditorZoomWidget::get_max_zoom() { + return max_zoom; +} + +void EditorZoomWidget::setup_zoom_limits(float p_min, float p_max) { + ERR_FAIL_COND(p_min < 0 || p_min > p_max); + + min_zoom = p_min; + max_zoom = p_max; + + if (zoom > max_zoom) { + set_zoom(max_zoom); + emit_signal(SNAME("zoom_changed"), zoom); + } else if (zoom < min_zoom) { + set_zoom(min_zoom); + emit_signal(SNAME("zoom_changed"), zoom); + } +} + void EditorZoomWidget::set_zoom_by_increments(int p_increment_count, bool p_integer_only) { // Remove editor scale from the index computation. const float zoom_noscale = zoom / MAX(1, EDSCALE); @@ -97,7 +121,7 @@ void EditorZoomWidget::set_zoom_by_increments(int p_increment_count, bool p_inte } } else { if (p_increment_count >= 1) { - // Zooming. Convert the current zoom into a denominator. + // Zooming in. Convert the current zoom into a denominator. float new_zoom = 1.0 / Math::ceil(1.0 / zoom_noscale - p_increment_count); if (Math::is_equal_approx(zoom_noscale, new_zoom)) { // New zoom is identical to the old zoom, so try again. @@ -106,7 +130,7 @@ void EditorZoomWidget::set_zoom_by_increments(int p_increment_count, bool p_inte } set_zoom(new_zoom * MAX(1, EDSCALE)); } else { - // Dezooming. Convert the current zoom into a denominator. + // Zooming out. Convert the current zoom into a denominator. float new_zoom = 1.0 / Math::floor(1.0 / zoom_noscale - p_increment_count); if (Math::is_equal_approx(zoom_noscale, new_zoom)) { // New zoom is identical to the old zoom, so try again. @@ -118,9 +142,9 @@ void EditorZoomWidget::set_zoom_by_increments(int p_increment_count, bool p_inte } } else { // Base increment factor defined as the twelveth root of two. - // This allow a smooth geometric evolution of the zoom, with the advantage of + // This allows for a smooth geometric evolution of the zoom, with the advantage of // visiting all integer power of two scale factors. - // note: this is analogous to the 'semitones' interval in the music world + // Note: this is analogous to the 'semitone' interval in the music world // In order to avoid numerical imprecisions, we compute and edit a zoom index // with the following relation: zoom = 2 ^ (index / 12) @@ -179,10 +203,11 @@ EditorZoomWidget::EditorZoomWidget() { zoom_reset = memnew(Button); zoom_reset->set_flat(true); - zoom_reset->add_theme_style_override("normal", memnew(StyleBoxEmpty)); - zoom_reset->add_theme_style_override("hover", memnew(StyleBoxEmpty)); - zoom_reset->add_theme_style_override("focus", memnew(StyleBoxEmpty)); - zoom_reset->add_theme_style_override("pressed", memnew(StyleBoxEmpty)); + Ref<StyleBoxEmpty> empty_stylebox = memnew(StyleBoxEmpty); + zoom_reset->add_theme_style_override("normal", empty_stylebox); + zoom_reset->add_theme_style_override("hover", empty_stylebox); + zoom_reset->add_theme_style_override("focus", empty_stylebox); + zoom_reset->add_theme_style_override("pressed", empty_stylebox); add_child(zoom_reset); zoom_reset->add_theme_constant_override("outline_size", Math::ceil(2 * EDSCALE)); zoom_reset->add_theme_color_override("font_outline_color", Color(0, 0, 0)); diff --git a/editor/gui/editor_zoom_widget.h b/editor/gui/editor_zoom_widget.h index be54043d93..6b2fe4d3e9 100644 --- a/editor/gui/editor_zoom_widget.h +++ b/editor/gui/editor_zoom_widget.h @@ -42,6 +42,8 @@ class EditorZoomWidget : public HBoxContainer { Button *zoom_plus = nullptr; float zoom = 1.0; + float min_zoom = 1.0 / 128; + float max_zoom = 128.0; void _update_zoom_label(); void _button_zoom_minus(); void _button_zoom_reset(); @@ -57,6 +59,11 @@ public: float get_zoom(); void set_zoom(float p_zoom); void set_zoom_by_increments(int p_increment_count, bool p_integer_only = false); + + float get_min_zoom(); + float get_max_zoom(); + // It's best to setup simultaneously, so min < max can be checked easily. + void setup_zoom_limits(float p_min, float p_max); // Sets the shortcut context for the zoom buttons. By default their context is this EditorZoomWidget control. void set_shortcut_context(Node *p_node) const; }; diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp index d27b0aea40..be63973ea7 100644 --- a/editor/import/collada.cpp +++ b/editor/import/collada.cpp @@ -2197,7 +2197,7 @@ bool Collada::_move_geometry_to_skeletons(VisualScene *p_vscene, Node *p_node, L ERR_FAIL_COND_V(!state.scene_map.has(nodeid), false); //weird, it should have it... NodeJoint *nj = dynamic_cast<NodeJoint *>(state.scene_map[nodeid]); ERR_FAIL_NULL_V(nj, false); - ERR_FAIL_COND_V(!nj->owner, false); //weird, node should have a skeleton owner + ERR_FAIL_NULL_V(nj->owner, false); // Weird, node should have a skeleton owner. NodeSkeleton *sk = nj->owner; diff --git a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/post_import_plugin_skeleton_rest_fixer.cpp index cd31499b80..3390bf4ed4 100644 --- a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp +++ b/editor/import/post_import_plugin_skeleton_rest_fixer.cpp @@ -242,6 +242,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory if (rot_track == -1) { int track = anim->add_track(Animation::TYPE_ROTATION_3D); anim->track_set_path(track, insert_path); + anim->track_set_imported(track, true); anim->rotation_track_insert_key(track, 0, src_skeleton->get_bone_rest(src_idx).basis.get_rotation_quaternion()); } } diff --git a/editor/import/resource_importer_shader_file.cpp b/editor/import/resource_importer_shader_file.cpp index 1275e5b85a..bde2e3d0bf 100644 --- a/editor/import/resource_importer_shader_file.cpp +++ b/editor/import/resource_importer_shader_file.cpp @@ -96,7 +96,7 @@ Error ResourceImporterShaderFile::import(const String &p_source_file, const Stri Error err; Ref<FileAccess> file = FileAccess::open(p_source_file, FileAccess::READ, &err); ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN); - ERR_FAIL_COND_V(!file.operator->(), ERR_CANT_OPEN); + ERR_FAIL_COND_V(!file.is_valid(), ERR_CANT_OPEN); String file_txt = file->get_as_utf8_string(); Ref<RDShaderFile> shader_file; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 4bfa6adaae..55d45fdd2e 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -60,11 +60,6 @@ #include "scene/resources/packed_scene.h" #include "scene/resources/style_box_texture.h" -// Min and Max are power of two in order to play nicely with successive increment. -// That way, we can naturally reach a 100% zoom from boundaries. -constexpr real_t MIN_ZOOM = 1. / 128; -constexpr real_t MAX_ZOOM = 128; - #define RULER_WIDTH (15 * EDSCALE) constexpr real_t SCALE_HANDLE_DISTANCE = 25; constexpr real_t MOVE_HANDLE_DISTANCE = 25; @@ -4115,10 +4110,9 @@ void CanvasItemEditor::_update_scroll(real_t) { } void CanvasItemEditor::_zoom_on_position(real_t p_zoom, Point2 p_position) { - p_zoom = CLAMP(p_zoom, MIN_ZOOM, MAX_ZOOM); + p_zoom = CLAMP(p_zoom, zoom_widget->get_min_zoom(), zoom_widget->get_max_zoom()); if (p_zoom == zoom) { - zoom_widget->set_zoom(p_zoom); return; } @@ -4128,12 +4122,12 @@ void CanvasItemEditor::_zoom_on_position(real_t p_zoom, Point2 p_position) { view_offset += p_position / prev_zoom - p_position / zoom; // We want to align in-scene pixels to screen pixels, this prevents blurry rendering - // in small details (texts, lines). + // of small details (texts, lines). // This correction adds a jitter movement when zooming, so we correct only when the // zoom factor is an integer. (in the other cases, all pixels won't be aligned anyway) const real_t closest_zoom_factor = Math::round(zoom); if (Math::is_zero_approx(zoom - closest_zoom_factor)) { - // make sure scene pixel at view_offset is aligned on a screen pixel + // Make sure scene pixel at view_offset is aligned on a screen pixel. Vector2 view_offset_int = view_offset.floor(); Vector2 view_offset_frac = view_offset - view_offset_int; view_offset = view_offset_int + (view_offset_frac * closest_zoom_factor).round() / closest_zoom_factor; @@ -5147,9 +5141,9 @@ CanvasItemEditor::CanvasItemEditor() { button_center_view->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback).bind(VIEW_CENTER_TO_SELECTION)); zoom_widget = memnew(EditorZoomWidget); - controls_hb->add_child(zoom_widget); zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE); zoom_widget->set_shortcut_context(this); + controls_hb->add_child(zoom_widget); zoom_widget->connect("zoom_changed", callable_mp(this, &CanvasItemEditor::_update_zoom)); panner.instantiate(); diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp index 15829a55de..b636ec4f5c 100644 --- a/editor/plugins/debugger_editor_plugin.cpp +++ b/editor/plugins/debugger_editor_plugin.cpp @@ -79,6 +79,10 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) { debug_menu->set_item_tooltip(-1, TTR("When this option is enabled, avoidance objects shapes, radius and velocities will be visible in the running project.")); debug_menu->add_separator(); + debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_canvas_redraw", TTR("Debug CanvasItem Redraws")), RUN_DEBUG_CANVAS_REDRAW); + debug_menu->set_item_tooltip(-1, + TTR("When this option is enabled, redraw requests of 2D objects will become visible (as a short flash) in the running project.\nThis is useful to troubleshoot low processor mode.")); + debug_menu->add_separator(); debug_menu->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTR("Synchronize Scene Changes")), RUN_LIVE_DEBUG); debug_menu->set_item_tooltip(-1, TTR("When this option is enabled, any changes made to the scene in the editor will be replicated in the running project.\nWhen used remotely on a device, this is more efficient when the network filesystem option is enabled.")); @@ -175,6 +179,12 @@ void DebuggerEditorPlugin::_menu_option(int p_option) { EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_avoidance", !ischecked); } break; + case RUN_DEBUG_CANVAS_REDRAW: { + bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_CANVAS_REDRAW)); + debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_CANVAS_REDRAW), !ischecked); + EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_canvas_redraw", !ischecked); + + } break; case RUN_RELOAD_SCRIPTS: { bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_RELOAD_SCRIPTS)); debug_menu->set_item_checked(debug_menu->get_item_index(RUN_RELOAD_SCRIPTS), !ischecked); @@ -213,6 +223,7 @@ void DebuggerEditorPlugin::_update_debug_options() { bool check_debug_paths = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_paths", false); bool check_debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false); bool check_debug_avoidance = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_avoidance", false); + bool check_debug_canvas_redraw = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_canvas_redraw", false); bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", true); bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", true); bool check_server_keep_open = EditorSettings::get_singleton()->get_project_metadata("debug_options", "server_keep_open", false); @@ -236,6 +247,9 @@ void DebuggerEditorPlugin::_update_debug_options() { if (check_debug_avoidance) { _menu_option(RUN_DEBUG_AVOIDANCE); } + if (check_debug_canvas_redraw) { + _menu_option(RUN_DEBUG_CANVAS_REDRAW); + } if (check_live_debug) { _menu_option(RUN_LIVE_DEBUG); } diff --git a/editor/plugins/debugger_editor_plugin.h b/editor/plugins/debugger_editor_plugin.h index eb8da7ca8e..8d65dbd2e4 100644 --- a/editor/plugins/debugger_editor_plugin.h +++ b/editor/plugins/debugger_editor_plugin.h @@ -52,6 +52,7 @@ private: RUN_DEBUG_PATHS, RUN_DEBUG_NAVIGATION, RUN_DEBUG_AVOIDANCE, + RUN_DEBUG_CANVAS_REDRAW, RUN_DEPLOY_REMOTE_DEBUG, RUN_RELOAD_SCRIPTS, SERVER_KEEP_OPEN, diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 1872857130..afc72cdde6 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -496,6 +496,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, Color text_color = EDITOR_GET("text_editor/theme/highlighting/text_color"); Color symbol_color = EDITOR_GET("text_editor/theme/highlighting/symbol_color"); Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color"); + Color doc_comment_color = EDITOR_GET("text_editor/theme/highlighting/doc_comment_color"); if (bg_color.a == 0) { bg_color = Color(0, 0, 0, 0); @@ -513,6 +514,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, bool in_control_flow_keyword = false; bool in_keyword = false; bool in_comment = false; + bool in_doc_comment = false; for (int i = 0; i < code.length(); i++) { char32_t c = code[i]; if (c > 32) { @@ -520,11 +522,17 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, Color color = text_color; if (c == '#') { - in_comment = true; + if (i < code.length() - 1 && code[i + 1] == '#') { + in_doc_comment = true; + } else { + in_comment = true; + } } if (in_comment) { color = comment_color; + } else if (in_doc_comment) { + color = doc_comment_color; } else { if (is_symbol(c)) { //make symbol a little visible @@ -569,6 +577,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, if (c == '\n') { in_comment = false; + in_doc_comment = false; col = x0; line++; diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index ec927f6c5d..79e260ef35 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -191,6 +191,16 @@ void EditorStandardSyntaxHighlighter::_update_cache() { highlighter->add_color_region(beg, end, comment_color, end.is_empty()); } + /* Doc comments */ + const Color doc_comment_color = EDITOR_GET("text_editor/theme/highlighting/doc_comment_color"); + List<String> doc_comments; + scr->get_language()->get_doc_comment_delimiters(&doc_comments); + for (const String &doc_comment : doc_comments) { + String beg = doc_comment.get_slice(" ", 0); + String end = doc_comment.get_slice_count(" ") > 1 ? doc_comment.get_slice(" ", 1) : String(); + highlighter->add_color_region(beg, end, doc_comment_color, end.is_empty()); + } + /* Strings */ const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color"); List<String> strings; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 5322f3c813..5b67c6d509 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -234,9 +234,10 @@ void ScriptTextEditor::_set_theme_for_script() { } } + text_edit->clear_comment_delimiters(); + List<String> comments; script->get_language()->get_comment_delimiters(&comments); - text_edit->clear_comment_delimiters(); for (const String &comment : comments) { String beg = comment.get_slice(" ", 0); String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String(); @@ -246,6 +247,18 @@ void ScriptTextEditor::_set_theme_for_script() { text_edit->add_auto_brace_completion_pair(beg, end); } } + + List<String> doc_comments; + script->get_language()->get_doc_comment_delimiters(&doc_comments); + for (const String &doc_comment : doc_comments) { + String beg = doc_comment.get_slice(" ", 0); + String end = doc_comment.get_slice_count(" ") > 1 ? doc_comment.get_slice(" ", 1) : String(); + text_edit->add_comment_delimiter(beg, end, end.is_empty()); + + if (!end.is_empty() && !text_edit->has_auto_brace_completion_open_key(beg)) { + text_edit->add_auto_brace_completion_pair(beg, end); + } + } } void ScriptTextEditor::_show_errors_panel(bool p_show) { @@ -779,7 +792,7 @@ void ScriptEditor::_update_modified_scripts_for_external_editor(Ref<Script> p_fo return; } - ERR_FAIL_COND(!get_tree()); + ERR_FAIL_NULL(get_tree()); HashSet<Ref<Script>> scripts; diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index e6bb5532a3..20b91d8bfd 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -363,7 +363,7 @@ void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) { void Skeleton3DEditor::create_physical_skeleton() { EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); - ERR_FAIL_COND(!get_tree()); + ERR_FAIL_NULL(get_tree()); Node *owner = get_tree()->get_edited_scene_root(); const int bone_count = skeleton->get_bone_count(); diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 821d8151a4..9ba5f2faf1 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -31,12 +31,14 @@ #include "theme_editor_plugin.h" #include "core/os/keyboard.h" +#include "editor/editor_help.h" #include "editor/editor_node.h" #include "editor/editor_resource_picker.h" #include "editor/editor_scale.h" #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_file_dialog.h" +#include "editor/inspector_dock.h" #include "editor/progress_dialog.h" #include "scene/gui/check_button.h" #include "scene/gui/color_picker.h" @@ -2259,6 +2261,10 @@ ThemeTypeDialog::ThemeTypeDialog() { /////////////////////// +Control *ThemeItemLabel::make_custom_tooltip(const String &p_text) const { + return memnew(EditorHelpTooltip(p_text)); +} + VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) { VBoxContainer *items_tab = memnew(VBoxContainer); items_tab->set_custom_minimum_size(Size2(0, 160) * EDSCALE); @@ -2413,11 +2419,13 @@ HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_ item_name_container->set_stretch_ratio(2.0); item_control->add_child(item_name_container); - Label *item_name = memnew(Label); + Label *item_name = memnew(ThemeItemLabel); item_name->set_h_size_flags(SIZE_EXPAND_FILL); item_name->set_clip_text(true); item_name->set_text(p_item_name); - item_name->set_tooltip_text(p_item_name); + // `|` separators used in `EditorHelpTooltip` for formatting. + item_name->set_tooltip_text("theme_item|" + edited_type + "|" + p_item_name + "|"); + item_name->set_mouse_filter(Control::MOUSE_FILTER_STOP); item_name_container->add_child(item_name); if (p_editable) { @@ -2477,7 +2485,6 @@ void ThemeTypeEditor::_add_focusable(Control *p_control) { void ThemeTypeEditor::_update_type_items() { bool show_default = show_default_items_button->is_pressed(); - List<StringName> names; focusables.clear(); @@ -3528,6 +3535,16 @@ void ThemeEditor::_theme_edit_button_cbk() { theme_edit_dialog->popup_centered(Size2(850, 700) * EDSCALE); } +void ThemeEditor::_theme_close_button_cbk() { + plugin->make_visible(false); // Enables auto hide. + if (theme.is_valid() && InspectorDock::get_inspector_singleton()->get_edited_object() == theme.ptr()) { + EditorNode::get_singleton()->push_item(nullptr); + } else { + theme = Ref<Theme>(); + EditorNode::get_singleton()->hide_unused_editors(plugin); + } +} + void ThemeEditor::_add_preview_button_cbk() { preview_scene_dialog->popup_file_dialog(); } @@ -3645,6 +3662,12 @@ ThemeEditor::ThemeEditor() { theme_save_as_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_save_button_cbk).bind(true)); top_menu->add_child(theme_save_as_button); + Button *theme_close_button = memnew(Button); + theme_close_button->set_text(TTR("Close")); + theme_close_button->set_flat(true); + theme_close_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_close_button_cbk)); + top_menu->add_child(theme_close_button); + top_menu->add_child(memnew(VSeparator)); Button *theme_edit_button = memnew(Button); @@ -3711,20 +3734,12 @@ ThemeEditor::ThemeEditor() { /////////////////////// -void ThemeEditorPlugin::edit(Object *p_node) { - if (Object::cast_to<Theme>(p_node)) { - theme_editor->edit(Object::cast_to<Theme>(p_node)); - } else { - // We intentionally keep a reference to the last used theme to work around - // the the editor being hidden while base resources are edited. Uncomment - // the following line again and remove this comment once that bug has been - // fixed (scheduled for Godot 4.1 in PR 73098): - // theme_editor->edit(Ref<Theme>()); - } +void ThemeEditorPlugin::edit(Object *p_object) { + theme_editor->edit(Ref<Theme>(p_object)); } -bool ThemeEditorPlugin::handles(Object *p_node) const { - return Object::cast_to<Theme>(p_node) != nullptr; +bool ThemeEditorPlugin::handles(Object *p_object) const { + return Object::cast_to<Theme>(p_object) != nullptr; } void ThemeEditorPlugin::make_visible(bool p_visible) { @@ -3740,8 +3755,77 @@ void ThemeEditorPlugin::make_visible(bool p_visible) { } } +bool ThemeEditorPlugin::can_auto_hide() const { + Ref<Theme> edited_theme = theme_editor->theme; + if (edited_theme.is_null()) { + return true; + } + + Ref<Resource> edited_resource = Ref<Resource>(InspectorDock::get_inspector_singleton()->get_next_edited_object()); + if (edited_resource.is_null()) { + return true; + } + + // Don't hide if edited resource used by this theme. + Ref<StyleBox> sbox = edited_resource; + if (sbox.is_valid()) { + List<StringName> type_list; + edited_theme->get_stylebox_type_list(&type_list); + + for (const StringName &E : type_list) { + List<StringName> list; + edited_theme->get_stylebox_list(E, &list); + + for (const StringName &F : list) { + if (edited_theme->get_stylebox(F, E) == sbox) { + return false; + } + } + } + return true; + } + + Ref<Texture2D> tex = edited_resource; + if (tex.is_valid()) { + List<StringName> type_list; + edited_theme->get_icon_type_list(&type_list); + + for (const StringName &E : type_list) { + List<StringName> list; + edited_theme->get_icon_list(E, &list); + + for (const StringName &F : list) { + if (edited_theme->get_icon(F, E) == tex) { + return false; + } + } + } + return true; + } + + Ref<Font> fnt = edited_resource; + if (fnt.is_valid()) { + List<StringName> type_list; + edited_theme->get_font_type_list(&type_list); + + for (const StringName &E : type_list) { + List<StringName> list; + edited_theme->get_font_list(E, &list); + + for (const StringName &F : list) { + if (edited_theme->get_font(F, E) == fnt) { + return false; + } + } + } + return true; + } + return true; +} + ThemeEditorPlugin::ThemeEditorPlugin() { theme_editor = memnew(ThemeEditor); + theme_editor->plugin = this; theme_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE); button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Theme"), theme_editor); diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 077ce8e8f7..33accf587a 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -47,6 +47,7 @@ class OptionButton; class PanelContainer; class TabBar; class TabContainer; +class ThemeEditorPlugin; class TextureRect; class ThemeItemImportTree : public VBoxContainer { @@ -320,6 +321,11 @@ public: ThemeTypeDialog(); }; +// Custom `Label` needed to use `EditorHelpTooltip` to display theme item documentation. +class ThemeItemLabel : public Label { + virtual Control *make_custom_tooltip(const String &p_text) const; +}; + class ThemeTypeEditor : public MarginContainer { GDCLASS(ThemeTypeEditor, MarginContainer); @@ -419,6 +425,9 @@ public: class ThemeEditor : public VBoxContainer { GDCLASS(ThemeEditor, VBoxContainer); + friend class ThemeEditorPlugin; + ThemeEditorPlugin *plugin = nullptr; + Ref<Theme> theme; TabBar *preview_tabs = nullptr; @@ -433,6 +442,7 @@ class ThemeEditor : public VBoxContainer { void _theme_save_button_cbk(bool p_save_as); void _theme_edit_button_cbk(); + void _theme_close_button_cbk(); void _add_preview_button_cbk(); void _preview_scene_dialog_cbk(const String &p_path); @@ -462,9 +472,10 @@ class ThemeEditorPlugin : public EditorPlugin { public: virtual String get_name() const override { return "Theme"; } bool has_main_screen() const override { return false; } - virtual void edit(Object *p_node) override; - virtual bool handles(Object *p_node) const override; + virtual void edit(Object *p_object) override; + virtual bool handles(Object *p_object) const override; virtual void make_visible(bool p_visible) override; + virtual bool can_auto_hide() const override; ThemeEditorPlugin(); }; diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index b55be8b3f8..039213e545 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -90,8 +90,6 @@ Size2i TileAtlasView::_compute_alternative_tiles_control_size() { } void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) { - // Don't allow zoom to go below 1% or above 10000% - zoom_widget->set_zoom(CLAMP(zoom_widget->get_zoom(), 0.01f, 100.f)); float zoom = zoom_widget->get_zoom(); // Compute the minimum sizes. diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index d634d957b3..ad4844fe3e 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -361,8 +361,8 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) { } undo_redo->add_do_method(base_control, "queue_redraw"); undo_redo->add_do_method(this, "emit_signal", "polygons_changed"); - for (const PackedVector2Array &polygon : polygons) { - undo_redo->add_undo_method(this, "set_polygon", polygon); + for (unsigned int i = 0; i < polygons.size(); i++) { + undo_redo->add_undo_method(this, "set_polygon", i, polygons[i]); } undo_redo->add_undo_method(base_control, "queue_redraw"); undo_redo->add_undo_method(this, "emit_signal", "polygons_changed"); @@ -931,6 +931,7 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() { snap_subdivision->connect("value_changed", callable_mp(this, &GenericTilePolygonEditor::_store_snap_options).unbind(1)); editor_zoom_widget = memnew(EditorZoomWidget); + editor_zoom_widget->setup_zoom_limits(0.125, 128.0); editor_zoom_widget->set_position(Vector2(5, 5)); editor_zoom_widget->connect("zoom_changed", callable_mp(this, &GenericTilePolygonEditor::_zoom_changed).unbind(1)); editor_zoom_widget->set_shortcut_context(this); diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index afc929b547..6bac62d861 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -44,7 +44,7 @@ #include "scene/gui/separator.h" #define CHECK_PLUGIN_INITIALIZED() \ - ERR_FAIL_COND_MSG(!EditorVCSInterface::get_singleton(), "No VCS plugin is initialized. Select a Version Control Plugin from Project menu."); + ERR_FAIL_NULL_MSG(EditorVCSInterface::get_singleton(), "No VCS plugin is initialized. Select a Version Control Plugin from Project menu."); VersionControlEditorPlugin *VersionControlEditorPlugin::singleton = nullptr; diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index e7fe9a353c..b04773d371 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -397,7 +397,7 @@ void ProjectDialog::_nonempty_confirmation_ok_pressed() { } void ProjectDialog::_renderer_selected() { - ERR_FAIL_COND(!renderer_button_group->get_pressed_button()); + ERR_FAIL_NULL(renderer_button_group->get_pressed_button()); String renderer_type = renderer_button_group->get_pressed_button()->get_meta(SNAME("rendering_method")); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index e7727aec4b..a38487635f 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -651,8 +651,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Node *top_node = selection[i]; Node *bottom_node = selection[selection.size() - 1 - i]; - ERR_FAIL_COND(!top_node->get_parent()); - ERR_FAIL_COND(!bottom_node->get_parent()); + ERR_FAIL_NULL(top_node->get_parent()); + ERR_FAIL_NULL(bottom_node->get_parent()); int bottom_node_pos = bottom_node->get_index(false); int top_node_pos_next = top_node->get_index(false) + (MOVING_DOWN ? 1 : -1); @@ -1934,6 +1934,8 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V p_nodes.sort_custom<Node::Comparator>(); //Makes result reliable. + const int first_idx = p_position_in_parent == -1 ? p_new_parent->get_child_count(false) : p_position_in_parent; + int nodes_before = first_idx; bool no_change = true; for (int ni = 0; ni < p_nodes.size(); ni++) { if (p_nodes[ni] == p_new_parent) { @@ -1942,7 +1944,17 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V // `move_child` + `get_index` doesn't really work for internal nodes. ERR_FAIL_COND_MSG(p_nodes[ni]->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to move internal node, this is not supported."); - if (p_nodes[ni]->get_parent() != p_new_parent || p_position_in_parent + ni != p_nodes[ni]->get_index(false)) { + if (p_nodes[ni]->get_index(false) < first_idx) { + nodes_before--; + } + + if (p_nodes[ni]->get_parent() != p_new_parent) { + no_change = false; + } + } + + for (int ni = 0; ni < p_nodes.size() && no_change; ni++) { + if (p_nodes[ni]->get_index(false) != nodes_before + ni) { no_change = false; } } |
