Skip to content

Commit

Permalink
GH-463 Adapt comment nodes to use GraphFrame
Browse files Browse the repository at this point in the history
  • Loading branch information
Naros committed Jun 30, 2024
1 parent 80b2b98 commit 879ec9e
Show file tree
Hide file tree
Showing 12 changed files with 355 additions and 82 deletions.
115 changes: 102 additions & 13 deletions src/editor/graph/graph_edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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));

Expand Down Expand Up @@ -359,11 +362,7 @@ void OrchestratorGraphEdit::goto_class_help(const String& p_class_name)

void OrchestratorGraphEdit::for_each_graph_node(std::function<void(OrchestratorGraphNode*)> p_func)
{
for (int i = 0; i < get_child_count(); i++)
{
if (OrchestratorGraphNode* node = Object::cast_to<OrchestratorGraphNode>(get_child(i)))
p_func(node);
}
for_each_child_type<OrchestratorGraphNode>(p_func);
}

void OrchestratorGraphEdit::execute_action(const String& p_action_name)
Expand Down Expand Up @@ -1072,7 +1071,7 @@ void OrchestratorGraphEdit::_synchronize_graph_knots()
}
}

void OrchestratorGraphEdit::_synchronize_graph_node(Ref<OScriptNode> p_node)
void OrchestratorGraphEdit::_synchronize_graph_node(const Ref<OScriptNode>& p_node)
{
if (!p_node.is_valid())
return;
Expand All @@ -1082,11 +1081,32 @@ void OrchestratorGraphEdit::_synchronize_graph_node(Ref<OScriptNode> 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<OScriptNodeComment> 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
{
Expand All @@ -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<OrchestratorGraphFrameComment>([&, this](OrchestratorGraphFrameComment* frame) {
Ref<OScriptNodeComment> 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<StringName> overlaps;
for_each_child_type<OrchestratorGraphNode>([&](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<OrchestratorGraphNode>(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<OScriptNode>& p_spawned, const Callable& p_callback)
Expand Down Expand Up @@ -1402,15 +1467,21 @@ void OrchestratorGraphEdit::_on_node_selected(Node* p_node)
return;

OrchestratorGraphNode* graph_node = Object::cast_to<OrchestratorGraphNode>(p_node);
#if GODOT_VERSION >= 0x040300
OrchestratorGraphFrameComment* frame_node = Object::cast_to<OrchestratorGraphFrameComment>(p_node);
if (!graph_node && !frame_node)
return;
#else
if (!graph_node)
return;
#endif

Ref<OScriptNode> 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<Ref<OScriptNode>> selected_nodes;
Expand Down Expand Up @@ -1830,4 +1901,22 @@ void OrchestratorGraphEdit::_on_grid_style_selected(int p_index)
const GridPattern pattern = static_cast<GridPattern>(int(_grid_pattern->get_item_metadata(p_index)));
set_grid_pattern(pattern);
}

void OrchestratorGraphEdit::_on_attach_to_frame(const TypedArray<StringName>& p_elements, const StringName& p_frame_name)
{
for (int i = 0; i < p_elements.size(); i++)
{
OrchestratorGraphFrameComment* frame = _get_by_name<OrchestratorGraphFrameComment>(p_frame_name);
OrchestratorGraphNode* node = _get_by_name<OrchestratorGraphNode>(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
21 changes: 20 additions & 1 deletion src/editor/graph/graph_edit.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<void(OrchestratorGraphNode*)> 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 <typename T>
void for_each_child_type(std::function<void(T*)> p_func)
{
for (int i = 0; i < get_child_count(); i++)
{
if (T* child = Object::cast_to<T>(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);
Expand Down Expand Up @@ -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<OScriptNode> p_node);
void _synchronize_graph_node(const Ref<OScriptNode>& p_node);

/// Synchronizes the child order
void _synchronize_child_order();
Expand Down Expand Up @@ -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<StringName>& p_elements, const StringName& p_frame_name);
#endif
};

Expand Down
15 changes: 15 additions & 0 deletions src/editor/graph/graph_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <godot_cpp/classes/button.hpp>
Expand Down Expand Up @@ -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<OScriptNodeComment> 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())
Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions src/editor/graph/graph_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
76 changes: 75 additions & 1 deletion src/editor/graph/nodes/graph_node_comment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <godot_cpp/classes/input_event_mouse_button.hpp>
#include <godot_cpp/classes/margin_container.hpp>
Expand Down Expand Up @@ -151,4 +153,76 @@ void OrchestratorGraphNodeComment::raise_request_node_reorder()
List<OrchestratorGraphNode*> intersections = get_nodes_within_global_rect();
for (OrchestratorGraphNode* node : intersections)
get_parent()->move_child(node, -1);
}
}

#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<Label>(get_titlebar_hbox()->get_child(0));
if (title)
{
title->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
if (_node->is_title_center_aligned())
title->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
}

const int font_size = _node->get_font_size();
_text->add_theme_font_size_override("font_size", font_size != 0 ? font_size : 14);
_text->set_text(_node->get("comments"));
_text->add_theme_color_override("font_color", _node->get_text_color());
}

void OrchestratorGraphFrameComment::_notification(int p_what)
{
if (p_what == NOTIFICATION_READY)
{
// Used to replicate size/position state to underlying node resource
connect("dragged", callable_mp(this, &OrchestratorGraphFrameComment::_node_moved));
connect("resized", callable_mp(this, &OrchestratorGraphFrameComment::_node_resized));

_node->connect("changed", callable_mp(this, &OrchestratorGraphFrameComment::_script_node_changed));

Ref<OrchestratorThemeCache> cache = OrchestratorPlugin::get_singleton()->get_theme_cache();
if (cache.is_valid())
{
begin_bulk_theme_override();
add_theme_stylebox_override("panel", cache->get_theme_stylebox("panel", "GraphFrame"));
add_theme_stylebox_override("panel_selected", cache->get_theme_stylebox("panel_selected", "GraphFrame"));
end_bulk_theme_override();
}
}
}

void OrchestratorGraphFrameComment::_bind_methods()
{
}

OrchestratorGraphFrameComment::OrchestratorGraphFrameComment(OrchestratorGraphEdit* p_graph, const Ref<OScriptNodeComment>& p_node)
: _graph(p_graph)
, _node(p_node)
{
set_meta("__script_node", p_node);
set_tint_color_enabled(true);

_text = memnew(Label);
_text->set_vertical_alignment(VERTICAL_ALIGNMENT_TOP);
_text->set_v_size_flags(SIZE_SHRINK_BEGIN);

add_child(_text);

_script_node_changed();
}
#endif
Loading

0 comments on commit 879ec9e

Please sign in to comment.