From 879ec9ebfc09838949b3e91756e00081f49e427d Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Sun, 30 Jun 2024 15:09:11 -0400 Subject: [PATCH] GH-463 Adapt comment nodes to use `GraphFrame` --- src/editor/graph/graph_edit.cpp | 115 ++++++++++++++++-- src/editor/graph/graph_edit.h | 21 +++- src/editor/graph/graph_node.cpp | 15 +++ src/editor/graph/graph_node.h | 1 + src/editor/graph/nodes/graph_node_comment.cpp | 76 +++++++++++- src/editor/graph/nodes/graph_node_comment.h | 38 ++++++ src/editor/graph/nodes/graph_node_factory.cpp | 29 ----- src/editor/graph/nodes/graph_node_factory.h | 38 ------ src/editor/register_editor_types.cpp | 3 + src/editor/theme/theme_cache.cpp | 29 +++++ src/script/nodes/utilities/comment.cpp | 46 +++++++ src/script/nodes/utilities/comment.h | 26 ++++ 12 files changed, 355 insertions(+), 82 deletions(-) delete mode 100644 src/editor/graph/nodes/graph_node_factory.cpp delete mode 100644 src/editor/graph/nodes/graph_node_factory.h diff --git a/src/editor/graph/graph_edit.cpp b/src/editor/graph/graph_edit.cpp index c2fec447..2bfa2268 100644 --- a/src/editor/graph/graph_edit.cpp +++ b/src/editor/graph/graph_edit.cpp @@ -29,8 +29,8 @@ #include "editor/graph/graph_node_pin.h" #include "editor/graph/graph_node_spawner.h" #include "editor/graph/nodes/graph_node_comment.h" +#include "editor/graph/nodes/graph_node_default.h" #include "editor/plugins/orchestrator_editor_plugin.h" -#include "nodes/graph_node_factory.h" #include "script/language.h" #include "script/nodes/script_nodes.h" #include "script/script.h" @@ -249,6 +249,9 @@ void OrchestratorGraphEdit::_notification(int p_what) connect("copy_nodes_request", callable_mp(this, &OrchestratorGraphEdit::_on_copy_nodes_request)); connect("duplicate_nodes_request", callable_mp(this, &OrchestratorGraphEdit::_on_duplicate_nodes_request)); connect("paste_nodes_request", callable_mp(this, &OrchestratorGraphEdit::_on_paste_nodes_request)); + #if GODOT_VERSION >= 0x040300 + connect("graph_elements_linked_to_frame_request", callable_mp(this, &OrchestratorGraphEdit::_on_attach_to_frame)); + #endif _context_menu->connect("id_pressed", callable_mp(this, &OrchestratorGraphEdit::_on_context_menu_selection)); @@ -359,11 +362,7 @@ void OrchestratorGraphEdit::goto_class_help(const String& p_class_name) void OrchestratorGraphEdit::for_each_graph_node(std::function p_func) { - for (int i = 0; i < get_child_count(); i++) - { - if (OrchestratorGraphNode* node = Object::cast_to(get_child(i))) - p_func(node); - } + for_each_child_type(p_func); } void OrchestratorGraphEdit::execute_action(const String& p_action_name) @@ -1072,7 +1071,7 @@ void OrchestratorGraphEdit::_synchronize_graph_knots() } } -void OrchestratorGraphEdit::_synchronize_graph_node(Ref p_node) +void OrchestratorGraphEdit::_synchronize_graph_node(const Ref& p_node) { if (!p_node.is_valid()) return; @@ -1082,11 +1081,32 @@ void OrchestratorGraphEdit::_synchronize_graph_node(Ref p_node) { const Vector2 node_size = p_node->get_size(); - OrchestratorGraphNode* graph_node = OrchestratorGraphNodeFactory::create_node(this, p_node); - graph_node->set_title(p_node->get_node_title()); - graph_node->set_position_offset(p_node->get_position()); - graph_node->set_size(node_size.is_zero_approx() ? graph_node->get_size() : node_size); - add_child(graph_node); + Ref comment = p_node; + if (comment.is_valid()) + { + #if GODOT_VERSION >= 0x040300 + OrchestratorGraphFrameComment* frame = memnew(OrchestratorGraphFrameComment(this, p_node)); + frame->set_title(p_node->get_node_title()); + frame->set_position_offset(p_node->get_position()); + frame->set_size(node_size.is_zero_approx() ? frame->get_size() : node_size); + frame->set_name(vformat("%s", node_id)); + add_child(frame); + #else + OrchestratorGraphNodeComment* node = memnew(OrchestratorGraphNodeComment(this, p_node)); + node->set_title(p_node->get_node_title()); + node->set_position_offset(p_node->get_position()); + node->set_size(node_size.is_zero_approx() ? node->get_size() : node_size); + add_child(node); + #endif + } + else + { + OrchestratorGraphNode* graph_node = memnew(OrchestratorGraphNodeDefault(this, p_node)); + graph_node->set_title(p_node->get_node_title()); + graph_node->set_position_offset(p_node->get_position()); + graph_node->set_size(node_size.is_zero_approx() ? graph_node->get_size() : node_size); + add_child(graph_node); + } } else { @@ -1105,6 +1125,51 @@ void OrchestratorGraphEdit::_synchronize_child_order() comment->call_deferred("raise_request_node_reorder"); } }); + + #if GODOT_VERSION >= 0x040300 + // Performs a small update synchronization step for comment nodes to track their frame attachments + bool any_upgraded = false; + for_each_child_type([&, this](OrchestratorGraphFrameComment* frame) { + Ref comment = frame->get_comment_node(); + if (comment.is_valid()) + { + if (comment->get_state() == OScriptNodeComment::State_Initial) + { + // Collect a list of nodes that overlap but are not attached to the frame + Vector overlaps; + for_each_child_type([&](OrchestratorGraphNode* node) { + if (frame->get_rect().intersects(node->get_rect())) + overlaps.push_back(node->get_name()); + }); + + // Compare existing attachments with overlaps + PackedInt64Array attachment_node_ids; + for (const StringName& overlap : overlaps) + { + if (OrchestratorGraphNode* node = _get_by_name(overlap)) + { + attach_graph_element_to_frame(overlap, frame->get_name()); + attachment_node_ids.push_back(node->get_script_node_id()); + } + } + + // Update attachment list + comment->set_attachments(attachment_node_ids); + any_upgraded = true; + } + else if (comment->get_state() >= OScriptNodeComment::State_Tracks_Attachments) + { + const PackedInt64Array attachments = comment->get_attachments(); + for (int i = 0; i < attachments.size(); i++) + attach_graph_element_to_frame(itos(attachments[i]), frame->get_name()); + } + } + }); + + if (any_upgraded) + UtilityFunctions::print(get_orchestration()->get_self()->get_path(), ": Graph '", _script_graph->get_graph_name(), "': Comment nodes have been upgraded to GraphFrame(s)."); + + #endif } void OrchestratorGraphEdit::_complete_spawn(const Ref& p_spawned, const Callable& p_callback) @@ -1402,15 +1467,21 @@ void OrchestratorGraphEdit::_on_node_selected(Node* p_node) return; OrchestratorGraphNode* graph_node = Object::cast_to(p_node); + #if GODOT_VERSION >= 0x040300 + OrchestratorGraphFrameComment* frame_node = Object::cast_to(p_node); + if (!graph_node && !frame_node) + return; + #else if (!graph_node) return; + #endif Ref node = p_node->get_meta("__script_node"); if (node.is_null()) return; OrchestratorSettings* os = OrchestratorSettings::get_singleton(); - if (os->get_setting("ui/nodes/highlight_selected_connections", false)) + if (os->get_setting("ui/nodes/highlight_selected_connections", false) && !frame_node) { // Get list of all selected nodes List> selected_nodes; @@ -1830,4 +1901,22 @@ void OrchestratorGraphEdit::_on_grid_style_selected(int p_index) const GridPattern pattern = static_cast(int(_grid_pattern->get_item_metadata(p_index))); set_grid_pattern(pattern); } + +void OrchestratorGraphEdit::_on_attach_to_frame(const TypedArray& p_elements, const StringName& p_frame_name) +{ + for (int i = 0; i < p_elements.size(); i++) + { + OrchestratorGraphFrameComment* frame = _get_by_name(p_frame_name); + OrchestratorGraphNode* node = _get_by_name(p_elements[i]); + if (frame && node) + { + PackedInt64Array attachments = frame->get_comment_node()->get_attachments(); + attachments.push_back(node->get_script_node_id()); + + attach_graph_element_to_frame(p_elements[i], p_frame_name); + frame->get_comment_node()->set_attachments(attachments); + } + } +} + #endif \ No newline at end of file diff --git a/src/editor/graph/graph_edit.h b/src/editor/graph/graph_edit.h index 60445b0c..4bfe7a73 100644 --- a/src/editor/graph/graph_edit.h +++ b/src/editor/graph/graph_edit.h @@ -204,8 +204,22 @@ class OrchestratorGraphEdit : public GraphEdit /// Perform an action for each graph node /// @param p_func the lambda to be applied + /// @deprecated use for_each_child_type void for_each_graph_node(std::function p_func); + /// Perform an action for each child type + /// @tparam T the child class type to only operate on + /// @param p_func the lambda to be applied + template + void for_each_child_type(std::function p_func) + { + for (int i = 0; i < get_child_count(); i++) + { + if (T* child = Object::cast_to(get_child(i))) + p_func(child); + } + } + /// Execute the specified action /// @param p_action_name the action to execute void execute_action(const String& p_action_name); @@ -315,7 +329,7 @@ class OrchestratorGraphEdit : public GraphEdit /// Updates only the specific graph node /// @param p_node the node to update. - void _synchronize_graph_node(Ref p_node); + void _synchronize_graph_node(const Ref& p_node); /// Synchronizes the child order void _synchronize_child_order(); @@ -458,6 +472,11 @@ class OrchestratorGraphEdit : public GraphEdit /// Dispatched when a grid style option is selected /// @param p_index the selected item index void _on_grid_style_selected(int p_index); + + /// Dispatched when dragging nodes to be attached to a frame + /// @param p_elements graph elements to be attached + /// @param p_frame_name the frame name to be linked + void _on_attach_to_frame(const TypedArray& p_elements, const StringName& p_frame_name); #endif }; diff --git a/src/editor/graph/graph_node.cpp b/src/editor/graph/graph_node.cpp index 6adb7807..cdd7cca7 100644 --- a/src/editor/graph/graph_node.cpp +++ b/src/editor/graph/graph_node.cpp @@ -26,6 +26,7 @@ #include "script/nodes/editable_pin_node.h" #include "script/nodes/functions/call_function.h" #include "script/nodes/functions/call_script_function.h" +#include "script/nodes/utilities/comment.h" #include "script/script.h" #include @@ -470,7 +471,16 @@ void OrchestratorGraphNode::_show_context_menu(const Vector2& p_position) if (!call_script_function.is_valid()) _context_menu->set_item_disabled(_context_menu->get_item_index(CM_EXPAND_NODE), true); + #if GODOT_VERSION >= 0x040300 + Ref comment = _node; + if (comment.is_null()) + { + // Anything but comments + if (GraphFrame* frame = get_graph()->get_element_frame(get_name())) + _context_menu->add_item("Detach from comment frame", CM_DETACH_FRAME); + } + _context_menu->add_separator("Breakpoints"); _context_menu->add_item("Toggle Breakpoint", CM_TOGGLE_BREAKPOINT, KEY_F9); if (_node->has_breakpoint()) @@ -774,6 +784,11 @@ void OrchestratorGraphNode::_on_context_menu_selection(int p_id) _set_breakpoint_state(OScriptNode::BreakpointFlags::BREAKPOINT_DISABLED); break; } + case CM_DETACH_FRAME: + { + get_graph()->detach_graph_element_from_frame(get_name()); + break; + } #endif #ifdef _DEBUG case CM_SHOW_DETAILS: diff --git a/src/editor/graph/graph_node.h b/src/editor/graph/graph_node.h index d74c5a94..2c63505b 100644 --- a/src/editor/graph/graph_node.h +++ b/src/editor/graph/graph_node.h @@ -65,6 +65,7 @@ class OrchestratorGraphNode : public GraphNode CM_COLLAPSE_FUNCTION, CM_EXPAND_NODE, CM_RESIZABLE, + CM_DETACH_FRAME, #ifdef _DEBUG CM_SHOW_DETAILS = 999, #endif diff --git a/src/editor/graph/nodes/graph_node_comment.cpp b/src/editor/graph/nodes/graph_node_comment.cpp index 11bc3888..de3bb145 100644 --- a/src/editor/graph/nodes/graph_node_comment.cpp +++ b/src/editor/graph/nodes/graph_node_comment.cpp @@ -17,6 +17,8 @@ #include "graph_node_comment.h" #include "editor/graph/graph_edit.h" +#include "editor/plugins/orchestrator_editor_plugin.h" +#include "editor/theme/theme_cache.h" #include #include @@ -151,4 +153,76 @@ void OrchestratorGraphNodeComment::raise_request_node_reorder() List intersections = get_nodes_within_global_rect(); for (OrchestratorGraphNode* node : intersections) get_parent()->move_child(node, -1); -} \ No newline at end of file +} + +#if GODOT_VERSION >= 0x040300 +void OrchestratorGraphFrameComment::_node_moved(Vector2 p_old_pos, Vector2 p_new_pos) +{ + _node->set_position(p_new_pos); +} + +void OrchestratorGraphFrameComment::_node_resized() +{ + _node->set_size(get_size()); +} + +void OrchestratorGraphFrameComment::_script_node_changed() +{ + set_tint_color(_node->get_background_color()); + set_title(_node->get_node_title()); + + Label* title = Object::cast_to