From c0119037d32b3122a93e884558669bccc1f369b2 Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Fri, 19 Jul 2024 19:29:28 -0400 Subject: [PATCH] GH-537 Fix target resolution * Resolves methods, properties, and signals on nodes & scripts * Correctly assigns target types for global class types * Excludes private properties, prefixed with `_` * Excludes internal methods, prefixed with `@` * Reworks method/properties/signals for global classes --- src/common/method_utils.cpp | 66 +- src/common/method_utils.h | 5 + src/common/scene_utils.cpp | 11 + src/editor/graph/actions/action_menu.cpp | 4 +- .../actions/default_action_registrar.cpp | 580 ++++++++++-------- .../graph/actions/default_action_registrar.h | 101 ++- src/editor/graph/graph_edit.cpp | 8 +- src/editor/graph/graph_node.cpp | 2 +- src/editor/graph/graph_node_pin.cpp | 4 +- src/editor/graph/graph_node_spawner.h | 8 +- .../graph/pins/graph_node_pin_object.cpp | 2 +- src/script/node_pin.cpp | 5 + src/script/nodes/functions/call_function.cpp | 19 +- .../nodes/functions/call_member_function.cpp | 8 +- .../nodes/functions/call_static_function.cpp | 8 +- src/script/nodes/properties/property.cpp | 76 ++- src/script/nodes/properties/property.h | 10 + src/script/nodes/properties/property_get.cpp | 12 +- src/script/nodes/properties/property_set.cpp | 8 +- .../nodes/utilities/engine_singleton.cpp | 2 +- src/script/script.cpp | 2 +- src/script/script_server.cpp | 158 +++++ src/script/script_server.h | 101 +++ 23 files changed, 869 insertions(+), 331 deletions(-) create mode 100644 src/script/script_server.cpp create mode 100644 src/script/script_server.h diff --git a/src/common/method_utils.cpp b/src/common/method_utils.cpp index 6868ffb0..2046779d 100644 --- a/src/common/method_utils.cpp +++ b/src/common/method_utils.cpp @@ -16,6 +16,10 @@ // #include "common/method_utils.h" +#include "dictionary_utils.h" +#include "property_utils.h" +#include "script/script_server.h" + #include namespace MethodUtils @@ -60,7 +64,12 @@ namespace MethodUtils String class_name = p_class_name; while (!class_name.is_empty()) { - TypedArray methods = ClassDB::class_get_method_list(class_name, true); + TypedArray methods; + if (ScriptServer::is_global_class(p_class_name)) + methods = ScriptServer::get_global_class(p_class_name).get_method_list(); + else + ClassDB::class_get_method_list(class_name, true); + for (int i = 0; i < methods.size(); i++) { const Dictionary& method = methods[i]; @@ -71,4 +80,59 @@ namespace MethodUtils } return {}; } + + String get_signature(const MethodInfo& p_method) + { + String signature = p_method.name.replace("_", " ").capitalize() + "\n\n"; + + if (MethodUtils::has_return_value(p_method)) + { + if (PropertyUtils::is_variant(p_method.return_val)) + signature += "Variant"; + else if (p_method.return_val.hint == PROPERTY_HINT_ARRAY_TYPE) + signature += "Array[" + p_method.return_val.hint_string + "]"; + else + signature += Variant::get_type_name(p_method.return_val.type); + } + else + signature += "void"; + + signature += " " + p_method.name + " ("; + + int index = 0; + for (const PropertyInfo& property : p_method.arguments) + { + if (!signature.ends_with("(")) + signature += ", "; + + if (property.name.is_empty()) + signature += "p" + itos(index++); + else + signature += property.name; + + signature += ":" + PropertyUtils::get_property_type_name(property); + } + + if (p_method.flags & METHOD_FLAG_VARARG) + { + if (!p_method.arguments.empty()) + signature += ", "; + signature += "..."; + } + + signature += ")"; + + if (p_method.flags & METHOD_FLAG_CONST) + signature += " const"; + else if (p_method.flags & METHOD_FLAG_VIRTUAL) + signature += " virtual"; + + #if DEBUG_ENABLED + signature += "\n\n"; + signature += vformat("%s", DictionaryUtils::from_method(p_method)); + #endif + + return signature; + } + } \ No newline at end of file diff --git a/src/common/method_utils.h b/src/common/method_utils.h index 7031b8dd..9cbb2174 100644 --- a/src/common/method_utils.h +++ b/src/common/method_utils.h @@ -45,6 +45,11 @@ namespace MethodUtils /// @param p_method_name the name of the method to search for /// @return the class that contains the method, or an empty string if the method is not found String get_method_class(const String& p_class_name, const String& p_method_name); + + /// Generates a method signature based on the specified method. + /// @param p_method the method + /// @return the signature + String get_signature(const MethodInfo& p_method); } #endif // ORCHESTRATOR_METHOD_UTILS_H \ No newline at end of file diff --git a/src/common/scene_utils.cpp b/src/common/scene_utils.cpp index 8c25b557..72551992 100644 --- a/src/common/scene_utils.cpp +++ b/src/common/scene_utils.cpp @@ -17,11 +17,13 @@ #include "scene_utils.h" #include "editor/plugins/orchestrator_editor_plugin.h" +#include "script/script_server.h" #include #include #include #include +#include #include #include #include @@ -50,6 +52,15 @@ namespace SceneUtils return vbox->get_theme_icon(instantiable ? "Object" : "ObjectDisabled", "EditorIcons"); } + if (ScriptServer::is_global_class(p_class_name)) + { + const String icon = ScriptServer::get_global_class(p_class_name).icon_path; + if (!icon.is_empty()) + return ResourceLoader::get_singleton()->load(icon); + + return get_class_icon(ScriptServer::get_native_class_name(p_class_name)); + } + return nullptr; } diff --git a/src/editor/graph/actions/action_menu.cpp b/src/editor/graph/actions/action_menu.cpp index 0b650522..588ca8c7 100644 --- a/src/editor/graph/actions/action_menu.cpp +++ b/src/editor/graph/actions/action_menu.cpp @@ -338,7 +338,7 @@ TreeItem* OrchestratorGraphActionMenu::_make_item(TreeItem* p_parent, TreeItem* child = p_parent->create_child(); child->set_text(0, p_text); child->set_expand_right(0, true); - child->set_icon(0, SceneUtils::get_editor_icon(p_menu_item->get_spec().icon)); + child->set_icon(0, SceneUtils::get_class_icon(p_menu_item->get_spec().icon)); child->set_tooltip_text(0, p_menu_item->get_spec().tooltip); child->set_selectable(0, p_menu_item->get_handler().is_valid()); @@ -450,6 +450,8 @@ void OrchestratorGraphActionMenu::_on_filter_text_changed(const String& p_new_te // Update filters _filter.keywords.clear(); + _on_expand_tree(true); + const String filter_text = p_new_text.trim_prefix(" ").trim_suffix(" "); if (!filter_text.is_empty()) for (const String& element : filter_text.split(" ")) diff --git a/src/editor/graph/actions/default_action_registrar.cpp b/src/editor/graph/actions/default_action_registrar.cpp index aec0a7c6..191abad4 100644 --- a/src/editor/graph/actions/default_action_registrar.cpp +++ b/src/editor/graph/actions/default_action_registrar.cpp @@ -25,16 +25,16 @@ #include "common/variant_utils.h" #include "editor/graph/graph_edit.h" #include "editor/graph/graph_node_spawner.h" +#include "godot_cpp/templates/hash_set.hpp" #include "script/nodes/script_nodes.h" +#include "script/script_server.h" #include -void OrchestratorDefaultGraphActionRegistrar::_register_node(const OrchestratorGraphActionRegistrarContext& p_context, - const StringName& p_class_name, const StringName& p_category, - const Dictionary& p_data) +void OrchestratorDefaultGraphActionRegistrar::_register_node(const StringName& p_class_name, const StringName& p_category, const Dictionary& p_data) { OScriptLanguage* language = OScriptLanguage::get_singleton(); - Orchestration* orchestration = p_context.graph->get_orchestration(); + Orchestration* orchestration = _context->graph->get_orchestration(); const Ref node = language->create_node_from_name(p_class_name, orchestration, false); if (!node->get_flags().has_flag(OScriptNode::ScriptNodeFlags::CATALOGABLE)) @@ -52,7 +52,7 @@ void OrchestratorDefaultGraphActionRegistrar::_register_node(const OrchestratorG spec.keywords = StringUtils::join(",", keywords); spec.icon = node->get_icon(); spec.type_icon = "PluginScript"; - spec.graph_compatible = node->is_compatible_with_graph(p_context.graph->get_owning_graph()); + spec.graph_compatible = node->is_compatible_with_graph(_context->graph->get_owning_graph()); const Ref call_static_function = node; if (call_static_function.is_valid()) @@ -64,7 +64,7 @@ void OrchestratorDefaultGraphActionRegistrar::_register_node(const OrchestratorG node->initialize(context); Ref handler(memnew(OrchestratorGraphNodeSpawnerScriptNode(p_class_name, p_data, node))); - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(spec, handler))); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(spec, handler))); } PackedStringArray OrchestratorDefaultGraphActionRegistrar::_get_class_hierarchy(const String& p_derived_class_name) @@ -79,36 +79,6 @@ PackedStringArray OrchestratorDefaultGraphActionRegistrar::_get_class_hierarchy( return classes; } -String OrchestratorDefaultGraphActionRegistrar::_get_method_signature(const MethodInfo& p_method) -{ - String signature = p_method.name.replace("_", " ").capitalize(); - if (!p_method.arguments.empty()) - { - signature += " ("; - bool first = true; - int index = 0; - for (const PropertyInfo& property : p_method.arguments) - { - if (!first) - signature += ", "; - - if (property.name.is_empty()) - signature += "p" + itos(index++); - else - signature += property.name; - - if (property.type == Variant::NIL) - signature += ":Any"; - else - signature += ":" + Variant::get_type_name(property.type); - - first = false; - } - signature += ")"; - } - return signature; -} - String OrchestratorDefaultGraphActionRegistrar::_get_method_icon(const MethodInfo& p_method) { if (!OScriptNodeEvent::is_event_method(p_method)) @@ -123,7 +93,7 @@ String OrchestratorDefaultGraphActionRegistrar::_get_method_icon(const MethodInf else if (p_method.name.capitalize().begins_with("Set ") && p_method.arguments.size() == 1) { // Method is a setter, base icon on the argument type - String arg_type = Variant::get_type_name(p_method.arguments[0].type); + String arg_type = PropertyUtils::get_property_type_name(p_method.arguments[0]); if (!arg_type.is_empty()) return arg_type; } @@ -167,20 +137,63 @@ String OrchestratorDefaultGraphActionRegistrar::_get_builtin_type_display_name(V } } -void OrchestratorDefaultGraphActionRegistrar::_register_category(const OrchestratorGraphActionRegistrarContext& p_context, const String& p_category, const String& p_display_name, const String& p_icon) +OrchestratorGraphActionSpec OrchestratorDefaultGraphActionRegistrar::_get_method_spec(const MethodInfo& p_method, const String& p_base_type, const String& p_class_name) +{ + OrchestratorGraphActionSpec spec; + if (p_class_name.is_empty()) + { + spec.category = vformat("Call Function/%s", p_method.name); + spec.text = vformat("Call %s", _friendly_method_names ? p_method.name.capitalize() : String(p_method.name)); + } + else + { + spec.category = vformat("Methods/%s/%s", p_class_name, p_method.name); + spec.text = vformat("%s", p_method.name).capitalize(); + } + + spec.tooltip = MethodUtils::get_signature(p_method); + spec.keywords = vformat("%s,%s", StringUtils::default_if_empty(p_class_name, p_base_type), p_method.name); + spec.icon = _get_method_icon(p_method); + spec.type_icon = _get_method_type_icon(p_method); + + return spec; +} + +OrchestratorGraphActionSpec OrchestratorDefaultGraphActionRegistrar::_get_signal_spec(const StringName& p_signal_name, const String& p_base_type, const String& p_class_name) +{ + OrchestratorGraphActionSpec spec; + if (p_class_name.is_empty()) + spec.category = vformat("Signals/emit_%s", p_signal_name); + else + spec.category = vformat("Signals/%s/%s", p_class_name, p_signal_name); + + spec.tooltip = "Emit the signal " + p_signal_name; + spec.text = vformat("emit_%s", p_signal_name).capitalize(); + if (p_class_name.is_empty()) + spec.keywords = vformat("emit,signal,%s,%s", p_base_type, p_signal_name); + else + spec.keywords = vformat("emit,signal,%s,%s", p_signal_name, p_class_name); + + spec.icon = p_class_name.is_empty() ? "MemberSignal" : "Signal"; + spec.type_icon = "MemberSignal"; + + return spec; +} + +void OrchestratorDefaultGraphActionRegistrar::_register_category(const String& p_category, const String& p_display_name, const String& p_icon) { OrchestratorGraphActionSpec spec; spec.category = p_category; spec.text = p_display_name; spec.icon = p_icon; - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(spec))); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(spec))); } -void OrchestratorDefaultGraphActionRegistrar::_register_script_nodes(const OrchestratorGraphActionRegistrarContext& p_context) +void OrchestratorDefaultGraphActionRegistrar::_register_orchestration_nodes() { bool graph_function = false; - if (p_context.graph) - graph_function = p_context.graph->is_function(); + if (_context->graph) + graph_function = _context->graph->is_function(); // Groups const String func_or_macro_group = graph_function ? "Variables" : "Utilities/Macro"; @@ -191,128 +204,128 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_nodes(const Orche const Dictionary array_data = DictionaryUtils::of({ { "collection_type", Variant::ARRAY },{ "index_type", Variant::NIL } }); // Register several top-level categories - _register_category(p_context, "Project", "Project", "Godot"); - _register_category(p_context, "Call Function", "Call Function", "ScriptExtend"); - _register_category(p_context, "Constants", "Constants", "MemberConstant"); - _register_category(p_context, "Dialogue", "Dialogue", "Window"); - _register_category(p_context, "Flow Control", "Flow Control", "FileTree"); - _register_category(p_context, "Input", "Input", "InputEventKey"); - _register_category(p_context, "Math", "Math", "X509Certificate"); - _register_category(p_context, "Memory", "Memory", "MiniObject"); - _register_category(p_context, "Methods", "Methods", "MemberMethod"); - _register_category(p_context, "Properties", "Properties", "MemberProperty"); - _register_category(p_context, "Random Numbers", "Random Numbers", "RandomNumberGenerator"); - _register_category(p_context, "Resource", "Resource", "File"); - _register_category(p_context, "Scene", "Scene", "PackedScene"); - _register_category(p_context, "Singletons", "Singletons", "MiniObject"); - _register_category(p_context, "Static", "Static", "AudioBusSolo"); - _register_category(p_context, "Utilities", "Utilities", "Tools"); - _register_category(p_context, "Variables", "Variables", "Range"); + _register_category("Project", "Project", "Godot"); + _register_category("Call Function", "Call Function", "ScriptExtend"); + _register_category("Constants", "Constants", "MemberConstant"); + _register_category("Dialogue", "Dialogue", "Window"); + _register_category("Flow Control", "Flow Control", "FileTree"); + _register_category("Input", "Input", "InputEventKey"); + _register_category("Math", "Math", "X509Certificate"); + _register_category("Memory", "Memory", "MiniObject"); + _register_category("Methods", "Methods", "MemberMethod"); + _register_category("Properties", "Properties", "MemberProperty"); + _register_category("Random Numbers", "Random Numbers", "RandomNumberGenerator"); + _register_category("Resource", "Resource", "File"); + _register_category("Scene", "Scene", "PackedScene"); + _register_category("Singletons", "Singletons", "MiniObject"); + _register_category("Static", "Static", "AudioBusSolo"); + _register_category("Utilities", "Utilities", "Tools"); + _register_category("Variables", "Variables", "Range"); // Comments - _register_node(p_context, "Utilities/add_comment"); + _register_node("Utilities/add_comment"); // Constants - _register_node(p_context, "Constants/global_constant"); - _register_node(p_context, "Constants/math_constant"); - _register_node(p_context, "Constants/type_constant"); - _register_node(p_context, "Constants/class_constant"); - _register_node(p_context, "Constants/singleton_constant"); + _register_node("Constants/global_constant"); + _register_node("Constants/math_constant"); + _register_node("Constants/type_constant"); + _register_node("Constants/class_constant"); + _register_node("Constants/singleton_constant"); // Data - _register_node(p_context, "Types/Array/Operators/get_at_index", array_data); - _register_node(p_context, "Types/Array/Operators/set_at_index", array_data); - _register_node(p_context, "Types/Array/find_array_element"); - _register_node(p_context, "Types/Array/clear_array"); - _register_node(p_context, "Types/Array/append_arrays"); - _register_node(p_context, "Types/Array/add_element"); - _register_node(p_context, "Types/Array/remove_element"); - _register_node(p_context, "Types/Array/remove_element_by_index"); - _register_node(p_context, "Types/Array/make_array"); - _register_node(p_context, "Types/Dictionary/make_dictionary"); - _register_node(p_context, "Types/Dictionary/set"); + _register_node("Types/Array/Operators/get_at_index", array_data); + _register_node("Types/Array/Operators/set_at_index", array_data); + _register_node("Types/Array/find_array_element"); + _register_node("Types/Array/clear_array"); + _register_node("Types/Array/append_arrays"); + _register_node("Types/Array/add_element"); + _register_node("Types/Array/remove_element"); + _register_node("Types/Array/remove_element_by_index"); + _register_node("Types/Array/make_array"); + _register_node("Types/Dictionary/make_dictionary"); + _register_node("Types/Dictionary/set"); // Dialogue - _register_node(p_context, "Dialogue/choice"); - _register_node(p_context, "Dialogue/show_message"); + _register_node("Dialogue/choice"); + _register_node("Dialogue/show_message"); // Flow Control - _register_node(p_context, "Flow Control/branch"); - _register_node(p_context, "Flow Control/chance"); - _register_node(p_context, "Flow Control/delay"); - _register_node(p_context, "Flow Control/for_each", without_break); - _register_node(p_context, "Flow Control/for_each_with_break", with_break); - _register_node(p_context, "Flow Control/for", without_break); - _register_node(p_context, "Flow Control/for_with_break", with_break); - _register_node(p_context, "Flow Control/random"); - _register_node(p_context, "Flow Control/select"); - _register_node(p_context, "Flow Control/sequence"); - _register_node(p_context, "Flow Control/switch"); - _register_node(p_context, "Flow Control/switch_on_integer"); - _register_node(p_context, "Flow Control/switch_on_string"); - _register_node(p_context, "Flow Control/type_cast"); - _register_node(p_context, "Flow Control/while"); + _register_node("Flow Control/branch"); + _register_node("Flow Control/chance"); + _register_node("Flow Control/delay"); + _register_node("Flow Control/for_each", without_break); + _register_node("Flow Control/for_each_with_break", with_break); + _register_node("Flow Control/for", without_break); + _register_node("Flow Control/for_with_break", with_break); + _register_node("Flow Control/random"); + _register_node("Flow Control/select"); + _register_node("Flow Control/sequence"); + _register_node("Flow Control/switch"); + _register_node("Flow Control/switch_on_integer"); + _register_node("Flow Control/switch_on_string"); + _register_node("Flow Control/type_cast"); + _register_node("Flow Control/while"); // Switch on Enums for (const String& enum_name : ExtensionDB::get_global_enum_names()) { const EnumInfo ei = ExtensionDB::get_global_enum(enum_name); const String category = vformat("Flow Control/Switch On/switch_on_%s", ei.name); - _register_node(p_context, category, DictionaryUtils::of({ { "enum" , ei.name } })); + _register_node(category, DictionaryUtils::of({ { "enum" , ei.name } })); } // Functions - _register_node(p_context, "add_return_node"); + _register_node("add_return_node"); // Input - _register_node(p_context, "Input/input_action"); + _register_node("Input/input_action"); // Memory { Dictionary new_object; - if (!p_context.filter->target_classes.is_empty()) - new_object["class_name"] = p_context.filter->target_classes[0]; - _register_node(p_context, "Memory/new_object", new_object); + if (!_context->filter->target_classes.is_empty()) + new_object["class_name"] = _context->filter->target_classes[0]; + _register_node("Memory/new_object", new_object); - _register_node(p_context, "Memory/free_object"); + _register_node("Memory/free_object"); } // Resource - _register_node(p_context, "Resource/preload_resource"); - _register_node(p_context, "Resource/get_resource_path"); + _register_node("Resource/preload_resource"); + _register_node("Resource/get_resource_path"); // Scene - _register_node(p_context, "Scene/instantiate_scene"); - _register_node(p_context, "Scene/get_scene_node"); - _register_node(p_context, "Scene/get_scene_tree"); + _register_node("Scene/instantiate_scene"); + _register_node("Scene/get_scene_node"); + _register_node("Scene/get_scene_tree"); // Signals - _register_node(p_context, "Signals/Await Signal"); + _register_node("Signals/Await Signal"); // Utilities - _register_node(p_context, "Utilities/get_autoload"); - _register_node(p_context, "Utilities/engine_singleton"); - _register_node(p_context, "Utilities/print_string"); + _register_node("Utilities/get_autoload"); + _register_node("Utilities/engine_singleton"); + _register_node("Utilities/print_string"); // Register each Engine singleton type for (const String& name : Engine::get_singleton()->get_singleton_list()) { const String category = vformat("Singletons/%s", name); const Dictionary data = DictionaryUtils::of({ { "singleton_name", name } }); - _register_node(p_context, category, data); + _register_node(category, data); } // Variables - _register_node(p_context, "Variables/get_self"); + _register_node("Scene/get_self"); // Register variable assignment differently for macros const String local_var_category = vformat("%s/assign_local", func_or_macro_group); - _register_node(p_context, local_var_category); + _register_node(local_var_category); // Register Local Object variables const String lv_object_name = vformat("%s/local_object", func_or_macro_group); const Dictionary object_type_dict = DictionaryUtils::of({ { "type", Variant::OBJECT } }); - _register_node(p_context, lv_object_name, object_type_dict); + _register_node(lv_object_name, object_type_dict); // Static Function Calls for (const String& class_name : ClassDB::get_class_list()) @@ -320,7 +333,7 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_nodes(const Orche for (const String& function_name : ExtensionDB::get_static_function_names(class_name)) { const String category = vformat("Static/%s/%s", class_name, function_name); - _register_node(p_context, category, + _register_node(category, DictionaryUtils::of({ { "class_name", class_name }, { "method_name", function_name } }));; } } @@ -332,24 +345,24 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_nodes(const Orche const String type_icon = _get_builtin_type_icon_name(type.type); const String type_name = _get_builtin_type_display_name(type.type); - _register_category(p_context, vformat("Types/%s", type_name), type_name, type_icon); + _register_category(vformat("Types/%s", type_name), type_name, type_icon); const Dictionary type_dict = DictionaryUtils::of({ { "type", type.type } }); // Register local variables differently for macros const String lv_name = vformat("Types/%s/local_%s_variable", type_name, type_name); - _register_node(p_context, lv_name, type_dict); + _register_node(lv_name, type_dict); if (!type.properties.is_empty()) { if (OScriptNodeCompose::is_supported(type.type)) { const String make_category = vformat("Types/%s/make_%s", type_name, type_name.to_lower()); - _register_node(p_context, make_category, type_dict); + _register_node(make_category, type_dict); } const String break_category = vformat("Types/%s/break_%s", type_name, type_name.to_lower()); - _register_node(p_context, break_category, type_dict); + _register_node(break_category, type_dict); } if (!type.constructors.is_empty()) @@ -381,7 +394,7 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_nodes(const Orche const String args = StringUtils::join(" and ", type_names); const String category = vformat("Types/%s/make_%s_from_%s", type_name, type_name.to_lower(), args); - _register_node(p_context, category, ctor_dict); + _register_node(category, ctor_dict); } } } @@ -391,7 +404,7 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_nodes(const Orche const String category = vformat("Types/%s/%s", type_name, mi.name); const Dictionary method_dict = DictionaryUtils::from_method(mi); const Dictionary data = DictionaryUtils::of({ { "target_type", type.type }, { "method", method_dict } }); - _register_node(p_context, category, data); + _register_node(category, data); } if (OScriptNodeOperator::is_supported(type.type)) @@ -423,7 +436,7 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_nodes(const Orche { "right_type_name", op.right_type_name }, { "return_type", op.return_type } }); - _register_node(p_context, category, data); + _register_node(category, data); } } @@ -436,8 +449,8 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_nodes(const Orche { "collection_type", type.type }, { "index_type", type.index_returning_type } }); - _register_node(p_context, get_category, data); - _register_node(p_context, set_category, data); + _register_node(get_category, data); + _register_node(set_category, data); } } @@ -466,73 +479,110 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_nodes(const Orche top_category = "random_numbers"; const String category = vformat("%s/%s", top_category.capitalize(), fi.name); - _register_node(p_context, category, DictionaryUtils::from_method(mi)); + _register_node(category, DictionaryUtils::from_method(mi)); } // Autoloads for (const String& class_name : OScriptLanguage::get_singleton()->get_global_constant_names()) { String category = vformat("project/Autoloads/%s", class_name); - _register_node(p_context, category, DictionaryUtils::of({{ "class_name" , class_name }})); + _register_node(category, DictionaryUtils::of({{ "class_name" , class_name }})); } } -void OrchestratorDefaultGraphActionRegistrar::_register_graph_items(const OrchestratorGraphActionRegistrarContext& p_context) +void OrchestratorDefaultGraphActionRegistrar::_register_class(const String& p_class_name) { - if (!p_context.graph) - return; + const OrchestratorGraphActionSpec spec(p_class_name, p_class_name, p_class_name); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(spec))); - PackedStringArray classes; - if (p_context.filter->has_target_object()) - { - classes.push_back(p_context.filter->get_target_class()); - } - else if (!p_context.filter->target_classes.is_empty()) - { - for (String target_class : p_context.filter->target_classes) - { - PackedStringArray target_hierarchy = _get_class_hierarchy(target_class); - for (const String& target : target_hierarchy) - classes.push_back(target); - } - } - else + _register_methods(p_class_name, ClassDB::class_get_method_list(p_class_name, true)); + _register_properties(p_class_name, ClassDB::class_get_property_list(p_class_name, true)); + _register_signals(p_class_name, ClassDB::class_get_signal_list(p_class_name, true)); +} + +void OrchestratorDefaultGraphActionRegistrar::_register_methods(const String& p_class_name, const TypedArray& p_methods) +{ + if (ClassDB::can_instantiate(p_class_name) && !_classes_new_instances.has(p_class_name)) { - classes = _get_class_hierarchy(p_context.graph->get_orchestration()->get_base_type()); + _classes_new_instances.push_back(p_class_name); + + const String category = vformat("Methods/%s/New Instance", p_class_name); + _register_node(category, DictionaryUtils::of({ { "class_name", p_class_name } })); } - for (const String& class_name : classes) + Orchestration* orchestration = _context->filter->get_orchestration(); + for (int i = 0; i < p_methods.size(); i++) { - const OrchestratorGraphActionSpec spec(class_name, class_name, class_name); - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(spec))); - _register_class_properties(p_context, class_name); - _register_class_methods(p_context, class_name); - _register_class_signals(p_context, class_name); + const MethodInfo mi = DictionaryUtils::to_method(p_methods[i]); + + // Skip private methods + if (mi.name.begins_with("_") && !(mi.flags & METHOD_FLAG_VIRTUAL)) + continue; + + // Skip internal methods (found from scripts like GDScript) + if (mi.name.begins_with("@")) + continue; + + const OrchestratorGraphActionSpec spec = _get_method_spec(mi, orchestration->get_base_type(), p_class_name); + + OrchestratorGraphActionHandler* handler_ptr; + if (OScriptNodeEvent::is_event_method(mi)) + handler_ptr = memnew(OrchestratorGraphNodeSpawnerEvent(mi)); + else + handler_ptr = memnew(OrchestratorGraphNodeSpawnerCallMemberFunction(mi, p_class_name)); + + Ref handler(handler_ptr); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(spec, handler))); } } -void OrchestratorDefaultGraphActionRegistrar::_register_class_properties(const OrchestratorGraphActionRegistrarContext& p_context, - const StringName& p_class_name) +void OrchestratorDefaultGraphActionRegistrar::_register_properties(const String& p_class_name, const TypedArray& p_properties) { - String class_name = p_class_name; - TypedArray properties; - if (p_context.filter->has_target_object()) - { - properties = p_context.filter->target_object->get_target_property_list(); - class_name = p_context.filter->target_object->get_class(); - } - else - properties = ClassDB::class_get_property_list(p_class_name, true); + ScriptServer::GlobalClass global_class; + if (ScriptServer::is_global_class(p_class_name)) + global_class = ScriptServer::get_global_class(p_class_name); - for (int i = 0; i < properties.size(); i++) + for (int i = 0; i < p_properties.size(); i++) { - const PropertyInfo pi = DictionaryUtils::to_property(properties[i]); + const PropertyInfo pi = DictionaryUtils::to_property(p_properties[i]); // Exclude properties that are not included in the class reference if (pi.usage & PROPERTY_USAGE_INTERNAL) continue; - if (!ClassDB::class_has_method(class_name, vformat("get_%s", pi.name))) + // Exclude category and group properties + if (pi.usage & PROPERTY_USAGE_CATEGORY || pi.usage & PROPERTY_USAGE_GROUP) + continue; + + // Skip private properties + if (pi.name.begins_with("_")) + continue; + + // For script variables, checks whether its define in the parent or child type + // If it's defined in the parent type, skip it + if (pi.usage & PROPERTY_USAGE_SCRIPT_VARIABLE && !global_class.name.is_empty()) + { + if (ScriptServer::is_global_class(global_class.base_type)) + { + if (ScriptServer::get_global_class(global_class.base_type).has_property(pi.name)) + continue; + } + } + + const String getter_name = vformat("get_%s", pi.name); + const String setter_name = vformat("set_%s", pi.name); + + bool has_getter = false; + if ((global_class.name.is_empty() && ClassDB::class_has_method(p_class_name, getter_name)) + || (!global_class.name.is_empty() && global_class.has_method(getter_name))) + has_getter = true; + + bool has_setter = false; + if ((global_class.name.is_empty() && ClassDB::class_has_method(p_class_name, setter_name)) + || (!global_class.name.is_empty() && global_class.has_method(setter_name))) + has_setter = true; + + if (!has_getter) { OrchestratorGraphActionSpec getter_spec; getter_spec.category = vformat("Properties/%s/get_%s", p_class_name, pi.name); @@ -542,11 +592,11 @@ void OrchestratorDefaultGraphActionRegistrar::_register_class_properties(const O getter_spec.icon = Variant::get_type_name(pi.type); getter_spec.type_icon = "MemberProperty"; - Ref getter_handler(memnew(OrchestratorGraphNodeSpawnerPropertyGet(pi, p_context.filter->target_classes))); - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(getter_spec, getter_handler))); + Ref getter_handler(memnew(OrchestratorGraphNodeSpawnerPropertyGet(pi, Array::make(p_class_name)))); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(getter_spec, getter_handler))); } - if (!ClassDB::class_has_method(class_name, vformat("set_%s", pi.name))) + if (!has_setter) { OrchestratorGraphActionSpec setter_spec; setter_spec.category = vformat("Properties/%s/set_%s", p_class_name, pi.name); @@ -556,78 +606,29 @@ void OrchestratorDefaultGraphActionRegistrar::_register_class_properties(const O setter_spec.icon = Variant::get_type_name(pi.type); setter_spec.type_icon = "MemberProperty"; - Ref setter_handler(memnew(OrchestratorGraphNodeSpawnerPropertySet(pi, p_context.filter->target_classes))); - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(setter_spec, setter_handler))); + Ref setter_handler(memnew(OrchestratorGraphNodeSpawnerPropertySet(pi, Array::make(p_class_name)))); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(setter_spec, setter_handler))); } } } -void OrchestratorDefaultGraphActionRegistrar::_register_class_methods(const OrchestratorGraphActionRegistrarContext& p_context, - const StringName& p_class_name) +void OrchestratorDefaultGraphActionRegistrar::_register_signals(const String& p_class_name, const TypedArray& p_signals) { - TypedArray methods; - StringName class_name; - if (p_context.filter->has_target_object()) - { - methods = p_context.filter->target_object->get_target_method_list(); - class_name = p_context.filter->target_object->get_class(); - } - else - { - methods = ClassDB::class_get_method_list(p_class_name, true); - class_name = p_class_name; - } + ScriptServer::GlobalClass global_class; + if (ScriptServer::is_global_class(p_class_name)) + global_class = ScriptServer::get_global_class(p_class_name); - if (ClassDB::can_instantiate(class_name)) + Orchestration* orchestration = _context->filter->get_orchestration(); + for (int i = 0; i < p_signals.size(); i++) { - const String category = vformat("Methods/%s/New Instance", class_name); - _register_node(p_context, category, DictionaryUtils::of({ { "class_name", class_name } })); - } + const MethodInfo si = DictionaryUtils::to_method(p_signals[i]); - for (int i = 0; i < methods.size(); i++) - { - const MethodInfo mi = DictionaryUtils::to_method(methods[i]); - if (mi.name.begins_with("_") && !(mi.flags & METHOD_FLAG_VIRTUAL)) - continue; - - OrchestratorGraphActionSpec spec; - spec.category = vformat("Methods/%s/%s", class_name, mi.name); - spec.tooltip = _get_method_signature(mi); - spec.text = vformat("%s", mi.name).capitalize(); - spec.keywords = vformat("%s,%s", class_name, mi.name); - spec.icon = _get_method_icon(mi); - spec.type_icon = _get_method_type_icon(mi); - - OrchestratorGraphActionHandler* handler_ptr; - if (OScriptNodeEvent::is_event_method(mi)) - handler_ptr = memnew(OrchestratorGraphNodeSpawnerEvent(mi)); - else - handler_ptr = memnew(OrchestratorGraphNodeSpawnerCallMemberFunction(mi, class_name)); - - Ref handler(handler_ptr); - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(spec, handler))); - } -} - -void OrchestratorDefaultGraphActionRegistrar::_register_class_signals(const OrchestratorGraphActionRegistrarContext& p_context, - const StringName& p_class_name) -{ - TypedArray signals; - if (p_context.filter->has_target_object()) - signals = p_context.filter->target_object->get_target_signal_list(); - else - signals = ClassDB::class_get_signal_list(p_class_name, true); + // Skip signals that are defined in parent global class types + if (!global_class.name.is_empty() && ScriptServer::is_global_class(global_class.base_type)) + if (ScriptServer::get_global_class(global_class.base_type).has_signal(si.name)) + continue; - for (int i = 0; i < signals.size(); i++) - { - const MethodInfo si = DictionaryUtils::to_method(signals[i]); - OrchestratorGraphActionSpec spec; - spec.category = vformat("Signals/%s/%s", p_class_name, si.name); - spec.tooltip = "Emit the signal " + si.name; - spec.text = vformat("emit_%s", si.name).capitalize(); - spec.keywords = vformat("emit,signal,%s,%s", p_class_name, si.name); - spec.icon = "Signal"; - spec.type_icon = "MemberSignal"; + const OrchestratorGraphActionSpec spec = _get_signal_spec(si.name, orchestration->get_base_type(), p_class_name); MethodInfo mi; mi.name = si.name; @@ -635,11 +636,11 @@ void OrchestratorDefaultGraphActionRegistrar::_register_class_signals(const Orch mi.return_val = PropertyInfo(Variant::NIL, ""); Ref handler(memnew(OrchestratorGraphNodeSpawnerEmitMemberSignal(mi, p_class_name))); - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(spec, handler))); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(spec, handler))); } } -void OrchestratorDefaultGraphActionRegistrar::_register_script_functions(const OrchestratorGraphActionRegistrarContext& p_context) +void OrchestratorDefaultGraphActionRegistrar::_register_orchestration_functions() { OrchestratorGraphActionSpec call_function_spec; call_function_spec.category = "call_function"; @@ -648,13 +649,10 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_functions(const O call_function_spec.keywords = "call,function"; call_function_spec.icon = "MemberMethod"; call_function_spec.type_icon = "MemberMethod"; - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(call_function_spec))); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(call_function_spec))); - if (OrchestratorGraphEdit* graph = p_context.graph) + if (OrchestratorGraphEdit* graph = _context->graph) { - OrchestratorSettings* settings = OrchestratorSettings::get_singleton(); - bool friendly_names = settings->get_setting("ui/components_panel/show_function_friendly_names", true); - Orchestration* orchestration = graph->get_orchestration(); for (const Ref& function : orchestration->get_functions()) { @@ -662,22 +660,15 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_functions(const O continue; const MethodInfo& mi = function->get_method_info(); - - OrchestratorGraphActionSpec spec; - spec.category = vformat("Call Function/%s", mi.name); - spec.tooltip = _get_method_signature(mi); - spec.text = vformat("Call %s", friendly_names ? mi.name.capitalize() : String(mi.name)); - spec.keywords = vformat("call,%s,%s", orchestration->get_base_type(), mi.name); - spec.icon = _get_method_icon(mi); - spec.type_icon = _get_method_type_icon(mi); + const OrchestratorGraphActionSpec spec = _get_method_spec(mi, orchestration->get_base_type()); Ref handler(memnew(OrchestratorGraphNodeSpawnerCallScriptFunction(mi))); - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(spec, handler))); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(spec, handler))); } } } -void OrchestratorDefaultGraphActionRegistrar::_register_script_variables(const OrchestratorGraphActionRegistrarContext& p_context) +void OrchestratorDefaultGraphActionRegistrar::_register_orchestration_variables() { OrchestratorGraphActionSpec variables_spec; variables_spec.category = "variables"; @@ -686,9 +677,9 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_variables(const O variables_spec.keywords = "variable,variables"; variables_spec.icon = "MemberProperty"; variables_spec.type_icon = "MemberProperty"; - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(variables_spec))); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(variables_spec))); - if (OrchestratorGraphEdit* graph = p_context.graph) + if (OrchestratorGraphEdit* graph = _context->graph) { for (const Ref& variable : graph->get_orchestration()->get_variables()) { @@ -706,7 +697,7 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_variables(const O getter_spec.type_icon = "MemberProperty"; Ref getter_handler(memnew(OrchestratorGraphNodeSpawnerVariableGet(variable_name))); - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(getter_spec, getter_handler))); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(getter_spec, getter_handler))); OrchestratorGraphActionSpec setter_spec; setter_spec.category = vformat("Variables/set_%s", variable_name); @@ -717,12 +708,12 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_variables(const O setter_spec.type_icon = "MemberProperty"; Ref setter_handler(memnew(OrchestratorGraphNodeSpawnerVariableSet(variable_name))); - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(setter_spec, setter_handler))); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(setter_spec, setter_handler))); } } } -void OrchestratorDefaultGraphActionRegistrar::_register_script_signals(const OrchestratorGraphActionRegistrarContext& p_context) +void OrchestratorDefaultGraphActionRegistrar::_register_orchestration_signals() { OrchestratorGraphActionSpec signals_spec; signals_spec.category = "emit_signals"; @@ -731,9 +722,9 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_signals(const Orc signals_spec.keywords = "signal,signals"; signals_spec.icon = "MemberSignal"; signals_spec.type_icon = "MemberSignal"; - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(signals_spec))); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(signals_spec))); - if (OrchestratorGraphEdit* graph = p_context.graph) + if (OrchestratorGraphEdit* graph = _context->graph) { Orchestration* orchestration = graph->get_owning_graph()->get_orchestration(); for (const Ref& signal : orchestration->get_custom_signals()) @@ -741,25 +732,80 @@ void OrchestratorDefaultGraphActionRegistrar::_register_script_signals(const Orc if (!signal.is_valid()) continue; - OrchestratorGraphActionSpec spec; - spec.category = vformat("Signals/emit_%s", signal->get_signal_name()); - spec.tooltip = vformat("Emit signal '%s'", signal->get_signal_name()); - spec.text = vformat("Emit %s", signal->get_signal_name()); - spec.keywords = vformat("emit,signal,%s,%s", orchestration->get_base_type(), signal->get_signal_name()); - spec.icon = "MemberSignal"; - spec.type_icon = "MemberSignal"; + const OrchestratorGraphActionSpec spec = _get_signal_spec(signal->get_signal_name(), orchestration->get_base_type()); Ref handler(memnew(OrchestratorGraphNodeSpawnerEmitSignal(signal->get_method_info()))); - p_context.list->push_back(memnew(OrchestratorGraphActionMenuItem(spec, handler))); + _context->list->push_back(memnew(OrchestratorGraphActionMenuItem(spec, handler))); } } } void OrchestratorDefaultGraphActionRegistrar::register_actions(const OrchestratorGraphActionRegistrarContext& p_context) { - _register_graph_items(p_context); - _register_script_nodes(p_context); - _register_script_functions(p_context); - _register_script_variables(p_context); - _register_script_signals(p_context); + OrchestratorSettings* settings = OrchestratorSettings::get_singleton(); + _friendly_method_names = settings->get_setting("ui/components_panel/show_function_friendly_names", true); + + _context = &p_context; + + if (p_context.graph) + { + if (p_context.filter->has_target_object()) + { + const Object* object = p_context.filter->target_object->get_target(); + const Ref