Rework GraphEdit connections (drawing, API, optimizations)

- GraphEdit now uses Line2D nodes to draw connection lines and uses a dedicated canvas item shader for them
This commit is contained in:
Hendrik Brucker 2024-01-18 16:16:17 +01:00
parent 1952f64b07
commit 9d7c2978f4
10 changed files with 700 additions and 259 deletions

View File

@ -119,6 +119,10 @@ public:
} }
} }
static real_t get_distance_to_segment(const Vector2 &p_point, const Vector2 *p_segment) {
return p_point.distance_to(get_closest_point_to_segment(p_point, p_segment));
}
static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) { static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) {
Vector2 an = a - s; Vector2 an = a - s;
Vector2 bn = b - s; Vector2 bn = b - s;
@ -249,6 +253,28 @@ public:
return -1; return -1;
} }
static bool segment_intersects_rect(const Vector2 &p_from, const Vector2 &p_to, const Rect2 &p_rect) {
if (p_rect.has_point(p_from) || p_rect.has_point(p_to)) {
return true;
}
const Vector2 rect_points[4] = {
p_rect.position,
p_rect.position + Vector2(p_rect.size.x, 0),
p_rect.position + p_rect.size,
p_rect.position + Vector2(0, p_rect.size.y)
};
// Check if any of the rect's edges intersect the segment.
for (int i = 0; i < 4; i++) {
if (segment_intersects_segment(p_from, p_to, rect_points[i], rect_points[(i + 1) % 4], nullptr)) {
return true;
}
}
return false;
}
enum PolyBooleanOperation { enum PolyBooleanOperation {
OPERATION_UNION, OPERATION_UNION,
OPERATION_DIFFERENCE, OPERATION_DIFFERENCE,

View File

@ -143,7 +143,22 @@
[b]Note:[/b] This method suppresses any other connection request signals apart from [signal connection_drag_ended]. [b]Note:[/b] This method suppresses any other connection request signals apart from [signal connection_drag_ended].
</description> </description>
</method> </method>
<method name="get_connection_line"> <method name="get_closest_connection_at_point" qualifiers="const">
<return type="Dictionary" />
<param index="0" name="point" type="Vector2" />
<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].
For example, getting a connection at a given mouse position can be achieved like this:
[codeblocks]
[gdscript]
var connection = get_closest_connection_at_point(mouse_event.get_position())
[/gdscript]
[/codeblocks]
</description>
</method>
<method name="get_connection_line" qualifiers="const">
<return type="PackedVector2Array" /> <return type="PackedVector2Array" />
<param index="0" name="from_node" type="Vector2" /> <param index="0" name="from_node" type="Vector2" />
<param index="1" name="to_node" type="Vector2" /> <param index="1" name="to_node" type="Vector2" />
@ -154,7 +169,14 @@
<method name="get_connection_list" qualifiers="const"> <method name="get_connection_list" qualifiers="const">
<return type="Dictionary[]" /> <return type="Dictionary[]" />
<description> <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]. 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].
</description> </description>
</method> </method>
<method name="get_menu_hbox"> <method name="get_menu_hbox">
@ -233,7 +255,7 @@
<member name="connection_lines_curvature" type="float" setter="set_connection_lines_curvature" getter="get_connection_lines_curvature" default="0.5"> <member name="connection_lines_curvature" type="float" setter="set_connection_lines_curvature" getter="get_connection_lines_curvature" default="0.5">
The curvature of the lines between the nodes. 0 results in straight lines. The curvature of the lines between the nodes. 0 results in straight lines.
</member> </member>
<member name="connection_lines_thickness" type="float" setter="set_connection_lines_thickness" getter="get_connection_lines_thickness" default="2.0"> <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. The thickness of the lines between the nodes.
</member> </member>
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="2" /> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="2" />
@ -417,7 +439,16 @@
</constants> </constants>
<theme_items> <theme_items>
<theme_item name="activity" data_type="color" type="Color" default="Color(1, 1, 1, 1)"> <theme_item name="activity" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Color of the connection's activity (see [method set_connection_activity]). Color the connection line is interpolated to based on the activity value of a connection (see [method set_connection_activity]).
</theme_item>
<theme_item name="connection_hover_tint_color" data_type="color" type="Color" default="Color(0, 0, 0, 0.3)">
Color which is blended with the connection line when the mouse is hovering over it.
</theme_item>
<theme_item name="connection_rim_color" data_type="color" type="Color" default="Color(0.1, 0.1, 0.1, 0.6)">
Color of the rim around each connection line used for making intersecting lines more distinguishable.
</theme_item>
<theme_item name="connection_valid_target_tint_color" data_type="color" type="Color" default="Color(1, 1, 1, 0.4)">
Color which is blended with the connection line when the currently dragged connection is hovering over a valid target port.
</theme_item> </theme_item>
<theme_item name="grid_major" data_type="color" type="Color" default="Color(1, 1, 1, 0.2)"> <theme_item name="grid_major" data_type="color" type="Color" default="Color(1, 1, 1, 0.2)">
Color of major grid lines/dots. Color of major grid lines/dots.

View File

@ -1367,6 +1367,10 @@ void EditorThemeManager::_populate_standard_styles(const Ref<Theme> &p_theme, Th
p_theme->set_color("selection_stroke", "GraphEdit", p_theme->get_color(SNAME("box_selection_stroke_color"), EditorStringName(Editor))); p_theme->set_color("selection_stroke", "GraphEdit", p_theme->get_color(SNAME("box_selection_stroke_color"), EditorStringName(Editor)));
p_theme->set_color("activity", "GraphEdit", p_config.accent_color); p_theme->set_color("activity", "GraphEdit", p_config.accent_color);
p_theme->set_color("connection_hover_tint_color", "GraphEdit", p_config.dark_theme ? Color(0, 0, 0, 0.3) : Color(1, 1, 1, 0.3));
p_theme->set_color("connection_valid_target_tint_color", "GraphEdit", p_config.dark_theme ? Color(1, 1, 1, 0.4) : Color(0, 0, 0, 0.4));
p_theme->set_color("connection_rim_color", "GraphEdit", p_config.tree_panel_style->get_bg_color());
p_theme->set_icon("zoom_out", "GraphEdit", p_theme->get_icon(SNAME("ZoomLess"), EditorStringName(EditorIcons))); p_theme->set_icon("zoom_out", "GraphEdit", p_theme->get_icon(SNAME("ZoomLess"), EditorStringName(EditorIcons)));
p_theme->set_icon("zoom_in", "GraphEdit", p_theme->get_icon(SNAME("ZoomMore"), EditorStringName(EditorIcons))); p_theme->set_icon("zoom_in", "GraphEdit", p_theme->get_icon(SNAME("ZoomMore"), EditorStringName(EditorIcons)));
p_theme->set_icon("zoom_reset", "GraphEdit", p_theme->get_icon(SNAME("ZoomReset"), EditorStringName(EditorIcons))); p_theme->set_icon("zoom_reset", "GraphEdit", p_theme->get_icon(SNAME("ZoomReset"), EditorStringName(EditorIcons)));

View File

@ -59,3 +59,10 @@ Validate extension JSON: Error: Field 'classes/TileMap/methods/get_collision_vis
Validate extension JSON: Error: Field 'classes/TileMap/methods/get_navigation_visibility_mode': is_const changed value in new API, from false to true. Validate extension JSON: Error: Field 'classes/TileMap/methods/get_navigation_visibility_mode': is_const changed value in new API, from false to true.
Two TileMap getters were made const. No adjustments should be necessary. Two TileMap getters were made const. No adjustments should be necessary.
GH-86158
--------
Validate extension JSON: Error: Field 'classes/GraphEdit/methods/get_connection_line': is_const changed value in new API, from false to true.
get_connection_line was made const.

View File

@ -38,9 +38,14 @@ void GraphEdit::_set_arrange_nodes_button_hidden_bind_compat_81582(bool p_enable
set_show_arrange_button(!p_enable); set_show_arrange_button(!p_enable);
} }
PackedVector2Array GraphEdit::_get_connection_line_bind_compat_86158(const Vector2 &p_from, const Vector2 &p_to) {
return get_connection_line(p_from, p_to);
}
void GraphEdit::_bind_compatibility_methods() { 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("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("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);
} }
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,7 @@ class GraphEdit;
class GraphEditArranger; class GraphEditArranger;
class HScrollBar; class HScrollBar;
class Label; class Label;
class Line2D;
class PanelContainer; class PanelContainer;
class SpinBox; class SpinBox;
class ViewPanner; class ViewPanner;
@ -112,12 +113,25 @@ class GraphEdit : public Control {
GDCLASS(GraphEdit, Control); GDCLASS(GraphEdit, Control);
public: public:
struct Connection { struct Connection : RefCounted {
StringName from_node; StringName from_node;
StringName to_node; StringName to_node;
int from_port = 0; int from_port = 0;
int to_port = 0; int to_port = 0;
float activity = 0.0; float activity = 0.0;
private:
struct Cache {
bool dirty = true;
Vector2 from_pos; // In graph space.
Vector2 to_pos; // In graph space.
Color from_color;
Color to_color;
Rect2 aabb; // In local screen space.
Line2D *line = nullptr; // In local screen space.
} _cache;
friend class GraphEdit;
}; };
// Should be in sync with ControlScheme in ViewPanner. // Should be in sync with ControlScheme in ViewPanner.
@ -184,15 +198,15 @@ private:
GridPattern grid_pattern = GRID_PATTERN_LINES; GridPattern grid_pattern = GRID_PATTERN_LINES;
bool connecting = false; bool connecting = false;
String connecting_from; StringName connecting_from_node;
bool connecting_out = false; bool connecting_from_output = false;
int connecting_index = 0;
int connecting_type = 0; int connecting_type = 0;
Color connecting_color; Color connecting_color;
bool connecting_target = false; Vector2 connecting_to_point; // In local screen space.
Vector2 connecting_to; bool connecting_target_valid = false;
StringName connecting_target_to; StringName connecting_target_node;
int connecting_target_index = 0; int connecting_from_port_index = 0;
int connecting_target_port_index = 0;
bool just_disconnected = false; bool just_disconnected = false;
bool connecting_valid = false; bool connecting_valid = false;
@ -222,18 +236,28 @@ private:
bool right_disconnects = false; bool right_disconnects = false;
bool updating = false; bool updating = false;
bool awaiting_scroll_offset_update = false; bool awaiting_scroll_offset_update = false;
List<Connection> connections;
float lines_thickness = 2.0f; List<Ref<Connection>> connections;
HashMap<StringName, List<Ref<Connection>>> connection_map;
Ref<Connection> hovered_connection;
float lines_thickness = 4.0f;
float lines_curvature = 0.5f; float lines_curvature = 0.5f;
bool lines_antialiased = true; bool lines_antialiased = true;
PanelContainer *menu_panel = nullptr; PanelContainer *menu_panel = nullptr;
HBoxContainer *menu_hbox = nullptr; HBoxContainer *menu_hbox = nullptr;
Control *connections_layer = nullptr; Control *connections_layer = nullptr;
GraphEditFilter *top_layer = nullptr;
GraphEditFilter *top_connection_layer = nullptr; // Draws a dragged connection. Necessary since the connection line shader can't be applied to the whole top layer.
Line2D *dragged_connection_line = nullptr;
Control *top_layer = nullptr; // Used for drawing the box selection rect. Contains the minimap, menu panel and the scrollbars.
GraphEditMinimap *minimap = nullptr; GraphEditMinimap *minimap = nullptr;
static Ref<Shader> default_connections_shader;
Ref<Shader> connections_shader;
Ref<GraphEditArranger> arranger; Ref<GraphEditArranger> arranger;
HashSet<ConnectionType, ConnectionType> valid_connection_types; HashSet<ConnectionType, ConnectionType> valid_connection_types;
@ -248,6 +272,10 @@ private:
Color grid_minor; Color grid_minor;
Color activity_color; Color activity_color;
Color connection_hover_tint_color;
Color connection_valid_target_tint_color;
Color connection_rim_color;
Color selection_fill; Color selection_fill;
Color selection_stroke; Color selection_stroke;
@ -274,30 +302,35 @@ private:
void _zoom_plus(); void _zoom_plus();
void _update_zoom_label(); void _update_zoom_label();
void _draw_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_zoom);
void _graph_element_selected(Node *p_node); void _graph_element_selected(Node *p_node);
void _graph_element_deselected(Node *p_node); void _graph_element_deselected(Node *p_node);
void _graph_element_moved_to_front(Node *p_node); void _graph_element_moved_to_front(Node *p_node);
void _graph_element_resized(Vector2 p_new_minsize, Node *p_node); void _graph_element_resized(Vector2 p_new_minsize, Node *p_node);
void _graph_element_moved(Node *p_node); void _graph_element_moved(Node *p_node);
void _graph_node_slot_updated(int p_index, Node *p_node); void _graph_node_slot_updated(int p_index, Node *p_node);
void _graph_node_rect_changed(GraphNode *p_node);
void _update_scroll(); void _update_scroll();
void _update_scroll_offset(); void _update_scroll_offset();
void _scroll_moved(double); void _scroll_moved(double);
virtual void gui_input(const Ref<InputEvent> &p_ev) override; virtual void gui_input(const Ref<InputEvent> &p_ev) override;
void _top_layer_input(const Ref<InputEvent> &p_ev); void _top_connection_layer_input(const Ref<InputEvent> &p_ev);
float _get_shader_line_width();
void _draw_minimap_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color);
void _invalidate_connection_line_cache();
void _update_top_connection_layer();
void _update_connections();
void _top_layer_draw();
void _minimap_draw();
void _draw_grid();
bool is_in_port_hotzone(const Vector2 &p_pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left); bool is_in_port_hotzone(const Vector2 &p_pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);
void _top_layer_draw();
void _connections_layer_draw();
void _minimap_draw();
void _draw_grid();
TypedArray<Dictionary> _get_connection_list() const; 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;
friend class GraphEditFilter; friend class GraphEditFilter;
bool _filter_input(const Point2 &p_point); bool _filter_input(const Point2 &p_point);
@ -313,6 +346,7 @@ private:
#ifndef DISABLE_DEPRECATED #ifndef DISABLE_DEPRECATED
bool _is_arrange_nodes_button_hidden_bind_compat_81582() const; bool _is_arrange_nodes_button_hidden_bind_compat_81582() const;
void _set_arrange_nodes_button_hidden_bind_compat_81582(bool p_enable); 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);
#endif #endif
protected: protected:
@ -336,6 +370,9 @@ protected:
GDVIRTUAL4R(bool, _is_node_hover_valid, StringName, int, StringName, int); GDVIRTUAL4R(bool, _is_node_hover_valid, StringName, int, StringName, int);
public: public:
static void init_shaders();
static void finish_shaders();
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
PackedStringArray get_configuration_warnings() const override; PackedStringArray get_configuration_warnings() const override;
@ -344,12 +381,17 @@ public:
bool is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); bool is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
void clear_connections(); void clear_connections();
void force_connection_drag_end();
virtual PackedVector2Array get_connection_line(const Vector2 &p_from, const Vector2 &p_to); void force_connection_drag_end();
const List<Ref<Connection>> &get_connection_list() const;
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;
virtual bool is_node_hover_valid(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); virtual bool is_node_hover_valid(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
void set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity); void set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity);
void reset_all_connection_activity();
void add_valid_connection_type(int p_type, int p_with_type); void add_valid_connection_type(int p_type, int p_with_type);
void remove_valid_connection_type(int p_type, int p_with_type); void remove_valid_connection_type(int p_type, int p_with_type);
@ -392,10 +434,10 @@ public:
void set_show_arrange_button(bool p_hidden); void set_show_arrange_button(bool p_hidden);
bool is_showing_arrange_button() const; bool is_showing_arrange_button() const;
GraphEditFilter *get_top_layer() const { return top_layer; } Control *get_top_layer() const { return top_layer; }
GraphEditMinimap *get_minimap() const { return minimap; } GraphEditMinimap *get_minimap() const { return minimap; }
void get_connection_list(List<Connection> *r_connections) const; void override_connections_shader(const Ref<Shader> &p_shader);
void set_right_disconnects(bool p_enable); void set_right_disconnects(bool p_enable);
bool is_right_disconnects_enabled() const; bool is_right_disconnects_enabled() const;

View File

@ -65,8 +65,7 @@ void GraphEditArranger::arrange_nodes() {
float gap_v = 100.0f; float gap_v = 100.0f;
float gap_h = 100.0f; float gap_h = 100.0f;
List<GraphEdit::Connection> connection_list; List<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connection_list();
graph_edit->get_connection_list(&connection_list);
for (int i = graph_edit->get_child_count() - 1; i >= 0; i--) { for (int i = graph_edit->get_child_count() - 1; i >= 0; i--) {
GraphNode *graph_element = Object::cast_to<GraphNode>(graph_edit->get_child(i)); GraphNode *graph_element = Object::cast_to<GraphNode>(graph_edit->get_child(i));
@ -77,15 +76,16 @@ void GraphEditArranger::arrange_nodes() {
if (graph_element->is_selected() || arrange_entire_graph) { if (graph_element->is_selected() || arrange_entire_graph) {
selected_nodes.insert(graph_element->get_name()); selected_nodes.insert(graph_element->get_name());
HashSet<StringName> s; HashSet<StringName> s;
for (List<GraphEdit::Connection>::Element *E = connection_list.front(); E; E = E->next()) {
GraphNode *p_from = Object::cast_to<GraphNode>(node_names[E->get().from_node]); for (const Ref<GraphEdit::Connection> &connection : connection_list) {
if (E->get().to_node == graph_element->get_name() && (p_from->is_selected() || arrange_entire_graph) && E->get().to_node != E->get().from_node) { GraphNode *p_from = Object::cast_to<GraphNode>(node_names[connection->from_node]);
if (connection->to_node == graph_element->get_name() && (p_from->is_selected() || arrange_entire_graph) && connection->to_node != connection->from_node) {
if (!s.has(p_from->get_name())) { if (!s.has(p_from->get_name())) {
s.insert(p_from->get_name()); s.insert(p_from->get_name());
} }
String s_connection = String(p_from->get_name()) + " " + String(E->get().to_node); String s_connection = String(p_from->get_name()) + " " + String(connection->to_node);
StringName _connection(s_connection); StringName _connection(s_connection);
Pair<int, int> ports(E->get().from_port, E->get().to_port); Pair<int, int> ports(connection->from_port, connection->to_port);
port_info.insert(_connection, ports); port_info.insert(_connection, ports);
} }
} }
@ -437,31 +437,30 @@ float GraphEditArranger::_calculate_threshold(const StringName &p_v, const Strin
float threshold = p_current_threshold; float threshold = p_current_threshold;
if (p_v == p_w) { if (p_v == p_w) {
int min_order = MAX_ORDER; int min_order = MAX_ORDER;
GraphEdit::Connection incoming; Ref<GraphEdit::Connection> incoming;
List<GraphEdit::Connection> connection_list; List<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connection_list();
graph_edit->get_connection_list(&connection_list); for (const Ref<GraphEdit::Connection> &connection : connection_list) {
for (List<GraphEdit::Connection>::Element *E = connection_list.front(); E; E = E->next()) { if (connection->to_node == p_w) {
if (E->get().to_node == p_w) { ORDER(connection->from_node, r_layers);
ORDER(E->get().from_node, r_layers);
if (min_order > order) { if (min_order > order) {
min_order = order; min_order = order;
incoming = E->get(); incoming = connection;
} }
} }
} }
if (incoming.from_node != StringName()) { if (incoming.is_valid()) {
GraphNode *gnode_from = Object::cast_to<GraphNode>(r_node_names[incoming.from_node]); GraphNode *gnode_from = Object::cast_to<GraphNode>(r_node_names[incoming->from_node]);
GraphNode *gnode_to = Object::cast_to<GraphNode>(r_node_names[p_w]); GraphNode *gnode_to = Object::cast_to<GraphNode>(r_node_names[p_w]);
Vector2 pos_from = gnode_from->get_output_port_position(incoming.from_port) * graph_edit->get_zoom(); Vector2 pos_from = gnode_from->get_output_port_position(incoming->from_port) * graph_edit->get_zoom();
Vector2 pos_to = gnode_to->get_input_port_position(incoming.to_port) * graph_edit->get_zoom(); Vector2 pos_to = gnode_to->get_input_port_position(incoming->to_port) * graph_edit->get_zoom();
// If connected block node is selected, calculate thershold or add current block to list. // If connected block node is selected, calculate thershold or add current block to list.
if (gnode_from->is_selected()) { if (gnode_from->is_selected()) {
Vector2 connected_block_pos = r_node_positions[r_root[incoming.from_node]]; Vector2 connected_block_pos = r_node_positions[r_root[incoming->from_node]];
if (connected_block_pos.y != FLT_MAX) { if (connected_block_pos.y != FLT_MAX) {
//Connected block is placed, calculate threshold. //Connected block is placed, calculate threshold.
threshold = connected_block_pos.y + (real_t)r_inner_shift[incoming.from_node] - (real_t)r_inner_shift[p_w] + pos_from.y - pos_to.y; threshold = connected_block_pos.y + (real_t)r_inner_shift[incoming->from_node] - (real_t)r_inner_shift[p_w] + pos_from.y - pos_to.y;
} }
} }
} }
@ -469,31 +468,30 @@ float GraphEditArranger::_calculate_threshold(const StringName &p_v, const Strin
if (threshold == FLT_MIN && (StringName)r_align[p_w] == p_v) { if (threshold == FLT_MIN && (StringName)r_align[p_w] == p_v) {
// This time, pick an outgoing edge and repeat as above! // This time, pick an outgoing edge and repeat as above!
int min_order = MAX_ORDER; int min_order = MAX_ORDER;
GraphEdit::Connection outgoing; Ref<GraphEdit::Connection> outgoing;
List<GraphEdit::Connection> connection_list; List<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connection_list();
graph_edit->get_connection_list(&connection_list); for (const Ref<GraphEdit::Connection> &connection : connection_list) {
for (List<GraphEdit::Connection>::Element *E = connection_list.front(); E; E = E->next()) { if (connection->from_node == p_w) {
if (E->get().from_node == p_w) { ORDER(connection->to_node, r_layers);
ORDER(E->get().to_node, r_layers);
if (min_order > order) { if (min_order > order) {
min_order = order; min_order = order;
outgoing = E->get(); outgoing = connection;
} }
} }
} }
if (outgoing.to_node != StringName()) { if (outgoing.is_valid()) {
GraphNode *gnode_from = Object::cast_to<GraphNode>(r_node_names[p_w]); GraphNode *gnode_from = Object::cast_to<GraphNode>(r_node_names[p_w]);
GraphNode *gnode_to = Object::cast_to<GraphNode>(r_node_names[outgoing.to_node]); GraphNode *gnode_to = Object::cast_to<GraphNode>(r_node_names[outgoing->to_node]);
Vector2 pos_from = gnode_from->get_output_port_position(outgoing.from_port) * graph_edit->get_zoom(); Vector2 pos_from = gnode_from->get_output_port_position(outgoing->from_port) * graph_edit->get_zoom();
Vector2 pos_to = gnode_to->get_input_port_position(outgoing.to_port) * graph_edit->get_zoom(); Vector2 pos_to = gnode_to->get_input_port_position(outgoing->to_port) * graph_edit->get_zoom();
// If connected block node is selected, calculate thershold or add current block to list. // If connected block node is selected, calculate thershold or add current block to list.
if (gnode_to->is_selected()) { if (gnode_to->is_selected()) {
Vector2 connected_block_pos = r_node_positions[r_root[outgoing.to_node]]; Vector2 connected_block_pos = r_node_positions[r_root[outgoing->to_node]];
if (connected_block_pos.y != FLT_MAX) { if (connected_block_pos.y != FLT_MAX) {
//Connected block is placed. Calculate threshold //Connected block is placed. Calculate threshold
threshold = connected_block_pos.y + (real_t)r_inner_shift[outgoing.to_node] - (real_t)r_inner_shift[p_w] + pos_from.y - pos_to.y; threshold = connected_block_pos.y + (real_t)r_inner_shift[outgoing->to_node] - (real_t)r_inner_shift[p_w] + pos_from.y - pos_to.y;
} }
} }
} }

View File

@ -1184,7 +1184,9 @@ void register_scene_types() {
} }
if (RenderingServer::get_singleton()) { if (RenderingServer::get_singleton()) {
ColorPicker::init_shaders(); // RenderingServer needs to exist for this to succeed. // RenderingServer needs to exist for this to succeed.
ColorPicker::init_shaders();
GraphEdit::init_shaders();
} }
SceneDebugger::initialize(); SceneDebugger::initialize();
@ -1236,6 +1238,7 @@ void unregister_scene_types() {
ParticleProcessMaterial::finish_shaders(); ParticleProcessMaterial::finish_shaders();
CanvasItemMaterial::finish_shaders(); CanvasItemMaterial::finish_shaders();
ColorPicker::finish_shaders(); ColorPicker::finish_shaders();
GraphEdit::finish_shaders();
SceneStringNames::free(); SceneStringNames::free();
OS::get_singleton()->benchmark_end_measure("Scene", "Unregister Types"); OS::get_singleton()->benchmark_end_measure("Scene", "Unregister Types");

View File

@ -1161,6 +1161,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("selection_fill", "GraphEdit", Color(1, 1, 1, 0.3)); theme->set_color("selection_fill", "GraphEdit", Color(1, 1, 1, 0.3));
theme->set_color("selection_stroke", "GraphEdit", Color(1, 1, 1, 0.8)); theme->set_color("selection_stroke", "GraphEdit", Color(1, 1, 1, 0.8));
theme->set_color("activity", "GraphEdit", Color(1, 1, 1)); theme->set_color("activity", "GraphEdit", Color(1, 1, 1));
theme->set_color("connection_hover_tint_color", "GraphEdit", Color(0, 0, 0, 0.3));
theme->set_color("connection_valid_target_tint_color", "GraphEdit", Color(1, 1, 1, 0.4));
theme->set_color("connection_rim_color", "GraphEdit", style_normal_color);
// Visual Node Ports // Visual Node Ports