diff options
Diffstat (limited to 'editor')
71 files changed, 877 insertions, 380 deletions
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 219f3fdbe1..3b87b3e65e 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -667,8 +667,8 @@ void AnimationBezierTrackEdit::set_timeline(AnimationTimelineEdit *p_timeline) { void AnimationBezierTrackEdit::set_editor(AnimationTrackEditor *p_editor) { editor = p_editor; - connect("clear_selection", Callable(editor, "_clear_selection").bind(false)); - connect("select_key", Callable(editor, "_key_selected"), CONNECT_DEFERRED); + connect("clear_selection", callable_mp(editor, &AnimationTrackEditor::_clear_selection).bind(false)); + connect("select_key", callable_mp(editor, &AnimationTrackEditor::_key_selected), CONNECT_DEFERRED); } void AnimationBezierTrackEdit::_play_position_draw() { diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 70b5501692..9529460ab1 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -1445,7 +1445,9 @@ void AnimationTimelineEdit::_anim_loop_pressed() { default: break; } + undo_redo->add_do_method(this, "update_values"); undo_redo->add_undo_method(animation.ptr(), "set_loop_mode", animation->get_loop_mode()); + undo_redo->add_undo_method(this, "update_values"); undo_redo->commit_action(); } else { String base_path = animation->get_path(); @@ -1913,6 +1915,8 @@ void AnimationTimelineEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "drag"), PropertyInfo(Variant::BOOL, "timeline_only"))); ADD_SIGNAL(MethodInfo("track_added", PropertyInfo(Variant::INT, "track"))); ADD_SIGNAL(MethodInfo("length_changed", PropertyInfo(Variant::FLOAT, "size"))); + + ClassDB::bind_method(D_METHOD("update_values"), &AnimationTimelineEdit::update_values); } AnimationTimelineEdit::AnimationTimelineEdit() { @@ -3420,9 +3424,6 @@ void AnimationTrackEditGroup::_zoom_changed() { queue_redraw(); } -void AnimationTrackEditGroup::_bind_methods() { -} - AnimationTrackEditGroup::AnimationTrackEditGroup() { set_mouse_filter(MOUSE_FILTER_PASS); } @@ -6050,10 +6051,9 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { real_t to_diff = fmod(b - a, Math_TAU); to_v = a + fmod(2.0 * to_diff, Math_TAU) - to_diff; } - Variant delta_v; - Variant::sub(to_v, from_v, delta_v); + Variant delta_v = Animation::subtract_variant(to_v, from_v); double duration = to_t - from_t; - double fixed_duration = duration - 0.01; // Prevent to overwrap keys... + double fixed_duration = duration - UNIT_EPSILON; // Prevent to overwrap keys... for (double delta_t = dur_step; delta_t < fixed_duration; delta_t += dur_step) { Pair<real_t, Variant> keydata; keydata.first = from_t + delta_t; @@ -6458,16 +6458,11 @@ void AnimationTrackEditor::_select_all_tracks_for_copy() { } void AnimationTrackEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_animation_update"), &AnimationTrackEditor::_animation_update); - ClassDB::bind_method(D_METHOD("_track_grab_focus"), &AnimationTrackEditor::_track_grab_focus); - ClassDB::bind_method(D_METHOD("_update_tracks"), &AnimationTrackEditor::_update_tracks); - ClassDB::bind_method(D_METHOD("_redraw_tracks"), &AnimationTrackEditor::_redraw_tracks); - ClassDB::bind_method(D_METHOD("_clear_selection_for_anim"), &AnimationTrackEditor::_clear_selection_for_anim); - ClassDB::bind_method(D_METHOD("_select_at_anim"), &AnimationTrackEditor::_select_at_anim); - - ClassDB::bind_method(D_METHOD("_key_selected"), &AnimationTrackEditor::_key_selected); // Still used by some connect_compat. - ClassDB::bind_method(D_METHOD("_key_deselected"), &AnimationTrackEditor::_key_deselected); // Still used by some connect_compat. - ClassDB::bind_method(D_METHOD("_clear_selection"), &AnimationTrackEditor::_clear_selection); // Still used by some connect_compat. + ClassDB::bind_method("_animation_update", &AnimationTrackEditor::_animation_update); + ClassDB::bind_method("_track_grab_focus", &AnimationTrackEditor::_track_grab_focus); + ClassDB::bind_method("_clear_selection_for_anim", &AnimationTrackEditor::_clear_selection_for_anim); + ClassDB::bind_method("_select_at_anim", &AnimationTrackEditor::_select_at_anim); + ClassDB::bind_method("_clear_selection", &AnimationTrackEditor::_clear_selection); ClassDB::bind_method(D_METHOD("_bezier_track_set_key_handle_mode", "animation", "track_idx", "key_idx", "key_handle_mode", "key_handle_set_mode"), &AnimationTrackEditor::_bezier_track_set_key_handle_mode, DEFVAL(Animation::HANDLE_SET_MODE_NONE)); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index ac69b88e99..5c51921d93 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -280,7 +280,6 @@ class AnimationTrackEditGroup : public Control { void _zoom_changed(); protected: - static void _bind_methods(); void _notification(int p_what); public: @@ -407,7 +406,6 @@ class AnimationTrackEditor : public VBoxContainer { void _insert_key_from_track(float p_ofs, int p_track); void _add_method_key(const String &p_method); - void _clear_selection(bool p_update = false); void _clear_selection_for_anim(const Ref<Animation> &p_anim); void _select_at_anim(const Ref<Animation> &p_anim, int p_track, float p_pos); @@ -425,9 +423,6 @@ class AnimationTrackEditor : public VBoxContainer { RBMap<SelectedKey, KeyInfo> selection; - void _key_selected(int p_key, bool p_single, int p_track); - void _key_deselected(int p_key, int p_track); - bool moving_selection = false; float moving_selection_offset = 0.0f; void _move_selection_begin(); @@ -531,6 +526,11 @@ protected: void _notification(int p_what); public: + // Public for use with callable_mp. + void _clear_selection(bool p_update = false); + void _key_selected(int p_key, bool p_single, int p_track); + void _key_deselected(int p_key, int p_track); + enum { EDIT_COPY_TRACKS, EDIT_COPY_TRACKS_CONFIRM, diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 861d05f17a..c83011845b 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -810,7 +810,7 @@ void ConnectionsDock::_go_to_script(TreeItem &p_item) { } if (script.is_valid() && ScriptEditor::get_singleton()->script_goto_method(script, cd.method)) { - EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->editor_select(EditorNode::EDITOR_SCRIPT); } } diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 4ac32d7317..3e72c6211d 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -296,6 +296,15 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback)); } + bool is_deprecated = EditorHelp::get_doc_data()->class_list[p_type].is_deprecated; + bool is_experimental = EditorHelp::get_doc_data()->class_list[p_type].is_experimental; + + if (is_deprecated) { + r_item->add_button(0, get_theme_icon("StatusError", SNAME("EditorIcons")), 0, false, TTR("This class is marked as deprecated.")); + } else if (is_experimental) { + r_item->add_button(0, get_theme_icon("NodeWarning", SNAME("EditorIcons")), 0, false, TTR("This class is marked as experimental.")); + } + if (!search_box->get_text().is_empty()) { r_item->set_collapsed(false); } else { diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index cf48366bd3..a882275375 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -104,6 +104,10 @@ void EditorProfiler::clear() { updating_frame = false; hover_metric = -1; seeking = false; + + // Ensure button text (start, stop) is correct + _set_button_text(); + emit_signal(SNAME("enable_profiling"), activate->is_pressed()); } static String _get_percent_txt(float p_value, float p_total) { @@ -374,15 +378,23 @@ void EditorProfiler::_update_frame() { updating_frame = false; } -void EditorProfiler::_activate_pressed() { +void EditorProfiler::_set_button_text() { if (activate->is_pressed()) { activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); activate->set_text(TTR("Stop")); - _clear_pressed(); } else { activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); activate->set_text(TTR("Start")); } +} + +void EditorProfiler::_activate_pressed() { + _set_button_text(); + + if (activate->is_pressed()) { + _clear_pressed(); + } + emit_signal(SNAME("enable_profiling"), activate->is_pressed()); } @@ -499,8 +511,12 @@ void EditorProfiler::_bind_methods() { ADD_SIGNAL(MethodInfo("break_request")); } -void EditorProfiler::set_enabled(bool p_enable) { +void EditorProfiler::set_enabled(bool p_enable, bool p_clear) { + activate->set_pressed(false); activate->set_disabled(!p_enable); + if (p_clear) { + clear(); + } } bool EditorProfiler::is_profiling() { diff --git a/editor/debugger/editor_profiler.h b/editor/debugger/editor_profiler.h index df92125258..e9ecc285ed 100644 --- a/editor/debugger/editor_profiler.h +++ b/editor/debugger/editor_profiler.h @@ -122,6 +122,7 @@ private: Timer *frame_delay = nullptr; Timer *plot_delay = nullptr; + void _set_button_text(); void _update_frame(); void _activate_pressed(); @@ -153,7 +154,7 @@ protected: public: void add_frame_metric(const Metric &p_metric, bool p_final = false); - void set_enabled(bool p_enable); + void set_enabled(bool p_enable, bool p_clear = true); bool is_profiling(); bool is_seeking() { return seeking; } void disable_seeking(); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 5baa9970af..6bc1536cb9 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -52,7 +52,6 @@ #include "editor/plugins/node_3d_editor_plugin.h" #include "main/performance.h" #include "scene/3d/camera_3d.h" -#include "scene/debugger/scene_debugger.h" #include "scene/gui/dialogs.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" @@ -317,7 +316,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da if (!error.is_empty()) { tabs->set_current_tab(0); } - profiler->set_enabled(false); + profiler->set_enabled(false, false); inspector->clear_cache(); // Take a chance to force remote objects update. } else if (p_msg == "debug_exit") { @@ -327,7 +326,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da _update_buttons_state(); _set_reason_text(TTR("Execution resumed."), MESSAGE_SUCCESS); emit_signal(SNAME("breaked"), false, false, "", false); - profiler->set_enabled(true); + profiler->set_enabled(true, false); profiler->disable_seeking(); } else if (p_msg == "set_pid") { ERR_FAIL_COND(p_data.size() < 1); @@ -916,6 +915,8 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { _clear_errors_list(); stop(); + profiler->set_enabled(true, true); + peer = p_peer; ERR_FAIL_COND(p_peer.is_null()); @@ -971,6 +972,8 @@ void ScriptEditorDebugger::stop() { res_path_cache.clear(); profiler_signature.clear(); + profiler->set_enabled(true, false); + inspector->edit(nullptr); _update_buttons_state(); } diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index ec9a744e57..7d6eb186dc 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -85,6 +85,9 @@ void DocTools::merge_from(const DocTools &p_data) { const DocData::ClassDoc &cf = p_data.class_list[c.name]; + c.is_deprecated = cf.is_deprecated; + c.is_experimental = cf.is_experimental; + c.description = cf.description; c.brief_description = cf.brief_description; c.tutorials = cf.tutorials; @@ -133,6 +136,8 @@ void DocTools::merge_from(const DocTools &p_data) { const DocData::MethodDoc &mf = cf.constructors[j]; m.description = mf.description; + m.is_deprecated = mf.is_deprecated; + m.is_experimental = mf.is_experimental; break; } } @@ -148,6 +153,8 @@ void DocTools::merge_from(const DocTools &p_data) { const DocData::MethodDoc &mf = cf.methods[j]; m.description = mf.description; + m.is_deprecated = mf.is_deprecated; + m.is_experimental = mf.is_experimental; break; } } @@ -162,6 +169,8 @@ void DocTools::merge_from(const DocTools &p_data) { const DocData::MethodDoc &mf = cf.signals[j]; m.description = mf.description; + m.is_deprecated = mf.is_deprecated; + m.is_experimental = mf.is_experimental; break; } } @@ -176,6 +185,8 @@ void DocTools::merge_from(const DocTools &p_data) { const DocData::ConstantDoc &mf = cf.constants[j]; m.description = mf.description; + m.is_deprecated = mf.is_deprecated; + m.is_experimental = mf.is_experimental; break; } } @@ -190,6 +201,8 @@ void DocTools::merge_from(const DocTools &p_data) { const DocData::MethodDoc &mf = cf.annotations[j]; m.description = mf.description; + m.is_deprecated = mf.is_deprecated; + m.is_experimental = mf.is_experimental; break; } } @@ -204,6 +217,8 @@ void DocTools::merge_from(const DocTools &p_data) { const DocData::PropertyDoc &pf = cf.properties[j]; p.description = pf.description; + p.is_deprecated = pf.is_deprecated; + p.is_experimental = pf.is_experimental; break; } } @@ -266,6 +281,8 @@ void DocTools::merge_from(const DocTools &p_data) { const DocData::MethodDoc &mf = cf.operators[j]; m.description = mf.description; + m.is_deprecated = mf.is_deprecated; + m.is_experimental = mf.is_experimental; break; } } @@ -1007,6 +1024,12 @@ static Error _parse_methods(Ref<XMLParser> &parser, Vector<DocData::MethodDoc> & if (parser->has_attribute("qualifiers")) { method.qualifiers = parser->get_attribute_value("qualifiers"); } + if (parser->has_attribute("is_deprecated")) { + method.is_deprecated = parser->get_attribute_value("is_deprecated").to_lower() == "true"; + } + if (parser->has_attribute("is_experimental")) { + method.is_experimental = parser->get_attribute_value("is_experimental").to_lower() == "true"; + } while (parser->read() == OK) { if (parser->get_node_type() == XMLParser::NODE_ELEMENT) { @@ -1138,6 +1161,14 @@ Error DocTools::_load(Ref<XMLParser> parser) { c.inherits = parser->get_attribute_value("inherits"); } + if (parser->has_attribute("is_deprecated")) { + c.is_deprecated = parser->get_attribute_value("is_deprecated").to_lower() == "true"; + } + + if (parser->has_attribute("is_experimental")) { + c.is_experimental = parser->get_attribute_value("is_experimental").to_lower() == "true"; + } + while (parser->read() == OK) { if (parser->get_node_type() == XMLParser::NODE_ELEMENT) { String name2 = parser->get_node_name(); @@ -1211,6 +1242,12 @@ Error DocTools::_load(Ref<XMLParser> parser) { if (parser->has_attribute("enum")) { prop2.enumeration = parser->get_attribute_value("enum"); } + if (parser->has_attribute("is_deprecated")) { + prop2.is_deprecated = parser->get_attribute_value("is_deprecated").to_lower() == "true"; + } + if (parser->has_attribute("is_experimental")) { + prop2.is_experimental = parser->get_attribute_value("is_experimental").to_lower() == "true"; + } if (!parser->is_empty()) { parser->read(); if (parser->get_node_type() == XMLParser::NODE_TEXT) { @@ -1275,6 +1312,12 @@ Error DocTools::_load(Ref<XMLParser> parser) { if (parser->has_attribute("is_bitfield")) { constant2.is_bitfield = parser->get_attribute_value("is_bitfield").to_lower() == "true"; } + if (parser->has_attribute("is_deprecated")) { + constant2.is_deprecated = parser->get_attribute_value("is_deprecated").to_lower() == "true"; + } + if (parser->has_attribute("is_experimental")) { + constant2.is_experimental = parser->get_attribute_value("is_experimental").to_lower() == "true"; + } if (!parser->is_empty()) { parser->read(); if (parser->get_node_type() == XMLParser::NODE_TEXT) { @@ -1327,7 +1370,15 @@ static void _write_method_doc(Ref<FileAccess> f, const String &p_name, Vector<Do qualifiers += " qualifiers=\"" + m.qualifiers.xml_escape() + "\""; } - _write_string(f, 2, "<" + p_name + " name=\"" + m.name.xml_escape() + "\"" + qualifiers + ">"); + String additional_attributes; + if (m.is_deprecated) { + additional_attributes += " is_deprecated=\"true\""; + } + if (m.is_experimental) { + additional_attributes += " is_experimental=\"true\""; + } + + _write_string(f, 2, "<" + p_name + " name=\"" + m.name.xml_escape() + "\"" + qualifiers + additional_attributes + ">"); if (!m.return_type.is_empty()) { String enum_text; @@ -1390,6 +1441,12 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String, String header = "<class name=\"" + c.name + "\""; if (!c.inherits.is_empty()) { header += " inherits=\"" + c.inherits + "\""; + if (c.is_deprecated) { + header += " is_deprecated=\"true\""; + } + if (c.is_experimental) { + header += " is_experimental=\"true\""; + } } header += String(" version=\"") + VERSION_BRANCH + "\""; // Reference the XML schema so editors can provide error checking. @@ -1433,6 +1490,12 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String, if (!c.properties[i].default_value.is_empty()) { additional_attributes += " default=\"" + c.properties[i].default_value.xml_escape(true) + "\""; } + if (c.properties[i].is_deprecated) { + additional_attributes += " is_deprecated=\"true\""; + } + if (c.properties[i].is_experimental) { + additional_attributes += " is_experimental=\"true\""; + } const DocData::PropertyDoc &p = c.properties[i]; @@ -1453,21 +1516,30 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String, _write_string(f, 1, "<constants>"); for (int i = 0; i < c.constants.size(); i++) { const DocData::ConstantDoc &k = c.constants[i]; + + String additional_attributes; + if (c.constants[i].is_deprecated) { + additional_attributes += " is_deprecated=\"true\""; + } + if (c.constants[i].is_experimental) { + additional_attributes += " is_experimental=\"true\""; + } + if (k.is_value_valid) { if (!k.enumeration.is_empty()) { if (k.is_bitfield) { - _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\" is_bitfield=\"true\">"); + _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\" is_bitfield=\"true\"" + additional_attributes + ">"); } else { - _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\">"); + _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\"" + additional_attributes + ">"); } } else { - _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\">"); + _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\"" + additional_attributes + ">"); } } else { if (!k.enumeration.is_empty()) { - _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"platform-dependent\" enum=\"" + k.enumeration + "\">"); + _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"platform-dependent\" enum=\"" + k.enumeration + "\"" + additional_attributes + ">"); } else { - _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"platform-dependent\">"); + _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"platform-dependent\"" + additional_attributes + ">"); } } _write_string(f, 3, _translate_doc_string(k.description).strip_edges().xml_escape()); diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp index ba1f2fd6af..a0913265eb 100644 --- a/editor/editor_command_palette.cpp +++ b/editor/editor_command_palette.cpp @@ -130,7 +130,7 @@ void EditorCommandPalette::_update_command_search(const String &search_text) { ti->set_metadata(0, entries[i].key_name); ti->set_text_alignment(1, HORIZONTAL_ALIGNMENT_RIGHT); ti->set_text(1, shortcut_text); - Color c = Color(1, 1, 1, 0.5); + Color c = get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, 0.5); ti->set_custom_color(1, c); } diff --git a/editor/editor_file_dialog.h b/editor/editor_file_dialog.h index 6d11cb10ed..2e7302aaf9 100644 --- a/editor/editor_file_dialog.h +++ b/editor/editor_file_dialog.h @@ -202,7 +202,6 @@ private: void _select_drive(int p_idx); void _dir_submitted(String p_dir); - void _file_submitted(const String &p_file); void _action_pressed(); void _save_confirm_pressed(); void _cancel_pressed(); @@ -240,6 +239,9 @@ protected: static void _bind_methods(); public: + // Public for use with callable_mp. + void _file_submitted(const String &p_file); + void popup_file_dialog(); void clear_filters(); void add_filter(const String &p_filter, const String &p_description = ""); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 177bc6d2b2..b89bd23859 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -690,7 +690,6 @@ void EditorFileSystem::scan() { _update_extensions(); - abort_scan = false; if (!use_threads) { scanning = true; scan_total = 0; @@ -1162,8 +1161,6 @@ void EditorFileSystem::scan_changes() { scanning_changes = true; scanning_changes_done = false; - abort_scan = false; - if (!use_threads) { if (filesystem) { EditorProgressBG pr("sources", TTR("ScanSources"), 1000); @@ -1195,8 +1192,6 @@ void EditorFileSystem::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { Thread &active_thread = thread.is_started() ? thread : thread_sources; if (use_threads && active_thread.is_started()) { - //abort thread if in progress - abort_scan = true; while (scanning) { OS::get_singleton()->delay_usec(1000); } diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index f4e69b95e7..e06c6e4593 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -168,7 +168,6 @@ class EditorFileSystem : public Node { EditorFileSystemDirectory *new_filesystem = nullptr; - bool abort_scan = false; bool scanning = false; bool importing = false; bool first_scan = true; diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index fffe77f1c4..c31d13d122 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -96,6 +96,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)EditorSettings::get_singleton()->get("interface/editor/font_subpixel_positioning"); TextServer::Hinting font_hinting; + TextServer::Hinting font_mono_hinting; switch (font_hinting_setting) { case 0: // The "Auto" setting uses the setting that best matches the OS' font rendering: @@ -104,18 +105,23 @@ void editor_register_fonts(Ref<Theme> p_theme) { // - Linux has configurable font hinting, but most distributions including Ubuntu default to "Light". #ifdef MACOS_ENABLED font_hinting = TextServer::HINTING_NONE; + font_mono_hinting = TextServer::HINTING_NONE; #else font_hinting = TextServer::HINTING_LIGHT; + font_mono_hinting = TextServer::HINTING_LIGHT; #endif break; case 1: font_hinting = TextServer::HINTING_NONE; + font_mono_hinting = TextServer::HINTING_NONE; break; case 2: font_hinting = TextServer::HINTING_LIGHT; + font_mono_hinting = TextServer::HINTING_LIGHT; break; default: font_hinting = TextServer::HINTING_NORMAL; + font_mono_hinting = TextServer::HINTING_LIGHT; break; } @@ -163,7 +169,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { default_font_bold->set_fallbacks(fallbacks_bold); default_font_bold_msdf->set_fallbacks(fallbacks_bold); - Ref<FontFile> default_font_mono = load_internal_font(_font_JetBrainsMono_Regular, _font_JetBrainsMono_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning); + Ref<FontFile> default_font_mono = load_internal_font(_font_JetBrainsMono_Regular, _font_JetBrainsMono_Regular_size, font_mono_hinting, font_antialiasing, true, font_subpixel_positioning); default_font_mono->set_fallbacks(fallbacks); // Init base font configs and load custom fonts. @@ -260,7 +266,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { Ref<FontVariation> mono_fc; mono_fc.instantiate(); if (custom_font_path_source.length() > 0 && dir->file_exists(custom_font_path_source)) { - Ref<FontFile> custom_font = load_external_font(custom_font_path_source, font_hinting, font_antialiasing, true, font_subpixel_positioning); + Ref<FontFile> custom_font = load_external_font(custom_font_path_source, font_mono_hinting, font_antialiasing, true, font_subpixel_positioning); { TypedArray<Font> fallback_custom; fallback_custom.push_back(default_font_mono); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index b8f115e82e..745dcdd04c 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -276,6 +276,23 @@ String EditorHelp::_fix_constant(const String &p_constant) const { return p_constant; } +// Macros for assigning the deprecation/experimental information to class members +#define DEPRECATED_DOC_TAG \ + class_desc->push_color(get_theme_color(SNAME("error_color"), SNAME("Editor"))); \ + Ref<Texture2D> error_icon = get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons")); \ + class_desc->add_text(" "); \ + class_desc->add_image(error_icon, error_icon->get_width(), error_icon->get_height()); \ + class_desc->add_text(" (" + TTR("Deprecated") + ")"); \ + class_desc->pop(); + +#define EXPERIMENTAL_DOC_TAG \ + class_desc->push_color(get_theme_color(SNAME("warning_color"), SNAME("Editor"))); \ + Ref<Texture2D> warning_icon = get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons")); \ + class_desc->add_text(" "); \ + class_desc->add_image(warning_icon, warning_icon->get_width(), warning_icon->get_height()); \ + class_desc->add_text(" (" + TTR("Experimental") + ")"); \ + class_desc->pop(); + void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview) { method_line[p_method.name] = class_desc->get_paragraph_count() - 2; //gets overridden if description @@ -356,6 +373,14 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview class_desc->pop(); } + if (p_method.is_deprecated) { + DEPRECATED_DOC_TAG; + } + + if (p_method.is_experimental) { + EXPERIMENTAL_DOC_TAG; + } + if (p_overview) { class_desc->pop(); //cell } @@ -565,6 +590,17 @@ void EditorHelp::_update_doc() { class_desc->pop(); // color class_desc->pop(); // font size class_desc->pop(); // font + + if (cd.is_deprecated) { + class_desc->add_text(" "); + Ref<Texture2D> error_icon = get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons")); + class_desc->add_image(error_icon, error_icon->get_width(), error_icon->get_height()); + } + if (cd.is_experimental) { + class_desc->add_text(" "); + Ref<Texture2D> warning_icon = get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons")); + class_desc->add_image(warning_icon, warning_icon->get_width(), warning_icon->get_height()); + } class_desc->add_newline(); const String non_breaking_space = String::chr(160); @@ -627,6 +663,26 @@ void EditorHelp::_update_doc() { } } + // Note if deprecated. + if (cd.is_deprecated) { + Ref<Texture2D> error_icon = get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons")); + class_desc->push_color(get_theme_color(SNAME("error_color"), SNAME("Editor"))); + class_desc->add_image(error_icon, error_icon->get_width(), error_icon->get_height()); + class_desc->add_text(String(" ") + TTR("This class is marked as deprecated. It will be removed in future versions.")); + class_desc->pop(); + class_desc->add_newline(); + } + + // Note if experimental. + if (cd.is_experimental) { + Ref<Texture2D> warning_icon = get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons")); + class_desc->push_color(get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + class_desc->add_image(warning_icon, warning_icon->get_width(), warning_icon->get_height()); + class_desc->add_text(String(" ") + TTR("This class is marked as experimental. It is subject to likely change or possible removal in future versions. Use at your own discretion.")); + class_desc->pop(); + class_desc->add_newline(); + } + class_desc->add_newline(); class_desc->add_newline(); @@ -817,6 +873,13 @@ void EditorHelp::_update_doc() { class_desc->pop(); } + if (cd.properties[i].is_deprecated) { + DEPRECATED_DOC_TAG; + } + if (cd.properties[i].is_experimental) { + EXPERIMENTAL_DOC_TAG; + } + class_desc->pop(); class_desc->pop(); // cell @@ -1066,6 +1129,14 @@ void EditorHelp::_update_doc() { class_desc->push_color(symbol_color); class_desc->add_text(")"); + + if (cd.signals[i].is_deprecated) { + DEPRECATED_DOC_TAG; + } + if (cd.signals[i].is_experimental) { + EXPERIMENTAL_DOC_TAG; + } + class_desc->pop(); class_desc->pop(); // end monofont if (!cd.signals[i].description.strip_edges().is_empty()) { @@ -1187,6 +1258,14 @@ void EditorHelp::_update_doc() { class_desc->pop(); class_desc->pop(); + if (enum_list[i].is_deprecated) { + DEPRECATED_DOC_TAG; + } + + if (enum_list[i].is_experimental) { + EXPERIMENTAL_DOC_TAG; + } + class_desc->add_newline(); if (!enum_list[i].description.strip_edges().is_empty()) { @@ -1258,6 +1337,14 @@ void EditorHelp::_update_doc() { class_desc->pop(); + if (constants[i].is_deprecated) { + DEPRECATED_DOC_TAG; + } + + if (constants[i].is_experimental) { + EXPERIMENTAL_DOC_TAG; + } + class_desc->add_newline(); if (!constants[i].description.strip_edges().is_empty()) { @@ -1438,6 +1525,13 @@ void EditorHelp::_update_doc() { class_desc->pop(); // color } + if (cd.properties[i].is_deprecated) { + DEPRECATED_DOC_TAG; + } + if (cd.properties[i].is_experimental) { + EXPERIMENTAL_DOC_TAG; + } + if (cd.is_script_doc && (!cd.properties[i].setter.is_empty() || !cd.properties[i].getter.is_empty())) { class_desc->push_color(symbol_color); class_desc->add_text(" [" + TTR("property:") + " "); diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp index af0cff9ad6..1b8146a0f0 100644 --- a/editor/editor_help_search.cpp +++ b/editor/editor_help_search.cpp @@ -324,10 +324,16 @@ bool EditorHelpSearch::Runner::_phase_match_classes_init() { } bool EditorHelpSearch::Runner::_phase_match_classes() { + if (!iterator_doc) { + return true; + } + DocData::ClassDoc &class_doc = iterator_doc->value; if (class_doc.name.is_empty()) { + ++iterator_doc; return false; } + if (!_is_class_disabled_by_feature_profile(class_doc.name)) { ClassMatch match; match.doc = &class_doc; @@ -432,7 +438,7 @@ bool EditorHelpSearch::Runner::_phase_class_items_init() { bool EditorHelpSearch::Runner::_phase_class_items() { if (!iterator_match) { - return false; + return true; } ClassMatch &match = iterator_match->value; @@ -459,10 +465,8 @@ bool EditorHelpSearch::Runner::_phase_member_items_init() { bool EditorHelpSearch::Runner::_phase_member_items() { ClassMatch &match = iterator_match->value; - if (!match.doc) { - return false; - } - if (match.doc->name.is_empty()) { + if (!match.doc || match.doc->name.is_empty()) { + ++iterator_match; return false; } @@ -599,6 +603,14 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const item->set_custom_color(1, disabled_color); } + if (p_doc->is_deprecated) { + Ref<Texture2D> error_icon = ui_service->get_theme_icon("StatusError", SNAME("EditorIcons")); + item->add_button(0, error_icon, 0, false, TTR("This class is marked as deprecated.")); + } else if (p_doc->is_experimental) { + Ref<Texture2D> warning_icon = ui_service->get_theme_icon("NodeWarning", SNAME("EditorIcons")); + item->add_button(0, warning_icon, 0, false, TTR("This class is marked as experimental.")); + } + _match_item(item, p_doc->name); return item; @@ -606,37 +618,37 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const TreeItem *EditorHelpSearch::Runner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc) { String tooltip = _build_method_tooltip(p_class_doc, p_doc); - return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_doc->name, p_text, TTRC("Method"), "method", tooltip); + return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_doc->name, p_text, TTRC("Method"), "method", tooltip, p_doc->is_deprecated, p_doc->is_experimental); } TreeItem *EditorHelpSearch::Runner::_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) { String tooltip = _build_method_tooltip(p_class_doc, p_doc); - return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_doc->name, p_doc->name, TTRC("Signal"), "signal", tooltip); + return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_doc->name, p_doc->name, TTRC("Signal"), "signal", tooltip, p_doc->is_deprecated, p_doc->is_experimental); } TreeItem *EditorHelpSearch::Runner::_create_annotation_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc) { String tooltip = _build_method_tooltip(p_class_doc, p_doc); - return _create_member_item(p_parent, p_class_doc->name, "MemberAnnotation", p_doc->name, p_text, TTRC("Annotation"), "annotation", tooltip); + return _create_member_item(p_parent, p_class_doc->name, "MemberAnnotation", p_doc->name, p_text, TTRC("Annotation"), "annotation", tooltip, p_doc->is_deprecated, p_doc->is_experimental); } TreeItem *EditorHelpSearch::Runner::_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc) { String tooltip = p_class_doc->name + "." + p_doc->name; - return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_doc->name, p_doc->name, TTRC("Constant"), "constant", tooltip); + return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_doc->name, p_doc->name, TTRC("Constant"), "constant", tooltip, p_doc->is_deprecated, p_doc->is_experimental); } TreeItem *EditorHelpSearch::Runner::_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc) { String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name; tooltip += "\n " + p_class_doc->name + "." + p_doc->setter + "(value) setter"; tooltip += "\n " + p_class_doc->name + "." + p_doc->getter + "() getter"; - return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_doc->name, p_doc->name, TTRC("Property"), "property", tooltip); + return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_doc->name, p_doc->name, TTRC("Property"), "property", tooltip, p_doc->is_deprecated, p_doc->is_experimental); } TreeItem *EditorHelpSearch::Runner::_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc) { String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name; - return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_doc->name, p_doc->name, TTRC("Theme Property"), "theme_item", tooltip); + return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_doc->name, p_doc->name, TTRC("Theme Property"), "theme_item", tooltip, false, false); } -TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip) { +TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, bool is_deprecated, bool is_experimental) { Ref<Texture2D> icon; String text; if (search_flags & SEARCH_SHOW_HIERARCHY) { @@ -655,6 +667,14 @@ TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, cons item->set_tooltip_text(1, p_tooltip); item->set_metadata(0, "class_" + p_metatype + ":" + p_class_name + ":" + p_name); + if (is_deprecated) { + Ref<Texture2D> error_icon = ui_service->get_theme_icon("StatusError", SNAME("EditorIcons")); + item->add_button(0, error_icon, 0, false, TTR("This member is marked as deprecated.")); + } else if (is_experimental) { + Ref<Texture2D> warning_icon = ui_service->get_theme_icon("NodeWarning", SNAME("EditorIcons")); + item->add_button(0, warning_icon, 0, false, TTR("This member is marked as experimental.")); + } + _match_item(item, p_name); return item; diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h index 26abaec6e6..efd8645cd7 100644 --- a/editor/editor_help_search.h +++ b/editor/editor_help_search.h @@ -155,7 +155,7 @@ class EditorHelpSearch::Runner : public RefCounted { TreeItem *_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc); TreeItem *_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc); TreeItem *_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc); - TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip); + TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, bool is_deprecated, bool is_experimental); public: bool work(uint64_t slot = 100000); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index f001887ef9..fb819f418b 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -426,6 +426,9 @@ void EditorProperty::_set_read_only(bool p_read_only) { void EditorProperty::set_read_only(bool p_read_only) { read_only = p_read_only; + if (GDVIRTUAL_CALL(_set_read_only, p_read_only)) { + return; + } _set_read_only(p_read_only); } @@ -458,7 +461,7 @@ StringName EditorProperty::_get_revert_property() const { return property; } -void EditorProperty::update_revert_and_pin_status() { +void EditorProperty::update_editor_property_status() { if (property == StringName()) { return; //no property, so nothing to do } @@ -469,15 +472,26 @@ void EditorProperty::update_revert_and_pin_status() { CRASH_COND(!node); new_pinned = node->is_property_pinned(property); } + Variant current = object->get(_get_revert_property()); bool new_can_revert = EditorPropertyRevert::can_property_revert(object, property, ¤t) && !is_read_only(); - if (new_can_revert != can_revert || new_pinned != pinned) { + bool new_checked = checked; + if (checkable) { // for properties like theme overrides. + bool valid = false; + Variant value = object->get(property, &valid); + if (valid) { + new_checked = value.get_type() != Variant::NIL; + } + } + + if (new_can_revert != can_revert || new_pinned != pinned || new_checked != checked) { if (new_can_revert != can_revert) { emit_signal(SNAME("property_can_revert_changed"), property, new_can_revert); } can_revert = new_can_revert; pinned = new_pinned; + checked = new_checked; queue_redraw(); } } @@ -974,7 +988,9 @@ void EditorProperty::_bind_methods() { ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "focusable_idx"))); GDVIRTUAL_BIND(_update_property) - ClassDB::bind_method(D_METHOD("_update_revert_and_pin_status"), &EditorProperty::update_revert_and_pin_status); + GDVIRTUAL_BIND(_set_read_only, "read_only") + + ClassDB::bind_method(D_METHOD("_update_editor_property_status"), &EditorProperty::update_editor_property_status); } EditorProperty::EditorProperty() { @@ -1651,6 +1667,10 @@ void EditorInspectorArray::_panel_draw(int p_index) { void EditorInspectorArray::_panel_gui_input(Ref<InputEvent> p_event, int p_index) { ERR_FAIL_INDEX(p_index, (int)array_elements.size()); + if (read_only) { + return; + } + Ref<InputEventKey> key_ref = p_event; if (key_ref.is_valid()) { const InputEventKey &key = **key_ref; @@ -2151,7 +2171,7 @@ void EditorInspectorArray::drop_data_fw(const Point2 &p_point, const Variant &p_ } bool EditorInspectorArray::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { - if (!movable) { + if (!movable || read_only) { return false; } // First, update drawing. @@ -2271,7 +2291,9 @@ VBoxContainer *EditorInspectorArray::get_vbox(int p_index) { } } -EditorInspectorArray::EditorInspectorArray() { +EditorInspectorArray::EditorInspectorArray(bool p_read_only) { + read_only = p_read_only; + set_mouse_filter(Control::MOUSE_FILTER_STOP); odd_style.instantiate(); @@ -2297,6 +2319,7 @@ EditorInspectorArray::EditorInspectorArray() { add_button = EditorInspector::create_inspector_action_button(TTR("Add Element")); add_button->connect("pressed", callable_mp(this, &EditorInspectorArray::_add_button_pressed)); + add_button->set_disabled(read_only); vbox->add_child(add_button); control_dropping = memnew(Control); @@ -2317,6 +2340,7 @@ EditorInspectorArray::EditorInspectorArray() { new_size_spin_box->set_max(16384); new_size_spin_box->connect("value_changed", callable_mp(this, &EditorInspectorArray::_new_size_spin_box_value_changed)); new_size_spin_box->get_line_edit()->connect("text_submitted", callable_mp(this, &EditorInspectorArray::_new_size_spin_box_text_submitted)); + new_size_spin_box->set_editable(!read_only); resize_dialog_vbox->add_margin_child(TTRC("New Size:"), new_size_spin_box); vbox->connect("visibility_changed", callable_mp(this, &EditorInspectorArray::_vbox_visibility_changed)); @@ -2544,7 +2568,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorIn ep->set_read_only(read_only); ep->update_property(); ep->_update_pin_flags(); - ep->update_revert_and_pin_status(); + ep->update_editor_property_status(); ep->set_deletable(deletable_properties); ep->update_cache(); } @@ -3030,7 +3054,7 @@ void EditorInspector::update_tree() { bool movable = true; bool numbered = false; bool foldable = use_folding; - String add_button_text; + String add_button_text = TTR("Add Element"); String swap_method; for (int i = (p.type == Variant::NIL ? 1 : 2); i < class_name_components.size(); i++) { if (class_name_components[i].begins_with("page_size") && class_name_components[i].get_slice_count("=") == 2) { @@ -3051,7 +3075,7 @@ void EditorInspector::update_tree() { if (p.type == Variant::NIL) { // Setup the array to use a method to create/move/delete elements. array_element_prefix = class_name_components[0]; - editor_inspector_array = memnew(EditorInspectorArray); + editor_inspector_array = memnew(EditorInspectorArray(all_read_only)); String array_label = path.contains("/") ? path.substr(path.rfind("/") + 1) : path; array_label = EditorPropertyNameProcessor::get_singleton()->process_name(property_label_string, property_name_style); @@ -3063,7 +3087,7 @@ void EditorInspector::update_tree() { // Setup the array to use the count property and built-in functions to create/move/delete elements. if (class_name_components.size() >= 2) { array_element_prefix = class_name_components[1]; - editor_inspector_array = memnew(EditorInspectorArray); + editor_inspector_array = memnew(EditorInspectorArray(all_read_only)); int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0; editor_inspector_array->setup_with_count_property(object, class_name_components[0], p.name, array_element_prefix, page, c, foldable, movable, numbered, page_size, add_button_text, swap_method); @@ -3203,6 +3227,7 @@ void EditorInspector::update_tree() { // Use the existing one. ep->set_label(property_label_string); } + for (int j = 0; j < properties.size(); j++) { String prop = properties[j]; @@ -3250,7 +3275,7 @@ void EditorInspector::update_tree() { ep->set_doc_path(doc_info.path); ep->update_property(); ep->_update_pin_flags(); - ep->update_revert_and_pin_status(); + ep->update_editor_property_status(); ep->update_cache(); if (current_selected && ep->property == current_selected) { @@ -3289,7 +3314,7 @@ void EditorInspector::update_property(const String &p_prop) { for (EditorProperty *E : editor_property_map[p_prop]) { E->update_property(); - E->update_revert_and_pin_status(); + E->update_editor_property_status(); E->update_cache(); } } @@ -3632,7 +3657,7 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo if (editor_property_map.has(p_name)) { for (EditorProperty *E : editor_property_map[p_name]) { - E->update_revert_and_pin_status(); + E->update_editor_property_status(); } } } @@ -3746,7 +3771,7 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) { for (EditorProperty *E : editor_property_map[p_path]) { E->set_checked(p_checked); E->update_property(); - E->update_revert_and_pin_status(); + E->update_editor_property_status(); E->update_cache(); } } @@ -3770,8 +3795,8 @@ void EditorInspector::_property_pinned(const String &p_path, bool p_pinned) { undo_redo->add_undo_method(node, "_set_property_pinned", p_path, !p_pinned); if (editor_property_map.has(p_path)) { for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) { - undo_redo->add_do_method(E->get(), "_update_revert_and_pin_status"); - undo_redo->add_undo_method(E->get(), "_update_revert_and_pin_status"); + undo_redo->add_do_method(E->get(), "_update_editor_property_status"); + undo_redo->add_undo_method(E->get(), "_update_editor_property_status"); } } undo_redo->commit_action(); @@ -3779,7 +3804,7 @@ void EditorInspector::_property_pinned(const String &p_path, bool p_pinned) { node->set_property_pinned(p_path, p_pinned); if (editor_property_map.has(p_path)) { for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) { - E->get()->update_revert_and_pin_status(); + E->get()->update_editor_property_status(); } } } @@ -3860,7 +3885,7 @@ void EditorInspector::_notification(int p_what) { for (EditorProperty *E : F.value) { if (E && !E->is_cache_valid()) { E->update_property(); - E->update_revert_and_pin_status(); + E->update_editor_property_status(); E->update_cache(); } } @@ -3882,7 +3907,7 @@ void EditorInspector::_notification(int p_what) { if (editor_property_map.has(prop)) { for (EditorProperty *E : editor_property_map[prop]) { E->update_property(); - E->update_revert_and_pin_status(); + E->update_editor_property_status(); E->update_cache(); } } @@ -3975,13 +4000,7 @@ void EditorInspector::_check_meta_name(const String &p_name) { } else if (!p_name.is_valid_identifier()) { error = TTR("Metadata name must be a valid identifier."); } else if (object->has_meta(p_name)) { - Node *node = Object::cast_to<Node>(object); - if (node) { - error = vformat(TTR("Metadata with name \"%s\" already exists on \"%s\"."), p_name, node->get_name()); - } else { - // This should normally never be reached, but the error is set just in case. - error = vformat(TTR("Metadata with name \"%s\" already exists."), p_name, node->get_name()); - } + error = vformat(TTR("Metadata with name \"%s\" already exists."), p_name); } else if (p_name[0] == '_') { error = TTR("Names starting with _ are reserved for editor-only metadata."); } @@ -4001,14 +4020,6 @@ void EditorInspector::_show_add_meta_dialog() { if (!add_meta_dialog) { add_meta_dialog = memnew(ConfirmationDialog); - Node *node = Object::cast_to<Node>(object); - if (node) { - add_meta_dialog->set_title(vformat(TTR("Add Metadata Property for \"%s\""), node->get_name())); - } else { - // This should normally never be reached, but the title is set just in case. - add_meta_dialog->set_title(vformat(TTR("Add Metadata Property"), node->get_name())); - } - VBoxContainer *vbc = memnew(VBoxContainer); add_meta_dialog->add_child(vbc); HBoxContainer *hbc = memnew(HBoxContainer); @@ -4038,6 +4049,14 @@ void EditorInspector::_show_add_meta_dialog() { add_meta_name->connect("text_changed", callable_mp(this, &EditorInspector::_check_meta_name)); } + Node *node = Object::cast_to<Node>(object); + if (node) { + add_meta_dialog->set_title(vformat(TTR("Add Metadata Property for \"%s\""), node->get_name())); + } else { + // This should normally be reached when the object is derived from Resource. + add_meta_dialog->set_title(vformat(TTR("Add Metadata Property for \"%s\""), object->get_class())); + } + add_meta_dialog->popup_centered(); add_meta_name->set_text(""); _check_meta_name(""); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index d634eae23f..872007e637 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -120,6 +120,8 @@ private: HashMap<StringName, Variant> cache; GDVIRTUAL0(_update_property) + GDVIRTUAL1(_set_read_only, bool) + void _update_pin_flags(); protected: @@ -151,7 +153,7 @@ public: void set_doc_path(const String &p_doc_path); virtual void update_property(); - void update_revert_and_pin_status(); + void update_editor_property_status(); virtual bool use_keying_next() const; @@ -333,6 +335,7 @@ class EditorInspectorArray : public EditorInspectorSection { int begin_array_index = 0; int end_array_index = 0; + bool read_only = false; bool movable = true; bool numbered = false; @@ -404,7 +407,7 @@ public: void setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "", const String &p_swap_method = ""); VBoxContainer *get_vbox(int p_index); - EditorInspectorArray(); + EditorInspectorArray(bool p_read_only); }; class EditorPaginator : public HBoxContainer { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index b00127f5b1..e79619e57b 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -465,15 +465,15 @@ void EditorNode::shortcut_input(const Ref<InputEvent> &p_event) { } if (ED_IS_SHORTCUT("editor/editor_2d", p_event)) { - _editor_select(EDITOR_2D); + editor_select(EDITOR_2D); } else if (ED_IS_SHORTCUT("editor/editor_3d", p_event)) { - _editor_select(EDITOR_3D); + editor_select(EDITOR_3D); } else if (ED_IS_SHORTCUT("editor/editor_script", p_event)) { - _editor_select(EDITOR_SCRIPT); + editor_select(EDITOR_SCRIPT); } else if (ED_IS_SHORTCUT("editor/editor_help", p_event)) { emit_signal(SNAME("request_help_search"), ""); } else if (ED_IS_SHORTCUT("editor/editor_assetlib", p_event) && AssetLibraryEditorPlugin::is_available()) { - _editor_select(EDITOR_ASSETLIB); + editor_select(EDITOR_ASSETLIB); } else if (ED_IS_SHORTCUT("editor/editor_next", p_event)) { _editor_select_next(); } else if (ED_IS_SHORTCUT("editor/editor_prev", p_event)) { @@ -584,7 +584,7 @@ void EditorNode::_update_from_settings() { void EditorNode::_select_default_main_screen_plugin() { if (EDITOR_3D < main_editor_buttons.size() && main_editor_buttons[EDITOR_3D]->is_visible()) { // If the 3D editor is enabled, use this as the default. - _editor_select(EDITOR_3D); + editor_select(EDITOR_3D); return; } @@ -593,12 +593,12 @@ void EditorNode::_select_default_main_screen_plugin() { for (int i = 0; i < main_editor_buttons.size(); i++) { Button *editor_button = main_editor_buttons[i]; if (editor_button->is_visible()) { - _editor_select(i); + editor_select(i); return; } } - _editor_select(-1); + editor_select(-1); } void EditorNode::_notification(int p_what) { @@ -1190,7 +1190,7 @@ void EditorNode::_editor_select_next() { } } while (!main_editor_buttons[editor]->is_visible()); - _editor_select(editor); + editor_select(editor); } void EditorNode::_open_command_palette() { @@ -1208,7 +1208,7 @@ void EditorNode::_editor_select_prev() { } } while (!main_editor_buttons[editor]->is_visible()); - _editor_select(editor); + editor_select(editor); } Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_deps) { @@ -2390,7 +2390,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) { else if (main_plugin != editor_plugin_screen && (!ScriptEditor::get_singleton() || !ScriptEditor::get_singleton()->is_visible_in_tree() || ScriptEditor::get_singleton()->can_take_away_focus())) { // Update screen main_plugin. - _editor_select(plugin_index); + editor_select(plugin_index); main_plugin->edit(current_obj); } else { editor_plugin_screen->edit(current_obj); @@ -2763,18 +2763,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } } else if (extensions.size()) { String root_name = scene->get_name(); - // Very similar to node naming logic. - switch (ProjectSettings::get_singleton()->get("editor/scene/scene_naming").operator int()) { - case SCENE_NAME_CASING_AUTO: - // Use casing of the root node. - break; - case SCENE_NAME_CASING_PASCAL_CASE: { - root_name = root_name.to_pascal_case(); - } break; - case SCENE_NAME_CASING_SNAKE_CASE: - root_name = root_name.replace("-", "_").to_snake_case(); - break; - } + root_name = EditorNode::adjust_scene_name_casing(root_name); file->set_current_path(root_name + "." + extensions.front()->get().to_lower()); } file->popup_file_dialog(); @@ -3090,6 +3079,19 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } } +String EditorNode::adjust_scene_name_casing(const String &root_name) { + switch (ProjectSettings::get_singleton()->get("editor/scene/scene_naming").operator int()) { + case SCENE_NAME_CASING_AUTO: + // Use casing of the root node. + break; + case SCENE_NAME_CASING_PASCAL_CASE: + return root_name.to_pascal_case(); + case SCENE_NAME_CASING_SNAKE_CASE: + return root_name.replace("-", "_").to_snake_case(); + } + return root_name; +} + void EditorNode::_request_screenshot() { _screenshot(); } @@ -3303,7 +3305,7 @@ VBoxContainer *EditorNode::get_main_screen_control() { return main_screen_vbox; } -void EditorNode::_editor_select(int p_which) { +void EditorNode::editor_select(int p_which) { static bool selecting = false; if (selecting || changing_scene) { return; @@ -3357,7 +3359,7 @@ void EditorNode::select_editor_by_name(const String &p_name) { for (int i = 0; i < main_editor_buttons.size(); i++) { if (main_editor_buttons[i]->get_text() == p_name) { - _editor_select(i); + editor_select(i); return; } } @@ -3370,7 +3372,7 @@ void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed Button *tb = memnew(Button); tb->set_flat(true); tb->set_toggle_mode(true); - tb->connect("pressed", callable_mp(singleton, &EditorNode::_editor_select).bind(singleton->main_editor_buttons.size())); + tb->connect("pressed", callable_mp(singleton, &EditorNode::editor_select).bind(singleton->main_editor_buttons.size())); tb->set_name(p_editor->get_name()); tb->set_text(p_editor->get_name()); @@ -3404,7 +3406,7 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor, bool p_config_chan for (int i = 0; i < singleton->main_editor_buttons.size(); i++) { if (p_editor->get_name() == singleton->main_editor_buttons[i]->get_text()) { if (singleton->main_editor_buttons[i]->is_pressed()) { - singleton->_editor_select(EDITOR_SCRIPT); + singleton->editor_select(EDITOR_SCRIPT); } memdelete(singleton->main_editor_buttons[i]); @@ -3628,7 +3630,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { int index = p_state["editor_index"]; if (current < 2) { // If currently in spatial/2d, only switch to spatial/2d. If currently in script, stay there. if (index < 2 || !get_edited_scene()) { - _editor_select(index); + editor_select(index); } } } @@ -3639,9 +3641,9 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { int n2d = 0, n3d = 0; _find_node_types(get_edited_scene(), n2d, n3d); if (n2d > n3d) { - _editor_select(EDITOR_2D); + editor_select(EDITOR_2D); } else if (n3d > n2d) { - _editor_select(EDITOR_3D); + editor_select(EDITOR_3D); } } } @@ -3668,10 +3670,6 @@ bool EditorNode::is_changing_scene() const { return changing_scene; } -void EditorNode::_clear_undo_history() { - get_undo_redo()->clear_history(false); -} - void EditorNode::set_current_scene(int p_idx) { // Save the folding in case the scene gets reloaded. if (editor_data.get_scene_path(p_idx) != "" && editor_data.get_edited_scene_root(p_idx)) { @@ -3932,7 +3930,7 @@ void EditorNode::edit_foreign_resource(Ref<Resource> p_resource) { InspectorDock::get_singleton()->call_deferred("edit_resource", p_resource); } -bool EditorNode::is_resource_read_only(Ref<Resource> p_resource) { +bool EditorNode::is_resource_read_only(Ref<Resource> p_resource, bool p_foreign_resources_are_writable) { ERR_FAIL_COND_V(p_resource.is_null(), false); String path = p_resource->get_path(); @@ -3944,7 +3942,11 @@ bool EditorNode::is_resource_read_only(Ref<Resource> p_resource) { // If the base resource is a packed scene, we treat it as read-only if it is not the currently edited scene. if (ResourceLoader::get_resource_type(base) == "PackedScene") { if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) { - return true; + // If we have not flagged foreign resources as writable or the base scene the resource is + // part was imported, it can be considered read-only. + if (!p_foreign_resources_are_writable || FileAccess::exists(base + ".import")) { + return true; + } } } else { // If a corresponding .import file exists for the base file, we assume it to be imported and should therefore treated as read-only. @@ -5933,7 +5935,7 @@ void EditorNode::_feature_profile_changed() { if ((profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D) && singleton->main_editor_buttons[EDITOR_3D]->is_pressed()) || (profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT) && singleton->main_editor_buttons[EDITOR_SCRIPT]->is_pressed()) || (AssetLibraryEditorPlugin::is_available() && profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB) && singleton->main_editor_buttons[EDITOR_ASSETLIB]->is_pressed())) { - _editor_select(EDITOR_2D); + editor_select(EDITOR_2D); } } else { import_tabs->set_tab_hidden(import_tabs->get_tab_idx_from_control(ImportDock::get_singleton()), false); @@ -5956,19 +5958,14 @@ void EditorNode::_bind_methods() { GLOBAL_DEF("editor/scene/scene_naming", SCENE_NAME_CASING_SNAKE_CASE); ProjectSettings::get_singleton()->set_custom_property_info("editor/scene/scene_naming", PropertyInfo(Variant::INT, "editor/scene/scene_naming", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case")); ClassDB::bind_method("edit_current", &EditorNode::edit_current); - ClassDB::bind_method("_editor_select", &EditorNode::_editor_select); - ClassDB::bind_method("_node_renamed", &EditorNode::_node_renamed); ClassDB::bind_method("edit_node", &EditorNode::edit_node); ClassDB::bind_method(D_METHOD("push_item", "object", "property", "inspector_only"), &EditorNode::push_item, DEFVAL(""), DEFVAL(false)); - ClassDB::bind_method("_get_scene_metadata", &EditorNode::_get_scene_metadata); ClassDB::bind_method("set_edited_scene", &EditorNode::set_edited_scene); ClassDB::bind_method("open_request", &EditorNode::open_request); ClassDB::bind_method("edit_foreign_resource", &EditorNode::edit_foreign_resource); ClassDB::bind_method("is_resource_read_only", &EditorNode::is_resource_read_only); - ClassDB::bind_method("_close_messages", &EditorNode::_close_messages); - ClassDB::bind_method("_show_messages", &EditorNode::_show_messages); ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process); @@ -5977,19 +5974,10 @@ void EditorNode::_bind_methods() { ClassDB::bind_method("_set_main_scene_state", &EditorNode::_set_main_scene_state); ClassDB::bind_method("_update_recent_scenes", &EditorNode::_update_recent_scenes); - ClassDB::bind_method("_clear_undo_history", &EditorNode::_clear_undo_history); - ClassDB::bind_method("edit_item_resource", &EditorNode::edit_item_resource); ClassDB::bind_method(D_METHOD("get_gui_base"), &EditorNode::get_gui_base); - ClassDB::bind_method(D_METHOD("_on_plugin_ready"), &EditorNode::_on_plugin_ready); // Still used by some connect_compat. - - 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")); @@ -6750,8 +6738,10 @@ EditorNode::EditorNode() { project_menu->add_separator(); project_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/export", TTR("Export..."), Key::NONE, TTR("Export")), FILE_EXPORT_PROJECT); +#ifndef ANDROID_ENABLED project_menu->add_item(TTR("Install Android Build Template..."), FILE_INSTALL_ANDROID_SOURCE); project_menu->add_item(TTR("Open User Data Folder"), RUN_USER_DATA_FOLDER); +#endif project_menu->add_separator(); project_menu->add_item(TTR("Customize Engine Build Configuration..."), TOOLS_BUILD_PROFILE_MANAGER); @@ -6814,12 +6804,14 @@ EditorNode::EditorNode() { settings_menu->set_item_tooltip(-1, TTR("Screenshots are stored in the Editor Data/Settings Folder.")); +#ifndef ANDROID_ENABLED ED_SHORTCUT_AND_COMMAND("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KeyModifierMask::SHIFT | Key::F11); ED_SHORTCUT_OVERRIDE("editor/fullscreen_mode", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::F); settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/fullscreen_mode"), SETTINGS_TOGGLE_FULLSCREEN); - +#endif settings_menu->add_separator(); +#ifndef ANDROID_ENABLED if (OS::get_singleton()->get_data_path() == OS::get_singleton()->get_config_path()) { // Configuration and data folders are located in the same place (Windows/MacOS). settings_menu->add_item(TTR("Open Editor Data/Settings Folder"), SETTINGS_EDITOR_DATA_FOLDER); @@ -6829,9 +6821,12 @@ EditorNode::EditorNode() { settings_menu->add_item(TTR("Open Editor Settings Folder"), SETTINGS_EDITOR_CONFIG_FOLDER); } settings_menu->add_separator(); +#endif settings_menu->add_item(TTR("Manage Editor Features..."), SETTINGS_MANAGE_FEATURE_PROFILES); +#ifndef ANDROID_ENABLED settings_menu->add_item(TTR("Manage Export Templates..."), SETTINGS_MANAGE_EXPORT_TEMPLATES); +#endif help_menu = memnew(PopupMenu); help_menu->set_name(TTR("Help")); @@ -7601,8 +7596,8 @@ EditorPlugin::AfterGUIInput EditorPluginList::forward_spatial_gui_input(Camera3D if (current_after == EditorPlugin::AFTER_GUI_INPUT_STOP) { after = EditorPlugin::AFTER_GUI_INPUT_STOP; } - if (after != EditorPlugin::AFTER_GUI_INPUT_STOP && current_after == EditorPlugin::AFTER_GUI_INPUT_DESELECT) { - after = EditorPlugin::AFTER_GUI_INPUT_DESELECT; + if (after != EditorPlugin::AFTER_GUI_INPUT_STOP && current_after == EditorPlugin::AFTER_GUI_INPUT_CUSTOM) { + after = EditorPlugin::AFTER_GUI_INPUT_CUSTOM; } } diff --git a/editor/editor_node.h b/editor/editor_node.h index df3d2ae0f8..ac79b2d13a 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -125,6 +125,12 @@ public: EDITOR_ASSETLIB }; + enum SceneNameCasing { + SCENE_NAME_CASING_AUTO, + SCENE_NAME_CASING_PASCAL_CASE, + SCENE_NAME_CASING_SNAKE_CASE + }; + struct ExecuteThreadArgs { String path; List<String> args; @@ -233,12 +239,6 @@ private: MAX_BUILD_CALLBACKS = 128 }; - enum ScriptNameCasing { - SCENE_NAME_CASING_AUTO, - SCENE_NAME_CASING_PASCAL_CASE, - SCENE_NAME_CASING_SNAKE_CASE - }; - struct BottomPanelItem { String name; Control *control = nullptr; @@ -543,7 +543,6 @@ private: void _update_file_menu_opened(); void _update_file_menu_closed(); - void _on_plugin_ready(Object *p_script, const String &p_activate_name); void _remove_plugin_from_enabled(const String &p_name); void _fs_changed(); @@ -553,7 +552,6 @@ private: void _node_renamed(); void _editor_select_next(); void _editor_select_prev(); - void _editor_select(int p_which); void _set_scene_metadata(const String &p_file, int p_idx = -1); void _get_scene_metadata(const String &p_file); void _update_title(); @@ -658,8 +656,6 @@ private: void _update_layouts_menu(); void _layout_menu_option(int p_id); - void _clear_undo_history(); - void _update_addon_config(); void _toggle_distraction_free_mode(); @@ -703,7 +699,11 @@ protected: void set_current_tab(int p_tab); public: - void set_visible_editor(EditorTable p_table) { _editor_select(p_table); } + // Public for use with callable_mp. + void _on_plugin_ready(Object *p_script, const String &p_activate_name); + + void editor_select(int p_which); + void set_visible_editor(EditorTable p_table) { editor_select(p_table); } bool call_build(); @@ -720,6 +720,8 @@ public: static HBoxContainer *get_menu_hb() { return singleton->menu_hb; } static VSplitContainer *get_top_split() { return singleton->top_split; } + static String adjust_scene_name_casing(const String &root_name); + static bool has_unsaved_changes() { return singleton->unsaved_cache; } static void disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames); static void add_io_error(const String &p_error); @@ -784,7 +786,7 @@ public: void open_request(const String &p_path); void edit_foreign_resource(Ref<Resource> p_resource); - bool is_resource_read_only(Ref<Resource> p_resource); + bool is_resource_read_only(Ref<Resource> p_resource, bool p_foreign_resources_are_writable = false); bool is_changing_scene() const; diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index f3ac8f4ba0..981dad2d2a 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -972,6 +972,10 @@ void EditorPlugin::_bind_methods() { BIND_ENUM_CONSTANT(DOCK_SLOT_RIGHT_UR); BIND_ENUM_CONSTANT(DOCK_SLOT_RIGHT_BR); BIND_ENUM_CONSTANT(DOCK_SLOT_MAX); + + BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_PASS); + BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_STOP); + BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_CUSTOM); } Ref<EditorUndoRedoManager> EditorPlugin::get_undo_redo() { diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index fe01524bea..a048b174e4 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -204,7 +204,7 @@ public: enum AfterGUIInput { AFTER_GUI_INPUT_PASS, AFTER_GUI_INPUT_STOP, - AFTER_GUI_INPUT_DESELECT + AFTER_GUI_INPUT_CUSTOM }; //TODO: send a resource for editing to the editor node? @@ -312,6 +312,7 @@ public: VARIANT_ENUM_CAST(EditorPlugin::CustomControlContainer); VARIANT_ENUM_CAST(EditorPlugin::DockSlot); +VARIANT_ENUM_CAST(EditorPlugin::AfterGUIInput); typedef EditorPlugin *(*EditorPluginCreateFunc)(); diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp index a8df486381..bd19df41fe 100644 --- a/editor/editor_plugin_settings.cpp +++ b/editor/editor_plugin_settings.cpp @@ -46,7 +46,7 @@ void EditorPluginSettings::_notification(int p_what) { } break; case Node::NOTIFICATION_READY: { - plugin_config_dialog->connect("plugin_ready", Callable(EditorNode::get_singleton(), "_on_plugin_ready")); + plugin_config_dialog->connect("plugin_ready", callable_mp(EditorNode::get_singleton(), &EditorNode::_on_plugin_ready)); plugin_list->connect("button_clicked", callable_mp(this, &EditorPluginSettings::_cell_button_pressed)); } break; } diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 7364258a07..3b99962435 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1758,7 +1758,7 @@ void EditorPropertyVector2::_value_changed(double val, const String &p_name) { Vector2 v2; v2.x = spin[0]->get_value(); v2.y = spin[1]->get_value(); - emit_changed(get_edited_property(), v2, p_name); + emit_changed(get_edited_property(), v2, linked->is_pressed() ? "" : p_name); } void EditorPropertyVector2::update_property() { @@ -2005,7 +2005,7 @@ void EditorPropertyVector3::_value_changed(double val, const String &p_name) { v3.y = Math::deg_to_rad(v3.y); v3.z = Math::deg_to_rad(v3.z); } - emit_changed(get_edited_property(), v3, p_name); + emit_changed(get_edited_property(), v3, linked->is_pressed() ? "" : p_name); } void EditorPropertyVector3::update_property() { @@ -2171,7 +2171,7 @@ void EditorPropertyVector2i::_value_changed(double val, const String &p_name) { Vector2i v2; v2.x = spin[0]->get_value(); v2.y = spin[1]->get_value(); - emit_changed(get_edited_property(), v2, p_name); + emit_changed(get_edited_property(), v2, linked->is_pressed() ? "" : p_name); } void EditorPropertyVector2i::update_property() { @@ -2413,7 +2413,7 @@ void EditorPropertyVector3i::_value_changed(double val, const String &p_name) { v3.x = spin[0]->get_value(); v3.y = spin[1]->get_value(); v3.z = spin[2]->get_value(); - emit_changed(get_edited_property(), v3, p_name); + emit_changed(get_edited_property(), v3, linked->is_pressed() ? "" : p_name); } void EditorPropertyVector3i::update_property() { diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index ad84b30689..728a3b0f80 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -268,6 +268,7 @@ void EditorPropertyArray::update_property() { size_slider->set_step(1); size_slider->set_max(1000000); size_slider->set_h_size_flags(SIZE_EXPAND_FILL); + size_slider->set_read_only(is_read_only()); size_slider->connect("value_changed", callable_mp(this, &EditorPropertyArray::_length_changed)); hbox->add_child(size_slider); @@ -278,6 +279,7 @@ void EditorPropertyArray::update_property() { button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Element")); button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); button_add_item->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyArray::_add_element)); + button_add_item->set_disabled(is_read_only()); vbox->add_child(button_add_item); paginator = memnew(EditorPaginator); @@ -328,6 +330,7 @@ void EditorPropertyArray::update_property() { Button *reorder_button = memnew(Button); reorder_button->set_icon(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons"))); reorder_button->set_default_cursor_shape(Control::CURSOR_MOVE); + reorder_button->set_disabled(is_read_only()); reorder_button->connect("gui_input", callable_mp(this, &EditorPropertyArray::_reorder_button_gui_input)); reorder_button->connect("button_down", callable_mp(this, &EditorPropertyArray::_reorder_button_down).bind(i + offset)); reorder_button->connect("button_up", callable_mp(this, &EditorPropertyArray::_reorder_button_up)); @@ -358,6 +361,7 @@ void EditorPropertyArray::update_property() { prop->connect("property_changed", callable_mp(this, &EditorPropertyArray::_property_changed)); prop->connect("object_id_selected", callable_mp(this, &EditorPropertyArray::_object_id_selected)); prop->set_h_size_flags(SIZE_EXPAND_FILL); + prop->set_read_only(is_read_only()); hbox->add_child(prop); bool is_untyped_array = array.get_type() == Variant::ARRAY && subtype == Variant::NIL; @@ -366,10 +370,12 @@ void EditorPropertyArray::update_property() { Button *edit = memnew(Button); edit->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); hbox->add_child(edit); + edit->set_disabled(is_read_only()); edit->connect("pressed", callable_mp(this, &EditorPropertyArray::_change_type).bind(edit, i + offset)); } else { Button *remove = memnew(Button); remove->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); + remove->set_disabled(is_read_only()); remove->connect("pressed", callable_mp(this, &EditorPropertyArray::_remove_pressed).bind(i + offset)); hbox->add_child(remove); } @@ -409,6 +415,10 @@ void EditorPropertyArray::_button_draw() { } bool EditorPropertyArray::_is_drop_valid(const Dictionary &p_drag_data) const { + if (is_read_only()) { + return false; + } + String allowed_type = Variant::get_type_name(subtype); // When the subtype is of type Object, an additional subtype may be specified in the hint string @@ -609,7 +619,7 @@ void EditorPropertyArray::setup(Variant::Type p_array_type, const String &p_hint } void EditorPropertyArray::_reorder_button_gui_input(const Ref<InputEvent> &p_event) { - if (reorder_from_index < 0) { + if (reorder_from_index < 0 || is_read_only()) { return; } @@ -646,6 +656,10 @@ void EditorPropertyArray::_reorder_button_gui_input(const Ref<InputEvent> &p_eve } void EditorPropertyArray::_reorder_button_down(int p_index) { + if (is_read_only()) { + return; + } + reorder_from_index = p_index; reorder_to_index = p_index; reorder_selected_element_hbox = Object::cast_to<HBoxContainer>(property_vbox->get_child(p_index % page_length)); @@ -656,6 +670,10 @@ void EditorPropertyArray::_reorder_button_down(int p_index) { } void EditorPropertyArray::_reorder_button_up() { + if (is_read_only()) { + return; + } + if (reorder_from_index != reorder_to_index) { // Move the element. Variant array = object->get_array(); @@ -1097,6 +1115,10 @@ void EditorPropertyDictionary::update_property() { } } + ERR_FAIL_COND(!prop); + + prop->set_read_only(is_read_only()); + if (i == amount) { PanelContainer *pc = memnew(PanelContainer); property_vbox->add_child(pc); @@ -1135,6 +1157,7 @@ void EditorPropertyDictionary::update_property() { prop->set_h_size_flags(SIZE_EXPAND_FILL); Button *edit = memnew(Button); edit->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); + edit->set_disabled(is_read_only()); hbox->add_child(edit); edit->connect("pressed", callable_mp(this, &EditorPropertyDictionary::_change_type).bind(edit, change_index)); @@ -1143,6 +1166,7 @@ void EditorPropertyDictionary::update_property() { if (i == amount + 1) { button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Key/Value Pair")); button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); + button_add_item->set_disabled(is_read_only()); button_add_item->connect("pressed", callable_mp(this, &EditorPropertyDictionary::_add_key_value)); add_vbox->add_child(button_add_item); } diff --git a/editor/editor_quick_open.cpp b/editor/editor_quick_open.cpp index 539cb7cd8a..b4ec3bca15 100644 --- a/editor/editor_quick_open.cpp +++ b/editor/editor_quick_open.cpp @@ -31,6 +31,7 @@ #include "editor_quick_open.h" #include "core/os/keyboard.h" +#include "editor/editor_node.h" void EditorQuickOpen::popup_dialog(const StringName &p_base, bool p_enable_multi, bool p_dontclear) { base_type = p_base; @@ -57,17 +58,29 @@ void EditorQuickOpen::_build_search_cache(EditorFileSystemDirectory *p_efsd) { Vector<String> base_types = String(base_type).split(String(",")); for (int i = 0; i < p_efsd->get_file_count(); i++) { - String file_type = p_efsd->get_file_type(i); + String file = p_efsd->get_file_path(i); + String engine_type = p_efsd->get_file_type(i); + // TODO: Fix lack of caching for resource's script's global class name (if applicable). + String script_type; + if (_load_resources) { + Ref<Resource> res = ResourceLoader::load(file); + if (res.is_valid()) { + Ref<Script> scr = res->get_script(); + if (scr.is_valid()) { + script_type = scr->get_language()->get_global_class_name(file); + } + } + } + String actual_type = script_type.is_empty() ? engine_type : script_type; // Iterate all possible base types. for (String &parent_type : base_types) { - if (ClassDB::is_parent_class(file_type, parent_type)) { - String file = p_efsd->get_file_path(i); + if (ClassDB::is_parent_class(engine_type, parent_type) || EditorNode::get_editor_data().script_class_is_parent(script_type, parent_type)) { files.push_back(file.substr(6, file.length())); // Store refs to used icons. String ext = file.get_extension(); if (!icons.has(ext)) { - icons.insert(ext, get_theme_icon((has_theme_icon(file_type, SNAME("EditorIcons")) ? file_type : String("Object")), SNAME("EditorIcons"))); + icons.insert(ext, get_theme_icon((has_theme_icon(actual_type, SNAME("EditorIcons")) ? actual_type : String("Object")), SNAME("EditorIcons"))); } // Stop testing base types as soon as we got a match. diff --git a/editor/editor_quick_open.h b/editor/editor_quick_open.h index e41a8c7e75..86a419b538 100644 --- a/editor/editor_quick_open.h +++ b/editor/editor_quick_open.h @@ -43,6 +43,7 @@ class EditorQuickOpen : public ConfirmationDialog { Tree *search_options = nullptr; StringName base_type; bool allow_multi_select = false; + bool _load_resources = true; Vector<String> files; OAHashMap<String, Ref<Texture2D>> icons; diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 5346052f4d..de8259c25c 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -53,6 +53,7 @@ void EditorResourcePicker::_update_resource() { if (edited_resource.is_valid() && edited_resource->get_path().is_resource_file()) { resource_path = edited_resource->get_path() + "\n"; } + String class_name = _get_resource_type(edited_resource); if (preview_rect) { preview_rect->set_texture(Ref<Texture2D>()); @@ -64,16 +65,20 @@ void EditorResourcePicker::_update_resource() { assign_button->set_text(TTR("<empty>")); assign_button->set_tooltip_text(""); } else { - assign_button->set_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->(), "Object")); + assign_button->set_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->(), SNAME("Object"))); if (!edited_resource->get_name().is_empty()) { assign_button->set_text(edited_resource->get_name()); } else if (edited_resource->get_path().is_resource_file()) { assign_button->set_text(edited_resource->get_path().get_file()); } else { - assign_button->set_text(edited_resource->get_class()); + assign_button->set_text(class_name); } - assign_button->set_tooltip_text(resource_path + TTR("Type:") + " " + edited_resource->get_class()); + + if (edited_resource->get_path().is_resource_file()) { + resource_path = edited_resource->get_path() + "\n"; + } + assign_button->set_tooltip_text(resource_path + TTR("Type:") + " " + class_name); // Preview will override the above, so called at the end. EditorResourcePreview::get_singleton()->queue_edited_resource_preview(edited_resource, this, "_update_resource_preview", edited_resource->get_instance_id()); @@ -134,16 +139,29 @@ void EditorResourcePicker::_file_selected(const String &p_path) { if (!base_type.is_empty()) { bool any_type_matches = false; + String res_type = loaded_resource->get_class(); + Ref<Script> res_script = loaded_resource->get_script(); + bool is_global_class = false; + if (res_script.is_valid()) { + String script_type = EditorNode::get_editor_data().script_class_get_name(res_script->get_path()); + if (!script_type.is_empty()) { + is_global_class = true; + res_type = script_type; + } + } + for (int i = 0; i < base_type.get_slice_count(","); i++) { String base = base_type.get_slice(",", i); - if (loaded_resource->is_class(base)) { - any_type_matches = true; + + any_type_matches = is_global_class ? EditorNode::get_editor_data().script_class_is_parent(res_type, base) : loaded_resource->is_class(base); + + if (!any_type_matches) { break; } } if (!any_type_matches) { - EditorNode::get_singleton()->show_warning(vformat(TTR("The selected resource (%s) does not match any type expected for this property (%s)."), loaded_resource->get_class(), base_type)); + EditorNode::get_singleton()->show_warning(vformat(TTR("The selected resource (%s) does not match any type expected for this property (%s)."), res_type, base_type)); return; } } @@ -186,7 +204,7 @@ void EditorResourcePicker::_update_menu_items() { // Add options for changing existing value of the resource. if (edited_resource.is_valid()) { // Determine if the edited resource is part of another scene (foreign) which was imported - bool is_edited_resource_foreign_import = EditorNode::get_singleton()->is_resource_read_only(edited_resource); + bool is_edited_resource_foreign_import = EditorNode::get_singleton()->is_resource_read_only(edited_resource, true); // If the resource is determined to be foreign and imported, change the menu entry's description to 'inspect' rather than 'edit' // since will only be able to view its properties in read-only mode. @@ -227,16 +245,19 @@ void EditorResourcePicker::_update_menu_items() { // Add options to copy/paste resource. Ref<Resource> cb = EditorSettings::get_singleton()->get_resource_clipboard(); bool paste_valid = false; - if (is_editable()) { - if (cb.is_valid()) { - if (base_type.is_empty()) { - paste_valid = true; - } else { - for (int i = 0; i < base_type.get_slice_count(","); i++) { - if (ClassDB::is_parent_class(cb->get_class(), base_type.get_slice(",", i))) { - paste_valid = true; - break; - } + if (is_editable() && cb.is_valid()) { + if (base_type.is_empty()) { + paste_valid = true; + } else { + String res_type = _get_resource_type(cb); + + for (int i = 0; i < base_type.get_slice_count(","); i++) { + String base = base_type.get_slice(",", i); + + paste_valid = ClassDB::is_parent_class(res_type, base) || EditorNode::get_editor_data().script_class_is_parent(res_type, base); + + if (!paste_valid) { + break; } } } @@ -281,6 +302,9 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { for (int i = 0; i < base_type.get_slice_count(","); i++) { String base = base_type.get_slice(",", i); ResourceLoader::get_recognized_extensions_for_type(base, &extensions); + if (ScriptServer::is_global_class(base)) { + ResourceLoader::get_recognized_extensions_for_type(ScriptServer::get_global_class_native_base(base), &extensions); + } } HashSet<String> valid_extensions; @@ -408,13 +432,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { Variant obj; if (ScriptServer::is_global_class(intype)) { - obj = ClassDB::instantiate(ScriptServer::get_global_class_native_base(intype)); - if (obj) { - Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(intype)); - if (script.is_valid()) { - ((Object *)obj)->set_script(script); - } - } + obj = EditorNode::get_editor_data().script_class_instance(intype); } else { obj = ClassDB::instantiate(intype); } @@ -512,23 +530,40 @@ void EditorResourcePicker::_button_draw() { void EditorResourcePicker::_button_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; - if (mb.is_valid()) { - if (mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) { - // Only attempt to update and show the menu if we have - // a valid resource or the Picker is editable, as - // there will otherwise be nothing to display. - if (edited_resource.is_valid() || is_editable()) { - _update_menu_items(); - - Vector2 pos = get_screen_position() + mb->get_position(); - edit_menu->reset_size(); - edit_menu->set_position(pos); - edit_menu->popup(); - } + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) { + // Only attempt to update and show the menu if we have + // a valid resource or the Picker is editable, as + // there will otherwise be nothing to display. + if (edited_resource.is_valid() || is_editable()) { + _update_menu_items(); + + Vector2 pos = get_screen_position() + mb->get_position(); + edit_menu->reset_size(); + edit_menu->set_position(pos); + edit_menu->popup(); } } } +String EditorResourcePicker::_get_resource_type(const Ref<Resource> &p_resource) const { + if (p_resource.is_null()) { + return String(); + } + String res_type = p_resource->get_class(); + + Ref<Script> res_script = p_resource->get_script(); + if (res_script.is_null()) { + return res_type; + } + + // TODO: Replace with EditorFileSystem when PR #60606 is merged to use cached resource type. + String script_type = EditorNode::get_editor_data().script_class_get_name(res_script->get_path()); + if (!script_type.is_empty()) { + res_type = script_type; + } + return res_type; +} + void EditorResourcePicker::_get_allowed_types(bool p_with_convert, HashSet<String> *p_vector) const { Vector<String> allowed_types = base_type.split(","); int size = allowed_types.size(); @@ -550,7 +585,9 @@ void EditorResourcePicker::_get_allowed_types(bool p_with_convert, HashSet<Strin List<StringName> allowed_subtypes; List<StringName> inheriters; - ClassDB::get_inheriters_from_class(base, &inheriters); + if (!ScriptServer::is_global_class(base)) { + ClassDB::get_inheriters_from_class(base, &inheriters); + } for (const StringName &subtype_name : inheriters) { p_vector->insert(subtype_name); allowed_subtypes.push_back(subtype_name); @@ -602,32 +639,29 @@ bool EditorResourcePicker::_is_drop_valid(const Dictionary &p_drag_data) const { } } else if (drag_data.has("type") && String(drag_data["type"]) == "resource") { res = drag_data["resource"]; + } else if (drag_data.has("type") && String(drag_data["type"]) == "files") { + Vector<String> files = drag_data["files"]; + + // TODO: Extract the typename of the dropped filepath's resource in a more performant way, without fully loading it. + if (files.size() == 1) { + String file = files[0]; + res = ResourceLoader::load(file); + } } HashSet<String> allowed_types; _get_allowed_types(true, &allowed_types); - if (res.is_valid() && _is_type_valid(res->get_class(), allowed_types)) { - return true; - } + if (res.is_valid()) { + String res_type = _get_resource_type(res); - if (res.is_valid() && res->get_script()) { - StringName custom_class = EditorNode::get_singleton()->get_object_custom_type_name(res->get_script()); - if (_is_type_valid(custom_class, allowed_types)) { + if (_is_type_valid(res_type, allowed_types)) { return true; } - } - - if (drag_data.has("type") && String(drag_data["type"]) == "files") { - Vector<String> files = drag_data["files"]; - - if (files.size() == 1) { - String file = files[0]; - String file_type = EditorFileSystem::get_singleton()->get_file_type(file); - if (!file_type.is_empty() && _is_type_valid(file_type, allowed_types)) { - return true; - } + StringName custom_class = EditorNode::get_singleton()->get_object_custom_type_name(res.ptr()); + if (_is_type_valid(custom_class, allowed_types)) { + return true; } } @@ -685,8 +719,10 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_ HashSet<String> allowed_types; _get_allowed_types(false, &allowed_types); + String res_type = _get_resource_type(dropped_resource); + // If the accepted dropped resource is from the extended list, it requires conversion. - if (!_is_type_valid(dropped_resource->get_class(), allowed_types)) { + if (!_is_type_valid(res_type, allowed_types)) { for (const String &E : allowed_types) { String at = E.strip_edges(); diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index 3d6127e656..d1a20f04b7 100644 --- a/editor/editor_resource_picker.h +++ b/editor/editor_resource_picker.h @@ -91,6 +91,7 @@ class EditorResourcePicker : public HBoxContainer { void _button_draw(); void _button_input(const Ref<InputEvent> &p_event); + String _get_resource_type(const Ref<Resource> &p_resource) const; void _get_allowed_types(bool p_with_convert, HashSet<String> *p_vector) const; bool _is_drop_valid(const Dictionary &p_drag_data) const; bool _is_type_valid(const String p_type_name, HashSet<String> p_allowed_types) const; diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp index ec67cde112..8062b6f756 100644 --- a/editor/editor_settings_dialog.cpp +++ b/editor/editor_settings_dialog.cpp @@ -552,6 +552,10 @@ void EditorSettingsDialog::_shortcut_cell_double_clicked() { const ShortcutButton edit_btn_id = EditorSettingsDialog::SHORTCUT_EDIT; const int edit_btn_col = 1; TreeItem *ti = shortcuts->get_selected(); + if (ti == nullptr) { + return; + } + String type = ti->get_meta("type"); int col = shortcuts->get_selected_column(); if (type == "shortcut" && col == 0) { diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 64ddecc588..edbd2dd62f 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -191,25 +191,6 @@ static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1, return style; } -static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, bool p_flip_x = false) { - if (!p_flip_y && !p_flip_x) { - return p_texture; - } - - Ref<Image> img = p_texture->get_image(); - ERR_FAIL_NULL_V(img, Ref<Texture2D>()); - img = img->duplicate(); - - if (p_flip_y) { - img->flip_y(); - } - if (p_flip_x) { - img->flip_x(); - } - - return ImageTexture::create_from_image(img); -} - #ifdef MODULE_SVG_ENABLED // See also `generate_icon()` in `scene/resources/default_theme.cpp`. static Ref<ImageTexture> editor_generate_icon(int p_index, float p_scale, float p_saturation, const HashMap<Color, Color> &p_convert_colors = HashMap<Color, Color>()) { @@ -1567,14 +1548,13 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_stylebox("camera", "GraphEditMinimap", style_minimap_camera); theme->set_stylebox("node", "GraphEditMinimap", style_minimap_node); - Ref<Texture2D> minimap_resizer_icon = theme->get_icon(SNAME("GuiResizer"), SNAME("EditorIcons")); Color minimap_resizer_color; if (dark_theme) { minimap_resizer_color = Color(1, 1, 1, 0.65); } else { minimap_resizer_color = Color(0, 0, 0, 0.65); } - theme->set_icon("resizer", "GraphEditMinimap", flip_icon(minimap_resizer_icon, true, true)); + theme->set_icon("resizer", "GraphEditMinimap", theme->get_icon(SNAME("GuiResizerTopLeft"), SNAME("EditorIcons"))); theme->set_color("resizer_color", "GraphEditMinimap", minimap_resizer_color); // GraphNode diff --git a/editor/editor_undo_redo_manager.cpp b/editor/editor_undo_redo_manager.cpp index eca2b3143b..064448fd96 100644 --- a/editor/editor_undo_redo_manager.cpp +++ b/editor/editor_undo_redo_manager.cpp @@ -124,19 +124,19 @@ void EditorUndoRedoManager::create_action(const String &p_name, UndoRedo::MergeM create_action_for_history(p_name, INVALID_HISTORY, p_mode); if (p_custom_context) { - // This assigns context to pending action. + // This assigns history to pending action. get_history_for_object(p_custom_context); } } void EditorUndoRedoManager::add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo; - undo_redo->add_do_methodp(p_object, p_method, p_args, p_argcount); + undo_redo->add_do_method(Callable(p_object, p_method).bindp(p_args, p_argcount)); } void EditorUndoRedoManager::add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo; - undo_redo->add_undo_methodp(p_object, p_method, p_args, p_argcount); + undo_redo->add_undo_method(Callable(p_object, p_method).bindp(p_args, p_argcount)); } void EditorUndoRedoManager::_add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { @@ -218,7 +218,10 @@ void EditorUndoRedoManager::add_undo_reference(Object *p_object) { } void EditorUndoRedoManager::commit_action(bool p_execute) { - ERR_FAIL_COND(pending_action.history_id == INVALID_HISTORY); + if (pending_action.history_id == INVALID_HISTORY) { + return; // Empty action, do nothing. + } + is_committing = true; History &history = get_or_create_history(pending_action.history_id); diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 2a444bb04f..bcc85570ed 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -1619,21 +1619,24 @@ void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags } bool EditorExportPlatform::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + bool valid = true; +#ifndef ANDROID_ENABLED String templates_error; - bool valid_export_configuration = has_valid_export_configuration(p_preset, templates_error, r_missing_templates); - - String project_configuration_error; - bool valid_project_configuration = has_valid_project_configuration(p_preset, project_configuration_error); + valid = valid && has_valid_export_configuration(p_preset, templates_error, r_missing_templates); if (!templates_error.is_empty()) { r_error += templates_error; } +#endif + + String project_configuration_error; + valid = valid && has_valid_project_configuration(p_preset, project_configuration_error); if (!project_configuration_error.is_empty()) { r_error += project_configuration_error; } - return valid_export_configuration && valid_project_configuration; + return valid; } EditorExportPlatform::EditorExportPlatform() { diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 00a0e08d3a..43aac5e981 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -865,10 +865,10 @@ void ProjectExportDialog::_validate_export_path(const String &p_path) { if (invalid_path) { export_project->get_ok_button()->set_disabled(true); - export_project->get_line_edit()->disconnect("text_submitted", Callable(export_project, "_file_submitted")); + export_project->get_line_edit()->disconnect("text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted)); } else { export_project->get_ok_button()->set_disabled(false); - export_project->get_line_edit()->connect("text_submitted", Callable(export_project, "_file_submitted")); + export_project->get_line_edit()->connect("text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted)); } } @@ -901,9 +901,9 @@ void ProjectExportDialog::_export_project() { // with _validate_export_path. // FIXME: This is a hack, we should instead change EditorFileDialog to allow // disabling validation by the "text_submitted" signal. - if (!export_project->get_line_edit()->is_connected("text_submitted", Callable(export_project, "_file_submitted"))) { + if (!export_project->get_line_edit()->is_connected("text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted))) { export_project->get_ok_button()->set_disabled(false); - export_project->get_line_edit()->connect("text_submitted", Callable(export_project, "_file_submitted")); + export_project->get_line_edit()->connect("text_submitted", callable_mp(export_project, &EditorFileDialog::_file_submitted)); } export_project->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); @@ -932,8 +932,10 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) { } void ProjectExportDialog::_export_all_dialog() { +#ifndef ANDROID_ENABLED export_all_dialog->show(); export_all_dialog->popup_centered(Size2(300, 80)); +#endif } void ProjectExportDialog::_export_all_dialog_action(const String &p_str) { @@ -1194,11 +1196,16 @@ ProjectExportDialog::ProjectExportDialog() { set_cancel_button_text(TTR("Close")); set_ok_button_text(TTR("Export PCK/ZIP...")); + get_ok_button()->set_disabled(true); +#ifdef ANDROID_ENABLED + export_button = memnew(Button); + export_button->hide(); +#else export_button = add_button(TTR("Export Project..."), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "export"); +#endif export_button->connect("pressed", callable_mp(this, &ProjectExportDialog::_export_project)); // Disable initially before we select a valid preset export_button->set_disabled(true); - get_ok_button()->set_disabled(true); export_all_dialog = memnew(ConfirmationDialog); add_child(export_all_dialog); @@ -1208,8 +1215,14 @@ ProjectExportDialog::ProjectExportDialog() { export_all_dialog->add_button(TTR("Debug"), true, "debug"); export_all_dialog->add_button(TTR("Release"), true, "release"); export_all_dialog->connect("custom_action", callable_mp(this, &ProjectExportDialog::_export_all_dialog_action)); +#ifdef ANDROID_ENABLED + export_all_dialog->hide(); + export_all_button = memnew(Button); + export_all_button->hide(); +#else export_all_button = add_button(TTR("Export All..."), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "export"); +#endif export_all_button->connect("pressed", callable_mp(this, &ProjectExportDialog::_export_all_dialog)); export_all_button->set_disabled(true); diff --git a/editor/icons/GuiResizerTopLeft.svg b/editor/icons/GuiResizerTopLeft.svg new file mode 100644 index 0000000000..a67c2c0722 --- /dev/null +++ b/editor/icons/GuiResizerTopLeft.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m4 12c.55228 0 1-.44772 1-1v-6h6c.55228 0 1-.44772 1-1s-.44772-1-1-1h-7c-.55226.000055-.99994.44774-1 1v7c0 .55228.44772 1 1 1z" fill="#fff" fill-opacity=".58824"/></svg> diff --git a/editor/icons/PreviewEnvironment.svg b/editor/icons/PreviewEnvironment.svg new file mode 100644 index 0000000000..e0b0257daf --- /dev/null +++ b/editor/icons/PreviewEnvironment.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0 -7-7zm-1.7305 2.3125c-.83125 1.5372-1.2685 3.1037-1.2695 4.6816-.64057-.11251-1.3005-.27158-1.9766-.47266a5 5 0 0 1 3.2461-4.209zm3.4629.00391a5 5 0 0 1 3.2383 4.1875c-.65187.17448-1.3077.32867-1.9727.44922-.0084-1.5627-.44294-3.1141-1.2656-4.6367zm-1.7324.0078088c1.0126 1.593 1.5 3.1425 1.5 4.6758 0 .054042-.00662.10803-.00781.16211-.96392.096801-1.9566.1103-2.9844.027344-.00163-.063192-.00781-.12632-.00781-.18945 0-1.5333.48744-3.0828 1.5-4.6758zm4.8789 5.7578a5 5 0 0 1 -3.1484 3.6055002c.57106-1.0564.95277-2.1268 1.1367-3.2051002.68204-.10905 1.3556-.23789 2.0117-.40039zm-9.7461.033203c.68377.18153 1.3555.33345 2.0098.43164.18781 1.0551002.56647 2.1026002 1.125 3.1367002a5 5 0 0 1 -3.1348-3.5684002zm6.168.55469c-.22615.9886602-.65424 1.9884002-1.3008 3.0059002-.63811-1.0042-1.0645-1.9908-1.293-2.9668002.89027.054126 1.7517.029377 2.5938-.039062z"/><path d="m8 1v2.3242c1.0126 1.593 1.5 3.1425 1.5 4.6758 0 .054042-.00662.10803-.00781.16211-.4894.049148-.98713.077552-1.4922.082031v1.4922c.43915-.0076.87287-.031628 1.3008-.066406-.22615.98866-.65424 1.9884-1.3008 3.0059v2.3242a7 7 0 0 0 7.000001-7 7 7 0 0 0 -7.000001-7zm1.7324 2.3164a5 5 0 0 1 3.2383 4.1875c-.65187.17448-1.3077.32867-1.9727.44922-.00845-1.5627-.44294-3.1141-1.2656-4.6367zm3.1465 5.7656a5 5 0 0 1 -3.1484 3.6055c.57106-1.0564.95277-2.1268 1.1367-3.2051.68204-.10905 1.3556-.23789 2.0117-.40039z"/></g></svg> diff --git a/editor/icons/PreviewSun.svg b/editor/icons/PreviewSun.svg new file mode 100644 index 0000000000..a8c652be65 --- /dev/null +++ b/editor/icons/PreviewSun.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v3h2v-3zm-2.5352 2.0508-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm7.0703 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-3.5352 1.9492c-1.6569 0-3 1.3432-3 3s1.3431 3 3 3 3-1.3432 3-3-1.3431-3-3-3zm-7 2v2h3v-2zm11 0v2h3v-2zm-7.5352 3.1211-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm7.0703 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-4.5352 1.8789v3h2v-3z" fill="#e0e0e0"/></svg> diff --git a/editor/import/dynamic_font_import_settings.cpp b/editor/import/dynamic_font_import_settings.cpp index d1f37179f3..c1761339b2 100644 --- a/editor/import/dynamic_font_import_settings.cpp +++ b/editor/import/dynamic_font_import_settings.cpp @@ -302,6 +302,7 @@ static UniRange unicode_ranges[] = { { 0x10D00, 0x10D3F, U"Hanifi Rohingya" }, { 0x10E60, 0x10E7F, U"Rumi Numeral Symbols" }, { 0x10E80, 0x10EBF, U"Yezidi" }, + { 0x10EC0, 0x10EFF, U"Arabic Extended-C" }, { 0x10F00, 0x10F2F, U"Old Sogdian" }, { 0x10F30, 0x10F6F, U"Sogdian" }, { 0x10F70, 0x10FAF, U"Old Uyghur" }, @@ -333,11 +334,13 @@ static UniRange unicode_ranges[] = { { 0x11A50, 0x11AAF, U"Soyombo" }, { 0x11AB0, 0x11ABF, U"Unified Canadian Aboriginal Syllabics Extended-A" }, { 0x11AC0, 0x11AFF, U"Pau Cin Hau" }, + { 0x11B00, 0x11B5F, U"Devanagari Extended-A" }, { 0x11C00, 0x11C6F, U"Bhaiksuki" }, { 0x11C70, 0x11CBF, U"Marchen" }, { 0x11D00, 0x11D5F, U"Masaram Gondi" }, { 0x11D60, 0x11DAF, U"Gunjala Gondi" }, { 0x11EE0, 0x11EFF, U"Makasar" }, + { 0x11F00, 0x11F5F, U"Kawi" }, { 0x11FB0, 0x11FBF, U"Lisu Supplement" }, { 0x11FC0, 0x11FFF, U"Tamil Supplement" }, { 0x12000, 0x123FF, U"Cuneiform" }, @@ -370,6 +373,7 @@ static UniRange unicode_ranges[] = { { 0x1D000, 0x1D0FF, U"Byzantine Musical Symbols" }, { 0x1D100, 0x1D1FF, U"Musical Symbols" }, { 0x1D200, 0x1D24F, U"Ancient Greek Musical Notation" }, + { 0x1D2C0, 0x1D2DF, U"Kaktovik Numerals" }, { 0x1D2E0, 0x1D2FF, U"Mayan Numerals" }, { 0x1D300, 0x1D35F, U"Tai Xuan Jing Symbols" }, { 0x1D360, 0x1D37F, U"Counting Rod Numerals" }, @@ -377,9 +381,11 @@ static UniRange unicode_ranges[] = { { 0x1D800, 0x1DAAF, U"Sutton SignWriting" }, { 0x1DF00, 0x1DFFF, U"Latin Extended-G" }, { 0x1E000, 0x1E02F, U"Glagolitic Supplement" }, + { 0x1E030, 0x1E08F, U"Cyrillic Extended-D" }, { 0x1E100, 0x1E14F, U"Nyiakeng Puachue Hmong" }, { 0x1E290, 0x1E2BF, U"Toto" }, { 0x1E2C0, 0x1E2FF, U"Wancho" }, + { 0x1E4D0, 0x1E4FF, U"Nag Mundari" }, { 0x1E7E0, 0x1E7FF, U"Ethiopic Extended-B" }, { 0x1E800, 0x1E8DF, U"Mende Kikakui" }, { 0x1E900, 0x1E95F, U"Adlam" }, @@ -409,6 +415,7 @@ static UniRange unicode_ranges[] = { { 0x2CEB0, 0x2EBEF, U"CJK Unified Ideographs Extension F" }, { 0x2F800, 0x2FA1F, U"CJK Compatibility Ideographs Supplement" }, { 0x30000, 0x3134F, U"CJK Unified Ideographs Extension G" }, + { 0x31350, 0x323AF, U"CJK Unified Ideographs Extension H" }, //{ 0xE0000, 0xE007F, U"Tags" }, //{ 0xE0100, 0xE01EF, U"Variation Selectors Supplement" }, { 0xF0000, 0xFFFFF, U"Supplementary Private Use Area-A" }, diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 41061c3fc3..b5798a5784 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -1891,6 +1891,39 @@ void ResourceImporterScene::_replace_owner(Node *p_node, Node *p_scene, Node *p_ } } +Array ResourceImporterScene::_get_skinned_pose_transforms(ImporterMeshInstance3D *p_src_mesh_node) { + Array skin_pose_transform_array; + + const Ref<Skin> skin = p_src_mesh_node->get_skin(); + if (skin.is_valid()) { + NodePath skeleton_path = p_src_mesh_node->get_skeleton_path(); + const Node *node = p_src_mesh_node->get_node_or_null(skeleton_path); + const Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node); + if (skeleton) { + int bind_count = skin->get_bind_count(); + + for (int i = 0; i < bind_count; i++) { + Transform3D bind_pose = skin->get_bind_pose(i); + String bind_name = skin->get_bind_name(i); + + int bone_idx = bind_name.is_empty() ? skin->get_bind_bone(i) : skeleton->find_bone(bind_name); + ERR_FAIL_COND_V(bone_idx >= skeleton->get_bone_count(), Array()); + + Transform3D bp_global_rest; + if (bone_idx >= 0) { + bp_global_rest = skeleton->get_bone_global_pose(bone_idx); + } else { + bp_global_rest = skeleton->get_bone_global_pose(i); + } + + skin_pose_transform_array.push_back(bp_global_rest * bind_pose); + } + } + } + + return skin_pose_transform_array; +} + void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches) { ImporterMeshInstance3D *src_mesh_node = Object::cast_to<ImporterMeshInstance3D>(p_node); if (src_mesh_node) { @@ -2007,7 +2040,8 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m } if (generate_lods) { - src_mesh_node->get_mesh()->generate_lods(merge_angle, split_angle); + Array skin_pose_transform_array = _get_skinned_pose_transforms(src_mesh_node); + src_mesh_node->get_mesh()->generate_lods(merge_angle, split_angle, skin_pose_transform_array); } if (create_shadow_meshes) { diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index da37893cc5..77bc06533c 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -34,6 +34,7 @@ #include "core/error/error_macros.h" #include "core/io/resource_importer.h" #include "core/variant/dictionary.h" +#include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/node_3d.h" #include "scene/resources/animation.h" #include "scene/resources/mesh.h" @@ -208,6 +209,7 @@ class ResourceImporterScene : public ResourceImporter { SHAPE_TYPE_CAPSULE, }; + Array _get_skinned_pose_transforms(ImporterMeshInstance3D *p_src_mesh_node); void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner); void _generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches); void _add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index f1e6c70549..ca7b2d0015 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -101,13 +101,13 @@ void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_propert undo_redo->create_action(TTR("Parameter Changed:") + " " + String(p_property), UndoRedo::MERGE_ENDS); undo_redo->add_do_property(tree, p_property, p_value); undo_redo->add_undo_property(tree, p_property, tree->get(p_property)); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "update_graph"); + undo_redo->add_undo_method(this, "update_graph"); undo_redo->commit_action(); updating = false; } -void AnimationNodeBlendTreeEditor::_update_graph() { +void AnimationNodeBlendTreeEditor::update_graph() { if (updating || blend_tree.is_null()) { return; } @@ -364,8 +364,8 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { to_slot = -1; } - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "update_graph"); + undo_redo->add_undo_method(this, "update_graph"); undo_redo->commit_action(); } @@ -416,8 +416,8 @@ void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Ve undo_redo->create_action(TTR("Node Moved")); undo_redo->add_do_method(blend_tree.ptr(), "set_node_position", p_which, p_to / EDSCALE); undo_redo->add_undo_method(blend_tree.ptr(), "set_node_position", p_which, p_from / EDSCALE); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "update_graph"); + undo_redo->add_undo_method(this, "update_graph"); undo_redo->commit_action(); updating = false; } @@ -437,8 +437,8 @@ void AnimationNodeBlendTreeEditor::_connection_request(const String &p_from, int undo_redo->create_action(TTR("Nodes Connected")); undo_redo->add_do_method(blend_tree.ptr(), "connect_node", p_to, p_to_index, p_from); undo_redo->add_undo_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "update_graph"); + undo_redo->add_undo_method(this, "update_graph"); undo_redo->commit_action(); } @@ -453,8 +453,8 @@ void AnimationNodeBlendTreeEditor::_disconnection_request(const String &p_from, undo_redo->create_action(TTR("Nodes Disconnected")); undo_redo->add_do_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index); undo_redo->add_undo_method(blend_tree.ptr(), "connect_node", p_to, p_to_index, p_from); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "update_graph"); + undo_redo->add_undo_method(this, "update_graph"); undo_redo->commit_action(); updating = false; } @@ -468,8 +468,8 @@ void AnimationNodeBlendTreeEditor::_anim_selected(int p_index, Array p_options, undo_redo->create_action(TTR("Set Animation")); undo_redo->add_do_method(anim.ptr(), "set_animation", option); undo_redo->add_undo_method(anim.ptr(), "set_animation", anim->get_animation()); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "update_graph"); + undo_redo->add_undo_method(this, "update_graph"); undo_redo->commit_action(); } @@ -491,8 +491,8 @@ void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { } } - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "update_graph"); + undo_redo->add_undo_method(this, "update_graph"); undo_redo->commit_action(); } @@ -819,7 +819,7 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { _update_theme(); if (is_visible_in_tree()) { - _update_graph(); + update_graph(); } } break; @@ -900,13 +900,28 @@ void AnimationNodeBlendTreeEditor::_scroll_changed(const Vector2 &p_scroll) { } void AnimationNodeBlendTreeEditor::_bind_methods() { - ClassDB::bind_method("_update_graph", &AnimationNodeBlendTreeEditor::_update_graph); + ClassDB::bind_method("update_graph", &AnimationNodeBlendTreeEditor::update_graph); ClassDB::bind_method("_update_filters", &AnimationNodeBlendTreeEditor::_update_filters); } AnimationNodeBlendTreeEditor *AnimationNodeBlendTreeEditor::singleton = nullptr; +// AnimationNode's "node_changed" signal means almost update_input. +void AnimationNodeBlendTreeEditor::_node_changed(const StringName &p_node_name) { + // TODO: + // Here is executed during the commit of EditorNode::undo_redo, it is not possible to create an undo_redo action here. + // The disconnect when the number of enabled inputs decreases is done in AnimationNodeBlendTree and update_graph(). + // This means that there is no place to register undo_redo actions. + // In order to implement undo_redo correctly, we may need to implement AnimationNodeEdit such as AnimationTrackKeyEdit + // and add it to _node_selected() with EditorNode::get_singleton()->push_item(AnimationNodeEdit). + update_graph(); +} + void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<AnimationNode> p_node) { + if (blend_tree.is_null()) { + return; + } + String prev_name = blend_tree->get_node_name(p_node); ERR_FAIL_COND(prev_name.is_empty()); GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(prev_name)); @@ -936,8 +951,8 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima undo_redo->add_undo_method(blend_tree.ptr(), "rename_node", name, prev_name); undo_redo->add_do_method(AnimationTreeEditor::get_singleton()->get_tree(), "rename_parameter", base_path + prev_name, base_path + name); undo_redo->add_undo_method(AnimationTreeEditor::get_singleton()->get_tree(), "rename_parameter", base_path + name, base_path + prev_name); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "update_graph"); + undo_redo->add_undo_method(this, "update_graph"); undo_redo->commit_action(); updating = false; gn->set_name(new_name); @@ -975,7 +990,7 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima } } - _update_graph(); // Needed to update the signal connections with the new name. + update_graph(); // Needed to update the signal connections with the new name. } void AnimationNodeBlendTreeEditor::_node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node) { @@ -992,6 +1007,7 @@ bool AnimationNodeBlendTreeEditor::can_edit(const Ref<AnimationNode> &p_node) { void AnimationNodeBlendTreeEditor::edit(const Ref<AnimationNode> &p_node) { if (blend_tree.is_valid()) { + blend_tree->disconnect("node_changed", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_changed)); blend_tree->disconnect("removed_from_graph", callable_mp(this, &AnimationNodeBlendTreeEditor::_removed_from_graph)); } @@ -1004,9 +1020,10 @@ void AnimationNodeBlendTreeEditor::edit(const Ref<AnimationNode> &p_node) { } else { read_only = EditorNode::get_singleton()->is_resource_read_only(blend_tree); + blend_tree->connect("node_changed", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_changed)); blend_tree->connect("removed_from_graph", callable_mp(this, &AnimationNodeBlendTreeEditor::_removed_from_graph)); - _update_graph(); + update_graph(); } add_node->set_disabled(read_only); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index 30a54930a2..46e0d18c69 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -71,8 +71,6 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { int to_slot = -1; String from_node = ""; - void _update_graph(); - struct AddOption { String name; String type; @@ -95,6 +93,7 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which); void _node_renamed(const String &p_text, Ref<AnimationNode> p_node); void _node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node); + void _node_changed(const StringName &p_node_name); bool updating; @@ -150,6 +149,8 @@ public: virtual bool can_edit(const Ref<AnimationNode> &p_node) override; virtual void edit(const Ref<AnimationNode> &p_node) override; + void update_graph(); + AnimationNodeBlendTreeEditor(); }; diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index ed231c446b..1de4fbaabc 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -50,10 +50,18 @@ #include "scene/scene_string_names.h" void AnimationTreeEditor::edit(AnimationTree *p_tree) { + if (p_tree && !p_tree->is_connected("animation_player_changed", callable_mp(this, &AnimationTreeEditor::_animation_list_changed))) { + p_tree->connect("animation_player_changed", callable_mp(this, &AnimationTreeEditor::_animation_list_changed), CONNECT_DEFERRED); + } + if (tree == p_tree) { return; } + if (tree && tree->is_connected("animation_player_changed", callable_mp(this, &AnimationTreeEditor::_animation_list_changed))) { + tree->disconnect("animation_player_changed", callable_mp(this, &AnimationTreeEditor::_animation_list_changed)); + } + tree = p_tree; Vector<String> path; @@ -73,6 +81,13 @@ void AnimationTreeEditor::_path_button_pressed(int p_path) { } } +void AnimationTreeEditor::_animation_list_changed() { + AnimationNodeBlendTreeEditor *bte = AnimationNodeBlendTreeEditor::get_singleton(); + if (bte) { + bte->update_graph(); + } +} + void AnimationTreeEditor::_update_path() { while (path_hb->get_child_count() > 1) { memdelete(path_hb->get_child(1)); diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h index a33d97f62f..9ef9fff8cd 100644 --- a/editor/plugins/animation_tree_editor_plugin.h +++ b/editor/plugins/animation_tree_editor_plugin.h @@ -65,6 +65,7 @@ class AnimationTreeEditor : public VBoxContainer { ObjectID current_root; void _path_button_pressed(int p_path); + void _animation_list_changed(); static Vector<String> get_animation_list(); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 436113093f..3c9486cdaa 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -648,7 +648,7 @@ void EditorAssetLibrary::shortcut_input(const Ref<InputEvent> &p_event) { const Ref<InputEventKey> key = p_event; if (key.is_valid() && key->is_pressed()) { - if (key->is_match(InputEventKey::create_reference(KeyModifierMask::CMD_OR_CTRL | Key::F))) { + if (key->is_match(InputEventKey::create_reference(KeyModifierMask::CMD_OR_CTRL | Key::F)) && is_visible_in_tree()) { filter->grab_focus(); filter->select_all(); accept_event(); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 070834b33b..8dc08e3a93 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -4715,12 +4715,10 @@ void CanvasItemEditor::_reset_drag() { } void CanvasItemEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_update_override_camera_button", "game_running"), &CanvasItemEditor::_update_override_camera_button); ClassDB::bind_method("_get_editor_data", &CanvasItemEditor::_get_editor_data); ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state); ClassDB::bind_method(D_METHOD("update_viewport"), &CanvasItemEditor::update_viewport); - ClassDB::bind_method(D_METHOD("_zoom_on_position"), &CanvasItemEditor::_zoom_on_position); ClassDB::bind_method("_set_owner_for_node_and_children", &CanvasItemEditor::_set_owner_for_node_and_children); @@ -4984,8 +4982,8 @@ CanvasItemEditor::CanvasItemEditor() { SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created)); SceneTreeDock::get_singleton()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position)); - EditorNode::get_singleton()->call_deferred(SNAME("connect"), "play_pressed", Callable(this, "_update_override_camera_button").bind(true)); - EditorNode::get_singleton()->call_deferred(SNAME("connect"), "stop_pressed", Callable(this, "_update_override_camera_button").bind(false)); + EditorNode::get_singleton()->call_deferred(SNAME("connect"), callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true)); + EditorNode::get_singleton()->call_deferred(SNAME("connect"), "stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false)); // A fluid container for all toolbars. HFlowContainer *main_flow = memnew(HFlowContainer); @@ -5883,9 +5881,6 @@ void CanvasItemEditorViewport::_notification(int p_what) { } } -void CanvasItemEditorViewport::_bind_methods() { -} - CanvasItemEditorViewport::CanvasItemEditorViewport(CanvasItemEditor *p_canvas_item_editor) { default_texture_node_type = "Sprite2D"; // Node2D diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 0a840d6fd6..b731d3cc7d 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -618,8 +618,6 @@ class CanvasItemEditorViewport : public Control { void _show_resource_type_selector(); void _update_theme(); - static void _bind_methods(); - protected: void _notification(int p_what); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 6c3ce20d7a..33aad0ac61 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1360,8 +1360,8 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (discard == EditorPlugin::AFTER_GUI_INPUT_STOP) { return; } - if (discard == EditorPlugin::AFTER_GUI_INPUT_DESELECT) { - after = EditorPlugin::AFTER_GUI_INPUT_DESELECT; + if (discard == EditorPlugin::AFTER_GUI_INPUT_CUSTOM) { + after = EditorPlugin::AFTER_GUI_INPUT_CUSTOM; } } } @@ -1373,8 +1373,8 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (discard == EditorPlugin::AFTER_GUI_INPUT_STOP) { return; } - if (discard == EditorPlugin::AFTER_GUI_INPUT_DESELECT) { - after = EditorPlugin::AFTER_GUI_INPUT_DESELECT; + if (discard == EditorPlugin::AFTER_GUI_INPUT_CUSTOM) { + after = EditorPlugin::AFTER_GUI_INPUT_CUSTOM; } } } @@ -1601,7 +1601,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { break; } - if (after != EditorPlugin::AFTER_GUI_INPUT_DESELECT) { + if (after != EditorPlugin::AFTER_GUI_INPUT_CUSTOM) { //clicking is always deferred to either move or release clicked = _select_ray(b->get_position()); selection_in_progress = true; @@ -1622,7 +1622,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { break; } - if (after != EditorPlugin::AFTER_GUI_INPUT_DESELECT) { + if (after != EditorPlugin::AFTER_GUI_INPUT_CUSTOM) { selection_in_progress = false; if (clicked.is_valid()) { @@ -5516,8 +5516,8 @@ Dictionary Node3DEditor::get_state() const { pd["sun_color"] = sun_color->get_pick_color(); pd["sun_energy"] = sun_energy->get_value(); - pd["sun_disabled"] = sun_button->is_pressed(); - pd["environ_disabled"] = environ_button->is_pressed(); + pd["sun_enabled"] = sun_button->is_pressed(); + pd["environ_enabled"] = environ_button->is_pressed(); d["preview_sun_env"] = pd; } @@ -5648,8 +5648,8 @@ void Node3DEditor::set_state(const Dictionary &p_state) { sun_color->set_pick_color(pd["sun_color"]); sun_energy->set_value(pd["sun_energy"]); - sun_button->set_pressed(pd["sun_disabled"]); - environ_button->set_pressed(pd["environ_disabled"]); + sun_button->set_pressed(pd["sun_enabled"]); + environ_button->set_pressed(pd["environ_enabled"]); sun_environ_updating = false; @@ -5657,8 +5657,8 @@ void Node3DEditor::set_state(const Dictionary &p_state) { _update_preview_environment(); } else { _load_default_preview_settings(); - sun_button->set_pressed(false); - environ_button->set_pressed(false); + sun_button->set_pressed(true); + environ_button->set_pressed(true); _preview_settings_changed(); _update_preview_environment(); } @@ -7159,8 +7159,8 @@ void Node3DEditor::_update_theme() { view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_theme_icon(SNAME("Panels3Alt"), SNAME("EditorIcons"))); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_theme_icon(SNAME("Panels4"), SNAME("EditorIcons"))); - sun_button->set_icon(get_theme_icon(SNAME("DirectionalLight3D"), SNAME("EditorIcons"))); - environ_button->set_icon(get_theme_icon(SNAME("WorldEnvironment"), SNAME("EditorIcons"))); + sun_button->set_icon(get_theme_icon(SNAME("PreviewSun"), SNAME("EditorIcons"))); + environ_button->set_icon(get_theme_icon(SNAME("PreviewEnvironment"), SNAME("EditorIcons"))); sun_environ_settings->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); sun_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); @@ -7636,7 +7636,7 @@ void Node3DEditor::_load_default_preview_settings() { } void Node3DEditor::_update_preview_environment() { - bool disable_light = directional_light_count > 0 || sun_button->is_pressed(); + bool disable_light = directional_light_count > 0 || !sun_button->is_pressed(); sun_button->set_disabled(directional_light_count > 0); @@ -7645,6 +7645,7 @@ void Node3DEditor::_update_preview_environment() { preview_sun->get_parent()->remove_child(preview_sun); sun_state->show(); sun_vb->hide(); + preview_sun_dangling = true; } if (directional_light_count > 0) { @@ -7658,13 +7659,14 @@ void Node3DEditor::_update_preview_environment() { add_child(preview_sun, true); sun_state->hide(); sun_vb->show(); + preview_sun_dangling = false; } } sun_angle_altitude->set_value(-Math::rad_to_deg(sun_rotation.x)); sun_angle_azimuth->set_value(180.0 - Math::rad_to_deg(sun_rotation.y)); - bool disable_env = world_env_count > 0 || environ_button->is_pressed(); + bool disable_env = world_env_count > 0 || !environ_button->is_pressed(); environ_button->set_disabled(world_env_count > 0); @@ -7673,6 +7675,7 @@ void Node3DEditor::_update_preview_environment() { preview_environment->get_parent()->remove_child(preview_environment); environ_state->show(); environ_vb->hide(); + preview_env_dangling = true; } if (world_env_count > 0) { environ_state->set_text(TTR("Scene contains\nWorldEnvironment.\nPreview disabled.")); @@ -7685,6 +7688,7 @@ void Node3DEditor::_update_preview_environment() { add_child(preview_environment); environ_state->hide(); environ_vb->show(); + preview_env_dangling = false; } } } @@ -7854,7 +7858,8 @@ Node3DEditor::Node3DEditor() { sun_button->set_toggle_mode(true); sun_button->set_flat(true); sun_button->connect("pressed", callable_mp(this, &Node3DEditor::_update_preview_environment), CONNECT_DEFERRED); - sun_button->set_disabled(true); + // Preview is enabled by default - ensure this applies on editor startup when there is no state yet. + sun_button->set_pressed(true); main_menu_hbox->add_child(sun_button); @@ -7863,7 +7868,8 @@ Node3DEditor::Node3DEditor() { environ_button->set_toggle_mode(true); environ_button->set_flat(true); environ_button->connect("pressed", callable_mp(this, &Node3DEditor::_update_preview_environment), CONNECT_DEFERRED); - environ_button->set_disabled(true); + // Preview is enabled by default - ensure this applies on editor startup when there is no state yet. + environ_button->set_pressed(true); main_menu_hbox->add_child(environ_button); @@ -8329,6 +8335,12 @@ void fragment() { } Node3DEditor::~Node3DEditor() { memdelete(preview_node); + if (preview_sun_dangling && preview_sun) { + memdelete(preview_sun); + } + if (preview_env_dangling && preview_environment) { + memdelete(preview_environment); + } } void Node3DEditorPlugin::make_visible(bool p_visible) { diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 580cb878ce..c76f534c22 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -763,7 +763,9 @@ private: Button *sun_environ_settings = nullptr; DirectionalLight3D *preview_sun = nullptr; + bool preview_sun_dangling = false; WorldEnvironment *preview_environment = nullptr; + bool preview_env_dangling = false; Ref<Environment> environment; Ref<CameraAttributesPhysical> camera_attributes; Ref<ProceduralSkyMaterial> sky_material; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 246bc4b183..ae3c578eaa 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -1366,6 +1366,7 @@ void ShaderEditorPlugin::_shader_selected(int p_index) { edited_shaders[p_index].shader_editor->validate_script(); } shader_tabs->set_current_tab(p_index); + shader_list->select(p_index); } void ShaderEditorPlugin::_shader_list_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index) { @@ -1375,11 +1376,10 @@ void ShaderEditorPlugin::_shader_list_clicked(int p_item, Vector2 p_local_mouse_ } void ShaderEditorPlugin::_close_shader(int p_index) { - int index = shader_tabs->get_current_tab(); - ERR_FAIL_INDEX(index, shader_tabs->get_tab_count()); - Control *c = shader_tabs->get_tab_control(index); + ERR_FAIL_INDEX(p_index, shader_tabs->get_tab_count()); + Control *c = shader_tabs->get_tab_control(p_index); memdelete(c); - edited_shaders.remove_at(index); + edited_shaders.remove_at(p_index); _update_shader_list(); EditorNode::get_singleton()->get_undo_redo()->clear_history(); // To prevent undo on deleted graphs. } diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index afd38ef71a..f48b2fc70e 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -115,8 +115,8 @@ public: ShaderTextEditor(); }; -class ShaderEditor : public PanelContainer { - GDCLASS(ShaderEditor, PanelContainer); +class ShaderEditor : public MarginContainer { + GDCLASS(ShaderEditor, MarginContainer); enum { EDIT_UNDO, diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 3c75ac6ca6..2478ac9514 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -1143,7 +1143,7 @@ EditorPlugin::AfterGUIInput Skeleton3DEditorPlugin::forward_spatial_gui_input(Ca se->update_bone_original(); } } - return EditorPlugin::AFTER_GUI_INPUT_DESELECT; + return EditorPlugin::AFTER_GUI_INPUT_CUSTOM; } return EditorPlugin::AFTER_GUI_INPUT_PASS; } diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index ae21aad337..d0a1ddafa1 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -984,11 +984,17 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) { } void SpriteFramesEditor::edit(SpriteFrames *p_frames) { - if (frames == p_frames) { + bool new_read_only_state = false; + if (p_frames) { + new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_frames); + } + + if (frames == p_frames && new_read_only_state == read_only) { return; } frames = p_frames; + read_only = new_read_only_state; if (p_frames) { if (!p_frames->has_animation(edited_anim)) { @@ -1009,6 +1015,20 @@ void SpriteFramesEditor::edit(SpriteFrames *p_frames) { } else { hide(); } + + new_anim->set_disabled(read_only); + remove_anim->set_disabled(read_only); + anim_speed->set_editable(!read_only); + anim_loop->set_disabled(read_only); + load->set_disabled(read_only); + load_sheet->set_disabled(read_only); + copy->set_disabled(read_only); + paste->set_disabled(read_only); + empty->set_disabled(read_only); + empty2->set_disabled(read_only); + move_up->set_disabled(read_only); + move_down->set_disabled(read_only); + _delete->set_disabled(read_only); } void SpriteFramesEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { @@ -1016,6 +1036,10 @@ void SpriteFramesEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { } Variant SpriteFramesEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + if (read_only) { + return false; + } + if (!frames->has_animation(edited_anim)) { return false; } @@ -1038,6 +1062,10 @@ Variant SpriteFramesEditor::get_drag_data_fw(const Point2 &p_point, Control *p_f } bool SpriteFramesEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + if (read_only) { + return false; + } + Dictionary d = p_data; if (!d.has("type")) { @@ -1169,6 +1197,7 @@ SpriteFramesEditor::SpriteFramesEditor() { remove_anim->set_flat(true); remove_anim->set_tooltip_text(TTR("Remove Animation")); hbc_animlist->add_child(remove_anim); + remove_anim->set_disabled(true); remove_anim->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_remove)); anim_search_box = memnew(LineEdit); diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index f2530b732f..092f556c63 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -57,6 +57,8 @@ class SpriteFramesEditor : public HSplitContainer { }; int dominant_param = PARAM_FRAME_COUNT; + bool read_only = false; + Button *load = nullptr; Button *load_sheet = nullptr; Button *_delete = nullptr; diff --git a/editor/plugins/texture_3d_editor_plugin.cpp b/editor/plugins/texture_3d_editor_plugin.cpp index 3ea62184c6..4b2f28658a 100644 --- a/editor/plugins/texture_3d_editor_plugin.cpp +++ b/editor/plugins/texture_3d_editor_plugin.cpp @@ -138,10 +138,6 @@ void Texture3DEditor::edit(Ref<Texture3D> p_texture) { } } -void Texture3DEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_layer_changed"), &Texture3DEditor::_layer_changed); -} - Texture3DEditor::Texture3DEditor() { set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED); set_custom_minimum_size(Size2(1, 150)); @@ -173,7 +169,7 @@ Texture3DEditor::Texture3DEditor() { info->add_theme_constant_override("shadow_offset_y", 2); setting = false; - layer->connect("value_changed", Callable(this, "_layer_changed")); + layer->connect("value_changed", callable_mp(this, &Texture3DEditor::_layer_changed)); } Texture3DEditor::~Texture3DEditor() { diff --git a/editor/plugins/texture_3d_editor_plugin.h b/editor/plugins/texture_3d_editor_plugin.h index 357bdb0845..7795c83c8a 100644 --- a/editor/plugins/texture_3d_editor_plugin.h +++ b/editor/plugins/texture_3d_editor_plugin.h @@ -66,8 +66,6 @@ class Texture3DEditor : public Control { protected: void _notification(int p_what); - static void _bind_methods(); - public: void edit(Ref<Texture3D> p_texture); Texture3DEditor(); diff --git a/editor/plugins/texture_layered_editor_plugin.cpp b/editor/plugins/texture_layered_editor_plugin.cpp index dd8633360e..b0a174c1bc 100644 --- a/editor/plugins/texture_layered_editor_plugin.cpp +++ b/editor/plugins/texture_layered_editor_plugin.cpp @@ -214,10 +214,6 @@ void TextureLayeredEditor::edit(Ref<TextureLayered> p_texture) { } } -void TextureLayeredEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_layer_changed"), &TextureLayeredEditor::_layer_changed); -} - TextureLayeredEditor::TextureLayeredEditor() { set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED); set_custom_minimum_size(Size2(1, 150)); @@ -249,7 +245,7 @@ TextureLayeredEditor::TextureLayeredEditor() { info->add_theme_constant_override("shadow_offset_y", 2); setting = false; - layer->connect("value_changed", Callable(this, "_layer_changed")); + layer->connect("value_changed", callable_mp(this, &TextureLayeredEditor::_layer_changed)); } TextureLayeredEditor::~TextureLayeredEditor() { diff --git a/editor/plugins/texture_layered_editor_plugin.h b/editor/plugins/texture_layered_editor_plugin.h index f49aa83eb2..f4dbc104c8 100644 --- a/editor/plugins/texture_layered_editor_plugin.h +++ b/editor/plugins/texture_layered_editor_plugin.h @@ -68,7 +68,6 @@ class TextureLayeredEditor : public Control { protected: void _notification(int p_what); virtual void gui_input(const Ref<InputEvent> &p_event) override; - static void _bind_methods(); public: void edit(Ref<TextureLayered> p_texture); diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index d9291503cb..c823487279 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -403,6 +403,9 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_ // Update everything. _update_zoom_and_panning(); + base_tiles_drawing_root->set_size(_compute_base_tiles_control_size()); + alternative_tiles_drawing_root->set_size(_compute_alternative_tiles_control_size()); + // Update. base_tiles_draw->queue_redraw(); base_tiles_texture_grid->queue_redraw(); @@ -601,7 +604,6 @@ TileAtlasView::TileAtlasView() { base_tiles_drawing_root = memnew(Control); base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); - base_tiles_drawing_root->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); base_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST); base_tiles_root_control->add_child(base_tiles_drawing_root); @@ -645,7 +647,6 @@ TileAtlasView::TileAtlasView() { alternative_tiles_drawing_root = memnew(Control); alternative_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); - alternative_tiles_drawing_root->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST); alternative_tiles_root_control->add_child(alternative_tiles_drawing_root); diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index a91f22ccd0..79230891f1 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -3677,6 +3677,7 @@ void TileMapEditor::_update_layers_selection() { tile_map_layer = -1; } tile_map->set_selected_layer(toggle_highlight_selected_layer_button->is_pressed() ? tile_map_layer : -1); + tileset_changed_needs_update = false; // Update is not needed here and actually causes problems. layers_selection_button->clear(); if (tile_map->get_layers_count() > 0) { diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index 761140b2d5..336ce9e4c8 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -1124,6 +1124,8 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { set_up_password->connect(SNAME("text_changed"), callable_mp(this, &VersionControlEditorPlugin::_update_set_up_warning)); set_up_password_input->add_child(set_up_password); + const String home_dir = OS::get_singleton()->has_environment("HOME") ? OS::get_singleton()->get_environment("HOME") : OS::get_singleton()->get_system_dir(OS::SYSTEM_DIR_DOCUMENTS); + HBoxContainer *set_up_ssh_public_key_input = memnew(HBoxContainer); set_up_ssh_public_key_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); set_up_settings_vbc->add_child(set_up_ssh_public_key_input); @@ -1147,10 +1149,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { set_up_ssh_public_key_file_dialog->set_access(FileDialog::ACCESS_FILESYSTEM); set_up_ssh_public_key_file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE); set_up_ssh_public_key_file_dialog->set_show_hidden_files(true); - // TODO: Make this start at the user's home folder - Ref<DirAccess> d = DirAccess::open(OS::get_singleton()->get_system_dir(OS::SYSTEM_DIR_DOCUMENTS)); - d->change_dir("../"); - set_up_ssh_public_key_file_dialog->set_current_dir(d->get_current_dir()); + set_up_ssh_public_key_file_dialog->set_current_dir(home_dir); set_up_ssh_public_key_file_dialog->connect(SNAME("file_selected"), callable_mp(this, &VersionControlEditorPlugin::_ssh_public_key_selected)); set_up_ssh_public_key_input_hbc->add_child(set_up_ssh_public_key_file_dialog); @@ -1183,8 +1182,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { set_up_ssh_private_key_file_dialog->set_access(FileDialog::ACCESS_FILESYSTEM); set_up_ssh_private_key_file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE); set_up_ssh_private_key_file_dialog->set_show_hidden_files(true); - // TODO: Make this start at the user's home folder - set_up_ssh_private_key_file_dialog->set_current_dir(d->get_current_dir()); + set_up_ssh_private_key_file_dialog->set_current_dir(home_dir); set_up_ssh_private_key_file_dialog->connect("file_selected", callable_mp(this, &VersionControlEditorPlugin::_ssh_private_key_selected)); set_up_ssh_private_key_input_hbc->add_child(set_up_ssh_private_key_file_dialog); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 2af8da02a3..ee8148f00a 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -5198,9 +5198,9 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Tangent", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "tangent", "TANGENT"), { "tangent" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Vertex", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "vertex", "VERTEX"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("View", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view", "VIEW"), { "view" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); - add_options.push_back(AddOption("ViewIndex", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_index", "VIEW_INDEX"), { "view_index" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); - add_options.push_back(AddOption("ViewMonoLeft", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_mono_left", "VIEW_MONO_LEFT"), { "view_mono_left" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); - add_options.push_back(AddOption("ViewRight", "Input/Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_right", "VIEW_RIGHT"), { "view_right" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("ViewIndex", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_index", "VIEW_INDEX"), { "view_index" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("ViewMonoLeft", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_mono_left", "VIEW_MONO_LEFT"), { "view_mono_left" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("ViewRight", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "view_right", "VIEW_RIGHT"), { "view_right" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("NodePositionWorld", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "node_position_world", "NODE_POSITION_WORLD"), { "node_position_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("CameraPositionWorld", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "camera_position_world", "CAMERA_POSITION_WORLD"), { "camera_position_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("CameraDirectionWorld", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "camera_direction_world", "CAMERA_DIRECTION_WORLD"), { "camera_direction_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 39b30b31fb..8df59c286c 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -38,6 +38,7 @@ const int ERROR_CODE = 77; #include "modules/regex/regex.h" +#include "core/io/dir_access.h" #include "core/os/time.h" #include "core/templates/hash_map.h" #include "core/templates/list.h" @@ -2229,22 +2230,23 @@ Vector<String> ProjectConverter3To4::check_for_files() { Vector<String> directories_to_check = Vector<String>(); directories_to_check.push_back("res://"); - core_bind::Directory dir = core_bind::Directory(); while (!directories_to_check.is_empty()) { String path = directories_to_check.get(directories_to_check.size() - 1); // Is there any pop_back function? - directories_to_check.resize(directories_to_check.size() - 1); // Remove last element. - if (dir.open(path) == OK) { - dir.set_include_hidden(true); - dir.list_dir_begin(); - String current_dir = dir.get_current_dir(); - String file_name = dir.get_next(); + directories_to_check.resize(directories_to_check.size() - 1); // Remove last element + + Ref<DirAccess> dir = DirAccess::create_for_path(path); + if (dir.is_valid()) { + dir->set_include_hidden(true); + dir->list_dir_begin(); + String current_dir = dir->get_current_dir(); + String file_name = dir->_get_next(); while (file_name != "") { if (file_name == ".git" || file_name == ".import" || file_name == ".godot") { - file_name = dir.get_next(); + file_name = dir->_get_next(); continue; } - if (dir.current_is_dir()) { + if (dir->current_is_dir()) { directories_to_check.append(current_dir.path_join(file_name) + "/"); } else { bool proper_extension = false; @@ -2255,7 +2257,7 @@ Vector<String> ProjectConverter3To4::check_for_files() { collected_files.append(current_dir.path_join(file_name)); } } - file_name = dir.get_next(); + file_name = dir->_get_next(); } } else { print_verbose("Failed to open " + path); @@ -3950,6 +3952,8 @@ String ProjectConverter3To4::collect_string_from_vector(Vector<String> &vector) #else // No RegEx. +ProjectConverter3To4::ProjectConverter3To4(int _p_maximum_file_size_kb, int _p_maximum_line_length) {} + int ProjectConverter3To4::convert() { ERR_FAIL_V_MSG(ERROR_CODE, "Can't run converter for Godot 3.x projects, because RegEx module is disabled."); } diff --git a/editor/project_converter_3_to_4.h b/editor/project_converter_3_to_4.h index 2cecb9da79..d03e645ac7 100644 --- a/editor/project_converter_3_to_4.h +++ b/editor/project_converter_3_to_4.h @@ -31,7 +31,6 @@ #ifndef PROJECT_CONVERTER_3_TO_4_H #define PROJECT_CONVERTER_3_TO_4_H -#include "core/core_bind.h" #include "core/io/file_access.h" #include "core/object/ref_counted.h" #include "core/string/ustring.h" diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 270e04050e..69a2670418 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -59,6 +59,8 @@ #include "servers/navigation_server_3d.h" #include "servers/physics_server_2d.h" +constexpr int GODOT4_CONFIG_VERSION = 5; + class ProjectDialog : public ConfirmationDialog { GDCLASS(ProjectDialog, ConfirmationDialog); @@ -1065,6 +1067,7 @@ public: int refresh_project(const String &dir_path); void add_project(const String &dir_path, bool favorite); void save_config(); + void set_project_version(const String &p_project_path, int version); private: static void _bind_methods(); @@ -1673,6 +1676,15 @@ void ProjectList::save_config() { _config.save(_config_path); } +void ProjectList::set_project_version(const String &p_project_path, int p_version) { + for (ProjectList::Item &E : _projects) { + if (E.path == p_project_path) { + E.version = p_version; + break; + } + } +} + int ProjectList::get_project_count() const { return _projects.size(); } @@ -2152,9 +2164,11 @@ void ProjectManager::_open_selected_projects_ask() { return; } + const Size2i popup_min_width = Size2i(600.0 * EDSCALE, 0); + if (selected_list.size() > 1) { - multi_open_ask->set_text(TTR("Are you sure to open more than one project?")); - multi_open_ask->popup_centered(); + multi_open_ask->set_text(vformat(TTR("You requested to open %d projects in parallel. Do you confirm?\nNote that usual checks for engine version compatibility will be bypassed."), selected_list.size())); + multi_open_ask->popup_centered(popup_min_width); return; } @@ -2163,30 +2177,40 @@ void ProjectManager::_open_selected_projects_ask() { return; } - // Update the project settings or don't open - const String conf = project.path.path_join("project.godot"); + // Update the project settings or don't open. const int config_version = project.version; PackedStringArray unsupported_features = project.unsupported_features; Label *ask_update_label = ask_update_settings->get_label(); ask_update_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT); // Reset in case of previous center align. + full_convert_button->hide(); - // Check if the config_version property was empty or 0 + ask_update_settings->get_ok_button()->set_text("OK"); + + // Check if the config_version property was empty or 0. if (config_version == 0) { - ask_update_settings->set_text(vformat(TTR("The following project settings file does not specify the version of Godot through which it was created.\n\n%s\n\nIf you proceed with opening it, it will be converted to Godot's current configuration file format.\nWarning: You won't be able to open the project with previous versions of the engine anymore."), conf)); - ask_update_settings->popup_centered(); + ask_update_settings->set_text(vformat(TTR("The selected project \"%s\" does not specify its supported Godot version in its configuration file (\"project.godot\").\n\nProject path: %s\n\nIf you proceed with opening it, it will be converted to Godot's current configuration file format.\n\nWarning: You won't be able to open the project with previous versions of the engine anymore."), project.project_name, project.path)); + ask_update_settings->popup_centered(popup_min_width); return; } - // Check if we need to convert project settings from an earlier engine version + // Check if we need to convert project settings from an earlier engine version. if (config_version < ProjectSettings::CONFIG_VERSION) { - ask_update_settings->set_text(vformat(TTR("The following project settings file was generated by an older engine version, and needs to be converted for this version:\n\n%s\n\nDo you want to convert it?\nWarning: You won't be able to open the project with previous versions of the engine anymore."), conf)); - ask_update_settings->popup_centered(); + if (config_version == GODOT4_CONFIG_VERSION - 1 && ProjectSettings::CONFIG_VERSION == GODOT4_CONFIG_VERSION) { // Conversion from Godot 3 to 4. + full_convert_button->show(); + ask_update_settings->set_text(vformat(TTR("The selected project \"%s\" was generated by Godot 3.x, and needs to be converted for Godot 4.x.\n\nProject path: %s\n\nYou have three options:\n- Convert only the configuration file (\"project.godot\"). Use this to open the project without attempting to convert its scenes, resources and scripts.\n- Convert the entire project including its scenes, resources and scripts (recommended if you are upgrading).\n- Do nothing and go back.\n\nWarning: If you select a conversion option, you won't be able to open the project with previous versions of the engine anymore."), project.project_name, project.path)); + ask_update_settings->get_ok_button()->set_text(TTR("Convert project.godot Only")); + } else { + ask_update_settings->set_text(vformat(TTR("The selected project \"%s\" was generated by an older engine version, and needs to be converted for this version.\n\nProject path: %s\n\nDo you want to convert it?\n\nWarning: You won't be able to open the project with previous versions of the engine anymore."), project.project_name, project.path)); + ask_update_settings->get_ok_button()->set_text(TTR("Convert project.godot")); + } + ask_update_settings->popup_centered(popup_min_width); + ask_update_settings->get_cancel_button()->grab_focus(); // To prevent accidents. return; } - // Check if the file was generated by a newer, incompatible engine version + // Check if the file was generated by a newer, incompatible engine version. if (config_version > ProjectSettings::CONFIG_VERSION) { - dialog_error->set_text(vformat(TTR("Can't open project at '%s'.") + "\n" + TTR("The project settings were created by a newer engine version, whose settings are not compatible with this version."), project.path)); - dialog_error->popup_centered(); + dialog_error->set_text(vformat(TTR("Can't open project \"%s\" at the following path:\n\n%s\n\nThe project settings were created by a newer engine version, whose settings are not compatible with this version."), project.project_name, project.path)); + dialog_error->popup_centered(popup_min_width); return; } // Check if the project is using features not supported by this build of Godot. @@ -2215,14 +2239,46 @@ void ProjectManager::_open_selected_projects_ask() { warning_message += TTR("Open anyway? Project will be modified."); ask_update_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); ask_update_settings->set_text(warning_message); - ask_update_settings->popup_centered(); + ask_update_settings->popup_centered(popup_min_width); return; } - // Open if the project is up-to-date + // Open if the project is up-to-date. _open_selected_projects(); } +void ProjectManager::_full_convert_button_pressed() { + ask_update_settings->hide(); + ask_full_convert_dialog->popup_centered(Size2i(600.0 * EDSCALE, 0)); + ask_full_convert_dialog->get_cancel_button()->grab_focus(); +} + +void ProjectManager::_perform_full_project_conversion() { + Vector<ProjectList::Item> selected_list = _project_list->get_selected_projects(); + if (selected_list.is_empty()) { + return; + } + + const String &path = selected_list[0].path; + + print_line("Converting project: " + path); + + Ref<ConfigFile> cf; + cf.instantiate(); + cf->load(path.path_join("project.godot")); + cf->set_value("", "config_version", GODOT4_CONFIG_VERSION); + cf->save(path.path_join("project.godot")); + _project_list->set_project_version(path, GODOT4_CONFIG_VERSION); + + List<String> args; + args.push_back("--path"); + args.push_back(path); + args.push_back("--convert-3to4"); + + Error err = OS::get_singleton()->create_instance(args); + ERR_FAIL_COND(err); +} + void ProjectManager::_run_project_confirm() { Vector<ProjectList::Item> selected_list = _project_list->get_selected_projects(); @@ -2724,9 +2780,10 @@ ProjectManager::ProjectManager() { settings_hb->add_child(h_spacer); language_btn = memnew(OptionButton); - language_btn->set_flat(true); language_btn->set_icon(get_theme_icon(SNAME("Environment"), SNAME("EditorIcons"))); language_btn->set_focus_mode(Control::FOCUS_NONE); + language_btn->set_fit_to_longest_item(false); + language_btn->set_flat(true); language_btn->connect("item_selected", callable_mp(this, &ProjectManager::_language_selected)); #ifdef ANDROID_ENABLED // The language selection dropdown doesn't work on Android (as the setting isn't saved), see GH-60353. @@ -2823,9 +2880,18 @@ ProjectManager::ProjectManager() { add_child(multi_scan_ask); ask_update_settings = memnew(ConfirmationDialog); + ask_update_settings->set_autowrap(true); ask_update_settings->get_ok_button()->connect("pressed", callable_mp(this, &ProjectManager::_confirm_update_settings)); + full_convert_button = ask_update_settings->add_button("Convert Full Project", !GLOBAL_GET("gui/common/swap_cancel_ok")); + full_convert_button->connect("pressed", callable_mp(this, &ProjectManager::_full_convert_button_pressed)); add_child(ask_update_settings); + ask_full_convert_dialog = memnew(ConfirmationDialog); + ask_full_convert_dialog->set_autowrap(true); + ask_full_convert_dialog->set_text(TTR("This option will perform full project conversion, updating scenes, resources and scripts from Godot 3.x to work in Godot 4.0.\n\nNote that this is a best-effort conversion, i.e. it makes upgrading the project easier, but it will not open out-of-the-box and will still require manual adjustments.\n\nIMPORTANT: Make sure to backup your project before converting, as this operation makes it impossible to open it in older versions of Godot.")); + ask_full_convert_dialog->connect("confirmed", callable_mp(this, &ProjectManager::_perform_full_project_conversion)); + add_child(ask_full_convert_dialog); + npdialog = memnew(ProjectDialog); npdialog->connect("projects_updated", callable_mp(this, &ProjectManager::_on_projects_updated)); npdialog->connect("project_created", callable_mp(this, &ProjectManager::_on_project_created)); diff --git a/editor/project_manager.h b/editor/project_manager.h index 10bf25c048..7c05429dde 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -87,6 +87,7 @@ class ProjectManager : public Control { ConfirmationDialog *multi_open_ask = nullptr; ConfirmationDialog *multi_run_ask = nullptr; ConfirmationDialog *multi_scan_ask = nullptr; + ConfirmationDialog *ask_full_convert_dialog = nullptr; ConfirmationDialog *ask_update_settings = nullptr; ConfirmationDialog *open_templates = nullptr; EditorAbout *about = nullptr; @@ -97,6 +98,7 @@ class ProjectManager : public Control { AcceptDialog *dialog_error = nullptr; ProjectDialog *npdialog = nullptr; + Button *full_convert_button = nullptr; OptionButton *language_btn = nullptr; LinkButton *version_btn = nullptr; @@ -106,6 +108,8 @@ class ProjectManager : public Control { void _run_project_confirm(); void _open_selected_projects(); void _open_selected_projects_ask(); + void _full_convert_button_pressed(); + void _perform_full_project_conversion(); void _import_project(); void _new_project(); void _rename_project(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index a437245c57..8a47e40f16 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -916,6 +916,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { String existing; if (extensions.size()) { String root_name(tocopy->get_name()); + root_name = EditorNode::adjust_scene_name_casing(root_name); existing = root_name + "." + extensions.front()->get().to_lower(); } new_scene_from_dialog->set_current_path(existing); @@ -1235,14 +1236,14 @@ void SceneTreeDock::_notification(int p_what) { CanvasItemEditorPlugin *canvas_item_plugin = Object::cast_to<CanvasItemEditorPlugin>(editor_data->get_editor("2D")); if (canvas_item_plugin) { - canvas_item_plugin->get_canvas_item_editor()->connect("item_lock_status_changed", Callable(scene_tree, "_update_tree")); - canvas_item_plugin->get_canvas_item_editor()->connect("item_group_status_changed", Callable(scene_tree, "_update_tree")); + canvas_item_plugin->get_canvas_item_editor()->connect("item_lock_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree)); + canvas_item_plugin->get_canvas_item_editor()->connect("item_group_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree)); scene_tree->connect("node_changed", callable_mp((CanvasItem *)canvas_item_plugin->get_canvas_item_editor()->get_viewport_control(), &CanvasItem::queue_redraw)); } Node3DEditorPlugin *spatial_editor_plugin = Object::cast_to<Node3DEditorPlugin>(editor_data->get_editor("3D")); - spatial_editor_plugin->get_spatial_editor()->connect("item_lock_status_changed", Callable(scene_tree, "_update_tree")); - spatial_editor_plugin->get_spatial_editor()->connect("item_group_status_changed", Callable(scene_tree, "_update_tree")); + spatial_editor_plugin->get_spatial_editor()->connect("item_lock_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree)); + spatial_editor_plugin->get_spatial_editor()->connect("item_group_status_changed", callable_mp(scene_tree, &SceneTreeEditor::_update_tree)); button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); button_instance->set_icon(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons"))); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index e1509a0b25..6fff2ab7cb 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -1332,7 +1332,7 @@ void SceneTreeEditor::set_connecting_signal(bool p_enable) { } void SceneTreeEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_update_tree"), &SceneTreeEditor::_update_tree, DEFVAL(false)); // Still used by some connect_compat. + ClassDB::bind_method(D_METHOD("_update_tree"), &SceneTreeEditor::_update_tree, DEFVAL(false)); // Still used by UndoRedo. ClassDB::bind_method("_rename_node", &SceneTreeEditor::_rename_node); ClassDB::bind_method("_test_update_tree", &SceneTreeEditor::_test_update_tree); diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h index 28ffa4b11b..8fbc3ab6d6 100644 --- a/editor/scene_tree_editor.h +++ b/editor/scene_tree_editor.h @@ -76,7 +76,6 @@ class SceneTreeEditor : public Control { void _add_nodes(Node *p_node, TreeItem *p_parent); void _test_update_tree(); - void _update_tree(bool p_scroll_to_selected = false); bool _update_filter(TreeItem *p_parent = nullptr, bool p_scroll_to_selected = false); bool _item_matches_all_terms(TreeItem *p_item, PackedStringArray p_terms); void _tree_changed(); @@ -138,6 +137,9 @@ class SceneTreeEditor : public Control { Vector<StringName> valid_types; public: + // Public for use with callable_mp. + void _update_tree(bool p_scroll_to_selected = false); + void set_filter(const String &p_filter); String get_filter() const; diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp index eef0f3eae1..473bbd69d9 100644 --- a/editor/shader_globals_editor.cpp +++ b/editor/shader_globals_editor.cpp @@ -483,7 +483,7 @@ ShaderGlobalsEditor::ShaderGlobalsEditor() { inspector->connect("property_deleted", callable_mp(this, &ShaderGlobalsEditor::_variable_deleted), CONNECT_DEFERRED); interface = memnew(ShaderGlobalsEditorInterface); - interface->connect("var_changed", Callable(this, "_changed")); + interface->connect("var_changed", callable_mp(this, &ShaderGlobalsEditor::_changed)); } ShaderGlobalsEditor::~ShaderGlobalsEditor() { |