Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GraphEdit] Make connections a property #97449

Merged
merged 1 commit into from
Dec 16, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 37 additions & 8 deletions doc/classes/GraphEdit.xml
Original file line number Diff line number Diff line change
@@ -130,8 +130,10 @@
<param index="1" name="from_port" type="int" />
<param index="2" name="to_node" type="StringName" />
<param index="3" name="to_port" type="int" />
<param index="4" name="keep_alive" type="bool" default="false" />
<description>
Create a connection between the [param from_port] of the [param from_node] [GraphNode] and the [param to_port] of the [param to_node] [GraphNode]. If the connection already exists, no connection is created.
Connections with [param keep_alive] set to [code]false[/code] may be deleted automatically if invalid during a redraw.
</description>
</method>
<method name="detach_graph_element_from_frame">
@@ -172,7 +174,16 @@
<param index="1" name="max_distance" type="float" default="4.0" />
<description>
Returns the closest connection to the given point in screen space. If no connection is found within [param max_distance] pixels, an empty [Dictionary] is returned.
A connection consists in a structure of the form [code]{ from_port: 0, from_node: "GraphNode name 0", to_port: 1, to_node: "GraphNode name 1" }[/code].
A connection is represented as a [Dictionary] in the form of:
[codeblock]
{
from_node: StringName,
from_port: int,
to_node: StringName,
to_port: int,
keep_alive: bool
}
[/codeblock]
For example, getting a connection at a given mouse position can be achieved like this:
[codeblocks]
[gdscript]
@@ -197,17 +208,21 @@
Returns the points which would make up a connection between [param from_node] and [param to_node].
</description>
</method>
<method name="get_connection_list" qualifiers="const">
<return type="Dictionary[]" />
<description>
Returns an [Array] containing the list of connections. A connection consists in a structure of the form [code]{ from_port: 0, from_node: "GraphNode name 0", to_port: 1, to_node: "GraphNode name 1" }[/code].
</description>
</method>
<method name="get_connections_intersecting_with_rect" qualifiers="const">
<return type="Dictionary[]" />
<param index="0" name="rect" type="Rect2" />
<description>
Returns an [Array] containing the list of connections that intersect with the given [Rect2]. A connection consists in a structure of the form [code]{ from_port: 0, from_node: "GraphNode name 0", to_port: 1, to_node: "GraphNode name 1" }[/code].
Returns an [Array] containing the list of connections that intersect with the given [Rect2].
A connection is represented as a [Dictionary] in the form of:
[codeblock]
{
from_node: StringName,
from_port: int,
to_node: StringName,
to_port: int,
keep_alive: bool
}
[/codeblock]
</description>
</method>
<method name="get_element_frame">
@@ -296,6 +311,20 @@
<member name="connection_lines_thickness" type="float" setter="set_connection_lines_thickness" getter="get_connection_lines_thickness" default="4.0">
The thickness of the lines between the nodes.
</member>
<member name="connections" type="Dictionary[]" setter="set_connections" getter="get_connection_list" default="[]">
The connections between [GraphNode]s.
A connection is represented as a [Dictionary] in the form of:
[codeblock]
{
from_node: StringName,
from_port: int,
to_node: StringName,
to_port: int,
keep_alive: bool
}
[/codeblock]
Connections with [code]keep_alive[/code] set to [code]false[/code] may be deleted automatically if invalid during a redraw.
</member>
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="2" />
<member name="grid_pattern" type="int" setter="set_grid_pattern" getter="get_grid_pattern" enum="GraphEdit.GridPattern" default="0">
The pattern used for drawing the grid.
7 changes: 7 additions & 0 deletions misc/extension_api_validation/4.3-stable.expected
Original file line number Diff line number Diff line change
@@ -236,3 +236,10 @@ Validate extension JSON: Error: Field 'classes/EditorInterface/methods/open_scen

Added optional argument to open_scene_from_path to create a new inherited scene.
Compatibility method registered.


GH-97449
--------
Validate extension JSON: Error: Field 'classes/GraphEdit/methods/connect_node/arguments': size changed value in new API, from 4 to 5.

Added optional argument to connect_node to specify whether the connection should be automatically deleted if invalid. Compatibility method registered.
5 changes: 5 additions & 0 deletions scene/gui/graph_edit.compat.inc
Original file line number Diff line number Diff line change
@@ -42,10 +42,15 @@ PackedVector2Array GraphEdit::_get_connection_line_bind_compat_86158(const Vecto
return get_connection_line(p_from, p_to);
}

Error GraphEdit::_connect_node_bind_compat_97449(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
return connect_node(p_from, p_from_port, p_to, p_to_port);
}

void GraphEdit::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("is_arrange_nodes_button_hidden"), &GraphEdit::_is_arrange_nodes_button_hidden_bind_compat_81582);
ClassDB::bind_compatibility_method(D_METHOD("set_arrange_nodes_button_hidden", "enable"), &GraphEdit::_set_arrange_nodes_button_hidden_bind_compat_81582);
ClassDB::bind_compatibility_method(D_METHOD("get_connection_line", "from_node", "to_node"), &GraphEdit::_get_connection_line_bind_compat_86158);
ClassDB::bind_compatibility_method(D_METHOD("connect_node", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::_connect_node_bind_compat_97449);
}

#endif
199 changes: 112 additions & 87 deletions scene/gui/graph_edit.cpp
Original file line number Diff line number Diff line change
@@ -259,7 +259,7 @@ PackedStringArray GraphEdit::get_configuration_warnings() const {
return warnings;
}

Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, bool p_keep_alive) {
ERR_FAIL_NULL_V_MSG(connections_layer, FAILED, "connections_layer is missing.");

if (is_node_connected(p_from, p_from_port, p_to, p_to_port)) {
@@ -272,6 +272,7 @@ Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const S
c->to_node = p_to;
c->to_port = p_to_port;
c->activity = 0;
c->keep_alive = p_keep_alive;
connections.push_back(c);
connection_map[p_from].push_back(c);
connection_map[p_to].push_back(c);
@@ -317,23 +318,28 @@ bool GraphEdit::is_node_connected(const StringName &p_from, int p_from_port, con
void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");

for (List<Ref<Connection>>::Element *E = connections.front(); E; E = E->next()) {
if (E->get()->from_node == p_from && E->get()->from_port == p_from_port && E->get()->to_node == p_to && E->get()->to_port == p_to_port) {
connection_map[p_from].erase(E->get());
connection_map[p_to].erase(E->get());
E->get()->_cache.line->queue_free();
connections.erase(E);

minimap->queue_redraw();
queue_redraw();
connections_layer->queue_redraw();
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
return;
Ref<Connection> conn_to_remove;
for (const Ref<Connection> &conn : connections) {
if (conn->from_node == p_from && conn->from_port == p_from_port && conn->to_node == p_to && conn->to_port == p_to_port) {
conn_to_remove = conn;
break;
}
}

if (conn_to_remove.is_valid()) {
connection_map[p_from].erase(conn_to_remove);
connection_map[p_to].erase(conn_to_remove);
conn_to_remove->_cache.line->queue_free();
connections.erase(conn_to_remove);

minimap->queue_redraw();
queue_redraw();
connections_layer->queue_redraw();
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
}
}

const List<Ref<GraphEdit::Connection>> &GraphEdit::get_connection_list() const {
const Vector<Ref<GraphEdit::Connection>> &GraphEdit::get_connections() const {
return connections;
}

@@ -585,8 +591,8 @@ void GraphEdit::_graph_node_rect_changed(GraphNode *p_node) {
return;
}

for (Ref<Connection> &c : connection_map[p_node->get_name()]) {
c->_cache.dirty = true;
for (Ref<Connection> &conn : connection_map[p_node->get_name()]) {
conn->_cache.dirty = true;
}
connections_layer->queue_redraw();
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
@@ -1299,16 +1305,16 @@ Ref<GraphEdit::Connection> GraphEdit::get_closest_connection_at_point(const Vect

Ref<GraphEdit::Connection> closest_connection;
float closest_distance = p_max_distance;
for (const Ref<Connection> &c : connections) {
if (c->_cache.aabb.distance_to(transformed_point) > p_max_distance) {
for (const Ref<Connection> &conn : connections) {
if (conn->_cache.aabb.distance_to(transformed_point) > p_max_distance) {
continue;
}

Vector<Vector2> points = get_connection_line(c->_cache.from_pos * zoom, c->_cache.to_pos * zoom);
Vector<Vector2> points = get_connection_line(conn->_cache.from_pos * zoom, conn->_cache.to_pos * zoom);
for (int i = 0; i < points.size() - 1; i++) {
float distance = Geometry2D::get_distance_to_segment(transformed_point, &points[i]);
if (distance <= lines_thickness * 0.5 + p_max_distance && distance < closest_distance) {
closest_connection = c;
closest_connection = conn;
closest_distance = distance;
}
}
@@ -1322,15 +1328,15 @@ List<Ref<GraphEdit::Connection>> GraphEdit::get_connections_intersecting_with_re
transformed_rect.position += get_scroll_offset();

List<Ref<Connection>> intersecting_connections;
for (const Ref<Connection> &c : connections) {
if (!c->_cache.aabb.intersects(transformed_rect)) {
for (const Ref<Connection> &conn : connections) {
if (!conn->_cache.aabb.intersects(transformed_rect)) {
continue;
}

Vector<Vector2> points = get_connection_line(c->_cache.from_pos * zoom, c->_cache.to_pos * zoom);
Vector<Vector2> points = get_connection_line(conn->_cache.from_pos * zoom, conn->_cache.to_pos * zoom);
for (int i = 0; i < points.size() - 1; i++) {
if (Geometry2D::segment_intersects_rect(points[i], points[i + 1], transformed_rect)) {
intersecting_connections.push_back(c);
intersecting_connections.push_back(conn);
break;
}
}
@@ -1362,47 +1368,49 @@ void GraphEdit::_draw_minimap_connection_line(const Vector2 &p_from_graph_positi

void GraphEdit::_update_connections() {
// Collect all dead connections and remove them.
List<List<Ref<Connection>>::Element *> dead_connections;
LocalVector<Ref<Connection>> dead_connections;

for (List<Ref<Connection>>::Element *E = connections.front(); E; E = E->next()) {
Ref<Connection> &c = E->get();

if (c->_cache.dirty) {
Node *from = get_node_or_null(NodePath(c->from_node));
for (const Ref<Connection> &conn : connections) {
if (conn->_cache.dirty) {
Node *from = get_node_or_null(NodePath(conn->from_node));
GraphNode *gnode_from = Object::cast_to<GraphNode>(from);
if (!gnode_from) {
dead_connections.push_back(E);
if (!gnode_from && !conn->keep_alive) {
dead_connections.push_back(conn);
continue;
}
Node *to = get_node_or_null(NodePath(c->to_node));
Node *to = get_node_or_null(NodePath(conn->to_node));
GraphNode *gnode_to = Object::cast_to<GraphNode>(to);

if (!gnode_to) {
dead_connections.push_back(E);
if (!gnode_to && !conn->keep_alive) {
dead_connections.push_back(conn);
continue;
}

if (conn->keep_alive && (!gnode_from || !gnode_to)) {
continue;
}

const Vector2 from_pos = gnode_from->get_output_port_position(c->from_port) + gnode_from->get_position_offset();
const Vector2 to_pos = gnode_to->get_input_port_position(c->to_port) + gnode_to->get_position_offset();
const Vector2 from_pos = gnode_from->get_output_port_position(conn->from_port) + gnode_from->get_position_offset();
const Vector2 to_pos = gnode_to->get_input_port_position(conn->to_port) + gnode_to->get_position_offset();

const Color from_color = gnode_from->get_output_port_color(c->from_port);
const Color to_color = gnode_to->get_input_port_color(c->to_port);
const Color from_color = gnode_from->get_output_port_color(conn->from_port);
const Color to_color = gnode_to->get_input_port_color(conn->to_port);

const int from_type = gnode_from->get_output_port_type(c->from_port);
const int to_type = gnode_to->get_input_port_type(c->to_port);
const int from_type = gnode_from->get_output_port_type(conn->from_port);
const int to_type = gnode_to->get_input_port_type(conn->to_port);

c->_cache.from_pos = from_pos;
c->_cache.to_pos = to_pos;
c->_cache.from_color = from_color;
c->_cache.to_color = to_color;
conn->_cache.from_pos = from_pos;
conn->_cache.to_pos = to_pos;
conn->_cache.from_color = from_color;
conn->_cache.to_color = to_color;

PackedVector2Array line_points = get_connection_line(from_pos * zoom, to_pos * zoom);
c->_cache.line->set_points(line_points);
conn->_cache.line->set_points(line_points);

Ref<ShaderMaterial> line_material = c->_cache.line->get_material();
Ref<ShaderMaterial> line_material = conn->_cache.line->get_material();
if (line_material.is_null()) {
line_material.instantiate();
c->_cache.line->set_material(line_material);
conn->_cache.line->set_material(line_material);
}

float line_width = _get_shader_line_width();
@@ -1412,31 +1420,31 @@ void GraphEdit::_update_connections() {
line_material->set_shader_parameter("rim_color", theme_cache.connection_rim_color);

// Compute bounding box of the line, including the line width.
c->_cache.aabb = Rect2(line_points[0], Vector2());
conn->_cache.aabb = Rect2(line_points[0], Vector2());
for (int i = 0; i < line_points.size(); i++) {
c->_cache.aabb.expand_to(line_points[i]);
conn->_cache.aabb.expand_to(line_points[i]);
}
c->_cache.aabb.grow_by(lines_thickness * 0.5);
conn->_cache.aabb.grow_by(lines_thickness * 0.5);

c->_cache.dirty = false;
conn->_cache.dirty = false;
}

// Skip updating/drawing connections that are not visible.
Rect2 viewport_rect = get_viewport_rect();
viewport_rect.position += get_scroll_offset();
if (!c->_cache.aabb.intersects(viewport_rect)) {
if (!conn->_cache.aabb.intersects(viewport_rect)) {
continue;
}

Color from_color = c->_cache.from_color;
Color to_color = c->_cache.to_color;
Color from_color = conn->_cache.from_color;
Color to_color = conn->_cache.to_color;

if (c->activity > 0) {
from_color = from_color.lerp(theme_cache.activity_color, c->activity);
to_color = to_color.lerp(theme_cache.activity_color, c->activity);
if (conn->activity > 0) {
from_color = from_color.lerp(theme_cache.activity_color, conn->activity);
to_color = to_color.lerp(theme_cache.activity_color, conn->activity);
}

if (c == hovered_connection) {
if (conn == hovered_connection) {
from_color = from_color.blend(theme_cache.connection_hover_tint_color);
to_color = to_color.blend(theme_cache.connection_hover_tint_color);
}
@@ -1445,21 +1453,21 @@ void GraphEdit::_update_connections() {
Ref<Gradient> line_gradient = memnew(Gradient);

float line_width = _get_shader_line_width();
c->_cache.line->set_width(line_width);
conn->_cache.line->set_width(line_width);
line_gradient->set_color(0, from_color);
line_gradient->set_color(1, to_color);

c->_cache.line->set_gradient(line_gradient);
conn->_cache.line->set_gradient(line_gradient);
}

for (const List<Ref<Connection>>::Element *E : dead_connections) {
List<Ref<Connection>> &connections_from = connection_map[E->get()->from_node];
List<Ref<Connection>> &connections_to = connection_map[E->get()->to_node];
connections_from.erase(E->get());
connections_to.erase(E->get());
E->get()->_cache.line->queue_free();
for (const Ref<Connection> &dead_conn : dead_connections) {
List<Ref<Connection>> &connections_from = connection_map[dead_conn->from_node];
List<Ref<Connection>> &connections_to = connection_map[dead_conn->to_node];
connections_from.erase(dead_conn);
connections_to.erase(dead_conn);
dead_conn->_cache.line->queue_free();

connections.erase(E->get());
connections.erase(dead_conn);
}
}

@@ -1611,15 +1619,15 @@ void GraphEdit::_minimap_draw() {
}

// Draw node connections.
for (const Ref<Connection> &c : connections) {
Vector2 from_graph_position = c->_cache.from_pos * zoom - graph_offset;
Vector2 to_graph_position = c->_cache.to_pos * zoom - graph_offset;
Color from_color = c->_cache.from_color;
Color to_color = c->_cache.to_color;
for (const Ref<Connection> &conn : connections) {
Vector2 from_graph_position = conn->_cache.from_pos * zoom - graph_offset;
Vector2 to_graph_position = conn->_cache.to_pos * zoom - graph_offset;
Color from_color = conn->_cache.from_color;
Color to_color = conn->_cache.to_color;

if (c->activity > 0) {
from_color = from_color.lerp(theme_cache.activity_color, c->activity);
to_color = to_color.lerp(theme_cache.activity_color, c->activity);
if (conn->activity > 0) {
from_color = from_color.lerp(theme_cache.activity_color, conn->activity);
to_color = to_color.lerp(theme_cache.activity_color, conn->activity);
}

_draw_minimap_connection_line(from_graph_position, to_graph_position, from_color, to_color);
@@ -2074,16 +2082,16 @@ void GraphEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputE
void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) {
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");

for (Ref<Connection> &c : connection_map[p_from]) {
if (c->from_node == p_from && c->from_port == p_from_port && c->to_node == p_to && c->to_port == p_to_port) {
if (!Math::is_equal_approx(c->activity, p_activity)) {
for (Ref<Connection> &conn : connection_map[p_from]) {
if (conn->from_node == p_from && conn->from_port == p_from_port && conn->to_node == p_to && conn->to_port == p_to_port) {
if (!Math::is_equal_approx(conn->activity, p_activity)) {
// Update only if changed.
minimap->queue_redraw();
c->_cache.dirty = true;
conn->_cache.dirty = true;
connections_layer->queue_redraw();
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
}
c->activity = p_activity;
conn->activity = p_activity;
return;
}
}
@@ -2108,8 +2116,8 @@ void GraphEdit::reset_all_connection_activity() {
void GraphEdit::clear_connections() {
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");

for (Ref<Connection> &c : connections) {
c->_cache.line->queue_free();
for (Ref<Connection> &conn : connections) {
conn->_cache.line->queue_free();
}

connections.clear();
@@ -2256,8 +2264,20 @@ void GraphEdit::remove_valid_left_disconnect_type(int p_type) {
valid_left_disconnect_types.erase(p_type);
}

void GraphEdit::set_connections(const TypedArray<Dictionary> &p_connections) {
clear_connections();

bool is_editor = Engine::get_singleton()->is_editor_hint();

for (const Dictionary d : p_connections) {
// Always keep the connection alive in case it is created using the inspector.
bool keep_alive = (is_editor && d.is_empty()) || d["keep_alive"];
connect_node(d["from_node"], d["from_port"], d["to_node"], d["to_port"], keep_alive);
}
}

TypedArray<Dictionary> GraphEdit::_get_connection_list() const {
List<Ref<Connection>> conns = get_connection_list();
Vector<Ref<Connection>> conns = get_connections();

TypedArray<Dictionary> arr;
for (const Ref<Connection> &conn : conns) {
@@ -2266,6 +2286,7 @@ TypedArray<Dictionary> GraphEdit::_get_connection_list() const {
d["from_port"] = conn->from_port;
d["to_node"] = conn->to_node;
d["to_port"] = conn->to_port;
d["keep_alive"] = conn->keep_alive;
arr.push_back(d);
}
return arr;
@@ -2279,6 +2300,7 @@ Dictionary GraphEdit::_get_closest_connection_at_point(const Vector2 &p_point, f
ret["from_port"] = c->from_port;
ret["to_node"] = c->to_node;
ret["to_port"] = c->to_port;
ret["keep_alive"] = c->keep_alive;
}
return ret;
}
@@ -2293,6 +2315,7 @@ TypedArray<Dictionary> GraphEdit::_get_connections_intersecting_with_rect(const
d["from_port"] = conn->from_port;
d["to_node"] = conn->to_node;
d["to_port"] = conn->to_port;
d["keep_alive"] = conn->keep_alive;
arr.push_back(d);
}
return arr;
@@ -2317,8 +2340,8 @@ void GraphEdit::_update_zoom_label() {
}

void GraphEdit::_invalidate_connection_line_cache() {
for (Ref<Connection> &c : connections) {
c->_cache.dirty = true;
for (Ref<Connection> &conn : connections) {
conn->_cache.dirty = true;
}
}

@@ -2645,10 +2668,11 @@ void GraphEdit::arrange_nodes() {
}

void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("connect_node", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::connect_node);
ClassDB::bind_method(D_METHOD("connect_node", "from_node", "from_port", "to_node", "to_port", "keep_alive"), &GraphEdit::connect_node, DEFVAL(false));
ClassDB::bind_method(D_METHOD("is_node_connected", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::is_node_connected);
ClassDB::bind_method(D_METHOD("disconnect_node", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::disconnect_node);
ClassDB::bind_method(D_METHOD("set_connection_activity", "from_node", "from_port", "to_node", "to_port", "amount"), &GraphEdit::set_connection_activity);
ClassDB::bind_method(D_METHOD("set_connections", "connections"), &GraphEdit::set_connections);
ClassDB::bind_method(D_METHOD("get_connection_list"), &GraphEdit::_get_connection_list);
ClassDB::bind_method(D_METHOD("get_connection_count", "from_node", "from_port"), &GraphEdit::get_connection_count);
ClassDB::bind_method(D_METHOD("get_closest_connection_at_point", "point", "max_distance"), &GraphEdit::_get_closest_connection_at_point, DEFVAL(4.0));
@@ -2761,6 +2785,7 @@ void GraphEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_curvature"), "set_connection_lines_curvature", "get_connection_lines_curvature");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_thickness", PROPERTY_HINT_RANGE, "0,100,0.1,suffix:px"), "set_connection_lines_thickness", "get_connection_lines_thickness");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "connection_lines_antialiased"), "set_connection_lines_antialiased", "is_connection_lines_antialiased");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "connections", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::DICTIONARY, PROPERTY_HINT_NONE, String())), "set_connections", "get_connection_list");

ADD_GROUP("Zoom", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom");
11 changes: 7 additions & 4 deletions scene/gui/graph_edit.h
Original file line number Diff line number Diff line change
@@ -120,6 +120,7 @@ class GraphEdit : public Control {
int from_port = 0;
int to_port = 0;
float activity = 0.0;
bool keep_alive = true;

private:
struct Cache {
@@ -238,7 +239,7 @@ class GraphEdit : public Control {
bool updating = false;
bool awaiting_scroll_offset_update = false;

List<Ref<Connection>> connections;
Vector<Ref<Connection>> connections;
HashMap<StringName, List<Ref<Connection>>> connection_map;
Ref<Connection> hovered_connection;

@@ -339,6 +340,7 @@ class GraphEdit : public Control {

bool is_in_port_hotzone(const Vector2 &p_pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);

void set_connections(const TypedArray<Dictionary> &p_connections);
TypedArray<Dictionary> _get_connection_list() const;
Dictionary _get_closest_connection_at_point(const Vector2 &p_point, float p_max_distance = 4.0) const;
TypedArray<Dictionary> _get_connections_intersecting_with_rect(const Rect2 &p_rect) const;
@@ -362,6 +364,7 @@ class GraphEdit : public Control {
bool _is_arrange_nodes_button_hidden_bind_compat_81582() const;
void _set_arrange_nodes_button_hidden_bind_compat_81582(bool p_enable);
PackedVector2Array _get_connection_line_bind_compat_86158(const Vector2 &p_from, const Vector2 &p_to);
Error _connect_node_bind_compat_97449(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
#endif

protected:
@@ -397,14 +400,14 @@ class GraphEdit : public Control {
void _update_graph_frame(GraphFrame *p_frame);

// Connection related methods.
Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, bool keep_alive = false);
bool is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
int get_connection_count(const StringName &p_node, int p_port);
void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
void clear_connections();

void force_connection_drag_end();
const List<Ref<Connection>> &get_connection_list() const;
const Vector<Ref<Connection>> &get_connections() const;
void clear_connections();
virtual PackedVector2Array get_connection_line(const Vector2 &p_from, const Vector2 &p_to) const;
Ref<Connection> get_closest_connection_at_point(const Vector2 &p_point, float p_max_distance = 4.0) const;
List<Ref<Connection>> get_connections_intersecting_with_rect(const Rect2 &p_rect) const;
6 changes: 3 additions & 3 deletions scene/gui/graph_edit_arranger.cpp
Original file line number Diff line number Diff line change
@@ -65,7 +65,7 @@ void GraphEditArranger::arrange_nodes() {
float gap_v = 100.0f;
float gap_h = 100.0f;

List<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connection_list();
const Vector<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connections();

for (int i = graph_edit->get_child_count() - 1; i >= 0; i--) {
GraphNode *graph_element = Object::cast_to<GraphNode>(graph_edit->get_child(i));
@@ -438,7 +438,7 @@ float GraphEditArranger::_calculate_threshold(const StringName &p_v, const Strin
if (p_v == p_w) {
int min_order = MAX_ORDER;
Ref<GraphEdit::Connection> incoming;
List<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connection_list();
const Vector<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connections();
for (const Ref<GraphEdit::Connection> &connection : connection_list) {
if (connection->to_node == p_w) {
ORDER(connection->from_node, r_layers);
@@ -469,7 +469,7 @@ float GraphEditArranger::_calculate_threshold(const StringName &p_v, const Strin
// This time, pick an outgoing edge and repeat as above!
int min_order = MAX_ORDER;
Ref<GraphEdit::Connection> outgoing;
List<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connection_list();
const Vector<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connections();
for (const Ref<GraphEdit::Connection> &connection : connection_list) {
if (connection->from_node == p_w) {
ORDER(connection->to_node, r_layers);