From 3f2f46186cae84d38379671a466027a42041a14e Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Tue, 14 May 2024 20:37:28 -0400 Subject: [PATCH] GH-360 Select action names from drop-down list --- src/editor/editor.cpp | 1 + src/editor/graph/graph_edit.cpp | 2 +- src/editor/graph/nodes/graph_node_default.cpp | 2 +- .../graph_node_factory.cpp | 2 +- .../{factories => nodes}/graph_node_factory.h | 0 .../graph_node_pin_factory.cpp | 36 ++++++- .../graph_node_pin_factory.h | 7 +- .../pins/graph_node_pin_input_action.cpp | 93 +++++++++++++++++++ .../graph/pins/graph_node_pin_input_action.h | 50 ++++++++++ src/editor/graph/pins/graph_node_pins.h | 1 + .../nodes/functions/call_member_function.cpp | 23 ++++- .../nodes/functions/call_member_function.h | 11 +++ 12 files changed, 221 insertions(+), 7 deletions(-) rename src/editor/graph/{factories => nodes}/graph_node_factory.cpp (100%) rename src/editor/graph/{factories => nodes}/graph_node_factory.h (100%) rename src/editor/graph/{factories => pins}/graph_node_pin_factory.cpp (61%) rename src/editor/graph/{factories => pins}/graph_node_pin_factory.h (80%) create mode 100644 src/editor/graph/pins/graph_node_pin_input_action.cpp create mode 100644 src/editor/graph/pins/graph_node_pin_input_action.h diff --git a/src/editor/editor.cpp b/src/editor/editor.cpp index a60b1c8a..c7d54c7c 100644 --- a/src/editor/editor.cpp +++ b/src/editor/editor.cpp @@ -109,6 +109,7 @@ void register_editor_classes() ORCHESTRATOR_REGISTER_INTERNAL_CLASS(OrchestratorGraphNodePinEnum) ORCHESTRATOR_REGISTER_INTERNAL_CLASS(OrchestratorGraphNodePinExec) ORCHESTRATOR_REGISTER_INTERNAL_CLASS(OrchestratorGraphNodePinFile) + ORCHESTRATOR_REGISTER_INTERNAL_CLASS(OrchestratorGraphNodePinInputAction) ORCHESTRATOR_REGISTER_INTERNAL_CLASS(OrchestratorGraphNodePinNodePath) ORCHESTRATOR_REGISTER_INTERNAL_CLASS(OrchestratorGraphNodePinNumeric) ORCHESTRATOR_REGISTER_INTERNAL_CLASS(OrchestratorGraphNodePinObject) diff --git a/src/editor/graph/graph_edit.cpp b/src/editor/graph/graph_edit.cpp index d42d7f66..610b832a 100644 --- a/src/editor/graph/graph_edit.cpp +++ b/src/editor/graph/graph_edit.cpp @@ -22,11 +22,11 @@ #include "common/name_utils.h" #include "common/scene_utils.h" #include "common/version.h" -#include "editor/graph/factories/graph_node_factory.h" #include "editor/graph/graph_knot.h" #include "editor/graph/graph_node_pin.h" #include "editor/graph/graph_node_spawner.h" #include "editor/graph/nodes/graph_node_comment.h" +#include "nodes/graph_node_factory.h" #include "plugin/plugin.h" #include "plugin/settings.h" #include "script/language.h" diff --git a/src/editor/graph/nodes/graph_node_default.cpp b/src/editor/graph/nodes/graph_node_default.cpp index 5a646137..80a0bdff 100644 --- a/src/editor/graph/nodes/graph_node_default.cpp +++ b/src/editor/graph/nodes/graph_node_default.cpp @@ -17,7 +17,7 @@ #include "graph_node_default.h" #include "common/scene_utils.h" -#include "editor/graph/factories/graph_node_pin_factory.h" +#include "editor/graph/pins/graph_node_pin_factory.h" #include #include diff --git a/src/editor/graph/factories/graph_node_factory.cpp b/src/editor/graph/nodes/graph_node_factory.cpp similarity index 100% rename from src/editor/graph/factories/graph_node_factory.cpp rename to src/editor/graph/nodes/graph_node_factory.cpp index 03613176..9de12454 100644 --- a/src/editor/graph/factories/graph_node_factory.cpp +++ b/src/editor/graph/nodes/graph_node_factory.cpp @@ -16,8 +16,8 @@ // #include "graph_node_factory.h" -#include "editor/graph/nodes/graph_node_default.h" #include "editor/graph/nodes/graph_node_comment.h" +#include "editor/graph/nodes/graph_node_default.h" OrchestratorGraphNode* OrchestratorGraphNodeFactory::create_node(OrchestratorGraphEdit* p_graph, const Ref& p_node) { diff --git a/src/editor/graph/factories/graph_node_factory.h b/src/editor/graph/nodes/graph_node_factory.h similarity index 100% rename from src/editor/graph/factories/graph_node_factory.h rename to src/editor/graph/nodes/graph_node_factory.h diff --git a/src/editor/graph/factories/graph_node_pin_factory.cpp b/src/editor/graph/pins/graph_node_pin_factory.cpp similarity index 61% rename from src/editor/graph/factories/graph_node_pin_factory.cpp rename to src/editor/graph/pins/graph_node_pin_factory.cpp index 12b70efd..b68fad7e 100644 --- a/src/editor/graph/factories/graph_node_pin_factory.cpp +++ b/src/editor/graph/pins/graph_node_pin_factory.cpp @@ -17,8 +17,40 @@ #include "graph_node_pin_factory.h" #include "editor/graph/pins/graph_node_pins.h" +#include "script/nodes/functions/call_function.h" +#include "script/nodes/functions/call_member_function.h" -OrchestratorGraphNodePin* OrchestratorGraphNodePinFactory::create_pin(OrchestratorGraphNode* p_node, Ref p_pin) +#include + +OrchestratorGraphNodePin* OrchestratorGraphNodePinFactory::_resolve_string_based_pin(OrchestratorGraphNode* p_node, const Ref& p_pin) +{ + static PackedStringArray input_event_names = Array::make("is_action_pressed", "is_action_released", + "is_action", "get_action_strength"); + + static PackedStringArray input_names = Array::make("action_press", "action_release", "get_action_raw_strength", + "get_action_strength", "is_action_just_pressed", "is_action_just_released", "is_action_pressed"); + + if (OScriptNodeCallMemberFunction* cmf = Object::cast_to(p_pin->get_owning_node())) + { + const String target_class_name = cmf->get_target_class(); + if (InputEvent::get_class_static().match(target_class_name)) + { + const MethodInfo& mi = cmf->get_function(); + if (input_event_names.has(mi.name) && p_pin->get_pin_name().match("action")) + return memnew(OrchestratorGraphNodePinInputAction(p_node, p_pin)); + } + else if (Input::get_class_static().match(target_class_name)) + { + const MethodInfo& mi = cmf->get_function(); + if (input_names.has(mi.name) && p_pin->get_pin_name().match("action")) + return memnew(OrchestratorGraphNodePinInputAction(p_node, p_pin)); + } + } + + return memnew(OrchestratorGraphNodePinString(p_node, p_pin)); +} + +OrchestratorGraphNodePin* OrchestratorGraphNodePinFactory::create_pin(OrchestratorGraphNode* p_node, const Ref& p_pin) { if (p_pin->get_flags().has_flag(OScriptNodePin::Flags::EXECUTION)) return memnew(OrchestratorGraphNodePinExec(p_node, p_pin)); @@ -36,7 +68,7 @@ OrchestratorGraphNodePin* OrchestratorGraphNodePinFactory::create_pin(Orchestrat { case Variant::STRING: case Variant::STRING_NAME: - return memnew(OrchestratorGraphNodePinString(p_node, p_pin)); + return _resolve_string_based_pin(p_node, p_pin); case Variant::FLOAT: case Variant::INT: diff --git a/src/editor/graph/factories/graph_node_pin_factory.h b/src/editor/graph/pins/graph_node_pin_factory.h similarity index 80% rename from src/editor/graph/factories/graph_node_pin_factory.h rename to src/editor/graph/pins/graph_node_pin_factory.h index 3f27a249..14252ba0 100644 --- a/src/editor/graph/factories/graph_node_pin_factory.h +++ b/src/editor/graph/pins/graph_node_pin_factory.h @@ -25,13 +25,18 @@ class OrchestratorGraphNodePinFactory // Intentionally private OrchestratorGraphNodePinFactory() = default; + /// Resolves the string-based pin type + /// @param p_node the graph node that will own the pin + /// @param p_pin the pin to create a rendering widget for + static OrchestratorGraphNodePin* _resolve_string_based_pin(OrchestratorGraphNode* p_node, const Ref& p_pin); + public: /// Creates the appropriate OrchestratorGraphNodePin implementation for the given node and pin. /// @param p_node the graph node that will own the pin /// @param p_pin the orchestration script pin reference /// @return the orchestration graph node pin instance, never null - static OrchestratorGraphNodePin* create_pin(OrchestratorGraphNode* p_node, Ref p_pin); + static OrchestratorGraphNodePin* create_pin(OrchestratorGraphNode* p_node, const Ref& p_pin); }; #endif // ORCHESTRATOR_GRAPH_NODE_PIN_FACTORY_H diff --git a/src/editor/graph/pins/graph_node_pin_input_action.cpp b/src/editor/graph/pins/graph_node_pin_input_action.cpp new file mode 100644 index 00000000..6b2e3c15 --- /dev/null +++ b/src/editor/graph/pins/graph_node_pin_input_action.cpp @@ -0,0 +1,93 @@ +// This file is part of the Godot Orchestrator project. +// +// Copyright (c) 2023-present Vahera Studios LLC and its contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "graph_node_pin_input_action.h" + +#include "common/callable_lambda.h" + +#include +#include +#include +#include +#include + +void OrchestratorGraphNodePinInputAction::_populate_action_list() +{ + _button->clear(); + + String default_value = _pin->get_effective_default_value(); + bool found = false; + + Ref project(memnew(ConfigFile)); + if (project->load("res://project.godot") == OK) + { + const PackedStringArray custom_action_names = project->get_section_keys("input"); + for (const String& custom_action_name : custom_action_names) + { + _button->add_item(custom_action_name); + if (custom_action_name.match(default_value)) + { + _button->select(_button->get_item_count() - 1); + found = true; + } + } + } + + const TypedArray action_names = InputMap::get_singleton()->get_actions(); + for (int i = 0; i < action_names.size(); i++) + { + const StringName& action_name = action_names[i]; + if (action_name.begins_with("spatial_editor/")) + continue; + + _button->add_item(action_name); + if (action_name.match(default_value)) + { + _button->select(_button->get_item_count() - 1); + found = true; + } + } + + if (!found) + _pin->set_default_value(Variant()); +} + +Control* OrchestratorGraphNodePinInputAction::_get_default_value_widget() +{ + _button = memnew(OptionButton); + _button->set_allow_reselect(true); + _button->get_popup()->set_max_size(Vector2(32768, 400)); + _button->set_tooltip_text("Actions defined in Project Settings: Input Map"); + + _button->connect("item_selected", callable_mp_lambda(this, [&](int index) { + const String action_name = _button->get_item_text(index); + _pin->set_default_value(action_name); + _button->release_focus(); + })); + + ProjectSettings::get_singleton()->connect("settings_changed", callable_mp_lambda(this, [&]() { + _populate_action_list(); + })); + + _populate_action_list(); + + return _button; +} + +OrchestratorGraphNodePinInputAction::OrchestratorGraphNodePinInputAction(OrchestratorGraphNode* p_node, const Ref& p_pin) + : OrchestratorGraphNodePin(p_node, p_pin) +{ +} \ No newline at end of file diff --git a/src/editor/graph/pins/graph_node_pin_input_action.h b/src/editor/graph/pins/graph_node_pin_input_action.h new file mode 100644 index 00000000..07922c69 --- /dev/null +++ b/src/editor/graph/pins/graph_node_pin_input_action.h @@ -0,0 +1,50 @@ +// This file is part of the Godot Orchestrator project. +// +// Copyright (c) 2023-present Vahera Studios LLC and its contributors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef ORCHESTRATOR_GRAPH_NODE_PIN_INPUT_ACTION_H +#define ORCHESTRATOR_GRAPH_NODE_PIN_INPUT_ACTION_H + +#include "editor/graph/graph_node_pin.h" + +#include + +/// Pin input widget for selecting input actions from a drop-down list +class OrchestratorGraphNodePinInputAction : public OrchestratorGraphNodePin +{ + GDCLASS(OrchestratorGraphNodePinInputAction, OrchestratorGraphNodePin); + static void _bind_methods() {} + +protected: + OptionButton* _button{ nullptr }; + + //~ Begin OrchestratorGraphNodePin Interface + Control* _get_default_value_widget() override; + //~ End OrchestratorGraphNodePin Interface + + /// Populates the button's action list + void _populate_action_list(); + + // Default constructor + OrchestratorGraphNodePinInputAction() = default; + +public: + /// Constructs the input action pin + /// @param p_node the graph node + /// @param p_pin the script node pin + OrchestratorGraphNodePinInputAction(OrchestratorGraphNode* p_node, const Ref& p_pin); +}; + +#endif // ORCHESTRATOR_GRAPH_NODE_PIN_INPUT_ACTION_H \ No newline at end of file diff --git a/src/editor/graph/pins/graph_node_pins.h b/src/editor/graph/pins/graph_node_pins.h index 100f793f..7d613cb2 100644 --- a/src/editor/graph/pins/graph_node_pins.h +++ b/src/editor/graph/pins/graph_node_pins.h @@ -23,6 +23,7 @@ #include "editor/graph/pins/graph_node_pin_enum.h" #include "editor/graph/pins/graph_node_pin_exec.h" #include "editor/graph/pins/graph_node_pin_file.h" +#include "editor/graph/pins/graph_node_pin_input_action.h" #include "editor/graph/pins/graph_node_pin_node_path.h" #include "editor/graph/pins/graph_node_pin_numeric.h" #include "editor/graph/pins/graph_node_pin_object.h" diff --git a/src/script/nodes/functions/call_member_function.cpp b/src/script/nodes/functions/call_member_function.cpp index bb3e5ec1..6a219823 100644 --- a/src/script/nodes/functions/call_member_function.cpp +++ b/src/script/nodes/functions/call_member_function.cpp @@ -17,7 +17,6 @@ #include "call_member_function.h" #include "common/dictionary_utils.h" -#include "common/method_utils.h" #include "common/variant_utils.h" #include "common/version.h" @@ -94,3 +93,25 @@ void OScriptNodeCallMemberFunction::initialize(const OScriptNodeInitContext& p_c super::initialize(p_context); } + +void OScriptNodeCallMemberFunction::on_pin_connected(const Ref& p_pin) +{ + if (p_pin->get_pin_name().match("target")) + { + _function_flags = int64_t(_function_flags) & ~FF_IS_SELF; + + const Ref supplier = p_pin->get_connections()[0]; + const String target_class = supplier->get_owning_node()->resolve_type_class(supplier); + _reference.target_class_name = target_class; + } +} + +void OScriptNodeCallMemberFunction::on_pin_disconnected(const Ref& p_pin) +{ + if (p_pin->get_pin_name().match("target")) + { + _function_flags.set_flag(FF_IS_SELF); + _reference.target_class_name = get_owning_script()->get_base_type(); + } + reconstruct_node(); +} diff --git a/src/script/nodes/functions/call_member_function.h b/src/script/nodes/functions/call_member_function.h index 42ed6450..02a9a186 100644 --- a/src/script/nodes/functions/call_member_function.h +++ b/src/script/nodes/functions/call_member_function.h @@ -37,7 +37,18 @@ class OScriptNodeCallMemberFunction : public OScriptNodeCallFunction String get_node_title_color_name() const override { return "function_call"; } String get_help_topic() const override; void initialize(const OScriptNodeInitContext& p_context) override; + void on_pin_connected(const Ref& p_pin) override; + void on_pin_disconnected(const Ref& p_pin) override; //~ End OScriptNode Interface + + /// Get the target function class + /// @return the target function class + String get_target_class() const { return _reference.target_class_name; } + + /// Get the Godot function reference + /// @return the function reference + const MethodInfo& get_function() const { return _reference.method; } + }; #endif // ORCHESTRATOR_SCRIPT_NODE_CALL_MEMBER_FUNCTION_H