From 5afc311783609cdc0d09f191b43372692c86df0d Mon Sep 17 00:00:00 2001 From: Daylily-Zeleen Date: Thu, 2 Feb 2023 15:25:53 +0800 Subject: [PATCH 001/368] Provide ability to override _export_end() in cpp. --- editor/export/editor_export_platform.cpp | 4 +++- editor/export/editor_export_plugin.cpp | 2 ++ editor/export/editor_export_plugin.h | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 4e173799115f..767278bb0930 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -512,8 +512,10 @@ EditorExportPlatform::ExportNotifier::~ExportNotifier() { for (int i = 0; i < export_plugins.size(); i++) { if (export_plugins[i]->get_script_instance()) { export_plugins.write[i]->_export_end_script(); + } else { + export_plugins.write[i]->_export_end(); } - export_plugins.write[i]->_export_end(); + export_plugins.write[i]->_export_end_clear(); export_plugins.write[i]->set_export_preset(Ref()); } } diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp index 4e2c1a9af72a..dd30078c7654 100644 --- a/editor/export/editor_export_plugin.cpp +++ b/editor/export/editor_export_plugin.cpp @@ -222,6 +222,8 @@ void EditorExportPlugin::_export_file(const String &p_path, const String &p_type void EditorExportPlugin::_export_begin(const HashSet &p_features, bool p_debug, const String &p_path, int p_flags) { } +void EditorExportPlugin::_export_end() {} + void EditorExportPlugin::skip() { skipped = true; } diff --git a/editor/export/editor_export_plugin.h b/editor/export/editor_export_plugin.h index 120141b34797..1a985e4c0fb7 100644 --- a/editor/export/editor_export_plugin.h +++ b/editor/export/editor_export_plugin.h @@ -70,7 +70,7 @@ class EditorExportPlugin : public RefCounted { skipped = false; } - _FORCE_INLINE_ void _export_end() { + _FORCE_INLINE_ void _export_end_clear() { ios_frameworks.clear(); ios_embedded_frameworks.clear(); ios_bundle_files.clear(); @@ -105,6 +105,7 @@ class EditorExportPlugin : public RefCounted { virtual void _export_file(const String &p_path, const String &p_type, const HashSet &p_features); virtual void _export_begin(const HashSet &p_features, bool p_debug, const String &p_path, int p_flags); + virtual void _export_end(); static void _bind_methods(); From c6e895050dc59d42d92e1f30f1cf847d1665c442 Mon Sep 17 00:00:00 2001 From: kleonc <9283098+kleonc@users.noreply.github.com> Date: Sun, 2 Jul 2023 20:44:42 +0200 Subject: [PATCH 002/368] Make editor inspector follow focus --- doc/classes/EditorInspector.xml | 1 + editor/editor_inspector.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml index 377a3b5a6af9..af4a006d3390 100644 --- a/doc/classes/EditorInspector.xml +++ b/doc/classes/EditorInspector.xml @@ -22,6 +22,7 @@ + diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index e3514b4691bb..3395068decb4 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -4189,6 +4189,7 @@ EditorInspector::EditorInspector() { main_vbox->add_theme_constant_override("separation", 0); add_child(main_vbox); set_horizontal_scroll_mode(SCROLL_MODE_DISABLED); + set_follow_focus(true); changing = 0; search_box = nullptr; From a31120b6f615b39f517a8b515d1da172afd25930 Mon Sep 17 00:00:00 2001 From: Septian Date: Sun, 20 Aug 2023 20:56:27 +0700 Subject: [PATCH 003/368] Improve PackedScene unit test by covering more methods --- tests/scene/test_packed_scene.h | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/scene/test_packed_scene.h b/tests/scene/test_packed_scene.h index 3517aba31fcb..1e784c199da8 100644 --- a/tests/scene/test_packed_scene.h +++ b/tests/scene/test_packed_scene.h @@ -150,6 +150,71 @@ TEST_CASE("[PackedScene] Instantiate Packed Scene With Children") { memdelete(instance); } +TEST_CASE("[PackedScene] Set Path") { + // Create a scene to pack. + Node *scene = memnew(Node); + scene->set_name("TestScene"); + + // Pack the scene. + PackedScene packed_scene; + packed_scene.pack(scene); + + // Set a new path for the packed scene. + const String new_path = "NewTestPath"; + packed_scene.set_path(new_path); + + // Check if the path has been set correctly. + Ref state = packed_scene.get_state(); + CHECK(state.is_valid()); + CHECK(state->get_path() == new_path); + + memdelete(scene); +} + +TEST_CASE("[PackedScene] Replace State") { + // Create a scene to pack. + Node *scene = memnew(Node); + scene->set_name("TestScene"); + + // Pack the scene. + PackedScene packed_scene; + packed_scene.pack(scene); + + // Create another scene state to replace with. + Ref new_state = memnew(SceneState); + new_state->set_path("NewPath"); + + // Replace the state. + packed_scene.replace_state(new_state); + + // Check if the state has been replaced. + Ref state = packed_scene.get_state(); + CHECK(state.is_valid()); + CHECK(state == new_state); + + memdelete(scene); +} + +TEST_CASE("[PackedScene] Recreate State") { + // Create a scene to pack. + Node *scene = memnew(Node); + scene->set_name("TestScene"); + + // Pack the scene. + PackedScene packed_scene; + packed_scene.pack(scene); + + // Recreate the state. + packed_scene.recreate_state(); + + // Check if the state has been recreated. + Ref state = packed_scene.get_state(); + CHECK(state.is_valid()); + CHECK(state->get_node_count() == 0); // Since the state was recreated, it should be empty. + + memdelete(scene); +} + } // namespace TestPackedScene #endif // TEST_PACKED_SCENE_H From 0095205917d97e4c4b4af2d5346ae295dda2d7fc Mon Sep 17 00:00:00 2001 From: MewPurPur Date: Sun, 27 Aug 2023 16:26:24 +0300 Subject: [PATCH 004/368] Add PhysicsMaterial icon --- editor/icons/PhysicsMaterial.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 editor/icons/PhysicsMaterial.svg diff --git a/editor/icons/PhysicsMaterial.svg b/editor/icons/PhysicsMaterial.svg new file mode 100644 index 000000000000..bdb41a1d2750 --- /dev/null +++ b/editor/icons/PhysicsMaterial.svg @@ -0,0 +1 @@ + From c7d0565681ab4aa40748056f4f4b83250b4af431 Mon Sep 17 00:00:00 2001 From: Michael Alexsander Date: Mon, 25 Sep 2023 01:09:29 -0300 Subject: [PATCH 005/368] Make the search bars in the "Project Settings" dialog grab focus when they appear --- editor/action_map_editor.cpp | 2 +- editor/editor_build_profile.cpp | 2 +- editor/editor_settings_dialog.cpp | 5 +---- editor/event_listener_line_edit.cpp | 6 +++--- editor/project_settings_editor.cpp | 25 +++++++++++++++++++++++-- editor/project_settings_editor.h | 3 +++ 6 files changed, 32 insertions(+), 11 deletions(-) diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index d47b315c4077..e8ca3cc3e2f2 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -532,7 +532,7 @@ ActionMapEditor::ActionMapEditor() { action_list_search = memnew(LineEdit); action_list_search->set_h_size_flags(Control::SIZE_EXPAND_FILL); - action_list_search->set_placeholder(TTR("Filter by name...")); + action_list_search->set_placeholder(TTR("Filter by Name")); action_list_search->set_clear_button_enabled(true); action_list_search->connect("text_changed", callable_mp(this, &ActionMapEditor::_search_term_updated)); top_hbox->add_child(action_list_search); diff --git a/editor/editor_build_profile.cpp b/editor/editor_build_profile.cpp index bca30b2d2d19..dc06bc228b25 100644 --- a/editor/editor_build_profile.cpp +++ b/editor/editor_build_profile.cpp @@ -891,7 +891,7 @@ EditorBuildProfileManager::EditorBuildProfileManager() { export_profile->set_access(EditorFileDialog::ACCESS_FILESYSTEM); force_detect_classes = memnew(LineEdit); - main_vbc->add_margin_child(TTR("Forced classes on detect:"), force_detect_classes); + main_vbc->add_margin_child(TTR("Forced Classes on Detect:"), force_detect_classes); force_detect_classes->connect("text_changed", callable_mp(this, &EditorBuildProfileManager::_force_detect_classes_changed)); set_title(TTR("Edit Build Configuration Profile")); diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp index a5e70c5b6ca2..33a176499d17 100644 --- a/editor/editor_settings_dialog.cpp +++ b/editor/editor_settings_dialog.cpp @@ -92,9 +92,6 @@ void EditorSettingsDialog::popup_edit_settings() { inspector->edit(EditorSettings::get_singleton()); inspector->get_inspector()->update_tree(); - search_box->select_all(); - search_box->grab_focus(); - _update_shortcuts(); set_process_shortcut_input(true); @@ -762,7 +759,7 @@ EditorSettingsDialog::EditorSettingsDialog() { tab_shortcuts->add_child(top_hbox); shortcut_search_box = memnew(LineEdit); - shortcut_search_box->set_placeholder(TTR("Filter by name...")); + shortcut_search_box->set_placeholder(TTR("Filter by Name")); shortcut_search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); top_hbox->add_child(shortcut_search_box); shortcut_search_box->connect("text_changed", callable_mp(this, &EditorSettingsDialog::_filter_shortcuts)); diff --git a/editor/event_listener_line_edit.cpp b/editor/event_listener_line_edit.cpp index e51808c78c17..2c46e1c20ad2 100644 --- a/editor/event_listener_line_edit.cpp +++ b/editor/event_listener_line_edit.cpp @@ -175,12 +175,12 @@ void EventListenerLineEdit::_on_text_changed(const String &p_text) { } void EventListenerLineEdit::_on_focus() { - set_placeholder(TTR("Listening for input...")); + set_placeholder(TTR("Listening for Input")); } void EventListenerLineEdit::_on_unfocus() { ignore_next_event = true; - set_placeholder(TTR("Filter by event...")); + set_placeholder(TTR("Filter by Event")); } Ref EventListenerLineEdit::get_event() const { @@ -227,5 +227,5 @@ void EventListenerLineEdit::_bind_methods() { EventListenerLineEdit::EventListenerLineEdit() { set_caret_blink_enabled(false); - set_placeholder(TTR("Filter by event...")); + set_placeholder(TTR("Filter by Event")); } diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 09de9cda4909..0bcfdc5f97e9 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -68,6 +68,8 @@ void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) { if (p_clear_filter) { search_box->clear(); } + + _focus_current_search_box(); } void ProjectSettingsEditor::queue_save() { @@ -253,8 +255,7 @@ void ProjectSettingsEditor::shortcut_input(const Ref &p_event) { } if (k->is_match(InputEventKey::create_reference(KeyModifierMask::CMD_OR_CTRL | Key::F))) { - search_box->grab_focus(); - search_box->select_all(); + _focus_current_search_box(); handled = true; } @@ -326,6 +327,25 @@ void ProjectSettingsEditor::_add_feature_overrides() { } } +void ProjectSettingsEditor::_tabs_tab_changed(int p_tab) { + _focus_current_search_box(); +} + +void ProjectSettingsEditor::_focus_current_search_box() { + Control *tab = tab_container->get_current_tab_control(); + LineEdit *current_search_box = nullptr; + if (tab == general_editor) { + current_search_box = search_box; + } else if (tab == action_map_editor) { + current_search_box = action_map_editor->get_search_box(); + } + + if (current_search_box) { + current_search_box->grab_focus(); + current_search_box->select_all(); + } +} + void ProjectSettingsEditor::_editor_restart() { ProjectSettings::get_singleton()->save(); EditorNode::get_singleton()->save_all_scenes(); @@ -596,6 +616,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { tab_container = memnew(TabContainer); tab_container->set_use_hidden_tabs_for_min_size(true); tab_container->set_theme_type_variation("TabContainerOdd"); + tab_container->connect("tab_changed", callable_mp(this, &ProjectSettingsEditor::_tabs_tab_changed)); add_child(tab_container); general_editor = memnew(VBoxContainer); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index eaac7e8c5a19..1205bf6ee979 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -93,6 +93,9 @@ class ProjectSettingsEditor : public AcceptDialog { void _add_setting(); void _delete_setting(); + void _tabs_tab_changed(int p_tab); + void _focus_current_search_box(); + void _editor_restart_request(); void _editor_restart(); void _editor_restart_close(); From 0506d6f19279b54bf1464a77614fc9b27898aaf5 Mon Sep 17 00:00:00 2001 From: aXu-AP <1621768+aXu-AP@users.noreply.github.com> Date: Fri, 29 Sep 2023 00:03:18 +0300 Subject: [PATCH 006/368] Fix opening docs writing extra navigation history Fix #82292 Removes extraneous call to change the tab if the page wasn't yet open when opening class member description. --- editor/plugins/script_editor_plugin.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 901ac91a468d..dbbb34614dcc 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3378,7 +3378,6 @@ void ScriptEditor::_help_class_goto(const String &p_desc) { eh->set_name(cname); tab_container->add_child(eh); - _go_to_tab(tab_container->get_tab_count() - 1); eh->go_to_help(p_desc); eh->connect("go_to_help", callable_mp(this, &ScriptEditor::_help_class_goto)); _add_recent_script(eh->get_class()); From afe25937e4ff1938447ea8f7c6b2c3ce9bf38e2e Mon Sep 17 00:00:00 2001 From: "Silc Lizard (Tokage) Renew" <61938263+TokageItLab@users.noreply.github.com> Date: Mon, 1 May 2023 03:20:23 +0900 Subject: [PATCH 007/368] Add useful functions to FilterEdit in AnimationBlendTreeEditor --- .../animation_blend_tree_editor_plugin.cpp | 125 +++++++++++++++++- .../animation_blend_tree_editor_plugin.h | 9 ++ 2 files changed, 133 insertions(+), 1 deletion(-) diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index dc3729393f04..cffd1811b940 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -610,6 +610,111 @@ void AnimationNodeBlendTreeEditor::_filter_edited() { updating = false; } +void AnimationNodeBlendTreeEditor::_filter_fill_selection() { + TreeItem *ti = filters->get_root(); + if (!ti) { + return; + } + + updating = true; + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Fill Selected Filter Children")); + + _filter_fill_selection_recursive(undo_redo, ti, false); + + undo_redo->add_do_method(this, "_update_filters", _filter_edit); + undo_redo->add_undo_method(this, "_update_filters", _filter_edit); + undo_redo->commit_action(); + updating = false; + + _update_filters(_filter_edit); +} + +void AnimationNodeBlendTreeEditor::_filter_invert_selection() { + TreeItem *ti = filters->get_root(); + if (!ti) { + return; + } + + updating = true; + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Invert Filter Selection")); + + _filter_invert_selection_recursive(undo_redo, ti); + + undo_redo->add_do_method(this, "_update_filters", _filter_edit); + undo_redo->add_undo_method(this, "_update_filters", _filter_edit); + undo_redo->commit_action(); + updating = false; + + _update_filters(_filter_edit); +} + +void AnimationNodeBlendTreeEditor::_filter_clear_selection() { + TreeItem *ti = filters->get_root(); + if (!ti) { + return; + } + + updating = true; + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Clear Filter Selection")); + + _filter_clear_selection_recursive(undo_redo, ti); + + undo_redo->add_do_method(this, "_update_filters", _filter_edit); + undo_redo->add_undo_method(this, "_update_filters", _filter_edit); + undo_redo->commit_action(); + updating = false; + + _update_filters(_filter_edit); +} + +void AnimationNodeBlendTreeEditor::_filter_fill_selection_recursive(EditorUndoRedoManager *p_undo_redo, TreeItem *p_item, bool p_parent_filtered) { + TreeItem *ti = p_item->get_first_child(); + bool parent_filtered = p_parent_filtered; + while (ti) { + NodePath item_path = ti->get_metadata(0); + bool filtered = _filter_edit->is_path_filtered(item_path); + parent_filtered |= filtered; + + p_undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_path", item_path, parent_filtered); + p_undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_path", item_path, filtered); + + _filter_fill_selection_recursive(p_undo_redo, ti, parent_filtered); + ti = ti->get_next(); + parent_filtered = p_parent_filtered; + } +} + +void AnimationNodeBlendTreeEditor::_filter_invert_selection_recursive(EditorUndoRedoManager *p_undo_redo, TreeItem *p_item) { + TreeItem *ti = p_item->get_first_child(); + while (ti) { + NodePath item_path = ti->get_metadata(0); + bool filtered = _filter_edit->is_path_filtered(item_path); + + p_undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_path", item_path, !filtered); + p_undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_path", item_path, filtered); + + _filter_invert_selection_recursive(p_undo_redo, ti); + ti = ti->get_next(); + } +} + +void AnimationNodeBlendTreeEditor::_filter_clear_selection_recursive(EditorUndoRedoManager *p_undo_redo, TreeItem *p_item) { + TreeItem *ti = p_item->get_first_child(); + while (ti) { + NodePath item_path = ti->get_metadata(0); + bool filtered = _filter_edit->is_path_filtered(item_path); + + p_undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_path", item_path, false); + p_undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_path", item_path, filtered); + + _filter_clear_selection_recursive(p_undo_redo, ti); + ti = ti->get_next(); + } +} + bool AnimationNodeBlendTreeEditor::_update_filters(const Ref &anode) { if (updating || _filter_edit != anode) { return false; @@ -1108,10 +1213,28 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { VBoxContainer *filter_vbox = memnew(VBoxContainer); filter_dialog->add_child(filter_vbox); + HBoxContainer *filter_hbox = memnew(HBoxContainer); + filter_vbox->add_child(filter_hbox); + filter_enabled = memnew(CheckBox); filter_enabled->set_text(TTR("Enable Filtering")); filter_enabled->connect("pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_filter_toggled)); - filter_vbox->add_child(filter_enabled); + filter_hbox->add_child(filter_enabled); + + filter_fill_selection = memnew(Button); + filter_fill_selection->set_text(TTR("Fill Selected Children")); + filter_fill_selection->connect("pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_filter_fill_selection)); + filter_hbox->add_child(filter_fill_selection); + + filter_invert_selection = memnew(Button); + filter_invert_selection->set_text(TTR("Invert")); + filter_invert_selection->connect("pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_filter_invert_selection)); + filter_hbox->add_child(filter_invert_selection); + + filter_clear_selection = memnew(Button); + filter_clear_selection->set_text(TTR("Clear")); + filter_clear_selection->connect("pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_filter_clear_selection)); + filter_hbox->add_child(filter_clear_selection); filters = memnew(Tree); filter_vbox->add_child(filters); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index 12b709515f69..e54a98cda850 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -66,6 +66,9 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { AcceptDialog *filter_dialog = nullptr; Tree *filters = nullptr; CheckBox *filter_enabled = nullptr; + Button *filter_fill_selection = nullptr; + Button *filter_invert_selection = nullptr; + Button *filter_clear_selection = nullptr; HashMap animations; Vector visible_properties; @@ -116,6 +119,12 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _inspect_filters(const String &p_which); void _filter_edited(); void _filter_toggled(); + void _filter_fill_selection(); + void _filter_invert_selection(); + void _filter_clear_selection(); + void _filter_fill_selection_recursive(EditorUndoRedoManager *p_undo_redo, TreeItem *p_item, bool p_parent_filtered); + void _filter_invert_selection_recursive(EditorUndoRedoManager *p_undo_redo, TreeItem *p_item); + void _filter_clear_selection_recursive(EditorUndoRedoManager *p_undo_redo, TreeItem *p_item); Ref _filter_edit; void _popup(bool p_has_input_ports, const Vector2 &p_node_position); From 5efbed51cce62cdd9a2927638030e76bf688cdf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Verschelde?= Date: Wed, 5 Oct 2022 20:49:35 +0200 Subject: [PATCH 008/368] GDScript: Improve error messages for invalid indexing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These errors are very common when using an invalid property name or calling on an object of the wrong type, and the previous message was a bit cryptic for users. Co-authored-by: Rémi Verschelde Co-authored-by: golfinq --- core/variant/variant.h | 17 +++++-- core/variant/variant_setget.cpp | 40 ++++++++++++++++- modules/gdscript/gdscript_analyzer.cpp | 2 +- modules/gdscript/gdscript_vm.cpp | 45 ++++++++++++------- .../analyzer/errors/outer_class_constants.out | 2 +- .../outer_class_constants_as_variant.out | 2 +- .../errors/outer_class_instance_constants.out | 2 +- ...er_class_instance_constants_as_variant.out | 2 +- .../runtime/errors/constant_array_is_deep.out | 2 +- .../errors/constant_dictionary_is_deep.out | 2 +- 10 files changed, 87 insertions(+), 29 deletions(-) diff --git a/core/variant/variant.h b/core/variant/variant.h index d698f857546a..190f4e130d0d 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -703,9 +703,20 @@ class Variant { bool has_key(const Variant &p_key, bool &r_valid) const; /* Generic */ - - void set(const Variant &p_index, const Variant &p_value, bool *r_valid = nullptr); - Variant get(const Variant &p_index, bool *r_valid = nullptr) const; + enum VariantSetError { + SET_OK, + SET_KEYED_ERR, + SET_NAMED_ERR, + SET_INDEXED_ERR + }; + enum VariantGetError { + GET_OK, + GET_KEYED_ERR, + GET_NAMED_ERR, + GET_INDEXED_ERR + }; + void set(const Variant &p_index, const Variant &p_value, bool *r_valid = nullptr, VariantSetError *err_code = nullptr); + Variant get(const Variant &p_index, bool *r_valid = nullptr, VariantGetError *err_code = nullptr) const; bool in(const Variant &p_index, bool *r_valid = nullptr) const; bool iter_init(Variant &r_iter, bool &r_valid) const; diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp index 05f7abf32c99..40e0601b0f62 100644 --- a/core/variant/variant_setget.cpp +++ b/core/variant/variant_setget.cpp @@ -1166,30 +1166,48 @@ bool Variant::has_key(const Variant &p_key, bool &r_valid) const { } } -void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) { +void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid, VariantSetError *err_code) { + if (err_code) { + *err_code = VariantSetError::SET_OK; + } if (type == DICTIONARY || type == OBJECT) { bool valid; set_keyed(p_index, p_value, valid); if (r_valid) { *r_valid = valid; + if (!valid && err_code) { + *err_code = VariantSetError::SET_KEYED_ERR; + } } } else { bool valid = false; if (p_index.get_type() == STRING_NAME) { set_named(*VariantGetInternalPtr::get_ptr(&p_index), p_value, valid); + if (!valid && err_code) { + *err_code = VariantSetError::SET_NAMED_ERR; + } } else if (p_index.get_type() == INT) { bool obb; set_indexed(*VariantGetInternalPtr::get_ptr(&p_index), p_value, valid, obb); if (obb) { valid = false; + if (err_code) { + *err_code = VariantSetError::SET_INDEXED_ERR; + } } } else if (p_index.get_type() == STRING) { // less efficient version of named set_named(*VariantGetInternalPtr::get_ptr(&p_index), p_value, valid); + if (!valid && err_code) { + *err_code = VariantSetError::SET_NAMED_ERR; + } } else if (p_index.get_type() == FLOAT) { // less efficient version of indexed bool obb; set_indexed(*VariantGetInternalPtr::get_ptr(&p_index), p_value, valid, obb); if (obb) { valid = false; + if (err_code) { + *err_code = VariantSetError::SET_INDEXED_ERR; + } } } if (r_valid) { @@ -1198,31 +1216,49 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) } } -Variant Variant::get(const Variant &p_index, bool *r_valid) const { +Variant Variant::get(const Variant &p_index, bool *r_valid, VariantGetError *err_code) const { + if (err_code) { + *err_code = VariantGetError::GET_OK; + } Variant ret; if (type == DICTIONARY || type == OBJECT) { bool valid; ret = get_keyed(p_index, valid); if (r_valid) { *r_valid = valid; + if (!valid && err_code) { + *err_code = VariantGetError::GET_KEYED_ERR; + } } } else { bool valid = false; if (p_index.get_type() == STRING_NAME) { ret = get_named(*VariantGetInternalPtr::get_ptr(&p_index), valid); + if (!valid && err_code) { + *err_code = VariantGetError::GET_NAMED_ERR; + } } else if (p_index.get_type() == INT) { bool obb; ret = get_indexed(*VariantGetInternalPtr::get_ptr(&p_index), valid, obb); if (obb) { valid = false; + if (err_code) { + *err_code = VariantGetError::GET_INDEXED_ERR; + } } } else if (p_index.get_type() == STRING) { // less efficient version of named ret = get_named(*VariantGetInternalPtr::get_ptr(&p_index), valid); + if (!valid && err_code) { + *err_code = VariantGetError::GET_NAMED_ERR; + } } else if (p_index.get_type() == FLOAT) { // less efficient version of indexed bool obb; ret = get_indexed(*VariantGetInternalPtr::get_ptr(&p_index), valid, obb); if (obb) { valid = false; + if (err_code) { + *err_code = VariantGetError::GET_INDEXED_ERR; + } } } if (r_valid) { diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 882c246706dd..4c55573e5917 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3595,7 +3595,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod switch (base.builtin_type) { case Variant::NIL: { if (base.is_hard_type()) { - push_error(vformat(R"(Invalid get index "%s" on base Nil)", name), p_identifier); + push_error(vformat(R"(Cannot get property "%s" on a null object.)", name), p_identifier); } return; } diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index be18dee2cfea..c0edaf5f6516 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -888,8 +888,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(value, 2); bool valid; +#ifdef DEBUG_ENABLED + Variant::VariantSetError err_code; + dst->set(*index, *value, &valid, &err_code); +#else dst->set(*index, *value, &valid); - +#endif #ifdef DEBUG_ENABLED if (!valid) { Object *obj = dst->get_validated_object(); @@ -906,7 +910,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } else { v = "of type '" + _get_var_type(index) + "'"; } - err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'"; + err_text = "Invalid assignment of property or key " + v + " with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'."; + if (err_code == Variant::VariantSetError::SET_INDEXED_ERR) { + err_text = "Invalid assignment of index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'."; + } } OPCODE_BREAK; } @@ -937,7 +944,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } else { v = "of type '" + _get_var_type(index) + "'"; } - err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'"; + err_text = "Invalid assignment of property or key " + v + " with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'."; OPCODE_BREAK; } #endif @@ -987,7 +994,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a bool valid; #ifdef DEBUG_ENABLED // Allow better error message in cases where src and dst are the same stack position. - Variant ret = src->get(*index, &valid); + Variant::VariantGetError err_code; + Variant ret = src->get(*index, &valid, &err_code); #else *dst = src->get(*index, &valid); @@ -1000,7 +1008,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } else { v = "of type '" + _get_var_type(index) + "'"; } - err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "')."; + err_text = "Invalid access to property or key " + v + " on a base object of type '" + _get_var_type(src) + "'."; + if (err_code == Variant::VariantGetError::GET_INDEXED_ERR) { + err_text = "Invalid access of index " + v + " on a base object of type: '" + _get_var_type(src) + "'."; + } OPCODE_BREAK; } *dst = ret; @@ -1036,7 +1047,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } else { v = "of type '" + _get_var_type(key) + "'"; } - err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "')."; + err_text = "Invalid access to property or key " + v + " on a base object of type '" + _get_var_type(src) + "'."; OPCODE_BREAK; } *dst = ret; @@ -1101,7 +1112,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (read_only_property) { err_text = vformat(R"(Cannot set value into property "%s" (on base "%s") because it is read-only.)", String(*index), _get_var_type(dst)); } else { - err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'."; + err_text = "Invalid assignment of property or key '" + String(*index) + "' with value of type '" + _get_var_type(value) + "' on a base object of type '" + _get_var_type(dst) + "'."; } OPCODE_BREAK; } @@ -1146,7 +1157,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif #ifdef DEBUG_ENABLED if (!valid) { - err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "')."; + err_text = "Invalid access to property or key '" + index->operator String() + "' on a base object of type '" + _get_var_type(src) + "'."; OPCODE_BREAK; } *dst = ret; @@ -2619,7 +2630,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Variant::construct(ret_type, retvalue, const_cast(&r), 1, ce); } else { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)", + err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)", Variant::get_type_name(r->get_type()), Variant::get_type_name(ret_type)); #endif // DEBUG_ENABLED @@ -2649,9 +2660,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (r->get_type() != Variant::ARRAY) { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return a value of type "%s" where expected return type is "Array[%s]".)", - _get_var_type(r), _get_element_type(builtin_type, native_type, *script_type)); -#endif // DEBUG_ENABLED + err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "Array[%s]".)", + Variant::get_type_name(r->get_type()), Variant::get_type_name(builtin_type)); +#endif OPCODE_BREAK; } @@ -2682,7 +2693,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(!nc); if (r->get_type() != Variant::OBJECT && r->get_type() != Variant::NIL) { - err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)", + err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)", Variant::get_type_name(r->get_type()), nc->get_name()); OPCODE_BREAK; } @@ -2700,7 +2711,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif // DEBUG_ENABLED if (ret_obj && !ClassDB::is_parent_class(ret_obj->get_class_name(), nc->get_name())) { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)", + err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)", ret_obj->get_class_name(), nc->get_name()); #endif // DEBUG_ENABLED OPCODE_BREAK; @@ -2723,7 +2734,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (r->get_type() != Variant::OBJECT && r->get_type() != Variant::NIL) { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)", + err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)", Variant::get_type_name(r->get_type()), GDScript::debug_get_script_name(Ref