Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/gui/graph_edit.cpp
20831 views
1
/**************************************************************************/
2
/* graph_edit.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "graph_edit.h"
32
#include "graph_edit.compat.inc"
33
34
#include "core/input/input.h"
35
#include "core/math/geometry_2d.h"
36
#include "core/math/math_funcs.h"
37
#include "core/os/keyboard.h"
38
#include "scene/2d/line_2d.h"
39
#include "scene/gui/box_container.h"
40
#include "scene/gui/button.h"
41
#include "scene/gui/graph_edit_arranger.h"
42
#include "scene/gui/label.h"
43
#include "scene/gui/panel_container.h"
44
#include "scene/gui/scroll_bar.h"
45
#include "scene/gui/spin_box.h"
46
#include "scene/gui/view_panner.h"
47
#include "scene/resources/material.h"
48
#include "scene/resources/style_box_flat.h"
49
#include "scene/theme/theme_db.h"
50
51
constexpr int MINIMAP_OFFSET = 12;
52
constexpr int MINIMAP_PADDING = 5;
53
constexpr int MIN_DRAG_DISTANCE_FOR_VALID_CONNECTION = 20;
54
constexpr int MAX_CONNECTION_LINE_CURVE_TESSELATION_STAGES = 5;
55
constexpr int GRID_MINOR_STEPS_PER_MAJOR_LINE = 10;
56
constexpr int GRID_MINOR_STEPS_PER_MAJOR_DOT = 5;
57
constexpr int GRID_MIN_SNAPPING_DISTANCE = 2;
58
constexpr int GRID_MAX_SNAPPING_DISTANCE = 100;
59
60
bool GraphEditFilter::has_point(const Point2 &p_point) const {
61
return ge->_filter_input(p_point);
62
}
63
64
GraphEditFilter::GraphEditFilter(GraphEdit *p_edit) {
65
ge = p_edit;
66
}
67
68
Control::CursorShape GraphEditMinimap::get_cursor_shape(const Point2 &p_pos) const {
69
if (is_resizing || (p_pos.x < theme_cache.resizer->get_width() && p_pos.y < theme_cache.resizer->get_height())) {
70
return CURSOR_FDIAGSIZE;
71
}
72
73
return Control::get_cursor_shape(p_pos);
74
}
75
76
void GraphEditMinimap::update_minimap() {
77
Vector2 graph_offset = _get_graph_offset();
78
Vector2 graph_size = _get_graph_size();
79
80
camera_position = ge->get_scroll_offset() - graph_offset;
81
camera_size = ge->get_size();
82
83
Vector2 render_size = _get_render_size();
84
float target_ratio = render_size.width / render_size.height;
85
float graph_ratio = graph_size.width / graph_size.height;
86
87
graph_proportions = graph_size;
88
graph_padding = Vector2(0, 0);
89
if (graph_ratio > target_ratio) {
90
graph_proportions.width = graph_size.width;
91
graph_proportions.height = graph_size.width / target_ratio;
92
graph_padding.y = Math::abs(graph_size.height - graph_proportions.y) / 2;
93
} else {
94
graph_proportions.width = graph_size.height * target_ratio;
95
graph_proportions.height = graph_size.height;
96
graph_padding.x = Math::abs(graph_size.width - graph_proportions.x) / 2;
97
}
98
99
// This centers minimap inside the minimap rectangle.
100
minimap_offset = minimap_padding + _convert_from_graph_position(graph_padding);
101
}
102
103
Rect2 GraphEditMinimap::get_camera_rect() {
104
Vector2 camera_center = _convert_from_graph_position(camera_position + camera_size / 2) + minimap_offset;
105
Vector2 camera_viewport = _convert_from_graph_position(camera_size);
106
Vector2 camera_pos = (camera_center - camera_viewport / 2);
107
return Rect2(camera_pos, camera_viewport);
108
}
109
110
Vector2 GraphEditMinimap::_get_render_size() {
111
if (!is_inside_tree()) {
112
return Vector2(0, 0);
113
}
114
115
return get_size() - 2 * minimap_padding;
116
}
117
118
Vector2 GraphEditMinimap::_get_graph_offset() {
119
return ge->min_scroll_offset;
120
}
121
122
Vector2 GraphEditMinimap::_get_graph_size() {
123
Vector2 graph_size = ge->max_scroll_offset - ge->min_scroll_offset;
124
125
if (graph_size.width == 0) {
126
graph_size.width = 1;
127
}
128
if (graph_size.height == 0) {
129
graph_size.height = 1;
130
}
131
132
return graph_size;
133
}
134
135
Vector2 GraphEditMinimap::_convert_from_graph_position(const Vector2 &p_position) {
136
Vector2 map_position = Vector2(0, 0);
137
Vector2 render_size = _get_render_size();
138
139
map_position.x = p_position.x * render_size.width / graph_proportions.x;
140
map_position.y = p_position.y * render_size.height / graph_proportions.y;
141
142
return map_position;
143
}
144
145
Vector2 GraphEditMinimap::_convert_to_graph_position(const Vector2 &p_position) {
146
Vector2 graph_position = Vector2(0, 0);
147
Vector2 render_size = _get_render_size();
148
149
graph_position.x = p_position.x * graph_proportions.x / render_size.width;
150
graph_position.y = p_position.y * graph_proportions.y / render_size.height;
151
152
return graph_position;
153
}
154
155
void GraphEditMinimap::gui_input(const Ref<InputEvent> &p_ev) {
156
ERR_FAIL_COND(p_ev.is_null());
157
158
if (!ge->is_minimap_enabled()) {
159
return;
160
}
161
162
Ref<InputEventMouseButton> mb = p_ev;
163
Ref<InputEventMouseMotion> mm = p_ev;
164
165
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
166
if (mb->is_pressed()) {
167
is_pressing = true;
168
169
Rect2 resizer_hitbox = Rect2(Point2(), theme_cache.resizer->get_size());
170
if (resizer_hitbox.has_point(mb->get_position())) {
171
is_resizing = true;
172
} else {
173
Vector2 click_position = _convert_to_graph_position(mb->get_position() - minimap_padding) - graph_padding;
174
_adjust_graph_scroll(click_position);
175
}
176
} else {
177
is_pressing = false;
178
is_resizing = false;
179
}
180
accept_event();
181
} else if (mm.is_valid() && is_pressing) {
182
if (is_resizing) {
183
// Prevent setting minimap wider than GraphEdit.
184
Vector2 new_minimap_size;
185
new_minimap_size = (get_size() - mm->get_relative()).min(ge->get_size() - 2.0 * minimap_padding);
186
ge->set_minimap_size(new_minimap_size);
187
188
queue_redraw();
189
} else {
190
Vector2 click_position = _convert_to_graph_position(mm->get_position() - minimap_padding) - graph_padding;
191
_adjust_graph_scroll(click_position);
192
}
193
accept_event();
194
}
195
}
196
197
void GraphEditMinimap::_adjust_graph_scroll(const Vector2 &p_offset) {
198
Vector2 graph_offset = _get_graph_offset();
199
ge->set_scroll_offset(p_offset + graph_offset - camera_size / 2);
200
}
201
202
void GraphEditMinimap::_bind_methods() {
203
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphEditMinimap, panel);
204
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, GraphEditMinimap, node_style, "node");
205
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, GraphEditMinimap, camera_style, "camera");
206
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEditMinimap, resizer);
207
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEditMinimap, resizer_color);
208
}
209
210
GraphEditMinimap::GraphEditMinimap(GraphEdit *p_edit) {
211
ge = p_edit;
212
213
minimap_padding = Vector2(MINIMAP_PADDING, MINIMAP_PADDING);
214
minimap_offset = minimap_padding + _convert_from_graph_position(graph_padding);
215
}
216
217
Ref<Shader> GraphEdit::default_connections_shader;
218
219
void GraphEdit::init_shaders() {
220
default_connections_shader.instantiate();
221
default_connections_shader->set_code(R"(
222
// Connection lines shader.
223
shader_type canvas_item;
224
render_mode blend_mix;
225
226
uniform vec4 rim_color : source_color;
227
uniform int from_type;
228
uniform int to_type;
229
uniform float line_width;
230
231
void fragment(){
232
float fake_aa_width = 1.5/line_width;
233
float rim_width = 1.5/line_width;
234
235
float dist = abs(UV.y - 0.5);
236
float alpha = smoothstep(0.5, 0.5-fake_aa_width, dist);
237
vec4 final_color = mix(rim_color, COLOR, smoothstep(0.5-rim_width, 0.5-fake_aa_width-rim_width, dist));
238
COLOR = vec4(final_color.rgb, final_color.a*alpha);
239
}
240
)");
241
}
242
243
void GraphEdit::finish_shaders() {
244
default_connections_shader.unref();
245
}
246
247
Control::CursorShape GraphEdit::get_cursor_shape(const Point2 &p_pos) const {
248
if (moving_selection) {
249
return CURSOR_MOVE;
250
}
251
252
return Control::get_cursor_shape(p_pos);
253
}
254
255
Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, bool p_keep_alive) {
256
ERR_FAIL_NULL_V_MSG(connections_layer, FAILED, "connections_layer is missing.");
257
258
if (is_node_connected(p_from, p_from_port, p_to, p_to_port)) {
259
return OK;
260
}
261
Ref<Connection> c;
262
c.instantiate();
263
c->from_node = p_from;
264
c->from_port = p_from_port;
265
c->to_node = p_to;
266
c->to_port = p_to_port;
267
c->activity = 0;
268
c->keep_alive = p_keep_alive;
269
connections.push_back(c);
270
connection_map[p_from].push_back(c);
271
connection_map[p_to].push_back(c);
272
273
Line2D *line = memnew(Line2D);
274
line->set_texture_mode(Line2D::LineTextureMode::LINE_TEXTURE_STRETCH);
275
276
Ref<ShaderMaterial> line_material;
277
line_material.instantiate();
278
line_material->set_shader(connections_shader);
279
280
float line_width = _get_shader_line_width();
281
line_material->set_shader_parameter("line_width", line_width);
282
line_material->set_shader_parameter("from_type", c->from_port);
283
line_material->set_shader_parameter("to_type", c->to_port);
284
285
Ref<StyleBoxFlat> bg_panel = theme_cache.panel;
286
Color connection_line_rim_color = bg_panel.is_valid() ? bg_panel->get_bg_color() : Color(0.0, 0.0, 0.0, 0.0);
287
line_material->set_shader_parameter("rim_color", connection_line_rim_color);
288
line->set_material(line_material);
289
290
connections_layer->add_child(line);
291
c->_cache.line = line;
292
293
minimap->queue_redraw();
294
queue_redraw();
295
connections_layer->queue_redraw();
296
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
297
298
return OK;
299
}
300
301
bool GraphEdit::is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
302
for (const Ref<Connection> &conn : connection_map[p_from]) {
303
if (conn->from_node == p_from && conn->from_port == p_from_port && conn->to_node == p_to && conn->to_port == p_to_port) {
304
return true;
305
}
306
}
307
308
return false;
309
}
310
311
void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
312
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
313
314
Ref<Connection> conn_to_remove;
315
for (const Ref<Connection> &conn : connections) {
316
if (conn->from_node == p_from && conn->from_port == p_from_port && conn->to_node == p_to && conn->to_port == p_to_port) {
317
conn_to_remove = conn;
318
break;
319
}
320
}
321
322
if (conn_to_remove.is_valid()) {
323
connection_map[p_from].erase(conn_to_remove);
324
connection_map[p_to].erase(conn_to_remove);
325
conn_to_remove->_cache.line->queue_free();
326
connections.erase(conn_to_remove);
327
328
minimap->queue_redraw();
329
queue_redraw();
330
connections_layer->queue_redraw();
331
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
332
}
333
}
334
335
const Vector<Ref<GraphEdit::Connection>> &GraphEdit::get_connections() const {
336
return connections;
337
}
338
339
int GraphEdit::get_connection_count(const StringName &p_node, int p_port) {
340
int count = 0;
341
for (const Ref<Connection> &conn : connections) {
342
if ((conn->from_node == p_node && conn->from_port == p_port) || (conn->to_node == p_node && conn->to_port == p_port)) {
343
count += 1;
344
}
345
}
346
return count;
347
}
348
349
GraphNode *GraphEdit::get_input_connection_target(const StringName &p_node, int p_port) {
350
for (const Ref<Connection> &conn : connections) {
351
if (conn->to_node == p_node && conn->to_port == p_port) {
352
GraphNode *from = Object::cast_to<GraphNode>(get_node(NodePath(conn->from_node)));
353
if (from) {
354
return from;
355
}
356
}
357
}
358
return nullptr;
359
}
360
361
GraphNode *GraphEdit::get_output_connection_target(const StringName &p_node, int p_port) {
362
for (const Ref<Connection> &conn : connections) {
363
if (conn->from_node == p_node && conn->from_port == p_port) {
364
GraphNode *to = Object::cast_to<GraphNode>(get_node(NodePath(conn->to_node)));
365
if (to) {
366
return to;
367
}
368
}
369
}
370
return nullptr;
371
}
372
373
String GraphEdit::get_connections_description(const StringName &p_node, int p_port) {
374
String out;
375
for (const Ref<Connection> &conn : connections) {
376
if (conn->from_node == p_node && conn->from_port == p_port) {
377
GraphNode *to = Object::cast_to<GraphNode>(get_node(NodePath(conn->to_node)));
378
if (to) {
379
if (!out.is_empty()) {
380
out += ", ";
381
}
382
String name = to->get_accessibility_name();
383
if (name.is_empty()) {
384
name = to->get_name();
385
}
386
out += vformat(ETR("connection to %s (%s) port %d"), name, to->get_title(), conn->to_port);
387
}
388
} else if (conn->to_node == p_node && conn->to_port == p_port) {
389
GraphNode *from = Object::cast_to<GraphNode>(get_node(NodePath(conn->from_node)));
390
if (from) {
391
if (!out.is_empty()) {
392
out += ", ";
393
}
394
String name = from->get_accessibility_name();
395
if (name.is_empty()) {
396
name = from->get_name();
397
}
398
out += vformat(ETR("connection from %s (%s) port %d"), name, from->get_title(), conn->from_port);
399
}
400
}
401
}
402
return out;
403
}
404
405
void GraphEdit::set_scroll_offset(const Vector2 &p_offset) {
406
setting_scroll_offset = true;
407
scroll_offset = p_offset.clamp(min_scroll_offset, max_scroll_offset - get_size());
408
if (!awaiting_scroll_offset_update) {
409
callable_mp(this, &GraphEdit::_update_scroll_offset).call_deferred();
410
awaiting_scroll_offset_update = true;
411
}
412
minimap->queue_redraw();
413
queue_redraw();
414
_update_scrollbars();
415
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
416
setting_scroll_offset = false;
417
}
418
419
Vector2 GraphEdit::get_scroll_offset() const {
420
return scroll_offset;
421
}
422
423
void GraphEdit::_scrollbar_moved(double) {
424
scroll_offset.x = h_scrollbar->get_value();
425
scroll_offset.y = v_scrollbar->get_value();
426
if (!awaiting_scroll_offset_update) {
427
callable_mp(this, &GraphEdit::_update_scroll_offset).call_deferred();
428
awaiting_scroll_offset_update = true;
429
}
430
minimap->queue_redraw();
431
queue_redraw();
432
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
433
}
434
435
void GraphEdit::_update_scroll_offset() {
436
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
437
438
set_block_minimum_size_adjust(true);
439
440
for (int i = 0; i < get_child_count(); i++) {
441
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
442
if (!graph_element) {
443
continue;
444
}
445
446
Point2 pos = graph_element->get_position_offset() * zoom;
447
pos -= scroll_offset;
448
graph_element->set_position(pos);
449
if (graph_element->get_scale() != Vector2(zoom, zoom)) {
450
graph_element->set_scale(Vector2(zoom, zoom));
451
}
452
}
453
454
connections_layer->set_position(-scroll_offset);
455
set_block_minimum_size_adjust(false);
456
awaiting_scroll_offset_update = false;
457
458
// In Godot, signals on value change are avoided by convention.
459
if (!setting_scroll_offset) {
460
emit_signal(SNAME("scroll_offset_changed"), get_scroll_offset());
461
}
462
}
463
464
void GraphEdit::_update_scrollbars() {
465
if (updating) {
466
return;
467
}
468
updating = true;
469
470
h_scrollbar->set_value_no_signal(scroll_offset.x);
471
v_scrollbar->set_value_no_signal(scroll_offset.y);
472
473
set_block_minimum_size_adjust(true);
474
475
// Determine the graph "canvas" size in screen space.
476
Rect2 screen_rect;
477
for (int i = 0; i < get_child_count(); i++) {
478
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
479
if (!graph_element) {
480
continue;
481
}
482
483
Rect2 node_rect;
484
node_rect.position = graph_element->get_position_offset() * zoom;
485
node_rect.size = graph_element->get_size() * zoom;
486
screen_rect = screen_rect.merge(node_rect);
487
}
488
489
screen_rect.position -= get_size();
490
screen_rect.size += get_size() * 2.0;
491
492
min_scroll_offset = screen_rect.position;
493
max_scroll_offset = screen_rect.position + screen_rect.size;
494
495
h_scrollbar->set_min(screen_rect.position.x);
496
h_scrollbar->set_max(screen_rect.position.x + screen_rect.size.width);
497
h_scrollbar->set_page(get_size().x);
498
if (h_scrollbar->get_max() - h_scrollbar->get_min() <= h_scrollbar->get_page()) {
499
h_scrollbar->hide();
500
} else {
501
h_scrollbar->show();
502
}
503
504
v_scrollbar->set_min(screen_rect.position.y);
505
v_scrollbar->set_max(screen_rect.position.y + screen_rect.size.height);
506
v_scrollbar->set_page(get_size().height);
507
508
if (v_scrollbar->get_max() - v_scrollbar->get_min() <= v_scrollbar->get_page()) {
509
v_scrollbar->hide();
510
} else {
511
v_scrollbar->show();
512
}
513
514
Size2 hmin = h_scrollbar->get_combined_minimum_size();
515
Size2 vmin = v_scrollbar->get_combined_minimum_size();
516
517
// Avoid scrollbar overlapping.
518
h_scrollbar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, v_scrollbar->is_visible() ? -vmin.width : 0);
519
v_scrollbar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, h_scrollbar->is_visible() ? -hmin.height : 0);
520
521
set_block_minimum_size_adjust(false);
522
523
if (!awaiting_scroll_offset_update) {
524
callable_mp(this, &GraphEdit::_update_scroll_offset).call_deferred();
525
awaiting_scroll_offset_update = true;
526
}
527
528
updating = false;
529
}
530
531
void GraphEdit::_ensure_node_order_from(Node *p_node) {
532
GraphElement *graph_node = Object::cast_to<GraphElement>(p_node);
533
ERR_FAIL_NULL(graph_node);
534
GraphFrame *frame = Object::cast_to<GraphFrame>(p_node);
535
536
// Move a non-frame node directly to the front.
537
if (!frame) {
538
graph_node->move_to_front();
539
return;
540
}
541
542
// Reorder the frames behind the connection layer.
543
List<GraphFrame *> attached_nodes_to_move;
544
attached_nodes_to_move.push_back(frame);
545
546
while (!attached_nodes_to_move.is_empty()) {
547
GraphFrame *attached_frame = attached_nodes_to_move.front()->get();
548
attached_nodes_to_move.pop_front();
549
550
// Move the frame to the front of the background node index range.
551
attached_frame->get_parent()->call_deferred("move_child", attached_frame, background_nodes_separator_idx - 1);
552
553
if (!frame_attached_nodes.has(attached_frame->get_name())) {
554
continue;
555
}
556
557
for (const StringName &attached_node_name : frame_attached_nodes.get(attached_frame->get_name())) {
558
GraphElement *attached_node = Object::cast_to<GraphElement>(get_node(NodePath(attached_node_name)));
559
560
GraphFrame *attached_child_frame_node = Object::cast_to<GraphFrame>(attached_node);
561
562
if (attached_child_frame_node && (attached_child_frame_node != frame)) {
563
attached_nodes_to_move.push_back(attached_child_frame_node);
564
}
565
}
566
}
567
}
568
569
void GraphEdit::_graph_element_selected(Node *p_node) {
570
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
571
ERR_FAIL_NULL(graph_element);
572
573
emit_signal(SNAME("node_selected"), graph_element);
574
}
575
576
void GraphEdit::_graph_element_deselected(Node *p_node) {
577
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
578
ERR_FAIL_NULL(graph_element);
579
580
emit_signal(SNAME("node_deselected"), graph_element);
581
}
582
583
void GraphEdit::_graph_element_visibility_changed(GraphElement *p_graph_element) {
584
if (p_graph_element->is_selected() && !p_graph_element->is_visible()) {
585
p_graph_element->set_selected(false);
586
}
587
}
588
589
void GraphEdit::_graph_element_resize_request(const Vector2 &p_new_minsize, Node *p_node) {
590
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
591
ERR_FAIL_NULL(graph_element);
592
593
// Snap the new size to the grid if snapping is enabled.
594
Vector2 new_size = p_new_minsize;
595
if (snapping_enabled ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) {
596
new_size = new_size.snappedf(snapping_distance);
597
}
598
599
// Disallow resizing the frame to a size smaller than the minimum size of the attached nodes.
600
GraphFrame *frame = Object::cast_to<GraphFrame>(graph_element);
601
if (frame && !frame->is_autoshrink_enabled()) {
602
Rect2 frame_rect = _compute_shrinked_frame_rect(frame);
603
Vector2 computed_min_size = (frame_rect.position + frame_rect.size) - frame->get_position_offset();
604
frame->set_size(new_size.max(computed_min_size));
605
} else {
606
graph_element->set_size(new_size);
607
}
608
609
// Update all parent frames recursively bottom-up.
610
if (linked_parent_map.has(graph_element->get_name())) {
611
GraphFrame *parent_frame = Object::cast_to<GraphFrame>(get_node_or_null(NodePath(linked_parent_map[graph_element->get_name()])));
612
if (parent_frame) {
613
_update_graph_frame(parent_frame);
614
}
615
}
616
}
617
618
void GraphEdit::_graph_frame_autoshrink_changed(const Vector2 &p_new_minsize, GraphFrame *p_frame) {
619
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
620
621
_update_graph_frame(p_frame);
622
623
minimap->queue_redraw();
624
queue_redraw();
625
connections_layer->queue_redraw();
626
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
627
}
628
629
void GraphEdit::_graph_element_moved(Node *p_node) {
630
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
631
ERR_FAIL_NULL(graph_element);
632
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
633
634
minimap->queue_redraw();
635
queue_redraw();
636
connections_layer->queue_redraw();
637
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
638
}
639
640
void GraphEdit::_graph_node_slot_updated(int p_index, Node *p_node) {
641
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
642
GraphNode *graph_node = Object::cast_to<GraphNode>(p_node);
643
ERR_FAIL_NULL(graph_node);
644
645
// Update all adjacent connections during the next redraw.
646
for (const Ref<Connection> &conn : connection_map[graph_node->get_name()]) {
647
conn->_cache.dirty = true;
648
}
649
650
minimap->queue_redraw();
651
queue_redraw();
652
connections_layer->queue_redraw();
653
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
654
}
655
656
void GraphEdit::_graph_node_rect_changed(GraphNode *p_node) {
657
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
658
659
// Only invalidate the cache when zooming or the node is moved/resized in graph space.
660
if (panner->is_panning()) {
661
return;
662
}
663
664
for (Ref<Connection> &conn : connection_map[p_node->get_name()]) {
665
conn->_cache.dirty = true;
666
}
667
connections_layer->queue_redraw();
668
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
669
670
// Update all parent frames recursively bottom-up.
671
if (linked_parent_map.has(p_node->get_name())) {
672
GraphFrame *parent_frame = Object::cast_to<GraphFrame>(get_node(NodePath(linked_parent_map[p_node->get_name()])));
673
if (parent_frame) {
674
_update_graph_frame(parent_frame);
675
}
676
}
677
}
678
679
void GraphEdit::_ensure_node_order_from_root(const StringName &p_node) {
680
// Find the root frame node of the frame tree starting from p_node.
681
GraphElement *root_frame = Object::cast_to<GraphElement>(get_node(NodePath(p_node)));
682
ERR_FAIL_NULL(root_frame);
683
684
while (linked_parent_map.has(root_frame->get_name())) {
685
root_frame = Object::cast_to<GraphElement>(get_node(NodePath(linked_parent_map[root_frame->get_name()])));
686
}
687
688
_ensure_node_order_from(root_frame);
689
}
690
691
void GraphEdit::add_child_notify(Node *p_child) {
692
Control::add_child_notify(p_child);
693
694
// Keep the top layer always on top!
695
callable_mp((CanvasItem *)top_layer, &CanvasItem::move_to_front).call_deferred();
696
697
GraphElement *graph_element = Object::cast_to<GraphElement>(p_child);
698
if (graph_element) {
699
graph_element->connect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_element_moved).bind(graph_element));
700
graph_element->connect("node_selected", callable_mp(this, &GraphEdit::_graph_element_selected).bind(graph_element));
701
graph_element->connect("node_deselected", callable_mp(this, &GraphEdit::_graph_element_deselected).bind(graph_element));
702
graph_element->connect(SceneStringName(visibility_changed), callable_mp(this, &GraphEdit::_graph_element_visibility_changed).bind(graph_element));
703
704
GraphNode *graph_node = Object::cast_to<GraphNode>(graph_element);
705
if (graph_node) {
706
graph_node->connect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated).bind(graph_element));
707
graph_node->connect("slot_sizes_changed", callable_mp(this, &GraphEdit::_graph_node_rect_changed).bind(graph_node));
708
graph_node->connect(SceneStringName(item_rect_changed), callable_mp(this, &GraphEdit::_graph_node_rect_changed).bind(graph_node));
709
_ensure_node_order_from(graph_node);
710
}
711
712
GraphFrame *graph_frame = Object::cast_to<GraphFrame>(graph_element);
713
if (graph_frame) {
714
background_nodes_separator_idx++;
715
716
callable_mp((Node *)this, &Node::move_child).call_deferred(graph_frame, 0);
717
callable_mp((Node *)this, &Node::move_child).call_deferred(connections_layer, background_nodes_separator_idx);
718
719
_update_graph_frame(graph_frame);
720
721
graph_frame->connect("autoshrink_changed", callable_mp(this, &GraphEdit::_graph_frame_autoshrink_changed).bind(graph_element));
722
}
723
graph_element->connect("raise_request", callable_mp(this, &GraphEdit::_ensure_node_order_from).bind(graph_element));
724
graph_element->connect("resize_request", callable_mp(this, &GraphEdit::_graph_element_resize_request).bind(graph_element));
725
if (connections_layer != nullptr) {
726
graph_element->connect(SceneStringName(item_rect_changed), callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
727
}
728
graph_element->connect(SceneStringName(item_rect_changed), callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw));
729
730
graph_element->set_scale(Vector2(zoom, zoom));
731
_graph_element_moved(graph_element);
732
graph_element->set_mouse_filter(MOUSE_FILTER_PASS);
733
}
734
}
735
736
void GraphEdit::remove_child_notify(Node *p_child) {
737
Control::remove_child_notify(p_child);
738
739
if (p_child == top_layer) {
740
top_layer = nullptr;
741
minimap = nullptr;
742
} else if (p_child == connections_layer) {
743
connections_layer = nullptr;
744
if (is_inside_tree()) {
745
WARN_PRINT("GraphEdit's connection_layer removed. This should not be done. If you like to remove all GraphElements from a GraphEdit node, do not simply remove all non-internal children but check their type since the connection layer has to be kept non-internal due to technical reasons.");
746
}
747
}
748
749
if (top_layer != nullptr && is_inside_tree()) {
750
// Keep the top layer always on top!
751
callable_mp((CanvasItem *)top_layer, &CanvasItem::move_to_front).call_deferred();
752
}
753
754
GraphElement *graph_element = Object::cast_to<GraphElement>(p_child);
755
if (graph_element) {
756
graph_element->disconnect("position_offset_changed", callable_mp(this, &GraphEdit::_graph_element_moved));
757
graph_element->disconnect("node_selected", callable_mp(this, &GraphEdit::_graph_element_selected));
758
graph_element->disconnect("node_deselected", callable_mp(this, &GraphEdit::_graph_element_deselected));
759
graph_element->disconnect(SceneStringName(visibility_changed), callable_mp(this, &GraphEdit::_graph_element_visibility_changed));
760
761
GraphNode *graph_node = Object::cast_to<GraphNode>(graph_element);
762
if (graph_node) {
763
graph_node->disconnect("slot_updated", callable_mp(this, &GraphEdit::_graph_node_slot_updated));
764
graph_node->disconnect("slot_sizes_changed", callable_mp(this, &GraphEdit::_graph_node_rect_changed));
765
graph_node->disconnect(SceneStringName(item_rect_changed), callable_mp(this, &GraphEdit::_graph_node_rect_changed));
766
767
// Invalidate all adjacent connections, so that they are removed before the next redraw.
768
for (const Ref<Connection> &conn : connection_map[graph_node->get_name()]) {
769
conn->_cache.dirty = true;
770
}
771
if (connections_layer != nullptr && connections_layer->is_inside_tree()) {
772
connections_layer->queue_redraw();
773
}
774
}
775
776
GraphFrame *frame = Object::cast_to<GraphFrame>(graph_element);
777
if (frame) {
778
background_nodes_separator_idx--;
779
graph_element->disconnect("autoshrink_changed", callable_mp(this, &GraphEdit::_graph_frame_autoshrink_changed));
780
}
781
782
if (linked_parent_map.has(graph_element->get_name())) {
783
GraphFrame *parent_frame = Object::cast_to<GraphFrame>(get_node(NodePath(linked_parent_map[graph_element->get_name()])));
784
if (parent_frame) {
785
if (frame_attached_nodes.has(parent_frame->get_name())) {
786
frame_attached_nodes.get(parent_frame->get_name()).erase(graph_element->get_name());
787
}
788
linked_parent_map.erase(graph_element->get_name());
789
_update_graph_frame(parent_frame);
790
}
791
}
792
793
if (frame_attached_nodes.has(graph_element->get_name())) {
794
for (const StringName &attached_node_name : frame_attached_nodes.get(graph_element->get_name())) {
795
GraphElement *attached_node = Object::cast_to<GraphElement>(get_node(NodePath(attached_node_name)));
796
if (attached_node) {
797
linked_parent_map.erase(attached_node->get_name());
798
}
799
}
800
frame_attached_nodes.erase(graph_element->get_name());
801
}
802
803
graph_element->disconnect("raise_request", callable_mp(this, &GraphEdit::_ensure_node_order_from));
804
graph_element->disconnect("resize_request", callable_mp(this, &GraphEdit::_graph_element_resize_request));
805
806
if (connections_layer != nullptr && connections_layer->is_inside_tree()) {
807
graph_element->disconnect(SceneStringName(item_rect_changed), callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
808
}
809
810
// In case of the whole GraphEdit being destroyed these references can already be freed.
811
if (minimap != nullptr && minimap->is_inside_tree()) {
812
graph_element->disconnect(SceneStringName(item_rect_changed), callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw));
813
}
814
}
815
}
816
817
void GraphEdit::_update_theme_item_cache() {
818
Control::_update_theme_item_cache();
819
820
theme_cache.base_scale = get_theme_default_base_scale();
821
}
822
823
void GraphEdit::_notification(int p_what) {
824
switch (p_what) {
825
case NOTIFICATION_THEME_CHANGED: {
826
zoom_minus_button->set_button_icon(theme_cache.zoom_out);
827
zoom_reset_button->set_button_icon(theme_cache.zoom_reset);
828
zoom_plus_button->set_button_icon(theme_cache.zoom_in);
829
830
toggle_snapping_button->set_button_icon(theme_cache.snapping_toggle);
831
toggle_grid_button->set_button_icon(theme_cache.grid_toggle);
832
minimap_button->set_button_icon(theme_cache.minimap_toggle);
833
arrange_button->set_button_icon(theme_cache.layout);
834
835
zoom_label->set_custom_minimum_size(Size2(48, 0) * theme_cache.base_scale);
836
837
menu_panel->add_theme_style_override(SceneStringName(panel), theme_cache.menu_panel);
838
} break;
839
case NOTIFICATION_READY: {
840
Size2 hmin = h_scrollbar->get_combined_minimum_size();
841
Size2 vmin = v_scrollbar->get_combined_minimum_size();
842
843
h_scrollbar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0);
844
h_scrollbar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
845
h_scrollbar->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height);
846
h_scrollbar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
847
848
v_scrollbar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width);
849
v_scrollbar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
850
v_scrollbar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);
851
v_scrollbar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
852
} break;
853
case NOTIFICATION_DRAW: {
854
// Draw background fill.
855
draw_style_box(theme_cache.panel, Rect2(Point2(), get_size()));
856
857
if (has_focus(true)) {
858
draw_style_box(theme_cache.panel_focus, Rect2(Point2(), get_size()));
859
}
860
861
// Draw background grid.
862
if (show_grid) {
863
_draw_grid();
864
}
865
} break;
866
867
case NOTIFICATION_RESIZED: {
868
_update_scrollbars();
869
minimap->queue_redraw();
870
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
871
} break;
872
873
case NOTIFICATION_ENTER_TREE: {
874
update_warped_panning();
875
} break;
876
}
877
}
878
879
Rect2 GraphEdit::_compute_shrinked_frame_rect(const GraphFrame *p_frame) {
880
Vector2 min_point{ FLT_MAX, FLT_MAX };
881
Vector2 max_point{ -FLT_MAX, -FLT_MAX };
882
883
if (!frame_attached_nodes.has(p_frame->get_name())) {
884
return Rect2(p_frame->get_position_offset(), Size2());
885
}
886
887
int autoshrink_margin = p_frame->get_autoshrink_margin();
888
889
for (const StringName &attached_node_name : frame_attached_nodes.get(p_frame->get_name())) {
890
GraphElement *attached_node = Object::cast_to<GraphElement>(get_node_or_null(NodePath(attached_node_name)));
891
892
if (!attached_node || attached_node == p_frame) {
893
if (!attached_node) {
894
frame_attached_nodes.get(p_frame->get_name()).erase(attached_node_name);
895
}
896
continue;
897
}
898
899
Vector2 node_pos = attached_node->get_position_offset();
900
Vector2 size = attached_node->get_size();
901
min_point = min_point.min(node_pos);
902
max_point = max_point.max(node_pos + size);
903
}
904
905
// It's sufficient to check only one value here.
906
if (min_point.x == FLT_MAX) {
907
return Rect2(p_frame->get_position_offset(), Size2());
908
}
909
910
const Size2 titlebar_size = p_frame->get_titlebar_size();
911
912
min_point -= Size2(autoshrink_margin, MAX(autoshrink_margin, titlebar_size.y));
913
max_point += Size2(autoshrink_margin, autoshrink_margin);
914
915
return Rect2(min_point, max_point - min_point);
916
}
917
918
void GraphEdit::_update_graph_frame(GraphFrame *p_frame) {
919
Rect2 frame_rect = _compute_shrinked_frame_rect(p_frame);
920
921
Vector2 min_point = frame_rect.position;
922
Vector2 max_point = frame_rect.position + frame_rect.size;
923
924
// Only update the size if there are attached nodes.
925
if (frame_attached_nodes.has(p_frame->get_name()) && frame_attached_nodes.get(p_frame->get_name()).size() > 0) {
926
if (!p_frame->is_autoshrink_enabled()) {
927
Vector2 old_offset = p_frame->get_position_offset();
928
min_point = min_point.min(old_offset);
929
max_point = max_point.max(old_offset + p_frame->get_size());
930
}
931
932
Rect2 old_rect = p_frame->get_rect();
933
934
p_frame->set_position_offset(min_point);
935
p_frame->set_size(max_point - min_point);
936
937
// Emit the signal only if the frame rect has changed.
938
if (old_rect != p_frame->get_rect()) {
939
emit_signal(SNAME("frame_rect_changed"), p_frame, p_frame->get_rect());
940
}
941
}
942
943
// Update all parent frames recursively bottom-up.
944
if (linked_parent_map.has(p_frame->get_name())) {
945
GraphFrame *parent_frame = Object::cast_to<GraphFrame>(get_node_or_null(NodePath(linked_parent_map[p_frame->get_name()])));
946
if (parent_frame) {
947
_update_graph_frame(parent_frame);
948
}
949
}
950
}
951
952
void GraphEdit::_set_drag_frame_attached_nodes(GraphFrame *p_frame, bool p_drag) {
953
if (!frame_attached_nodes.has(p_frame->get_name())) {
954
return;
955
}
956
957
for (const StringName &attached_node_name : frame_attached_nodes.get(p_frame->get_name())) {
958
GraphElement *attached_node = Object::cast_to<GraphElement>(get_node(NodePath(attached_node_name)));
959
960
attached_node->set_drag(p_drag);
961
GraphFrame *graph_frame = Object::cast_to<GraphFrame>(attached_node);
962
if (graph_frame) {
963
_set_drag_frame_attached_nodes(graph_frame, p_drag);
964
}
965
}
966
}
967
968
void GraphEdit::_set_position_of_frame_attached_nodes(GraphFrame *p_frame, const Vector2 &p_pos) {
969
if (!frame_attached_nodes.has(p_frame->get_name())) {
970
return;
971
}
972
973
for (const StringName &attached_node_name : frame_attached_nodes.get(p_frame->get_name())) {
974
GraphElement *attached_node = Object::cast_to<GraphElement>(get_node_or_null(NodePath(attached_node_name)));
975
if (!attached_node) {
976
continue;
977
}
978
979
Vector2 pos = (attached_node->get_drag_from() * zoom + drag_accum) / zoom;
980
if (snapping_enabled ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) {
981
pos = pos.snappedf(snapping_distance);
982
}
983
984
// Recursively move graph frames.
985
attached_node->set_position_offset(pos);
986
GraphFrame *graph_frame = Object::cast_to<GraphFrame>(attached_node);
987
if (graph_frame) {
988
_set_position_of_frame_attached_nodes(graph_frame, p_pos);
989
}
990
}
991
}
992
993
bool GraphEdit::_filter_input(const Point2 &p_point) {
994
for (int i = get_child_count() - 1; i >= 0; i--) {
995
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
996
if (!graph_node || !graph_node->is_visible_in_tree()) {
997
continue;
998
}
999
1000
Ref<Texture2D> port_icon = graph_node->theme_cache.port;
1001
1002
for (int j = 0; j < graph_node->get_input_port_count(); j++) {
1003
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
1004
1005
// Determine slot height.
1006
int slot_index = graph_node->get_input_port_slot(j);
1007
Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index, false));
1008
1009
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
1010
1011
if (is_in_input_hotzone(graph_node, j, p_point / zoom, port_size)) {
1012
return true;
1013
}
1014
}
1015
1016
for (int j = 0; j < graph_node->get_output_port_count(); j++) {
1017
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
1018
1019
// Determine slot height.
1020
int slot_index = graph_node->get_output_port_slot(j);
1021
Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index, false));
1022
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
1023
1024
if (is_in_output_hotzone(graph_node, j, p_point / zoom, port_size)) {
1025
return true;
1026
}
1027
}
1028
1029
// This prevents interactions with a port hotzone that is behind another node.
1030
Rect2 graph_node_rect = Rect2(graph_node->get_position(), graph_node->get_size() * zoom);
1031
if (graph_node_rect.has_point(p_point)) {
1032
break;
1033
}
1034
}
1035
1036
return false;
1037
}
1038
1039
void GraphEdit::start_keyboard_connecting(GraphNode *p_node, int p_in_port, int p_out_port) {
1040
if (!p_node || p_in_port == p_out_port || (p_in_port != -1 && p_out_port != -1)) {
1041
return;
1042
}
1043
connecting_valid = false;
1044
keyboard_connecting = true;
1045
if (p_in_port != -1) {
1046
Vector2 pos = p_node->get_input_port_position(p_in_port) * zoom + p_node->get_position();
1047
1048
if (right_disconnects || valid_right_disconnect_types.has(p_node->get_input_port_type(p_in_port))) {
1049
// Check disconnect.
1050
for (const Ref<Connection> &conn : connection_map[p_node->get_name()]) {
1051
if (conn->to_node == p_node->get_name() && conn->to_port == p_in_port) {
1052
Node *fr = get_node(NodePath(conn->from_node));
1053
if (Object::cast_to<GraphNode>(fr)) {
1054
connecting_from_node = conn->from_node;
1055
connecting_from_port_index = conn->from_port;
1056
connecting_from_output = true;
1057
connecting_type = Object::cast_to<GraphNode>(fr)->get_output_port_type(conn->from_port);
1058
connecting_color = Object::cast_to<GraphNode>(fr)->get_output_port_color(conn->from_port);
1059
connecting_target_valid = false;
1060
connecting_to_point = pos;
1061
just_disconnected = true;
1062
1063
if (connecting_type >= 0) {
1064
emit_signal(SNAME("disconnection_request"), conn->from_node, conn->from_port, conn->to_node, conn->to_port);
1065
fr = get_node(NodePath(connecting_from_node));
1066
if (Object::cast_to<GraphNode>(fr)) {
1067
connecting = true;
1068
emit_signal(SNAME("connection_drag_started"), connecting_from_node, connecting_from_port_index, true);
1069
}
1070
}
1071
return;
1072
}
1073
}
1074
}
1075
}
1076
1077
connecting_from_node = p_node->get_name();
1078
connecting_from_port_index = p_in_port;
1079
connecting_from_output = false;
1080
connecting_type = p_node->get_input_port_type(p_in_port);
1081
connecting_color = p_node->get_input_port_color(p_in_port);
1082
connecting_target_valid = false;
1083
connecting_to_point = pos;
1084
if (connecting_type >= 0) {
1085
connecting = true;
1086
just_disconnected = false;
1087
emit_signal(SNAME("connection_drag_started"), connecting_from_node, connecting_from_port_index, false);
1088
}
1089
return;
1090
}
1091
if (p_out_port != -1) {
1092
Vector2 pos = p_node->get_output_port_position(p_out_port) * zoom + p_node->get_position();
1093
1094
if (valid_left_disconnect_types.has(p_node->get_output_port_type(p_out_port))) {
1095
// Check disconnect.
1096
for (const Ref<Connection> &conn : connection_map[p_node->get_name()]) {
1097
if (conn->from_node == p_node->get_name() && conn->from_port == p_out_port) {
1098
Node *to = get_node(NodePath(conn->to_node));
1099
if (Object::cast_to<GraphNode>(to)) {
1100
connecting_from_node = conn->to_node;
1101
connecting_from_port_index = conn->to_port;
1102
connecting_from_output = false;
1103
connecting_type = Object::cast_to<GraphNode>(to)->get_input_port_type(conn->to_port);
1104
connecting_color = Object::cast_to<GraphNode>(to)->get_input_port_color(conn->to_port);
1105
connecting_target_valid = false;
1106
connecting_to_point = pos;
1107
1108
if (connecting_type >= 0) {
1109
just_disconnected = true;
1110
1111
emit_signal(SNAME("disconnection_request"), conn->from_node, conn->from_port, conn->to_node, conn->to_port);
1112
to = get_node(NodePath(connecting_from_node)); // Maybe it was erased.
1113
if (Object::cast_to<GraphNode>(to)) {
1114
connecting = true;
1115
emit_signal(SNAME("connection_drag_started"), connecting_from_node, connecting_from_port_index, false);
1116
}
1117
}
1118
return;
1119
}
1120
}
1121
}
1122
}
1123
1124
connecting_from_node = p_node->get_name();
1125
connecting_from_port_index = p_out_port;
1126
connecting_from_output = true;
1127
connecting_type = p_node->get_output_port_type(p_out_port);
1128
connecting_color = p_node->get_output_port_color(p_out_port);
1129
connecting_target_valid = false;
1130
connecting_to_point = pos;
1131
if (connecting_type >= 0) {
1132
connecting = true;
1133
just_disconnected = false;
1134
emit_signal(SNAME("connection_drag_started"), connecting_from_node, connecting_from_port_index, true);
1135
}
1136
return;
1137
}
1138
}
1139
1140
void GraphEdit::end_keyboard_connecting(GraphNode *p_node, int p_in_port, int p_out_port) {
1141
if (!p_node) {
1142
return;
1143
}
1144
connecting_valid = true;
1145
connecting_target_valid = false;
1146
if (p_in_port != -1) {
1147
Vector2 pos = p_node->get_input_port_position(p_in_port) * zoom + p_node->get_position();
1148
1149
int type = p_node->get_input_port_type(p_in_port);
1150
if (type == connecting_type || p_node->is_ignoring_valid_connection_type() || valid_connection_types.has(ConnectionType(connecting_type, type))) {
1151
connecting_target_valid = true;
1152
connecting_to_point = pos;
1153
connecting_target_node = p_node->get_name();
1154
connecting_target_port_index = p_in_port;
1155
}
1156
}
1157
if (p_out_port != -1) {
1158
Vector2 pos = p_node->get_output_port_position(p_out_port) * zoom + p_node->get_position();
1159
1160
int type = p_node->get_output_port_type(p_out_port);
1161
if (type == connecting_type || p_node->is_ignoring_valid_connection_type() || valid_connection_types.has(ConnectionType(type, connecting_type))) {
1162
connecting_target_valid = true;
1163
connecting_to_point = pos;
1164
connecting_target_node = p_node->get_name();
1165
connecting_target_port_index = p_out_port;
1166
}
1167
}
1168
if (connecting_valid) {
1169
if (connecting && connecting_target_valid) {
1170
if (connecting_from_output) {
1171
emit_signal(SNAME("connection_request"), connecting_from_node, connecting_from_port_index, connecting_target_node, connecting_target_port_index);
1172
} else {
1173
emit_signal(SNAME("connection_request"), connecting_target_node, connecting_target_port_index, connecting_from_node, connecting_from_port_index);
1174
}
1175
} else if (!just_disconnected) {
1176
if (connecting_from_output) {
1177
emit_signal(SNAME("connection_to_empty"), connecting_from_node, connecting_from_port_index, Vector2());
1178
} else {
1179
emit_signal(SNAME("connection_from_empty"), connecting_from_node, connecting_from_port_index, Vector2());
1180
}
1181
}
1182
}
1183
1184
keyboard_connecting = false;
1185
if (connecting) {
1186
force_connection_drag_end();
1187
}
1188
}
1189
1190
Dictionary GraphEdit::get_type_names() const {
1191
return type_names;
1192
}
1193
1194
void GraphEdit::set_type_names(const Dictionary &p_names) {
1195
type_names = p_names;
1196
}
1197
1198
void GraphEdit::_top_connection_layer_input(const Ref<InputEvent> &p_ev) {
1199
Ref<InputEventMouseButton> mb = p_ev;
1200
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
1201
if (keyboard_connecting) {
1202
force_connection_drag_end();
1203
keyboard_connecting = false;
1204
}
1205
connecting_valid = false;
1206
click_pos = mb->get_position() / zoom;
1207
for (int i = get_child_count() - 1; i >= 0; i--) {
1208
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
1209
if (!graph_node || !graph_node->is_visible_in_tree()) {
1210
continue;
1211
}
1212
1213
Ref<Texture2D> port_icon = graph_node->theme_cache.port;
1214
1215
for (int j = 0; j < graph_node->get_output_port_count(); j++) {
1216
Vector2 pos = graph_node->get_output_port_position(j) * zoom + graph_node->get_position();
1217
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
1218
1219
// Determine slot height.
1220
int slot_index = graph_node->get_output_port_slot(j);
1221
Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index, false));
1222
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
1223
1224
if (is_in_output_hotzone(graph_node, j, click_pos, port_size)) {
1225
if (valid_left_disconnect_types.has(graph_node->get_output_port_type(j))) {
1226
// Check disconnect.
1227
for (const Ref<Connection> &conn : connection_map[graph_node->get_name()]) {
1228
if (conn->from_node == graph_node->get_name() && conn->from_port == j) {
1229
Node *to = get_node(NodePath(conn->to_node));
1230
if (Object::cast_to<GraphNode>(to)) {
1231
connecting_from_node = conn->to_node;
1232
connecting_from_port_index = conn->to_port;
1233
connecting_from_output = false;
1234
connecting_type = Object::cast_to<GraphNode>(to)->get_input_port_type(conn->to_port);
1235
connecting_color = Object::cast_to<GraphNode>(to)->get_input_port_color(conn->to_port);
1236
connecting_target_valid = false;
1237
connecting_to_point = pos;
1238
1239
if (connecting_type >= 0) {
1240
just_disconnected = true;
1241
1242
emit_signal(SNAME("disconnection_request"), conn->from_node, conn->from_port, conn->to_node, conn->to_port);
1243
to = get_node(NodePath(connecting_from_node)); // Maybe it was erased.
1244
if (Object::cast_to<GraphNode>(to)) {
1245
connecting = true;
1246
emit_signal(SNAME("connection_drag_started"), connecting_from_node, connecting_from_port_index, false);
1247
}
1248
}
1249
return;
1250
}
1251
}
1252
}
1253
}
1254
1255
connecting_from_node = graph_node->get_name();
1256
connecting_from_port_index = j;
1257
connecting_from_output = true;
1258
connecting_type = graph_node->get_output_port_type(j);
1259
connecting_color = graph_node->get_output_port_color(j);
1260
connecting_target_valid = false;
1261
connecting_to_point = pos;
1262
if (connecting_type >= 0) {
1263
connecting = true;
1264
just_disconnected = false;
1265
emit_signal(SNAME("connection_drag_started"), connecting_from_node, connecting_from_port_index, true);
1266
}
1267
return;
1268
}
1269
}
1270
1271
for (int j = 0; j < graph_node->get_input_port_count(); j++) {
1272
Vector2 pos = graph_node->get_input_port_position(j) * zoom + graph_node->get_position();
1273
1274
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
1275
1276
// Determine slot height.
1277
int slot_index = graph_node->get_input_port_slot(j);
1278
Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index, false));
1279
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
1280
1281
if (is_in_input_hotzone(graph_node, j, click_pos, port_size)) {
1282
if (right_disconnects || valid_right_disconnect_types.has(graph_node->get_input_port_type(j))) {
1283
// Check disconnect.
1284
for (const Ref<Connection> &conn : connection_map[graph_node->get_name()]) {
1285
if (conn->to_node == graph_node->get_name() && conn->to_port == j) {
1286
Node *fr = get_node(NodePath(conn->from_node));
1287
if (Object::cast_to<GraphNode>(fr)) {
1288
connecting_from_node = conn->from_node;
1289
connecting_from_port_index = conn->from_port;
1290
connecting_from_output = true;
1291
connecting_type = Object::cast_to<GraphNode>(fr)->get_output_port_type(conn->from_port);
1292
connecting_color = Object::cast_to<GraphNode>(fr)->get_output_port_color(conn->from_port);
1293
connecting_target_valid = false;
1294
connecting_to_point = pos;
1295
just_disconnected = true;
1296
1297
if (connecting_type >= 0) {
1298
emit_signal(SNAME("disconnection_request"), conn->from_node, conn->from_port, conn->to_node, conn->to_port);
1299
fr = get_node(NodePath(connecting_from_node));
1300
if (Object::cast_to<GraphNode>(fr)) {
1301
connecting = true;
1302
emit_signal(SNAME("connection_drag_started"), connecting_from_node, connecting_from_port_index, true);
1303
}
1304
}
1305
return;
1306
}
1307
}
1308
}
1309
}
1310
1311
connecting_from_node = graph_node->get_name();
1312
connecting_from_port_index = j;
1313
connecting_from_output = false;
1314
connecting_type = graph_node->get_input_port_type(j);
1315
connecting_color = graph_node->get_input_port_color(j);
1316
connecting_target_valid = false;
1317
connecting_to_point = pos;
1318
if (connecting_type >= 0) {
1319
connecting = true;
1320
just_disconnected = false;
1321
emit_signal(SNAME("connection_drag_started"), connecting_from_node, connecting_from_port_index, false);
1322
}
1323
return;
1324
}
1325
}
1326
}
1327
}
1328
1329
Ref<InputEventMouseMotion> mm = p_ev;
1330
if (mm.is_valid() && connecting && !keyboard_connecting) {
1331
connecting_to_point = mm->get_position();
1332
minimap->queue_redraw();
1333
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
1334
1335
connecting_valid = just_disconnected || click_pos.distance_to(connecting_to_point / zoom) > MIN_DRAG_DISTANCE_FOR_VALID_CONNECTION;
1336
1337
if (connecting_valid) {
1338
Vector2 mpos = mm->get_position() / zoom;
1339
for (int i = get_child_count() - 1; i >= 0; i--) {
1340
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
1341
if (!graph_node || !graph_node->is_visible_in_tree()) {
1342
continue;
1343
}
1344
1345
Ref<Texture2D> port_icon = graph_node->theme_cache.port;
1346
1347
if (!connecting_from_output) {
1348
for (int j = 0; j < graph_node->get_output_port_count(); j++) {
1349
Vector2 pos = graph_node->get_output_port_position(j) * zoom + graph_node->get_position();
1350
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
1351
1352
// Determine slot height.
1353
int slot_index = graph_node->get_output_port_slot(j);
1354
Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index, false));
1355
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
1356
1357
int type = graph_node->get_output_port_type(j);
1358
if ((type == connecting_type || graph_node->is_ignoring_valid_connection_type() ||
1359
valid_connection_types.has(ConnectionType(type, connecting_type))) &&
1360
is_in_output_hotzone(graph_node, j, mpos, port_size)) {
1361
if (!is_node_hover_valid(graph_node->get_name(), j, connecting_from_node, connecting_from_port_index)) {
1362
continue;
1363
}
1364
connecting_target_valid = true;
1365
connecting_to_point = pos;
1366
connecting_target_node = graph_node->get_name();
1367
connecting_target_port_index = j;
1368
return;
1369
}
1370
}
1371
connecting_target_valid = false;
1372
} else {
1373
for (int j = 0; j < graph_node->get_input_port_count(); j++) {
1374
Vector2 pos = graph_node->get_input_port_position(j) * zoom + graph_node->get_position();
1375
Vector2i port_size = Vector2i(port_icon->get_width(), port_icon->get_height());
1376
1377
// Determine slot height.
1378
int slot_index = graph_node->get_input_port_slot(j);
1379
Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index, false));
1380
port_size.height = MAX(port_size.height, child ? child->get_size().y : 0);
1381
1382
int type = graph_node->get_input_port_type(j);
1383
if ((type == connecting_type || graph_node->is_ignoring_valid_connection_type() || valid_connection_types.has(ConnectionType(connecting_type, type))) &&
1384
is_in_input_hotzone(graph_node, j, mpos, port_size)) {
1385
if (!is_node_hover_valid(connecting_from_node, connecting_from_port_index, graph_node->get_name(), j)) {
1386
continue;
1387
}
1388
connecting_target_valid = true;
1389
connecting_to_point = pos;
1390
connecting_target_node = graph_node->get_name();
1391
connecting_target_port_index = j;
1392
return;
1393
}
1394
}
1395
connecting_target_valid = false;
1396
}
1397
1398
// This prevents interactions with a port hotzone that is behind another node.
1399
Rect2 graph_node_rect = Rect2(graph_node->get_position(), graph_node->get_size() * zoom);
1400
if (graph_node_rect.has_point(mm->get_position())) {
1401
break;
1402
}
1403
}
1404
}
1405
}
1406
1407
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) {
1408
if (connecting_valid) {
1409
if (connecting && connecting_target_valid) {
1410
if (connecting_from_output) {
1411
emit_signal(SNAME("connection_request"), connecting_from_node, connecting_from_port_index, connecting_target_node, connecting_target_port_index);
1412
} else {
1413
emit_signal(SNAME("connection_request"), connecting_target_node, connecting_target_port_index, connecting_from_node, connecting_from_port_index);
1414
}
1415
} else if (!just_disconnected) {
1416
if (connecting_from_output) {
1417
emit_signal(SNAME("connection_to_empty"), connecting_from_node, connecting_from_port_index, mb->get_position());
1418
} else {
1419
emit_signal(SNAME("connection_from_empty"), connecting_from_node, connecting_from_port_index, mb->get_position());
1420
}
1421
}
1422
} else {
1423
set_selected(get_node_or_null(NodePath(connecting_from_node)));
1424
}
1425
1426
if (connecting) {
1427
force_connection_drag_end();
1428
}
1429
}
1430
}
1431
1432
bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &mpos, const Vector2 &p_offset) {
1433
if (p_control->is_set_as_top_level() || !p_control->is_visible() || !p_control->is_inside_tree()) {
1434
return false;
1435
}
1436
1437
Rect2 control_rect = p_control->get_rect();
1438
control_rect.position *= zoom;
1439
control_rect.size *= zoom;
1440
control_rect.position += p_offset;
1441
1442
if (!control_rect.has_point(mpos) || p_control->get_mouse_filter_with_override() == MOUSE_FILTER_IGNORE) {
1443
// Test children.
1444
for (int i = 0; i < p_control->get_child_count(); i++) {
1445
Control *child_rect = Object::cast_to<Control>(p_control->get_child(i));
1446
if (!child_rect) {
1447
continue;
1448
}
1449
if (_check_clickable_control(child_rect, mpos, control_rect.position)) {
1450
return true;
1451
}
1452
}
1453
1454
return false;
1455
} else {
1456
return true;
1457
}
1458
}
1459
1460
bool GraphEdit::is_in_input_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
1461
bool success;
1462
if (GDVIRTUAL_CALL(_is_in_input_hotzone, p_graph_node, p_port_idx, p_mouse_pos, success)) {
1463
return success;
1464
} else {
1465
Vector2 pos = p_graph_node->get_input_port_position(p_port_idx) * zoom + p_graph_node->get_position();
1466
return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, true);
1467
}
1468
}
1469
1470
bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
1471
if (p_graph_node->is_resizable()) {
1472
Ref<Texture2D> resizer = p_graph_node->theme_cache.resizer;
1473
Rect2 resizer_rect = Rect2(p_graph_node->get_position() / zoom + p_graph_node->get_size() - resizer->get_size(), resizer->get_size());
1474
if (resizer_rect.has_point(p_mouse_pos)) {
1475
return false;
1476
}
1477
}
1478
1479
bool success;
1480
if (GDVIRTUAL_CALL(_is_in_output_hotzone, p_graph_node, p_port_idx, p_mouse_pos, success)) {
1481
return success;
1482
} else {
1483
Vector2 pos = p_graph_node->get_output_port_position(p_port_idx) * zoom + p_graph_node->get_position();
1484
return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, false);
1485
}
1486
}
1487
1488
bool GraphEdit::is_in_port_hotzone(const Vector2 &p_pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left) {
1489
Rect2 hotzone = Rect2(
1490
p_pos.x - (p_left ? theme_cache.port_hotzone_outer_extent : theme_cache.port_hotzone_inner_extent),
1491
p_pos.y - p_port_size.height / 2.0,
1492
theme_cache.port_hotzone_inner_extent + theme_cache.port_hotzone_outer_extent,
1493
p_port_size.height);
1494
1495
if (!hotzone.has_point(p_mouse_pos)) {
1496
return false;
1497
}
1498
1499
for (int i = 0; i < get_child_count(); i++) {
1500
GraphNode *child = Object::cast_to<GraphNode>(get_child(i));
1501
if (!child) {
1502
continue;
1503
}
1504
1505
Rect2 child_rect = child->get_rect();
1506
if (child_rect.has_point(p_mouse_pos * zoom)) {
1507
for (int j = 0; j < child->get_child_count(); j++) {
1508
Control *subchild = Object::cast_to<Control>(child->get_child(j));
1509
if (!subchild) {
1510
continue;
1511
}
1512
1513
if (_check_clickable_control(subchild, p_mouse_pos * zoom, child_rect.position)) {
1514
return false;
1515
}
1516
}
1517
}
1518
}
1519
1520
return true;
1521
}
1522
1523
PackedVector2Array GraphEdit::get_connection_line(const Vector2 &p_from, const Vector2 &p_to) const {
1524
Vector<Vector2> ret;
1525
if (GDVIRTUAL_CALL(_get_connection_line, p_from, p_to, ret)) {
1526
return ret;
1527
}
1528
1529
float x_diff = (p_to.x - p_from.x);
1530
float cp_offset = x_diff * lines_curvature;
1531
if (x_diff < 0) {
1532
cp_offset *= -1;
1533
}
1534
1535
Curve2D curve;
1536
curve.add_point(p_from);
1537
curve.set_point_out(0, Vector2(cp_offset, 0));
1538
curve.add_point(p_to);
1539
curve.set_point_in(1, Vector2(-cp_offset, 0));
1540
1541
if (lines_curvature > 0) {
1542
return curve.tessellate(MAX_CONNECTION_LINE_CURVE_TESSELATION_STAGES, 2.0);
1543
} else {
1544
return curve.tessellate(1);
1545
}
1546
}
1547
1548
Ref<GraphEdit::Connection> GraphEdit::get_closest_connection_at_point(const Vector2 &p_point, float p_max_distance) const {
1549
Vector2 transformed_point = p_point + get_scroll_offset();
1550
1551
Ref<GraphEdit::Connection> closest_connection;
1552
float closest_distance = p_max_distance;
1553
for (const Ref<Connection> &conn : connections) {
1554
if (conn->_cache.aabb.distance_to(transformed_point) > p_max_distance) {
1555
continue;
1556
}
1557
1558
Vector<Vector2> points = get_connection_line(conn->_cache.from_pos * zoom, conn->_cache.to_pos * zoom);
1559
for (int i = 0; i < points.size() - 1; i++) {
1560
const real_t distance = Geometry2D::get_distance_to_segment(transformed_point, points[i], points[i + 1]);
1561
if (distance <= lines_thickness * 0.5 + p_max_distance && distance < closest_distance) {
1562
closest_connection = conn;
1563
closest_distance = distance;
1564
}
1565
}
1566
}
1567
1568
return closest_connection;
1569
}
1570
1571
List<Ref<GraphEdit::Connection>> GraphEdit::get_connections_intersecting_with_rect(const Rect2 &p_rect) const {
1572
Rect2 transformed_rect = p_rect;
1573
transformed_rect.position += get_scroll_offset();
1574
1575
List<Ref<Connection>> intersecting_connections;
1576
for (const Ref<Connection> &conn : connections) {
1577
if (!conn->_cache.aabb.intersects(transformed_rect)) {
1578
continue;
1579
}
1580
1581
Vector<Vector2> points = get_connection_line(conn->_cache.from_pos * zoom, conn->_cache.to_pos * zoom);
1582
for (int i = 0; i < points.size() - 1; i++) {
1583
if (Geometry2D::segment_intersects_rect(points[i], points[i + 1], transformed_rect)) {
1584
intersecting_connections.push_back(conn);
1585
break;
1586
}
1587
}
1588
}
1589
return intersecting_connections;
1590
}
1591
1592
void GraphEdit::_draw_minimap_connection_line(const Vector2 &p_from_graph_position, const Vector2 &p_to_graph_position, const Color &p_from_color, const Color &p_to_color) {
1593
Vector<Vector2> points = get_connection_line(p_from_graph_position, p_to_graph_position);
1594
ERR_FAIL_COND_MSG(points.size() < 2, "\"_get_connection_line()\" returned an invalid line.");
1595
// Convert to minimap points.
1596
for (Vector2 &point : points) {
1597
point = minimap->_convert_from_graph_position(point) + minimap->minimap_offset;
1598
}
1599
1600
// Setup polyline colors.
1601
LocalVector<Color> colors;
1602
colors.reserve(points.size());
1603
const Vector2 &from = points[0];
1604
const Vector2 &to = points[points.size() - 1];
1605
float length_inv = 1.0 / (from).distance_to(to);
1606
for (const Vector2 &point : points) {
1607
float normalized_curve_position = from.distance_to(point) * length_inv;
1608
colors.push_back(p_from_color.lerp(p_to_color, normalized_curve_position));
1609
}
1610
1611
minimap->draw_polyline_colors(points, Vector<Color>(colors), 0.5, lines_antialiased);
1612
}
1613
1614
void GraphEdit::_update_connections() {
1615
// Collect all dead connections and remove them.
1616
LocalVector<Ref<Connection>> dead_connections;
1617
1618
for (const Ref<Connection> &conn : connections) {
1619
if (conn->_cache.dirty) {
1620
Node *from = get_node_or_null(NodePath(conn->from_node));
1621
GraphNode *gnode_from = Object::cast_to<GraphNode>(from);
1622
if (!gnode_from && !conn->keep_alive) {
1623
dead_connections.push_back(conn);
1624
continue;
1625
}
1626
Node *to = get_node_or_null(NodePath(conn->to_node));
1627
GraphNode *gnode_to = Object::cast_to<GraphNode>(to);
1628
1629
if (!gnode_to && !conn->keep_alive) {
1630
dead_connections.push_back(conn);
1631
continue;
1632
}
1633
1634
if (conn->keep_alive && (!gnode_from || !gnode_to)) {
1635
continue;
1636
}
1637
1638
const Vector2 from_pos = gnode_from->get_output_port_position(conn->from_port) + gnode_from->get_position_offset();
1639
const Vector2 to_pos = gnode_to->get_input_port_position(conn->to_port) + gnode_to->get_position_offset();
1640
1641
const Color from_color = gnode_from->get_output_port_color(conn->from_port);
1642
const Color to_color = gnode_to->get_input_port_color(conn->to_port);
1643
1644
const int from_type = gnode_from->get_output_port_type(conn->from_port);
1645
const int to_type = gnode_to->get_input_port_type(conn->to_port);
1646
1647
conn->_cache.from_pos = from_pos;
1648
conn->_cache.to_pos = to_pos;
1649
conn->_cache.from_color = from_color;
1650
conn->_cache.to_color = to_color;
1651
1652
PackedVector2Array line_points = get_connection_line(from_pos * zoom, to_pos * zoom);
1653
conn->_cache.line->set_points(line_points);
1654
1655
Ref<ShaderMaterial> line_material = conn->_cache.line->get_material();
1656
if (line_material.is_null()) {
1657
line_material.instantiate();
1658
conn->_cache.line->set_material(line_material);
1659
}
1660
1661
float line_width = _get_shader_line_width();
1662
line_material->set_shader_parameter("line_width", line_width);
1663
line_material->set_shader_parameter("from_type", from_type);
1664
line_material->set_shader_parameter("to_type", to_type);
1665
line_material->set_shader_parameter("rim_color", theme_cache.connection_rim_color);
1666
1667
// Compute bounding box of the line, including the line width.
1668
conn->_cache.aabb = Rect2(line_points[0], Vector2());
1669
for (int i = 0; i < line_points.size(); i++) {
1670
conn->_cache.aabb.expand_to(line_points[i]);
1671
}
1672
conn->_cache.aabb.grow_by(lines_thickness * 0.5);
1673
1674
conn->_cache.dirty = false;
1675
}
1676
1677
// Skip updating/drawing connections that are not visible.
1678
Rect2 viewport_rect = get_viewport_rect();
1679
viewport_rect.position += get_scroll_offset();
1680
if (!conn->_cache.aabb.intersects(viewport_rect)) {
1681
continue;
1682
}
1683
1684
Color from_color = conn->_cache.from_color;
1685
Color to_color = conn->_cache.to_color;
1686
1687
if (conn->activity > 0) {
1688
from_color = from_color.lerp(theme_cache.activity_color, conn->activity);
1689
to_color = to_color.lerp(theme_cache.activity_color, conn->activity);
1690
}
1691
1692
if (conn == hovered_connection) {
1693
from_color = from_color.blend(theme_cache.connection_hover_tint_color);
1694
to_color = to_color.blend(theme_cache.connection_hover_tint_color);
1695
}
1696
1697
// Update Line2D node.
1698
Ref<Gradient> line_gradient = memnew(Gradient);
1699
1700
float line_width = _get_shader_line_width();
1701
if (conn == hovered_connection) {
1702
line_width *= 1.0f + (theme_cache.connection_hover_thickness / 100.0f);
1703
}
1704
1705
conn->_cache.line->set_width(line_width);
1706
line_gradient->set_color(0, from_color);
1707
line_gradient->set_color(1, to_color);
1708
1709
conn->_cache.line->set_gradient(line_gradient);
1710
}
1711
1712
for (const Ref<Connection> &dead_conn : dead_connections) {
1713
List<Ref<Connection>> &connections_from = connection_map[dead_conn->from_node];
1714
List<Ref<Connection>> &connections_to = connection_map[dead_conn->to_node];
1715
connections_from.erase(dead_conn);
1716
connections_to.erase(dead_conn);
1717
dead_conn->_cache.line->queue_free();
1718
1719
connections.erase(dead_conn);
1720
}
1721
}
1722
1723
void GraphEdit::_top_layer_draw() {
1724
if (!box_selecting) {
1725
return;
1726
}
1727
1728
top_layer->draw_rect(box_selecting_rect, theme_cache.selection_fill);
1729
top_layer->draw_rect(box_selecting_rect, theme_cache.selection_stroke, false);
1730
}
1731
1732
void GraphEdit::_update_top_connection_layer() {
1733
_update_scrollbars();
1734
1735
if (!connecting) {
1736
dragged_connection_line->clear_points();
1737
1738
return;
1739
}
1740
1741
GraphNode *graph_node_from = Object::cast_to<GraphNode>(get_node_or_null(NodePath(connecting_from_node)));
1742
ERR_FAIL_NULL(graph_node_from);
1743
1744
Vector2 from_pos = graph_node_from->get_position() / zoom;
1745
Vector2 to_pos = connecting_to_point / zoom;
1746
int from_type;
1747
int to_type = connecting_type;
1748
Color from_color;
1749
Color to_color = connecting_color;
1750
1751
if (connecting_from_output) {
1752
from_pos += graph_node_from->get_output_port_position(connecting_from_port_index);
1753
from_type = graph_node_from->get_output_port_type(connecting_from_port_index);
1754
from_color = graph_node_from->get_output_port_color(connecting_from_port_index);
1755
} else {
1756
from_pos += graph_node_from->get_input_port_position(connecting_from_port_index);
1757
from_type = graph_node_from->get_input_port_type(connecting_from_port_index);
1758
from_color = graph_node_from->get_input_port_color(connecting_from_port_index);
1759
}
1760
1761
if (connecting_target_valid) {
1762
GraphNode *graph_node_to = Object::cast_to<GraphNode>(get_node_or_null(NodePath(connecting_target_node)));
1763
ERR_FAIL_NULL(graph_node_to);
1764
if (connecting_from_output) {
1765
to_type = graph_node_to->get_input_port_type(connecting_target_port_index);
1766
to_color = graph_node_to->get_input_port_color(connecting_target_port_index);
1767
} else {
1768
to_type = graph_node_to->get_output_port_type(connecting_target_port_index);
1769
to_color = graph_node_to->get_output_port_color(connecting_target_port_index);
1770
}
1771
1772
// Highlight the line to the mouse cursor when it's over a valid target port.
1773
from_color = from_color.blend(theme_cache.connection_valid_target_tint_color);
1774
to_color = to_color.blend(theme_cache.connection_valid_target_tint_color);
1775
}
1776
1777
if (!connecting_from_output) {
1778
SWAP(from_pos, to_pos);
1779
SWAP(from_type, to_type);
1780
SWAP(from_color, to_color);
1781
}
1782
1783
PackedVector2Array line_points = get_connection_line(from_pos * zoom, to_pos * zoom);
1784
dragged_connection_line->set_points(line_points);
1785
1786
Ref<ShaderMaterial> line_material = dragged_connection_line->get_material();
1787
if (line_material.is_null()) {
1788
line_material.instantiate();
1789
line_material->set_shader(connections_shader);
1790
dragged_connection_line->set_material(line_material);
1791
}
1792
1793
float line_width = _get_shader_line_width();
1794
line_material->set_shader_parameter("line_width", line_width);
1795
line_material->set_shader_parameter("from_type", from_type);
1796
line_material->set_shader_parameter("to_type", to_type);
1797
line_material->set_shader_parameter("rim_color", theme_cache.connection_rim_color);
1798
1799
Ref<Gradient> line_gradient = memnew(Gradient);
1800
dragged_connection_line->set_width(line_width);
1801
line_gradient->set_color(0, from_color);
1802
line_gradient->set_color(1, to_color);
1803
1804
dragged_connection_line->set_gradient(line_gradient);
1805
}
1806
1807
void GraphEdit::_minimap_draw() {
1808
if (!is_minimap_enabled()) {
1809
return;
1810
}
1811
1812
minimap->update_minimap();
1813
1814
// Draw the minimap background.
1815
Rect2 minimap_rect = Rect2(Point2(), minimap->get_size());
1816
minimap->draw_style_box(minimap->theme_cache.panel, minimap_rect);
1817
1818
Vector2 graph_offset = minimap->_get_graph_offset();
1819
Vector2 minimap_offset = minimap->minimap_offset;
1820
1821
// Draw frame nodes.
1822
for (int i = get_child_count() - 1; i >= 0; i--) {
1823
GraphFrame *graph_frame = Object::cast_to<GraphFrame>(get_child(i));
1824
if (!graph_frame || !graph_frame->is_visible()) {
1825
continue;
1826
}
1827
1828
Vector2 node_position = minimap->_convert_from_graph_position(graph_frame->get_position_offset() * zoom - graph_offset) + minimap_offset;
1829
Vector2 node_size = minimap->_convert_from_graph_position(graph_frame->get_size() * zoom);
1830
Rect2 node_rect = Rect2(node_position, node_size);
1831
1832
Ref<StyleBoxFlat> sb_minimap = minimap->theme_cache.node_style->duplicate();
1833
1834
// Override default values with colors provided by the GraphNode's stylebox, if possible.
1835
Ref<StyleBoxFlat> sb_frame = graph_frame->get_theme_stylebox(graph_frame->is_selected() ? SNAME("panel_selected") : SceneStringName(panel));
1836
if (sb_frame.is_valid()) {
1837
Color node_color = sb_frame->get_bg_color();
1838
if (graph_frame->is_tint_color_enabled()) {
1839
node_color = graph_frame->get_tint_color();
1840
}
1841
sb_minimap->set_bg_color(node_color);
1842
}
1843
1844
minimap->draw_style_box(sb_minimap, node_rect);
1845
}
1846
1847
// Draw regular graph nodes.
1848
for (int i = get_child_count() - 1; i >= 0; i--) {
1849
GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i));
1850
if (!graph_node || !graph_node->is_visible()) {
1851
continue;
1852
}
1853
1854
Vector2 node_position = minimap->_convert_from_graph_position(graph_node->get_position_offset() * zoom - graph_offset) + minimap_offset;
1855
Vector2 node_size = minimap->_convert_from_graph_position(graph_node->get_size() * zoom);
1856
Rect2 node_rect = Rect2(node_position, node_size);
1857
1858
Ref<StyleBoxFlat> sb_minimap = minimap->theme_cache.node_style->duplicate();
1859
1860
// Override default values with colors provided by the GraphNode's stylebox, if possible.
1861
Ref<StyleBoxFlat> sb_frame = graph_node->is_selected() ? graph_node->theme_cache.panel_selected : graph_node->theme_cache.panel;
1862
if (sb_frame.is_valid()) {
1863
Color node_color = sb_frame->get_bg_color();
1864
sb_minimap->set_bg_color(node_color);
1865
}
1866
1867
minimap->draw_style_box(sb_minimap, node_rect);
1868
}
1869
1870
// Draw node connections.
1871
for (const Ref<Connection> &conn : connections) {
1872
Vector2 from_graph_position = conn->_cache.from_pos * zoom - graph_offset;
1873
Vector2 to_graph_position = conn->_cache.to_pos * zoom - graph_offset;
1874
Color from_color = conn->_cache.from_color;
1875
Color to_color = conn->_cache.to_color;
1876
1877
if (conn->activity > 0) {
1878
from_color = from_color.lerp(theme_cache.activity_color, conn->activity);
1879
to_color = to_color.lerp(theme_cache.activity_color, conn->activity);
1880
}
1881
1882
_draw_minimap_connection_line(from_graph_position, to_graph_position, from_color, to_color);
1883
}
1884
1885
// Draw the "camera" viewport.
1886
Rect2 camera_rect = minimap->get_camera_rect();
1887
minimap->draw_style_box(minimap->theme_cache.camera_style, camera_rect);
1888
1889
// Draw the resizer control.
1890
Ref<Texture2D> resizer = minimap->theme_cache.resizer;
1891
Color resizer_color = minimap->theme_cache.resizer_color;
1892
minimap->draw_texture(resizer, Point2(), resizer_color);
1893
}
1894
1895
void GraphEdit::_draw_grid() {
1896
Vector2 offset = get_scroll_offset() / zoom;
1897
Size2 size = get_size() / zoom;
1898
1899
Point2i from_pos = (offset / float(snapping_distance)).floor();
1900
Point2i len = (size / float(snapping_distance)).floor() + Vector2(1, 1);
1901
1902
switch (grid_pattern) {
1903
case GRID_PATTERN_LINES: {
1904
for (int i = from_pos.x; i < from_pos.x + len.x; i++) {
1905
Color color;
1906
1907
if (Math::abs(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) {
1908
color = theme_cache.grid_major;
1909
} else {
1910
color = theme_cache.grid_minor;
1911
}
1912
1913
float base_offset = i * snapping_distance * zoom - offset.x * zoom;
1914
draw_line(Vector2(base_offset, 0), Vector2(base_offset, get_size().height), color);
1915
}
1916
1917
for (int i = from_pos.y; i < from_pos.y + len.y; i++) {
1918
Color color;
1919
1920
if (Math::abs(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) {
1921
color = theme_cache.grid_major;
1922
} else {
1923
color = theme_cache.grid_minor;
1924
}
1925
1926
float base_offset = i * snapping_distance * zoom - offset.y * zoom;
1927
draw_line(Vector2(0, base_offset), Vector2(get_size().width, base_offset), color);
1928
}
1929
} break;
1930
case GRID_PATTERN_DOTS: {
1931
Color transparent_grid_minor = theme_cache.grid_minor;
1932
transparent_grid_minor.a *= CLAMP(1.0 * (zoom - 0.4), 0, 1);
1933
1934
// Minor dots.
1935
if (transparent_grid_minor.a != 0) {
1936
for (int i = from_pos.x; i < from_pos.x + len.x; i++) {
1937
for (int j = from_pos.y; j < from_pos.y + len.y; j++) {
1938
if (Math::abs(i) % GRID_MINOR_STEPS_PER_MAJOR_DOT == 0 && Math::abs(j) % GRID_MINOR_STEPS_PER_MAJOR_DOT == 0) {
1939
continue;
1940
}
1941
1942
float base_offset_x = i * snapping_distance * zoom - offset.x * zoom;
1943
float base_offset_y = j * snapping_distance * zoom - offset.y * zoom;
1944
1945
draw_rect(Rect2(base_offset_x - 1, base_offset_y - 1, 3, 3), transparent_grid_minor);
1946
}
1947
}
1948
}
1949
1950
// Major dots.
1951
if (theme_cache.grid_major.a != 0) {
1952
for (int i = from_pos.x - from_pos.x % GRID_MINOR_STEPS_PER_MAJOR_DOT; i < from_pos.x + len.x; i += GRID_MINOR_STEPS_PER_MAJOR_DOT) {
1953
for (int j = from_pos.y - from_pos.y % GRID_MINOR_STEPS_PER_MAJOR_DOT; j < from_pos.y + len.y; j += GRID_MINOR_STEPS_PER_MAJOR_DOT) {
1954
float base_offset_x = i * snapping_distance * zoom - offset.x * zoom;
1955
float base_offset_y = j * snapping_distance * zoom - offset.y * zoom;
1956
1957
draw_rect(Rect2(base_offset_x - 1, base_offset_y - 1, 3, 3), theme_cache.grid_major);
1958
}
1959
}
1960
}
1961
1962
} break;
1963
}
1964
}
1965
1966
void GraphEdit::set_selected(Node *p_child) {
1967
for (int i = get_child_count() - 1; i >= 0; i--) {
1968
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
1969
if (!graph_element) {
1970
continue;
1971
}
1972
1973
graph_element->set_selected(graph_element == p_child);
1974
}
1975
}
1976
1977
void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
1978
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
1979
1980
ERR_FAIL_COND(p_ev.is_null());
1981
if (panner->gui_input(p_ev, get_global_rect())) {
1982
return;
1983
}
1984
1985
// Highlight the connection close to the mouse cursor.
1986
Ref<InputEventMouseMotion> mm = p_ev;
1987
if (mm.is_valid()) {
1988
Ref<Connection> new_highlighted_connection = get_closest_connection_at_point(mm->get_position());
1989
if (new_highlighted_connection != hovered_connection) {
1990
connections_layer->queue_redraw();
1991
}
1992
hovered_connection = new_highlighted_connection;
1993
}
1994
1995
// Logic for moving graph controls via mouse drag.
1996
if (mm.is_valid() && dragging) {
1997
if (!moving_selection) {
1998
emit_signal(SNAME("begin_node_move"));
1999
moving_selection = true;
2000
}
2001
2002
just_selected = true;
2003
drag_accum += mm->get_relative();
2004
for (int i = get_child_count() - 1; i >= 0; i--) {
2005
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
2006
if (graph_element && graph_element->is_selected() && graph_element->is_draggable()) {
2007
Vector2 pos = (graph_element->get_drag_from() * zoom + drag_accum) / zoom;
2008
2009
// Snapping can be toggled temporarily by holding down Ctrl.
2010
// This is done here as to not toggle the grid when holding down Ctrl.
2011
if (snapping_enabled ^ Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) {
2012
pos = pos.snappedf(snapping_distance);
2013
}
2014
2015
graph_element->set_position_offset(pos);
2016
2017
if (linked_parent_map.has(graph_element->get_name())) {
2018
GraphFrame *parent_frame = Object::cast_to<GraphFrame>(get_node_or_null(NodePath(linked_parent_map[graph_element->get_name()])));
2019
if (parent_frame) {
2020
_update_graph_frame(parent_frame);
2021
}
2022
}
2023
2024
// Update all frame transforms recursively.
2025
GraphFrame *graph_frame = Object::cast_to<GraphFrame>(get_child(i));
2026
if (graph_frame) {
2027
_set_position_of_frame_attached_nodes(graph_frame, drag_accum);
2028
}
2029
}
2030
}
2031
}
2032
2033
// Box selection logic.
2034
if (mm.is_valid() && box_selecting) {
2035
box_selecting_to = mm->get_position();
2036
2037
box_selecting_rect = Rect2(box_selecting_from.min(box_selecting_to), (box_selecting_from - box_selecting_to).abs());
2038
2039
for (int i = get_child_count() - 1; i >= 0; i--) {
2040
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
2041
if (!graph_element) {
2042
continue;
2043
}
2044
2045
if (!graph_element->is_visible()) {
2046
continue;
2047
}
2048
2049
// Only select frames when the box selection is fully enclosing them.
2050
bool is_frame = Object::cast_to<GraphFrame>(graph_element);
2051
Rect2 r = graph_element->get_rect();
2052
bool should_be_selected = is_frame ? box_selecting_rect.encloses(r) : box_selecting_rect.intersects(r);
2053
2054
if (should_be_selected) {
2055
graph_element->set_selected(box_selection_mode_additive);
2056
} else {
2057
graph_element->set_selected(prev_selected.find(graph_element) != nullptr);
2058
}
2059
}
2060
2061
top_layer->queue_redraw();
2062
minimap->queue_redraw();
2063
}
2064
2065
Ref<InputEventMouseButton> mb = p_ev;
2066
if (mb.is_valid()) {
2067
if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
2068
if (box_selecting) {
2069
box_selecting = false;
2070
for (int i = get_child_count() - 1; i >= 0; i--) {
2071
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
2072
if (!graph_element) {
2073
continue;
2074
}
2075
2076
graph_element->set_selected(prev_selected.find(graph_element) != nullptr);
2077
}
2078
top_layer->queue_redraw();
2079
minimap->queue_redraw();
2080
} else {
2081
if (connecting) {
2082
force_connection_drag_end();
2083
} else {
2084
emit_signal(SNAME("popup_request"), mb->get_position());
2085
}
2086
}
2087
}
2088
2089
if (mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed() && dragging) {
2090
if (!just_selected && drag_accum == Vector2() && Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) {
2091
// Deselect current node.
2092
for (int i = get_child_count() - 1; i >= 0; i--) {
2093
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
2094
2095
if (graph_element) {
2096
Rect2 r = graph_element->get_rect();
2097
if (r.has_point(mb->get_position())) {
2098
graph_element->set_selected(false);
2099
}
2100
}
2101
}
2102
}
2103
2104
if (drag_accum != Vector2()) {
2105
for (int i = get_child_count() - 1; i >= 0; i--) {
2106
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
2107
if (graph_element && graph_element->is_selected()) {
2108
graph_element->set_drag(false);
2109
GraphFrame *frame = Object::cast_to<GraphFrame>(get_child(i));
2110
if (frame) {
2111
_set_drag_frame_attached_nodes(frame, false);
2112
}
2113
}
2114
}
2115
}
2116
2117
if (moving_selection) {
2118
emit_signal(SNAME("end_node_move"));
2119
moving_selection = false;
2120
2121
Vector<GraphElement *> dragged_nodes;
2122
for (int i = get_child_count() - 1; i >= 0; i--) {
2123
GraphElement *moved_node = Object::cast_to<GraphElement>(get_child(i));
2124
if (moved_node && moved_node->is_selected() && moved_node->is_draggable()) {
2125
dragged_nodes.push_back(moved_node);
2126
}
2127
}
2128
2129
GraphFrame *frame_dropped_on = nullptr;
2130
2131
// Find frame on which the node(s) is/were dropped.
2132
// Count down to find the topmost frame.
2133
for (int i = get_child_count() - 1; i >= 0; i--) {
2134
GraphFrame *frame = Object::cast_to<GraphFrame>(get_child(i));
2135
2136
if (!frame || frame->is_resizing()) {
2137
continue;
2138
}
2139
2140
Rect2 frame_rect = frame->get_rect();
2141
if (frame_rect.has_point(mb->get_position()) && !dragged_nodes.has(frame)) {
2142
frame_dropped_on = frame;
2143
break;
2144
}
2145
}
2146
2147
if (frame_dropped_on) {
2148
dragged_nodes.erase(frame_dropped_on);
2149
2150
TypedArray<StringName> dragged_node_names;
2151
for (GraphElement *moved_node : dragged_nodes) {
2152
if (!linked_parent_map.has(moved_node->get_name())) {
2153
dragged_node_names.push_back(moved_node->get_name());
2154
}
2155
}
2156
if (dragged_node_names.size() > 0) {
2157
emit_signal(SNAME("graph_elements_linked_to_frame_request"), dragged_node_names, frame_dropped_on->get_name());
2158
}
2159
}
2160
}
2161
2162
dragging = false;
2163
2164
minimap->queue_redraw();
2165
queue_redraw();
2166
connections_layer->queue_redraw();
2167
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
2168
}
2169
2170
// Node selection logic.
2171
if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
2172
GraphElement *graph_element = nullptr;
2173
2174
// Find node which was clicked on.
2175
for (int i = get_child_count() - 1; i >= 0; i--) {
2176
GraphElement *selected_element = Object::cast_to<GraphElement>(get_child(i));
2177
2178
if (!selected_element) {
2179
continue;
2180
}
2181
2182
if (!selected_element->is_visible()) {
2183
continue;
2184
}
2185
2186
if (selected_element->is_resizing()) {
2187
continue;
2188
}
2189
2190
if (selected_element->has_point((mb->get_position() - selected_element->get_position()) / zoom)) {
2191
graph_element = selected_element;
2192
break;
2193
}
2194
}
2195
2196
if (graph_element) {
2197
if (_filter_input(mb->get_position())) {
2198
return;
2199
}
2200
2201
// Left-clicked on a node, select it.
2202
dragging = true;
2203
drag_accum = Vector2();
2204
just_selected = !graph_element->is_selected();
2205
if (!graph_element->is_selected() && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) {
2206
for (int i = 0; i < get_child_count(); i++) {
2207
GraphElement *child_element = Object::cast_to<GraphElement>(get_child(i));
2208
if (!child_element) {
2209
continue;
2210
}
2211
2212
child_element->set_selected(child_element == graph_element);
2213
}
2214
}
2215
2216
graph_element->set_selected(true);
2217
for (int i = 0; i < get_child_count(); i++) {
2218
GraphElement *child_element = Object::cast_to<GraphElement>(get_child(i));
2219
if (!child_element) {
2220
continue;
2221
}
2222
if (child_element->is_selected()) {
2223
child_element->set_drag(true);
2224
GraphFrame *frame_node = Object::cast_to<GraphFrame>(get_child(i));
2225
if (frame_node) {
2226
_ensure_node_order_from(frame_node);
2227
_set_drag_frame_attached_nodes(frame_node, true);
2228
}
2229
}
2230
}
2231
2232
} else {
2233
if (_filter_input(mb->get_position())) {
2234
return;
2235
}
2236
if (panner->is_panning()) {
2237
return;
2238
}
2239
2240
// Left-clicked on empty space, start box select.
2241
box_selecting = true;
2242
box_selecting_from = mb->get_position();
2243
if (mb->is_command_or_control_pressed()) {
2244
box_selection_mode_additive = true;
2245
prev_selected.clear();
2246
for (int i = get_child_count() - 1; i >= 0; i--) {
2247
GraphElement *child_element = Object::cast_to<GraphElement>(get_child(i));
2248
if (!child_element || !child_element->is_selected()) {
2249
continue;
2250
}
2251
2252
prev_selected.push_back(child_element);
2253
}
2254
} else if (mb->is_shift_pressed()) {
2255
box_selection_mode_additive = false;
2256
prev_selected.clear();
2257
for (int i = get_child_count() - 1; i >= 0; i--) {
2258
GraphElement *child_element = Object::cast_to<GraphElement>(get_child(i));
2259
if (!child_element || !child_element->is_selected()) {
2260
continue;
2261
}
2262
2263
prev_selected.push_back(child_element);
2264
}
2265
} else {
2266
box_selection_mode_additive = true;
2267
prev_selected.clear();
2268
for (int i = get_child_count() - 1; i >= 0; i--) {
2269
GraphElement *child_element = Object::cast_to<GraphElement>(get_child(i));
2270
if (!child_element) {
2271
continue;
2272
}
2273
2274
child_element->set_selected(false);
2275
}
2276
}
2277
}
2278
}
2279
2280
if (mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed() && box_selecting) {
2281
// Box selection ended. Nodes were selected during mouse movement.
2282
box_selecting = false;
2283
box_selecting_rect = Rect2();
2284
prev_selected.clear();
2285
top_layer->queue_redraw();
2286
minimap->queue_redraw();
2287
}
2288
}
2289
2290
key_input(p_ev);
2291
}
2292
2293
void GraphEdit::key_input(const Ref<InputEvent> &p_ev) {
2294
if (p_ev->is_pressed()) {
2295
if (p_ev->is_action("ui_graph_duplicate", true)) {
2296
emit_signal(SNAME("duplicate_nodes_request"));
2297
accept_event();
2298
} else if (p_ev->is_action("ui_copy", true)) {
2299
emit_signal(SNAME("copy_nodes_request"));
2300
accept_event();
2301
} else if (p_ev->is_action("ui_cut", true)) {
2302
emit_signal(SNAME("cut_nodes_request"));
2303
accept_event();
2304
} else if (p_ev->is_action("ui_paste", true)) {
2305
emit_signal(SNAME("paste_nodes_request"));
2306
accept_event();
2307
} else if (p_ev->is_action("ui_graph_delete", true)) {
2308
TypedArray<StringName> nodes;
2309
2310
for (int i = 0; i < get_child_count(); i++) {
2311
GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i));
2312
if (!graph_element) {
2313
continue;
2314
}
2315
if (graph_element->is_selected()) {
2316
nodes.push_back(graph_element->get_name());
2317
}
2318
}
2319
2320
emit_signal(SNAME("delete_nodes_request"), nodes);
2321
accept_event();
2322
}
2323
}
2324
}
2325
2326
void GraphEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) {
2327
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
2328
2329
scroll_offset = (scroll_offset - p_scroll_vec).clamp(min_scroll_offset, max_scroll_offset - get_size());
2330
2331
if (!awaiting_scroll_offset_update) {
2332
callable_mp(this, &GraphEdit::_update_scroll_offset).call_deferred();
2333
awaiting_scroll_offset_update = true;
2334
}
2335
minimap->queue_redraw();
2336
queue_redraw();
2337
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
2338
connections_layer->queue_redraw();
2339
}
2340
2341
void GraphEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) {
2342
// We need to invalidate all connections since we don't know whether
2343
// the user is zooming/panning at the same time.
2344
_invalidate_connection_line_cache();
2345
2346
set_zoom_custom(zoom * p_zoom_factor, p_origin);
2347
}
2348
2349
void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) {
2350
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
2351
2352
for (Ref<Connection> &conn : connection_map[p_from]) {
2353
if (conn->from_node == p_from && conn->from_port == p_from_port && conn->to_node == p_to && conn->to_port == p_to_port) {
2354
if (!Math::is_equal_approx(conn->activity, p_activity)) {
2355
// Update only if changed.
2356
minimap->queue_redraw();
2357
conn->_cache.dirty = true;
2358
connections_layer->queue_redraw();
2359
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
2360
}
2361
conn->activity = p_activity;
2362
return;
2363
}
2364
}
2365
}
2366
2367
void GraphEdit::reset_all_connection_activity() {
2368
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
2369
2370
bool changed = false;
2371
for (Ref<Connection> &conn : connections) {
2372
if (conn->activity > 0) {
2373
changed = true;
2374
conn->_cache.dirty = true;
2375
}
2376
conn->activity = 0;
2377
}
2378
if (changed) {
2379
connections_layer->queue_redraw();
2380
}
2381
}
2382
2383
void GraphEdit::clear_connections() {
2384
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
2385
2386
for (Ref<Connection> &conn : connections) {
2387
conn->_cache.line->queue_free();
2388
}
2389
2390
connections.clear();
2391
connection_map.clear();
2392
2393
minimap->queue_redraw();
2394
queue_redraw();
2395
connections_layer->queue_redraw();
2396
}
2397
2398
void GraphEdit::force_connection_drag_end() {
2399
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
2400
ERR_FAIL_COND_MSG(!connecting, "Drag end requested without active drag!");
2401
2402
connecting = false;
2403
connecting_valid = false;
2404
keyboard_connecting = false;
2405
minimap->queue_redraw();
2406
queue_redraw();
2407
connections_layer->queue_redraw();
2408
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
2409
emit_signal(SNAME("connection_drag_ended"));
2410
}
2411
2412
bool GraphEdit::is_node_hover_valid(const StringName &p_from, const int p_from_port, const StringName &p_to, const int p_to_port) {
2413
bool valid = true;
2414
GDVIRTUAL_CALL(_is_node_hover_valid, p_from, p_from_port, p_to, p_to_port, valid);
2415
return valid;
2416
}
2417
2418
void GraphEdit::set_panning_scheme(PanningScheme p_scheme) {
2419
panning_scheme = p_scheme;
2420
panner->set_control_scheme((ViewPanner::ControlScheme)p_scheme);
2421
}
2422
2423
GraphEdit::PanningScheme GraphEdit::get_panning_scheme() const {
2424
return panning_scheme;
2425
}
2426
2427
void GraphEdit::set_zoom(float p_zoom) {
2428
set_zoom_custom(p_zoom, get_size() / 2);
2429
}
2430
2431
void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) {
2432
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
2433
2434
p_zoom = CLAMP(p_zoom, zoom_min, zoom_max);
2435
if (zoom == p_zoom) {
2436
return;
2437
}
2438
2439
Point2 zoom_anchor = (scroll_offset + p_center) / zoom;
2440
2441
zoom = p_zoom;
2442
2443
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
2444
2445
zoom_minus_button->set_disabled(zoom == zoom_min);
2446
zoom_plus_button->set_disabled(zoom == zoom_max);
2447
2448
_update_scrollbars();
2449
minimap->queue_redraw();
2450
connections_layer->queue_redraw();
2451
2452
if (is_visible_in_tree()) {
2453
scroll_offset = zoom_anchor * zoom - p_center;
2454
}
2455
2456
_update_zoom_label();
2457
queue_redraw();
2458
}
2459
2460
float GraphEdit::get_zoom() const {
2461
return zoom;
2462
}
2463
2464
void GraphEdit::set_zoom_step(float p_zoom_step) {
2465
p_zoom_step = std::abs(p_zoom_step);
2466
ERR_FAIL_COND(!std::isfinite(p_zoom_step));
2467
if (zoom_step == p_zoom_step) {
2468
return;
2469
}
2470
2471
zoom_step = p_zoom_step;
2472
panner->set_scroll_zoom_factor(zoom_step);
2473
}
2474
2475
float GraphEdit::get_zoom_step() const {
2476
return zoom_step;
2477
}
2478
2479
void GraphEdit::set_zoom_min(float p_zoom_min) {
2480
ERR_FAIL_COND_MSG(p_zoom_min > zoom_max, "Cannot set min zoom level greater than max zoom level.");
2481
2482
if (zoom_min == p_zoom_min) {
2483
return;
2484
}
2485
2486
zoom_min = p_zoom_min;
2487
set_zoom(zoom);
2488
}
2489
2490
float GraphEdit::get_zoom_min() const {
2491
return zoom_min;
2492
}
2493
2494
void GraphEdit::set_zoom_max(float p_zoom_max) {
2495
ERR_FAIL_COND_MSG(p_zoom_max < zoom_min, "Cannot set max zoom level lesser than min zoom level.");
2496
2497
if (zoom_max == p_zoom_max) {
2498
return;
2499
}
2500
2501
zoom_max = p_zoom_max;
2502
set_zoom(zoom);
2503
}
2504
2505
float GraphEdit::get_zoom_max() const {
2506
return zoom_max;
2507
}
2508
2509
void GraphEdit::set_right_disconnects(bool p_enable) {
2510
right_disconnects = p_enable;
2511
}
2512
2513
bool GraphEdit::is_right_disconnects_enabled() const {
2514
return right_disconnects;
2515
}
2516
2517
void GraphEdit::add_valid_right_disconnect_type(int p_type) {
2518
valid_right_disconnect_types.insert(p_type);
2519
}
2520
2521
void GraphEdit::remove_valid_right_disconnect_type(int p_type) {
2522
valid_right_disconnect_types.erase(p_type);
2523
}
2524
2525
void GraphEdit::add_valid_left_disconnect_type(int p_type) {
2526
valid_left_disconnect_types.insert(p_type);
2527
}
2528
2529
void GraphEdit::remove_valid_left_disconnect_type(int p_type) {
2530
valid_left_disconnect_types.erase(p_type);
2531
}
2532
2533
void GraphEdit::set_connections(const TypedArray<Dictionary> &p_connections) {
2534
clear_connections();
2535
2536
bool is_editor = Engine::get_singleton()->is_editor_hint();
2537
2538
for (const Dictionary d : p_connections) {
2539
// Always keep the connection alive in case it is created using the inspector.
2540
bool keep_alive = (is_editor && d.is_empty()) || d["keep_alive"];
2541
connect_node(d["from_node"], d["from_port"], d["to_node"], d["to_port"], keep_alive);
2542
}
2543
}
2544
2545
TypedArray<Dictionary> GraphEdit::_get_connection_list() const {
2546
Vector<Ref<Connection>> conns = get_connections();
2547
2548
TypedArray<Dictionary> arr;
2549
for (const Ref<Connection> &conn : conns) {
2550
Dictionary d;
2551
d["from_node"] = conn->from_node;
2552
d["from_port"] = conn->from_port;
2553
d["to_node"] = conn->to_node;
2554
d["to_port"] = conn->to_port;
2555
d["keep_alive"] = conn->keep_alive;
2556
arr.push_back(d);
2557
}
2558
return arr;
2559
}
2560
2561
Dictionary GraphEdit::_get_closest_connection_at_point(const Vector2 &p_point, float p_max_distance) const {
2562
Dictionary ret;
2563
Ref<Connection> c = get_closest_connection_at_point(p_point, p_max_distance);
2564
if (c.is_valid()) {
2565
ret["from_node"] = c->from_node;
2566
ret["from_port"] = c->from_port;
2567
ret["to_node"] = c->to_node;
2568
ret["to_port"] = c->to_port;
2569
ret["keep_alive"] = c->keep_alive;
2570
}
2571
return ret;
2572
}
2573
2574
TypedArray<Dictionary> GraphEdit::_get_connections_intersecting_with_rect(const Rect2 &p_rect) const {
2575
List<Ref<Connection>> intersecting_connections = get_connections_intersecting_with_rect(p_rect);
2576
2577
TypedArray<Dictionary> arr;
2578
for (const Ref<Connection> &conn : intersecting_connections) {
2579
Dictionary d;
2580
d["from_node"] = conn->from_node;
2581
d["from_port"] = conn->from_port;
2582
d["to_node"] = conn->to_node;
2583
d["to_port"] = conn->to_port;
2584
d["keep_alive"] = conn->keep_alive;
2585
arr.push_back(d);
2586
}
2587
return arr;
2588
}
2589
2590
TypedArray<Dictionary> GraphEdit::_get_connection_list_from_node(const StringName &p_node) const {
2591
ERR_FAIL_COND_V(!connection_map.has(p_node), TypedArray<Dictionary>());
2592
2593
List<Ref<GraphEdit::Connection>> connections_from_node = connection_map.get(p_node);
2594
TypedArray<Dictionary> connections_from_node_dict;
2595
2596
for (const Ref<Connection> &conn : connections_from_node) {
2597
Dictionary d;
2598
d["from_node"] = conn->from_node;
2599
d["from_port"] = conn->from_port;
2600
d["to_node"] = conn->to_node;
2601
d["to_port"] = conn->to_port;
2602
d["keep_alive"] = conn->keep_alive;
2603
connections_from_node_dict.push_back(d);
2604
}
2605
return connections_from_node_dict;
2606
}
2607
2608
void GraphEdit::_zoom_minus() {
2609
set_zoom(zoom / zoom_step);
2610
}
2611
2612
void GraphEdit::_zoom_reset() {
2613
set_zoom(1);
2614
}
2615
2616
void GraphEdit::_zoom_plus() {
2617
set_zoom(zoom * zoom_step);
2618
}
2619
2620
void GraphEdit::_update_zoom_label() {
2621
int zoom_percent = static_cast<int>(Math::round(zoom * 100));
2622
String zoom_text = itos(zoom_percent) + "%";
2623
zoom_label->set_text(zoom_text);
2624
}
2625
2626
void GraphEdit::_invalidate_connection_line_cache() {
2627
for (Ref<Connection> &conn : connections) {
2628
conn->_cache.dirty = true;
2629
}
2630
}
2631
2632
float GraphEdit::_get_shader_line_width() {
2633
return lines_thickness * theme_cache.base_scale + 4.0;
2634
}
2635
2636
void GraphEdit::add_valid_connection_type(int p_type, int p_with_type) {
2637
ConnectionType ct(p_type, p_with_type);
2638
valid_connection_types.insert(ct);
2639
}
2640
2641
void GraphEdit::remove_valid_connection_type(int p_type, int p_with_type) {
2642
ConnectionType ct(p_type, p_with_type);
2643
valid_connection_types.erase(ct);
2644
}
2645
2646
bool GraphEdit::is_valid_connection_type(int p_type, int p_with_type) const {
2647
ConnectionType ct(p_type, p_with_type);
2648
return valid_connection_types.has(ct);
2649
}
2650
2651
void GraphEdit::attach_graph_element_to_frame(const StringName &p_graph_element, const StringName &p_parent_frame) {
2652
GraphFrame *frame = Object::cast_to<GraphFrame>(get_node(NodePath(p_parent_frame)));
2653
ERR_FAIL_NULL_MSG(frame, "Frame does not exist or is not of type GraphFrame.");
2654
GraphElement *graph_element = Object::cast_to<GraphElement>(get_node(NodePath(p_graph_element)));
2655
ERR_FAIL_NULL_MSG(graph_element, "Graph element to attach does not exist or is not of type GraphElement.");
2656
ERR_FAIL_COND_MSG(frame == graph_element, "Cannot attach a frame to itself.");
2657
2658
linked_parent_map.insert(p_graph_element, p_parent_frame);
2659
frame_attached_nodes[p_parent_frame].insert(p_graph_element);
2660
2661
_ensure_node_order_from_root(p_graph_element);
2662
_update_graph_frame(frame);
2663
}
2664
2665
void GraphEdit::detach_graph_element_from_frame(const StringName &p_graph_element) {
2666
if (!linked_parent_map.has(p_graph_element)) {
2667
return;
2668
}
2669
GraphFrame *frame = Object::cast_to<GraphFrame>(get_node(NodePath(linked_parent_map[p_graph_element])));
2670
ERR_FAIL_NULL_MSG(frame, "Frame does not exist or is not of type GraphFrame.");
2671
GraphElement *graph_element = Object::cast_to<GraphElement>(get_node(NodePath(p_graph_element)));
2672
ERR_FAIL_NULL_MSG(graph_element, "Graph element to detach does not exist or is not of type GraphElement.");
2673
2674
frame_attached_nodes.get(frame->get_name()).erase(p_graph_element);
2675
linked_parent_map.erase(p_graph_element);
2676
2677
_update_graph_frame(frame);
2678
}
2679
2680
GraphFrame *GraphEdit::get_element_frame(const StringName &p_attached_graph_element) {
2681
if (!linked_parent_map.has(p_attached_graph_element)) {
2682
return nullptr;
2683
}
2684
2685
Node *parent = get_node_or_null(NodePath(linked_parent_map[p_attached_graph_element]));
2686
2687
return Object::cast_to<GraphFrame>(parent);
2688
}
2689
2690
TypedArray<StringName> GraphEdit::get_attached_nodes_of_frame(const StringName &p_graph_frame) {
2691
if (!frame_attached_nodes.has(p_graph_frame)) {
2692
return TypedArray<StringName>();
2693
}
2694
2695
TypedArray<StringName> attached_nodes;
2696
for (const StringName &node : frame_attached_nodes.get(p_graph_frame)) {
2697
attached_nodes.push_back(node);
2698
}
2699
2700
return attached_nodes;
2701
}
2702
2703
void GraphEdit::set_snapping_enabled(bool p_enable) {
2704
if (snapping_enabled == p_enable) {
2705
return;
2706
}
2707
2708
snapping_enabled = p_enable;
2709
toggle_snapping_button->set_pressed(p_enable);
2710
queue_redraw();
2711
}
2712
2713
bool GraphEdit::is_snapping_enabled() const {
2714
return snapping_enabled;
2715
}
2716
2717
void GraphEdit::set_snapping_distance(int p_snapping_distance) {
2718
ERR_FAIL_COND_MSG(p_snapping_distance < GRID_MIN_SNAPPING_DISTANCE || p_snapping_distance > GRID_MAX_SNAPPING_DISTANCE,
2719
vformat("GraphEdit's snapping distance must be between %d and %d (inclusive)", GRID_MIN_SNAPPING_DISTANCE, GRID_MAX_SNAPPING_DISTANCE));
2720
snapping_distance = p_snapping_distance;
2721
snapping_distance_spinbox->set_value(p_snapping_distance);
2722
queue_redraw();
2723
}
2724
2725
int GraphEdit::get_snapping_distance() const {
2726
return snapping_distance;
2727
}
2728
2729
void GraphEdit::set_show_grid(bool p_show) {
2730
if (show_grid == p_show) {
2731
return;
2732
}
2733
2734
show_grid = p_show;
2735
toggle_grid_button->set_pressed(p_show);
2736
queue_redraw();
2737
}
2738
2739
bool GraphEdit::is_showing_grid() const {
2740
return show_grid;
2741
}
2742
2743
void GraphEdit::set_grid_pattern(GridPattern p_pattern) {
2744
if (grid_pattern == p_pattern) {
2745
return;
2746
}
2747
2748
grid_pattern = p_pattern;
2749
queue_redraw();
2750
}
2751
2752
GraphEdit::GridPattern GraphEdit::get_grid_pattern() const {
2753
return grid_pattern;
2754
}
2755
2756
void GraphEdit::_snapping_toggled() {
2757
snapping_enabled = toggle_snapping_button->is_pressed();
2758
}
2759
2760
void GraphEdit::_snapping_distance_changed(double) {
2761
snapping_distance = snapping_distance_spinbox->get_value();
2762
queue_redraw();
2763
}
2764
2765
void GraphEdit::_show_grid_toggled() {
2766
show_grid = toggle_grid_button->is_pressed();
2767
queue_redraw();
2768
}
2769
2770
void GraphEdit::set_minimap_size(Vector2 p_size) {
2771
minimap->set_size(p_size);
2772
Vector2 minimap_size = minimap->get_size(); // The size might've been adjusted by the minimum size.
2773
2774
minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT);
2775
minimap->set_offset(Side::SIDE_LEFT, -minimap_size.width - MINIMAP_OFFSET);
2776
minimap->set_offset(Side::SIDE_TOP, -minimap_size.height - MINIMAP_OFFSET);
2777
minimap->set_offset(Side::SIDE_RIGHT, -MINIMAP_OFFSET);
2778
minimap->set_offset(Side::SIDE_BOTTOM, -MINIMAP_OFFSET);
2779
minimap->queue_redraw();
2780
}
2781
2782
Vector2 GraphEdit::get_minimap_size() const {
2783
return minimap->get_size();
2784
}
2785
2786
void GraphEdit::set_minimap_opacity(float p_opacity) {
2787
if (minimap->get_modulate().a == p_opacity) {
2788
return;
2789
}
2790
minimap->set_modulate(Color(1, 1, 1, p_opacity));
2791
minimap->queue_redraw();
2792
}
2793
2794
float GraphEdit::get_minimap_opacity() const {
2795
Color minimap_modulate = minimap->get_modulate();
2796
return minimap_modulate.a;
2797
}
2798
2799
void GraphEdit::set_minimap_enabled(bool p_enable) {
2800
if (minimap_button->is_pressed() == p_enable) {
2801
return;
2802
}
2803
minimap_button->set_pressed(p_enable);
2804
_minimap_toggled();
2805
minimap->queue_redraw();
2806
}
2807
2808
bool GraphEdit::is_minimap_enabled() const {
2809
return minimap_button->is_pressed();
2810
}
2811
2812
void GraphEdit::set_show_menu(bool p_hidden) {
2813
show_menu = p_hidden;
2814
menu_panel->set_visible(show_menu);
2815
}
2816
2817
bool GraphEdit::is_showing_menu() const {
2818
return show_menu;
2819
}
2820
2821
void GraphEdit::set_show_zoom_label(bool p_hidden) {
2822
show_zoom_label = p_hidden;
2823
zoom_label->set_visible(show_zoom_label);
2824
}
2825
2826
bool GraphEdit::is_showing_zoom_label() const {
2827
return show_zoom_label;
2828
}
2829
2830
void GraphEdit::set_show_zoom_buttons(bool p_hidden) {
2831
show_zoom_buttons = p_hidden;
2832
2833
zoom_minus_button->set_visible(show_zoom_buttons);
2834
zoom_reset_button->set_visible(show_zoom_buttons);
2835
zoom_plus_button->set_visible(show_zoom_buttons);
2836
}
2837
2838
bool GraphEdit::is_showing_zoom_buttons() const {
2839
return show_zoom_buttons;
2840
}
2841
2842
void GraphEdit::set_show_grid_buttons(bool p_hidden) {
2843
show_grid_buttons = p_hidden;
2844
2845
toggle_grid_button->set_visible(show_grid_buttons);
2846
toggle_snapping_button->set_visible(show_grid_buttons);
2847
snapping_distance_spinbox->set_visible(show_grid_buttons);
2848
}
2849
2850
bool GraphEdit::is_showing_grid_buttons() const {
2851
return show_grid_buttons;
2852
}
2853
2854
void GraphEdit::set_show_minimap_button(bool p_hidden) {
2855
show_minimap_button = p_hidden;
2856
minimap_button->set_visible(show_minimap_button);
2857
}
2858
2859
bool GraphEdit::is_showing_minimap_button() const {
2860
return show_minimap_button;
2861
}
2862
2863
void GraphEdit::set_show_arrange_button(bool p_hidden) {
2864
show_arrange_button = p_hidden;
2865
arrange_button->set_visible(show_arrange_button);
2866
}
2867
2868
bool GraphEdit::is_showing_arrange_button() const {
2869
return show_arrange_button;
2870
}
2871
2872
void GraphEdit::override_connections_shader(const Ref<Shader> &p_shader) {
2873
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
2874
2875
connections_shader = p_shader;
2876
2877
_invalidate_connection_line_cache();
2878
connections_layer->queue_redraw();
2879
minimap->queue_redraw();
2880
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
2881
}
2882
2883
void GraphEdit::_minimap_toggled() {
2884
if (is_minimap_enabled()) {
2885
minimap->set_visible(true);
2886
minimap->queue_redraw();
2887
} else {
2888
minimap->set_visible(false);
2889
}
2890
}
2891
2892
void GraphEdit::set_connection_lines_curvature(float p_curvature) {
2893
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
2894
2895
lines_curvature = p_curvature;
2896
_invalidate_connection_line_cache();
2897
connections_layer->queue_redraw();
2898
queue_redraw();
2899
}
2900
2901
float GraphEdit::get_connection_lines_curvature() const {
2902
return lines_curvature;
2903
}
2904
2905
void GraphEdit::set_connection_lines_thickness(float p_thickness) {
2906
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
2907
ERR_FAIL_COND_MSG(p_thickness < 0, "Connection lines thickness must be greater than or equal to 0.");
2908
2909
if (lines_thickness == p_thickness) {
2910
return;
2911
}
2912
lines_thickness = p_thickness;
2913
_invalidate_connection_line_cache();
2914
connections_layer->queue_redraw();
2915
queue_redraw();
2916
}
2917
2918
float GraphEdit::get_connection_lines_thickness() const {
2919
return lines_thickness;
2920
}
2921
2922
void GraphEdit::set_connection_lines_antialiased(bool p_antialiased) {
2923
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
2924
2925
if (lines_antialiased == p_antialiased) {
2926
return;
2927
}
2928
lines_antialiased = p_antialiased;
2929
_invalidate_connection_line_cache();
2930
connections_layer->queue_redraw();
2931
queue_redraw();
2932
}
2933
2934
bool GraphEdit::is_connection_lines_antialiased() const {
2935
return lines_antialiased;
2936
}
2937
2938
HBoxContainer *GraphEdit::get_menu_hbox() {
2939
return menu_hbox;
2940
}
2941
2942
Ref<ViewPanner> GraphEdit::get_panner() {
2943
return panner;
2944
}
2945
2946
void GraphEdit::set_warped_panning(bool p_warped) {
2947
warped_panning = p_warped;
2948
update_warped_panning();
2949
}
2950
2951
void GraphEdit::update_warped_panning() {
2952
panner->setup_warped_panning(get_viewport(), warped_panning);
2953
}
2954
2955
void GraphEdit::arrange_nodes() {
2956
arranger->arrange_nodes();
2957
}
2958
2959
void GraphEdit::_bind_methods() {
2960
ClassDB::bind_method(D_METHOD("connect_node", "from_node", "from_port", "to_node", "to_port", "keep_alive"), &GraphEdit::connect_node, DEFVAL(false));
2961
ClassDB::bind_method(D_METHOD("is_node_connected", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::is_node_connected);
2962
ClassDB::bind_method(D_METHOD("disconnect_node", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::disconnect_node);
2963
ClassDB::bind_method(D_METHOD("set_connection_activity", "from_node", "from_port", "to_node", "to_port", "amount"), &GraphEdit::set_connection_activity);
2964
ClassDB::bind_method(D_METHOD("set_connections", "connections"), &GraphEdit::set_connections);
2965
ClassDB::bind_method(D_METHOD("get_connection_list"), &GraphEdit::_get_connection_list);
2966
ClassDB::bind_method(D_METHOD("get_connection_count", "from_node", "from_port"), &GraphEdit::get_connection_count);
2967
ClassDB::bind_method(D_METHOD("get_closest_connection_at_point", "point", "max_distance"), &GraphEdit::_get_closest_connection_at_point, DEFVAL(4.0));
2968
ClassDB::bind_method(D_METHOD("get_connection_list_from_node", "node"), &GraphEdit::_get_connection_list_from_node);
2969
ClassDB::bind_method(D_METHOD("get_connections_intersecting_with_rect", "rect"), &GraphEdit::_get_connections_intersecting_with_rect);
2970
ClassDB::bind_method(D_METHOD("clear_connections"), &GraphEdit::clear_connections);
2971
ClassDB::bind_method(D_METHOD("force_connection_drag_end"), &GraphEdit::force_connection_drag_end);
2972
ClassDB::bind_method(D_METHOD("get_scroll_offset"), &GraphEdit::get_scroll_offset);
2973
ClassDB::bind_method(D_METHOD("set_scroll_offset", "offset"), &GraphEdit::set_scroll_offset);
2974
2975
ClassDB::bind_method(D_METHOD("add_valid_right_disconnect_type", "type"), &GraphEdit::add_valid_right_disconnect_type);
2976
ClassDB::bind_method(D_METHOD("remove_valid_right_disconnect_type", "type"), &GraphEdit::remove_valid_right_disconnect_type);
2977
ClassDB::bind_method(D_METHOD("add_valid_left_disconnect_type", "type"), &GraphEdit::add_valid_left_disconnect_type);
2978
ClassDB::bind_method(D_METHOD("remove_valid_left_disconnect_type", "type"), &GraphEdit::remove_valid_left_disconnect_type);
2979
ClassDB::bind_method(D_METHOD("add_valid_connection_type", "from_type", "to_type"), &GraphEdit::add_valid_connection_type);
2980
ClassDB::bind_method(D_METHOD("remove_valid_connection_type", "from_type", "to_type"), &GraphEdit::remove_valid_connection_type);
2981
ClassDB::bind_method(D_METHOD("is_valid_connection_type", "from_type", "to_type"), &GraphEdit::is_valid_connection_type);
2982
ClassDB::bind_method(D_METHOD("get_connection_line", "from_node", "to_node"), &GraphEdit::get_connection_line);
2983
2984
ClassDB::bind_method(D_METHOD("attach_graph_element_to_frame", "element", "frame"), &GraphEdit::attach_graph_element_to_frame);
2985
ClassDB::bind_method(D_METHOD("detach_graph_element_from_frame", "element"), &GraphEdit::detach_graph_element_from_frame);
2986
ClassDB::bind_method(D_METHOD("get_element_frame", "element"), &GraphEdit::get_element_frame);
2987
ClassDB::bind_method(D_METHOD("get_attached_nodes_of_frame", "frame"), &GraphEdit::get_attached_nodes_of_frame);
2988
2989
ClassDB::bind_method(D_METHOD("set_panning_scheme", "scheme"), &GraphEdit::set_panning_scheme);
2990
ClassDB::bind_method(D_METHOD("get_panning_scheme"), &GraphEdit::get_panning_scheme);
2991
2992
ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &GraphEdit::set_zoom);
2993
ClassDB::bind_method(D_METHOD("get_zoom"), &GraphEdit::get_zoom);
2994
2995
ClassDB::bind_method(D_METHOD("set_zoom_min", "zoom_min"), &GraphEdit::set_zoom_min);
2996
ClassDB::bind_method(D_METHOD("get_zoom_min"), &GraphEdit::get_zoom_min);
2997
2998
ClassDB::bind_method(D_METHOD("set_zoom_max", "zoom_max"), &GraphEdit::set_zoom_max);
2999
ClassDB::bind_method(D_METHOD("get_zoom_max"), &GraphEdit::get_zoom_max);
3000
3001
ClassDB::bind_method(D_METHOD("set_zoom_step", "zoom_step"), &GraphEdit::set_zoom_step);
3002
ClassDB::bind_method(D_METHOD("get_zoom_step"), &GraphEdit::get_zoom_step);
3003
3004
ClassDB::bind_method(D_METHOD("set_show_grid", "enable"), &GraphEdit::set_show_grid);
3005
ClassDB::bind_method(D_METHOD("is_showing_grid"), &GraphEdit::is_showing_grid);
3006
3007
ClassDB::bind_method(D_METHOD("set_grid_pattern", "pattern"), &GraphEdit::set_grid_pattern);
3008
ClassDB::bind_method(D_METHOD("get_grid_pattern"), &GraphEdit::get_grid_pattern);
3009
3010
ClassDB::bind_method(D_METHOD("set_snapping_enabled", "enable"), &GraphEdit::set_snapping_enabled);
3011
ClassDB::bind_method(D_METHOD("is_snapping_enabled"), &GraphEdit::is_snapping_enabled);
3012
3013
ClassDB::bind_method(D_METHOD("set_snapping_distance", "pixels"), &GraphEdit::set_snapping_distance);
3014
ClassDB::bind_method(D_METHOD("get_snapping_distance"), &GraphEdit::get_snapping_distance);
3015
3016
ClassDB::bind_method(D_METHOD("set_connection_lines_curvature", "curvature"), &GraphEdit::set_connection_lines_curvature);
3017
ClassDB::bind_method(D_METHOD("get_connection_lines_curvature"), &GraphEdit::get_connection_lines_curvature);
3018
3019
ClassDB::bind_method(D_METHOD("set_connection_lines_thickness", "pixels"), &GraphEdit::set_connection_lines_thickness);
3020
ClassDB::bind_method(D_METHOD("get_connection_lines_thickness"), &GraphEdit::get_connection_lines_thickness);
3021
3022
ClassDB::bind_method(D_METHOD("set_connection_lines_antialiased", "pixels"), &GraphEdit::set_connection_lines_antialiased);
3023
ClassDB::bind_method(D_METHOD("is_connection_lines_antialiased"), &GraphEdit::is_connection_lines_antialiased);
3024
3025
ClassDB::bind_method(D_METHOD("set_minimap_size", "size"), &GraphEdit::set_minimap_size);
3026
ClassDB::bind_method(D_METHOD("get_minimap_size"), &GraphEdit::get_minimap_size);
3027
ClassDB::bind_method(D_METHOD("set_minimap_opacity", "opacity"), &GraphEdit::set_minimap_opacity);
3028
ClassDB::bind_method(D_METHOD("get_minimap_opacity"), &GraphEdit::get_minimap_opacity);
3029
3030
ClassDB::bind_method(D_METHOD("set_minimap_enabled", "enable"), &GraphEdit::set_minimap_enabled);
3031
ClassDB::bind_method(D_METHOD("is_minimap_enabled"), &GraphEdit::is_minimap_enabled);
3032
3033
ClassDB::bind_method(D_METHOD("set_show_menu", "hidden"), &GraphEdit::set_show_menu);
3034
ClassDB::bind_method(D_METHOD("is_showing_menu"), &GraphEdit::is_showing_menu);
3035
3036
ClassDB::bind_method(D_METHOD("set_show_zoom_label", "enable"), &GraphEdit::set_show_zoom_label);
3037
ClassDB::bind_method(D_METHOD("is_showing_zoom_label"), &GraphEdit::is_showing_zoom_label);
3038
3039
ClassDB::bind_method(D_METHOD("set_show_grid_buttons", "hidden"), &GraphEdit::set_show_grid_buttons);
3040
ClassDB::bind_method(D_METHOD("is_showing_grid_buttons"), &GraphEdit::is_showing_grid_buttons);
3041
3042
ClassDB::bind_method(D_METHOD("set_show_zoom_buttons", "hidden"), &GraphEdit::set_show_zoom_buttons);
3043
ClassDB::bind_method(D_METHOD("is_showing_zoom_buttons"), &GraphEdit::is_showing_zoom_buttons);
3044
3045
ClassDB::bind_method(D_METHOD("set_show_minimap_button", "hidden"), &GraphEdit::set_show_minimap_button);
3046
ClassDB::bind_method(D_METHOD("is_showing_minimap_button"), &GraphEdit::is_showing_minimap_button);
3047
3048
ClassDB::bind_method(D_METHOD("set_show_arrange_button", "hidden"), &GraphEdit::set_show_arrange_button);
3049
ClassDB::bind_method(D_METHOD("is_showing_arrange_button"), &GraphEdit::is_showing_arrange_button);
3050
3051
ClassDB::bind_method(D_METHOD("set_right_disconnects", "enable"), &GraphEdit::set_right_disconnects);
3052
ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled);
3053
3054
ClassDB::bind_method(D_METHOD("set_type_names", "type_names"), &GraphEdit::set_type_names);
3055
ClassDB::bind_method(D_METHOD("get_type_names"), &GraphEdit::get_type_names);
3056
3057
GDVIRTUAL_BIND(_is_in_input_hotzone, "in_node", "in_port", "mouse_position");
3058
GDVIRTUAL_BIND(_is_in_output_hotzone, "in_node", "in_port", "mouse_position");
3059
3060
ClassDB::bind_method(D_METHOD("get_menu_hbox"), &GraphEdit::get_menu_hbox);
3061
3062
ClassDB::bind_method(D_METHOD("arrange_nodes"), &GraphEdit::arrange_nodes);
3063
3064
ClassDB::bind_method(D_METHOD("set_selected", "node"), &GraphEdit::set_selected);
3065
3066
GDVIRTUAL_BIND(_get_connection_line, "from_position", "to_position")
3067
GDVIRTUAL_BIND(_is_node_hover_valid, "from_node", "from_port", "to_node", "to_port");
3068
3069
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_scroll_offset", "get_scroll_offset");
3070
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_grid"), "set_show_grid", "is_showing_grid");
3071
ADD_PROPERTY(PropertyInfo(Variant::INT, "grid_pattern", PROPERTY_HINT_ENUM, "Lines,Dots"), "set_grid_pattern", "get_grid_pattern");
3072
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snapping_enabled"), "set_snapping_enabled", "is_snapping_enabled");
3073
ADD_PROPERTY(PropertyInfo(Variant::INT, "snapping_distance", PROPERTY_HINT_NONE, "suffix:px"), "set_snapping_distance", "get_snapping_distance");
3074
ADD_PROPERTY(PropertyInfo(Variant::INT, "panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans"), "set_panning_scheme", "get_panning_scheme");
3075
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "right_disconnects"), "set_right_disconnects", "is_right_disconnects_enabled");
3076
3077
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "type_names", PROPERTY_HINT_DICTIONARY_TYPE, "int;String"), "set_type_names", "get_type_names");
3078
3079
ADD_GROUP("Connection Lines", "connection_lines");
3080
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_curvature"), "set_connection_lines_curvature", "get_connection_lines_curvature");
3081
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");
3082
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "connection_lines_antialiased"), "set_connection_lines_antialiased", "is_connection_lines_antialiased");
3083
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");
3084
3085
ADD_GROUP("Zoom", "");
3086
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom");
3087
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom_min"), "set_zoom_min", "get_zoom_min");
3088
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom_max"), "set_zoom_max", "get_zoom_max");
3089
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom_step"), "set_zoom_step", "get_zoom_step");
3090
3091
ADD_GROUP("Minimap", "minimap_");
3092
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_minimap_enabled", "is_minimap_enabled");
3093
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimap_size", PROPERTY_HINT_NONE, "suffix:px"), "set_minimap_size", "get_minimap_size");
3094
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "minimap_opacity"), "set_minimap_opacity", "get_minimap_opacity");
3095
3096
ADD_GROUP("Toolbar Menu", "");
3097
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_menu"), "set_show_menu", "is_showing_menu");
3098
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_zoom_label"), "set_show_zoom_label", "is_showing_zoom_label");
3099
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_zoom_buttons"), "set_show_zoom_buttons", "is_showing_zoom_buttons");
3100
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_grid_buttons"), "set_show_grid_buttons", "is_showing_grid_buttons");
3101
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_minimap_button"), "set_show_minimap_button", "is_showing_minimap_button");
3102
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_arrange_button"), "set_show_arrange_button", "is_showing_arrange_button");
3103
3104
ADD_SIGNAL(MethodInfo("connection_request", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port")));
3105
ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port")));
3106
ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::VECTOR2, "release_position")));
3107
ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port"), PropertyInfo(Variant::VECTOR2, "release_position")));
3108
ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::BOOL, "is_output")));
3109
ADD_SIGNAL(MethodInfo("connection_drag_ended"));
3110
3111
ADD_SIGNAL(MethodInfo("copy_nodes_request"));
3112
ADD_SIGNAL(MethodInfo("cut_nodes_request"));
3113
ADD_SIGNAL(MethodInfo("paste_nodes_request"));
3114
ADD_SIGNAL(MethodInfo("duplicate_nodes_request"));
3115
ADD_SIGNAL(MethodInfo("delete_nodes_request", PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_ARRAY_TYPE, "StringName")));
3116
3117
ADD_SIGNAL(MethodInfo("node_selected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, Node::get_class_static())));
3118
ADD_SIGNAL(MethodInfo("node_deselected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, Node::get_class_static())));
3119
ADD_SIGNAL(MethodInfo("frame_rect_changed", PropertyInfo(Variant::OBJECT, "frame", PROPERTY_HINT_RESOURCE_TYPE, GraphFrame::get_class_static()), PropertyInfo(Variant::RECT2, "new_rect")));
3120
3121
ADD_SIGNAL(MethodInfo("popup_request", PropertyInfo(Variant::VECTOR2, "at_position")));
3122
3123
ADD_SIGNAL(MethodInfo("begin_node_move"));
3124
ADD_SIGNAL(MethodInfo("end_node_move"));
3125
ADD_SIGNAL(MethodInfo("graph_elements_linked_to_frame_request", PropertyInfo(Variant::ARRAY, "elements"), PropertyInfo(Variant::STRING_NAME, "frame")));
3126
ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "offset")));
3127
3128
BIND_ENUM_CONSTANT(SCROLL_ZOOMS);
3129
BIND_ENUM_CONSTANT(SCROLL_PANS);
3130
3131
BIND_ENUM_CONSTANT(GRID_PATTERN_LINES);
3132
BIND_ENUM_CONSTANT(GRID_PATTERN_DOTS);
3133
3134
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphEdit, panel);
3135
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphEdit, panel_focus);
3136
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, grid_major);
3137
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, grid_minor);
3138
3139
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, GraphEdit, activity_color, "activity");
3140
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, connection_hover_tint_color);
3141
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphEdit, connection_hover_thickness);
3142
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, connection_valid_target_tint_color);
3143
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, connection_rim_color);
3144
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, selection_fill);
3145
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, selection_stroke);
3146
3147
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphEdit, menu_panel);
3148
3149
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, zoom_in);
3150
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, zoom_out);
3151
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, zoom_reset);
3152
3153
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, snapping_toggle);
3154
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, grid_toggle);
3155
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, minimap_toggle);
3156
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, layout);
3157
3158
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphEdit, port_hotzone_inner_extent);
3159
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphEdit, port_hotzone_outer_extent);
3160
3161
ADD_CLASS_DEPENDENCY("Button");
3162
ADD_CLASS_DEPENDENCY("GraphFrame");
3163
ADD_CLASS_DEPENDENCY("GraphNode");
3164
ADD_CLASS_DEPENDENCY("HScrollBar");
3165
ADD_CLASS_DEPENDENCY("SpinBox");
3166
ADD_CLASS_DEPENDENCY("VScrollBar");
3167
}
3168
3169
GraphEdit::GraphEdit() {
3170
set_focus_mode(FOCUS_ALL);
3171
3172
// Allow dezooming 8 times from the default zoom level.
3173
// At low zoom levels, text is unreadable due to its small size and poor filtering,
3174
// but this is still useful for previewing and navigation.
3175
zoom_min = (1 / Math::pow(zoom_step, 8));
3176
// Allow zooming 4 times from the default zoom level.
3177
zoom_max = (1 * Math::pow(zoom_step, 4));
3178
3179
panner.instantiate();
3180
panner->set_callbacks(callable_mp(this, &GraphEdit::_pan_callback), callable_mp(this, &GraphEdit::_zoom_callback));
3181
3182
top_layer = memnew(Control);
3183
add_child(top_layer, false, INTERNAL_MODE_BACK);
3184
top_layer->set_mouse_filter(MOUSE_FILTER_IGNORE);
3185
top_layer->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
3186
top_layer->connect(SceneStringName(draw), callable_mp(this, &GraphEdit::_top_layer_draw));
3187
top_layer->connect(SceneStringName(focus_exited), callable_mp(panner.ptr(), &ViewPanner::release_pan_key));
3188
3189
connections_layer = memnew(Control);
3190
add_child(connections_layer, false);
3191
connections_layer->connect(SceneStringName(draw), callable_mp(this, &GraphEdit::_update_connections));
3192
connections_layer->set_name("_connection_layer");
3193
connections_layer->set_disable_visibility_clip(true); // Necessary, so it can draw freely and be offset.
3194
connections_layer->set_mouse_filter(MOUSE_FILTER_IGNORE);
3195
3196
top_connection_layer = memnew(GraphEditFilter(this));
3197
add_child(top_connection_layer, false, INTERNAL_MODE_BACK);
3198
3199
connections_shader = default_connections_shader;
3200
3201
top_connection_layer->set_mouse_filter(MOUSE_FILTER_PASS);
3202
top_connection_layer->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
3203
top_connection_layer->connect(SceneStringName(gui_input), callable_mp(this, &GraphEdit::_top_connection_layer_input));
3204
3205
dragged_connection_line = memnew(Line2D);
3206
dragged_connection_line->set_texture_mode(Line2D::LINE_TEXTURE_STRETCH);
3207
top_connection_layer->add_child(dragged_connection_line);
3208
3209
h_scrollbar = memnew(HScrollBar);
3210
h_scrollbar->set_name("_h_scroll");
3211
top_layer->add_child(h_scrollbar);
3212
3213
v_scrollbar = memnew(VScrollBar);
3214
v_scrollbar->set_name("_v_scroll");
3215
top_layer->add_child(v_scrollbar);
3216
3217
// Set large minmax so it can scroll even if not resized yet.
3218
h_scrollbar->set_min(-10000);
3219
h_scrollbar->set_max(10000);
3220
3221
v_scrollbar->set_min(-10000);
3222
v_scrollbar->set_max(10000);
3223
3224
h_scrollbar->connect(SceneStringName(value_changed), callable_mp(this, &GraphEdit::_scrollbar_moved));
3225
v_scrollbar->connect(SceneStringName(value_changed), callable_mp(this, &GraphEdit::_scrollbar_moved));
3226
3227
// Toolbar menu.
3228
3229
menu_panel = memnew(PanelContainer);
3230
menu_panel->set_visible(show_menu);
3231
top_layer->add_child(menu_panel);
3232
menu_panel->set_position(Vector2(10, 10));
3233
3234
menu_hbox = memnew(HBoxContainer);
3235
menu_panel->add_child(menu_hbox);
3236
3237
// Zoom label and controls.
3238
3239
zoom_label = memnew(Label);
3240
zoom_label->set_visible(show_zoom_label);
3241
zoom_label->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
3242
zoom_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
3243
zoom_label->set_custom_minimum_size(Size2(48, 0));
3244
menu_hbox->add_child(zoom_label);
3245
_update_zoom_label();
3246
3247
zoom_minus_button = memnew(Button);
3248
zoom_minus_button->set_theme_type_variation(SceneStringName(FlatButton));
3249
zoom_minus_button->set_visible(show_zoom_buttons);
3250
zoom_minus_button->set_tooltip_text(ETR("Zoom Out"));
3251
zoom_minus_button->set_focus_mode(FOCUS_ACCESSIBILITY);
3252
menu_hbox->add_child(zoom_minus_button);
3253
zoom_minus_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_zoom_minus));
3254
3255
zoom_reset_button = memnew(Button);
3256
zoom_reset_button->set_theme_type_variation(SceneStringName(FlatButton));
3257
zoom_reset_button->set_visible(show_zoom_buttons);
3258
zoom_reset_button->set_tooltip_text(ETR("Zoom Reset"));
3259
zoom_reset_button->set_focus_mode(FOCUS_ACCESSIBILITY);
3260
menu_hbox->add_child(zoom_reset_button);
3261
zoom_reset_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_zoom_reset));
3262
3263
zoom_plus_button = memnew(Button);
3264
zoom_plus_button->set_theme_type_variation(SceneStringName(FlatButton));
3265
zoom_plus_button->set_visible(show_zoom_buttons);
3266
zoom_plus_button->set_tooltip_text(ETR("Zoom In"));
3267
zoom_plus_button->set_focus_mode(FOCUS_ACCESSIBILITY);
3268
menu_hbox->add_child(zoom_plus_button);
3269
zoom_plus_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_zoom_plus));
3270
3271
// Grid controls.
3272
3273
toggle_grid_button = memnew(Button);
3274
toggle_grid_button->set_theme_type_variation(SceneStringName(FlatButton));
3275
toggle_grid_button->set_visible(show_grid_buttons);
3276
toggle_grid_button->set_toggle_mode(true);
3277
toggle_grid_button->set_pressed(true);
3278
toggle_grid_button->set_tooltip_text(ETR("Toggle the visual grid."));
3279
toggle_grid_button->set_focus_mode(FOCUS_ACCESSIBILITY);
3280
menu_hbox->add_child(toggle_grid_button);
3281
toggle_grid_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_show_grid_toggled));
3282
3283
toggle_snapping_button = memnew(Button);
3284
toggle_snapping_button->set_theme_type_variation(SceneStringName(FlatButton));
3285
toggle_snapping_button->set_visible(show_grid_buttons);
3286
toggle_snapping_button->set_toggle_mode(true);
3287
toggle_snapping_button->set_tooltip_text(ETR("Toggle snapping to the grid."));
3288
toggle_snapping_button->set_pressed(snapping_enabled);
3289
toggle_snapping_button->set_focus_mode(FOCUS_ACCESSIBILITY);
3290
menu_hbox->add_child(toggle_snapping_button);
3291
toggle_snapping_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_snapping_toggled));
3292
3293
snapping_distance_spinbox = memnew(SpinBox);
3294
snapping_distance_spinbox->set_visible(show_grid_buttons);
3295
snapping_distance_spinbox->set_min(GRID_MIN_SNAPPING_DISTANCE);
3296
snapping_distance_spinbox->set_max(GRID_MAX_SNAPPING_DISTANCE);
3297
snapping_distance_spinbox->set_step(1);
3298
snapping_distance_spinbox->set_value(snapping_distance);
3299
snapping_distance_spinbox->set_tooltip_text(ETR("Change the snapping distance."));
3300
menu_hbox->add_child(snapping_distance_spinbox);
3301
snapping_distance_spinbox->connect(SceneStringName(value_changed), callable_mp(this, &GraphEdit::_snapping_distance_changed));
3302
3303
// Extra controls.
3304
3305
minimap_button = memnew(Button);
3306
minimap_button->set_theme_type_variation(SceneStringName(FlatButton));
3307
minimap_button->set_visible(show_minimap_button);
3308
minimap_button->set_toggle_mode(true);
3309
minimap_button->set_tooltip_text(ETR("Toggle the graph minimap."));
3310
minimap_button->set_pressed(show_grid);
3311
minimap_button->set_focus_mode(FOCUS_ACCESSIBILITY);
3312
menu_hbox->add_child(minimap_button);
3313
minimap_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_minimap_toggled));
3314
3315
arrange_button = memnew(Button);
3316
arrange_button->set_theme_type_variation(SceneStringName(FlatButton));
3317
arrange_button->set_visible(show_arrange_button);
3318
arrange_button->set_tooltip_text(ETR("Automatically arrange selected nodes."));
3319
arrange_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::arrange_nodes));
3320
arrange_button->set_focus_mode(FOCUS_ACCESSIBILITY);
3321
menu_hbox->add_child(arrange_button);
3322
3323
// Minimap.
3324
3325
const Vector2 minimap_size = Vector2(240, 160);
3326
const float minimap_opacity = 0.65;
3327
3328
minimap = memnew(GraphEditMinimap(this));
3329
top_layer->add_child(minimap);
3330
minimap->set_name("_minimap");
3331
minimap->set_modulate(Color(1, 1, 1, minimap_opacity));
3332
minimap->set_mouse_filter(MOUSE_FILTER_PASS);
3333
minimap->set_custom_minimum_size(Vector2(50, 50));
3334
minimap->set_size(minimap_size);
3335
minimap->set_anchors_preset(Control::PRESET_BOTTOM_RIGHT);
3336
minimap->set_offset(Side::SIDE_LEFT, -minimap_size.width - MINIMAP_OFFSET);
3337
minimap->set_offset(Side::SIDE_TOP, -minimap_size.height - MINIMAP_OFFSET);
3338
minimap->set_offset(Side::SIDE_RIGHT, -MINIMAP_OFFSET);
3339
minimap->set_offset(Side::SIDE_BOTTOM, -MINIMAP_OFFSET);
3340
minimap->connect(SceneStringName(draw), callable_mp(this, &GraphEdit::_minimap_draw));
3341
3342
set_clip_contents(true);
3343
3344
arranger.instantiate(this);
3345
}
3346
3347