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