1 /*************************************************************************/
2 /* visual_script_editor.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
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 "visual_script_editor.h"
32
33 #include "core/object.h"
34 #include "core/os/input.h"
35 #include "core/os/keyboard.h"
36 #include "core/script_language.h"
37 #include "core/variant.h"
38 #include "editor/editor_node.h"
39 #include "editor/editor_resource_preview.h"
40 #include "editor/editor_scale.h"
41 #include "scene/main/viewport.h"
42 #include "visual_script_expression.h"
43 #include "visual_script_flow_control.h"
44 #include "visual_script_func_nodes.h"
45 #include "visual_script_nodes.h"
46
47 #ifdef TOOLS_ENABLED
48 class VisualScriptEditorSignalEdit : public Object {
49
50 GDCLASS(VisualScriptEditorSignalEdit, Object);
51
52 StringName sig;
53
54 public:
55 UndoRedo *undo_redo;
56 Ref<VisualScript> script;
57
58 protected:
_bind_methods()59 static void _bind_methods() {
60 ClassDB::bind_method("_sig_changed", &VisualScriptEditorSignalEdit::_sig_changed);
61 ADD_SIGNAL(MethodInfo("changed"));
62 }
63
_sig_changed()64 void _sig_changed() {
65
66 _change_notify();
67 emit_signal("changed");
68 }
69
_set(const StringName & p_name,const Variant & p_value)70 bool _set(const StringName &p_name, const Variant &p_value) {
71
72 if (sig == StringName())
73 return false;
74
75 if (p_name == "argument_count") {
76
77 int new_argc = p_value;
78 int argc = script->custom_signal_get_argument_count(sig);
79 if (argc == new_argc)
80 return true;
81
82 undo_redo->create_action(TTR("Change Signal Arguments"));
83
84 if (new_argc < argc) {
85 for (int i = new_argc; i < argc; i++) {
86 undo_redo->add_do_method(script.ptr(), "custom_signal_remove_argument", sig, new_argc);
87 undo_redo->add_undo_method(script.ptr(), "custom_signal_add_argument", sig, script->custom_signal_get_argument_name(sig, i), script->custom_signal_get_argument_type(sig, i), -1);
88 }
89 } else if (new_argc > argc) {
90
91 for (int i = argc; i < new_argc; i++) {
92
93 undo_redo->add_do_method(script.ptr(), "custom_signal_add_argument", sig, Variant::NIL, "arg" + itos(i + 1), -1);
94 undo_redo->add_undo_method(script.ptr(), "custom_signal_remove_argument", sig, argc);
95 }
96 }
97
98 undo_redo->add_do_method(this, "_sig_changed");
99 undo_redo->add_undo_method(this, "_sig_changed");
100
101 undo_redo->commit_action();
102
103 return true;
104 }
105 if (String(p_name).begins_with("argument/")) {
106 int idx = String(p_name).get_slice("/", 1).to_int() - 1;
107 ERR_FAIL_INDEX_V(idx, script->custom_signal_get_argument_count(sig), false);
108 String what = String(p_name).get_slice("/", 2);
109 if (what == "type") {
110
111 int old_type = script->custom_signal_get_argument_type(sig, idx);
112 int new_type = p_value;
113 undo_redo->create_action(TTR("Change Argument Type"));
114 undo_redo->add_do_method(script.ptr(), "custom_signal_set_argument_type", sig, idx, new_type);
115 undo_redo->add_undo_method(script.ptr(), "custom_signal_set_argument_type", sig, idx, old_type);
116 undo_redo->commit_action();
117
118 return true;
119 }
120
121 if (what == "name") {
122
123 String old_name = script->custom_signal_get_argument_name(sig, idx);
124 String new_name = p_value;
125 undo_redo->create_action(TTR("Change Argument name"));
126 undo_redo->add_do_method(script.ptr(), "custom_signal_set_argument_name", sig, idx, new_name);
127 undo_redo->add_undo_method(script.ptr(), "custom_signal_set_argument_name", sig, idx, old_name);
128 undo_redo->commit_action();
129 return true;
130 }
131 }
132
133 return false;
134 }
135
_get(const StringName & p_name,Variant & r_ret) const136 bool _get(const StringName &p_name, Variant &r_ret) const {
137
138 if (sig == StringName())
139 return false;
140
141 if (p_name == "argument_count") {
142 r_ret = script->custom_signal_get_argument_count(sig);
143 return true;
144 }
145 if (String(p_name).begins_with("argument/")) {
146 int idx = String(p_name).get_slice("/", 1).to_int() - 1;
147 ERR_FAIL_INDEX_V(idx, script->custom_signal_get_argument_count(sig), false);
148 String what = String(p_name).get_slice("/", 2);
149 if (what == "type") {
150 r_ret = script->custom_signal_get_argument_type(sig, idx);
151 return true;
152 }
153 if (what == "name") {
154 r_ret = script->custom_signal_get_argument_name(sig, idx);
155 return true;
156 }
157 }
158
159 return false;
160 }
_get_property_list(List<PropertyInfo> * p_list) const161 void _get_property_list(List<PropertyInfo> *p_list) const {
162
163 if (sig == StringName())
164 return;
165
166 p_list->push_back(PropertyInfo(Variant::INT, "argument_count", PROPERTY_HINT_RANGE, "0,256"));
167 String argt = "Variant";
168 for (int i = 1; i < Variant::VARIANT_MAX; i++) {
169 argt += "," + Variant::get_type_name(Variant::Type(i));
170 }
171
172 for (int i = 0; i < script->custom_signal_get_argument_count(sig); i++) {
173 p_list->push_back(PropertyInfo(Variant::INT, "argument/" + itos(i + 1) + "/type", PROPERTY_HINT_ENUM, argt));
174 p_list->push_back(PropertyInfo(Variant::STRING, "argument/" + itos(i + 1) + "/name"));
175 }
176 }
177
178 public:
edit(const StringName & p_sig)179 void edit(const StringName &p_sig) {
180
181 sig = p_sig;
182 _change_notify();
183 }
184
VisualScriptEditorSignalEdit()185 VisualScriptEditorSignalEdit() { undo_redo = NULL; }
186 };
187
188 class VisualScriptEditorVariableEdit : public Object {
189
190 GDCLASS(VisualScriptEditorVariableEdit, Object);
191
192 StringName var;
193
194 public:
195 UndoRedo *undo_redo;
196 Ref<VisualScript> script;
197
198 protected:
_bind_methods()199 static void _bind_methods() {
200 ClassDB::bind_method("_var_changed", &VisualScriptEditorVariableEdit::_var_changed);
201 ClassDB::bind_method("_var_value_changed", &VisualScriptEditorVariableEdit::_var_value_changed);
202 ADD_SIGNAL(MethodInfo("changed"));
203 }
204
_var_changed()205 void _var_changed() {
206
207 _change_notify();
208 emit_signal("changed");
209 }
_var_value_changed()210 void _var_value_changed() {
211
212 _change_notify("value"); //so the whole tree is not redrawn, makes editing smoother in general
213 emit_signal("changed");
214 }
215
_set(const StringName & p_name,const Variant & p_value)216 bool _set(const StringName &p_name, const Variant &p_value) {
217
218 if (var == StringName())
219 return false;
220
221 if (String(p_name) == "value") {
222 undo_redo->create_action(TTR("Set Variable Default Value"));
223 Variant current = script->get_variable_default_value(var);
224 undo_redo->add_do_method(script.ptr(), "set_variable_default_value", var, p_value);
225 undo_redo->add_undo_method(script.ptr(), "set_variable_default_value", var, current);
226 undo_redo->add_do_method(this, "_var_value_changed");
227 undo_redo->add_undo_method(this, "_var_value_changed");
228 undo_redo->commit_action();
229 return true;
230 }
231
232 Dictionary d = script->call("get_variable_info", var);
233
234 if (String(p_name) == "type") {
235
236 Dictionary dc = d.duplicate();
237 dc["type"] = p_value;
238 undo_redo->create_action(TTR("Set Variable Type"));
239 undo_redo->add_do_method(script.ptr(), "set_variable_info", var, dc);
240 undo_redo->add_undo_method(script.ptr(), "set_variable_info", var, d);
241 undo_redo->add_do_method(this, "_var_changed");
242 undo_redo->add_undo_method(this, "_var_changed");
243 undo_redo->commit_action();
244 return true;
245 }
246
247 if (String(p_name) == "hint") {
248
249 Dictionary dc = d.duplicate();
250 dc["hint"] = p_value;
251 undo_redo->create_action(TTR("Set Variable Type"));
252 undo_redo->add_do_method(script.ptr(), "set_variable_info", var, dc);
253 undo_redo->add_undo_method(script.ptr(), "set_variable_info", var, d);
254 undo_redo->add_do_method(this, "_var_changed");
255 undo_redo->add_undo_method(this, "_var_changed");
256 undo_redo->commit_action();
257 return true;
258 }
259
260 if (String(p_name) == "hint_string") {
261
262 Dictionary dc = d.duplicate();
263 dc["hint_string"] = p_value;
264 undo_redo->create_action(TTR("Set Variable Type"));
265 undo_redo->add_do_method(script.ptr(), "set_variable_info", var, dc);
266 undo_redo->add_undo_method(script.ptr(), "set_variable_info", var, d);
267 undo_redo->add_do_method(this, "_var_changed");
268 undo_redo->add_undo_method(this, "_var_changed");
269 undo_redo->commit_action();
270 return true;
271 }
272
273 if (String(p_name) == "export") {
274 script->set_variable_export(var, p_value);
275 EditorNode::get_singleton()->get_inspector()->update_tree();
276 return true;
277 }
278
279 return false;
280 }
281
_get(const StringName & p_name,Variant & r_ret) const282 bool _get(const StringName &p_name, Variant &r_ret) const {
283
284 if (var == StringName())
285 return false;
286
287 if (String(p_name) == "value") {
288 r_ret = script->get_variable_default_value(var);
289 return true;
290 }
291
292 PropertyInfo pinfo = script->get_variable_info(var);
293
294 if (String(p_name) == "type") {
295 r_ret = pinfo.type;
296 return true;
297 }
298 if (String(p_name) == "hint") {
299 r_ret = pinfo.hint;
300 return true;
301 }
302 if (String(p_name) == "hint_string") {
303 r_ret = pinfo.hint_string;
304 return true;
305 }
306
307 if (String(p_name) == "export") {
308 r_ret = script->get_variable_export(var);
309 return true;
310 }
311
312 return false;
313 }
_get_property_list(List<PropertyInfo> * p_list) const314 void _get_property_list(List<PropertyInfo> *p_list) const {
315
316 if (var == StringName())
317 return;
318
319 String argt = "Variant";
320 for (int i = 1; i < Variant::VARIANT_MAX; i++) {
321 argt += "," + Variant::get_type_name(Variant::Type(i));
322 }
323 p_list->push_back(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt));
324 p_list->push_back(PropertyInfo(script->get_variable_info(var).type, "value", script->get_variable_info(var).hint, script->get_variable_info(var).hint_string, PROPERTY_USAGE_DEFAULT));
325 // Update this when PropertyHint changes
326 p_list->push_back(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,ExpRange,Enum,ExpEasing,Length,SpriteFrame,KeyAccel,Flags,Layers2dRender,Layers2dPhysics,Layer3dRender,Layer3dPhysics,File,Dir,GlobalFile,GlobalDir,ResourceType,MultilineText,PlaceholderText,ColorNoAlpha,ImageCompressLossy,ImageCompressLossLess,ObjectId,String,NodePathToEditedNode,MethodOfVariantType,MethodOfBaseType,MethodOfInstance,MethodOfScript,PropertyOfVariantType,PropertyOfBaseType,PropertyOfInstance,PropertyOfScript,ObjectTooBig,NodePathValidTypes"));
327 p_list->push_back(PropertyInfo(Variant::STRING, "hint_string"));
328 p_list->push_back(PropertyInfo(Variant::BOOL, "export"));
329 }
330
331 public:
edit(const StringName & p_var)332 void edit(const StringName &p_var) {
333
334 var = p_var;
335 _change_notify();
336 }
337
VisualScriptEditorVariableEdit()338 VisualScriptEditorVariableEdit() { undo_redo = NULL; }
339 };
340
_color_from_type(Variant::Type p_type,bool dark_theme=true)341 static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
342 Color color;
343 if (dark_theme)
344 switch (p_type) {
345 case Variant::NIL: color = Color(0.41, 0.93, 0.74); break;
346
347 case Variant::BOOL: color = Color(0.55, 0.65, 0.94); break;
348 case Variant::INT: color = Color(0.49, 0.78, 0.94); break;
349 case Variant::REAL: color = Color(0.38, 0.85, 0.96); break;
350 case Variant::STRING: color = Color(0.42, 0.65, 0.93); break;
351
352 case Variant::VECTOR2: color = Color(0.74, 0.57, 0.95); break;
353 case Variant::RECT2: color = Color(0.95, 0.57, 0.65); break;
354 case Variant::VECTOR3: color = Color(0.84, 0.49, 0.93); break;
355 case Variant::TRANSFORM2D: color = Color(0.77, 0.93, 0.41); break;
356 case Variant::PLANE: color = Color(0.97, 0.44, 0.44); break;
357 case Variant::QUAT: color = Color(0.93, 0.41, 0.64); break;
358 case Variant::AABB: color = Color(0.93, 0.47, 0.57); break;
359 case Variant::BASIS: color = Color(0.89, 0.93, 0.41); break;
360 case Variant::TRANSFORM: color = Color(0.96, 0.66, 0.43); break;
361
362 case Variant::COLOR: color = Color(0.62, 1.0, 0.44); break;
363 case Variant::NODE_PATH: color = Color(0.41, 0.58, 0.93); break;
364 case Variant::_RID: color = Color(0.41, 0.93, 0.6); break;
365 case Variant::OBJECT: color = Color(0.47, 0.95, 0.91); break;
366 case Variant::DICTIONARY: color = Color(0.47, 0.93, 0.69); break;
367
368 case Variant::ARRAY: color = Color(0.88, 0.88, 0.88); break;
369 case Variant::POOL_BYTE_ARRAY: color = Color(0.67, 0.96, 0.78); break;
370 case Variant::POOL_INT_ARRAY: color = Color(0.69, 0.86, 0.96); break;
371 case Variant::POOL_REAL_ARRAY: color = Color(0.59, 0.91, 0.97); break;
372 case Variant::POOL_STRING_ARRAY: color = Color(0.62, 0.77, 0.95); break;
373 case Variant::POOL_VECTOR2_ARRAY: color = Color(0.82, 0.7, 0.96); break;
374 case Variant::POOL_VECTOR3_ARRAY: color = Color(0.87, 0.61, 0.95); break;
375 case Variant::POOL_COLOR_ARRAY: color = Color(0.91, 1.0, 0.59); break;
376
377 default:
378 color.set_hsv(p_type / float(Variant::VARIANT_MAX), 0.7, 0.7);
379 }
380 else
381 switch (p_type) {
382 case Variant::NIL: color = Color(0.15, 0.89, 0.63); break;
383
384 case Variant::BOOL: color = Color(0.43, 0.56, 0.92); break;
385 case Variant::INT: color = Color(0.31, 0.7, 0.91); break;
386 case Variant::REAL: color = Color(0.15, 0.8, 0.94); break;
387 case Variant::STRING: color = Color(0.27, 0.56, 0.91); break;
388
389 case Variant::VECTOR2: color = Color(0.68, 0.46, 0.93); break;
390 case Variant::RECT2: color = Color(0.93, 0.46, 0.56); break;
391 case Variant::VECTOR3: color = Color(0.86, 0.42, 0.93); break;
392 case Variant::TRANSFORM2D: color = Color(0.59, 0.81, 0.1); break;
393 case Variant::PLANE: color = Color(0.97, 0.44, 0.44); break;
394 case Variant::QUAT: color = Color(0.93, 0.41, 0.64); break;
395 case Variant::AABB: color = Color(0.93, 0.47, 0.57); break;
396 case Variant::BASIS: color = Color(0.7, 0.73, 0.1); break;
397 case Variant::TRANSFORM: color = Color(0.96, 0.56, 0.28); break;
398
399 case Variant::COLOR: color = Color(0.24, 0.75, 0.0); break;
400 case Variant::NODE_PATH: color = Color(0.41, 0.58, 0.93); break;
401 case Variant::_RID: color = Color(0.17, 0.9, 0.45); break;
402 case Variant::OBJECT: color = Color(0.07, 0.84, 0.76); break;
403 case Variant::DICTIONARY: color = Color(0.34, 0.91, 0.62); break;
404
405 case Variant::ARRAY: color = Color(0.45, 0.45, 0.45); break;
406 case Variant::POOL_BYTE_ARRAY: color = Color(0.38, 0.92, 0.6); break;
407 case Variant::POOL_INT_ARRAY: color = Color(0.38, 0.73, 0.92); break;
408 case Variant::POOL_REAL_ARRAY: color = Color(0.25, 0.83, 0.95); break;
409 case Variant::POOL_STRING_ARRAY: color = Color(0.38, 0.62, 0.92); break;
410 case Variant::POOL_VECTOR2_ARRAY: color = Color(0.62, 0.36, 0.92); break;
411 case Variant::POOL_VECTOR3_ARRAY: color = Color(0.79, 0.35, 0.92); break;
412 case Variant::POOL_COLOR_ARRAY: color = Color(0.57, 0.73, 0.0); break;
413
414 default:
415 color.set_hsv(p_type / float(Variant::VARIANT_MAX), 0.3, 0.3);
416 }
417
418 return color;
419 }
420
_update_graph_connections()421 void VisualScriptEditor::_update_graph_connections() {
422
423 graph->clear_connections();
424
425 List<StringName> funcs;
426 script->get_function_list(&funcs);
427
428 if (funcs.size() <= 0) {
429 updating_graph = false;
430 return;
431 }
432
433 for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) {
434
435 List<VisualScript::SequenceConnection> sequence_conns;
436 script->get_sequence_connection_list(F->get(), &sequence_conns);
437
438 for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) {
439
440 graph->connect_node(itos(E->get().from_node), E->get().from_output, itos(E->get().to_node), 0);
441 }
442
443 List<VisualScript::DataConnection> data_conns;
444 script->get_data_connection_list(F->get(), &data_conns);
445
446 for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) {
447
448 VisualScript::DataConnection dc = E->get();
449
450 Ref<VisualScriptNode> from_node = script->get_node(F->get(), E->get().from_node);
451 Ref<VisualScriptNode> to_node = script->get_node(F->get(), E->get().to_node);
452
453 if (to_node->has_input_sequence_port()) {
454 dc.to_port++;
455 }
456
457 dc.from_port += from_node->get_output_sequence_port_count();
458
459 graph->connect_node(itos(E->get().from_node), dc.from_port, itos(E->get().to_node), dc.to_port);
460 }
461 }
462 }
463
_update_graph(int p_only_id)464 void VisualScriptEditor::_update_graph(int p_only_id) {
465
466 if (updating_graph)
467 return;
468
469 updating_graph = true;
470
471 //byebye all nodes
472 if (p_only_id >= 0) {
473 if (graph->has_node(itos(p_only_id))) {
474 Node *gid = graph->get_node(itos(p_only_id));
475 if (gid)
476 memdelete(gid);
477 }
478 } else {
479
480 for (int i = 0; i < graph->get_child_count(); i++) {
481
482 if (Object::cast_to<GraphNode>(graph->get_child(i))) {
483 memdelete(graph->get_child(i));
484 i--;
485 }
486 }
487 }
488
489 List<StringName> funcs;
490 script->get_function_list(&funcs);
491
492 if (funcs.size() <= 0) {
493 graph->hide();
494 select_func_text->show();
495 updating_graph = false;
496 return;
497 }
498
499 graph->show();
500 select_func_text->hide();
501
502 Ref<Texture> type_icons[Variant::VARIANT_MAX] = {
503 Control::get_icon("Variant", "EditorIcons"),
504 Control::get_icon("bool", "EditorIcons"),
505 Control::get_icon("int", "EditorIcons"),
506 Control::get_icon("float", "EditorIcons"),
507 Control::get_icon("String", "EditorIcons"),
508 Control::get_icon("Vector2", "EditorIcons"),
509 Control::get_icon("Rect2", "EditorIcons"),
510 Control::get_icon("Vector3", "EditorIcons"),
511 Control::get_icon("Transform2D", "EditorIcons"),
512 Control::get_icon("Plane", "EditorIcons"),
513 Control::get_icon("Quat", "EditorIcons"),
514 Control::get_icon("AABB", "EditorIcons"),
515 Control::get_icon("Basis", "EditorIcons"),
516 Control::get_icon("Transform", "EditorIcons"),
517 Control::get_icon("Color", "EditorIcons"),
518 Control::get_icon("NodePath", "EditorIcons"),
519 Control::get_icon("RID", "EditorIcons"),
520 Control::get_icon("MiniObject", "EditorIcons"),
521 Control::get_icon("Dictionary", "EditorIcons"),
522 Control::get_icon("Array", "EditorIcons"),
523 Control::get_icon("PoolByteArray", "EditorIcons"),
524 Control::get_icon("PoolIntArray", "EditorIcons"),
525 Control::get_icon("PoolRealArray", "EditorIcons"),
526 Control::get_icon("PoolStringArray", "EditorIcons"),
527 Control::get_icon("PoolVector2Array", "EditorIcons"),
528 Control::get_icon("PoolVector3Array", "EditorIcons"),
529 Control::get_icon("PoolColorArray", "EditorIcons")
530 };
531
532 Ref<Texture> seq_port = Control::get_icon("VisualShaderPort", "EditorIcons");
533
534 for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { // loop through all the functions
535
536 List<int> ids;
537 script->get_node_list(F->get(), &ids);
538 StringName editor_icons = "EditorIcons";
539
540 for (List<int>::Element *E = ids.front(); E; E = E->next()) {
541
542 if (p_only_id >= 0 && p_only_id != E->get())
543 continue;
544
545 Ref<VisualScriptNode> node = script->get_node(F->get(), E->get());
546 Vector2 pos = script->get_node_position(F->get(), E->get());
547
548 GraphNode *gnode = memnew(GraphNode);
549 gnode->set_title(node->get_caption());
550 gnode->set_offset(pos * EDSCALE);
551 if (error_line == E->get()) {
552 gnode->set_overlay(GraphNode::OVERLAY_POSITION);
553 } else if (node->is_breakpoint()) {
554 gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT);
555 }
556
557 gnode->set_meta("__vnode", node);
558 gnode->set_name(itos(E->get()));
559 gnode->connect("dragged", this, "_node_moved", varray(E->get()));
560 gnode->connect("close_request", this, "_remove_node", varray(E->get()), CONNECT_DEFERRED);
561
562 if (E->get() != script->get_function_node_id(F->get())) {
563 //function can't be erased
564 gnode->set_show_close_button(true);
565 }
566
567 bool has_gnode_text = false;
568
569 Ref<VisualScriptLists> nd_list = node;
570 bool is_vslist = nd_list.is_valid();
571 if (is_vslist) {
572 HBoxContainer *hbnc = memnew(HBoxContainer);
573 if (nd_list->is_input_port_editable()) {
574 has_gnode_text = true;
575 Button *btn = memnew(Button);
576 btn->set_text(TTR("Add Input Port"));
577 hbnc->add_child(btn);
578 btn->connect("pressed", this, "_add_input_port", varray(E->get()), CONNECT_DEFERRED);
579 }
580 if (nd_list->is_output_port_editable()) {
581 if (nd_list->is_input_port_editable())
582 hbnc->add_spacer();
583 has_gnode_text = true;
584 Button *btn = memnew(Button);
585 btn->set_text(TTR("Add Output Port"));
586 hbnc->add_child(btn);
587 btn->connect("pressed", this, "_add_output_port", varray(E->get()), CONNECT_DEFERRED);
588 }
589 gnode->add_child(hbnc);
590 } else if (Object::cast_to<VisualScriptExpression>(node.ptr())) {
591 has_gnode_text = true;
592 LineEdit *line_edit = memnew(LineEdit);
593 line_edit->set_text(node->get_text());
594 line_edit->set_expand_to_text_length(true);
595 line_edit->add_font_override("font", get_font("source", "EditorFonts"));
596 gnode->add_child(line_edit);
597 line_edit->connect("text_changed", this, "_expression_text_changed", varray(E->get()));
598 } else {
599 String text = node->get_text();
600 if (!text.empty()) {
601 has_gnode_text = true;
602 Label *label = memnew(Label);
603 label->set_text(text);
604 gnode->add_child(label);
605 }
606 }
607
608 if (Object::cast_to<VisualScriptComment>(node.ptr())) {
609 Ref<VisualScriptComment> vsc = node;
610 gnode->set_comment(true);
611 gnode->set_resizable(true);
612 gnode->set_custom_minimum_size(vsc->get_size() * EDSCALE);
613 gnode->connect("resize_request", this, "_comment_node_resized", varray(E->get()));
614 }
615
616 if (node_styles.has(node->get_category())) {
617 Ref<StyleBoxFlat> sbf = node_styles[node->get_category()];
618 if (gnode->is_comment())
619 sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox("comment", "GraphNode");
620
621 Color c = sbf->get_border_color();
622 c.a = 1;
623 if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) {
624 Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0);
625 mono_color.a = 0.85;
626 c = mono_color;
627 }
628 gnode->add_color_override("title_color", c);
629 c.a = 0.7;
630 gnode->add_color_override("close_color", c);
631 gnode->add_color_override("resizer_color", c);
632 gnode->add_style_override("frame", sbf);
633 }
634
635 const Color mono_color = get_color("mono_color", "Editor");
636
637 int slot_idx = 0;
638
639 bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String();
640 if ((node->has_input_sequence_port() || single_seq_output) || has_gnode_text) {
641 // IF has_gnode_text is true BUT we have no sequence ports to draw (in here),
642 // we still draw the disabled default ones to shift up the slots by one,
643 // so the slots DON'T start with the content text.
644
645 // IF has_gnode_text is false, but we DO want to draw default sequence ports,
646 // we draw a dummy text to take up the position of the sequence nodes, so all the other ports are still aligned correctly.
647 if (!has_gnode_text) {
648 Label *dummy = memnew(Label);
649 dummy->set_text(" ");
650 gnode->add_child(dummy);
651 }
652 gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
653 slot_idx++;
654 }
655
656 int mixed_seq_ports = 0;
657
658 if (!single_seq_output) {
659
660 if (node->has_mixed_input_and_sequence_ports()) {
661 mixed_seq_ports = node->get_output_sequence_port_count();
662 } else {
663 for (int i = 0; i < node->get_output_sequence_port_count(); i++) {
664
665 Label *text2 = memnew(Label);
666 text2->set_text(node->get_output_sequence_port_text(i));
667 text2->set_align(Label::ALIGN_RIGHT);
668 gnode->add_child(text2);
669 gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
670 slot_idx++;
671 }
672 }
673 }
674
675 for (int i = 0; i < MAX(node->get_output_value_port_count(), MAX(mixed_seq_ports, node->get_input_value_port_count())); i++) {
676
677 bool left_ok = false;
678 Variant::Type left_type = Variant::NIL;
679 String left_name;
680
681 if (i < node->get_input_value_port_count()) {
682 PropertyInfo pi = node->get_input_value_port_info(i);
683 left_ok = true;
684 left_type = pi.type;
685 left_name = pi.name;
686 }
687
688 bool right_ok = false;
689 Variant::Type right_type = Variant::NIL;
690 String right_name;
691
692 if (i >= mixed_seq_ports && i < node->get_output_value_port_count() + mixed_seq_ports) {
693 PropertyInfo pi = node->get_output_value_port_info(i - mixed_seq_ports);
694 right_ok = true;
695 right_type = pi.type;
696 right_name = pi.name;
697 }
698 VBoxContainer *vbc = memnew(VBoxContainer);
699 HBoxContainer *hbc = memnew(HBoxContainer);
700 HBoxContainer *hbc2 = memnew(HBoxContainer);
701 vbc->add_child(hbc);
702 vbc->add_child(hbc2);
703 if (left_ok) {
704
705 Ref<Texture> t;
706 if (left_type >= 0 && left_type < Variant::VARIANT_MAX) {
707 t = type_icons[left_type];
708 }
709 if (t.is_valid()) {
710 TextureRect *tf = memnew(TextureRect);
711 tf->set_texture(t);
712 tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
713 hbc->add_child(tf);
714 }
715
716 if (is_vslist) {
717 if (nd_list->is_input_port_name_editable()) {
718 LineEdit *name_box = memnew(LineEdit);
719 hbc->add_child(name_box);
720 name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
721 name_box->set_text(left_name);
722 name_box->set_expand_to_text_length(true);
723 name_box->connect("resized", this, "_update_node_size", varray(E->get()));
724 name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, E->get(), i, true));
725 } else {
726 hbc->add_child(memnew(Label(left_name)));
727 }
728
729 if (nd_list->is_input_port_type_editable()) {
730 OptionButton *opbtn = memnew(OptionButton);
731 for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) {
732 opbtn->add_item(Variant::get_type_name(Variant::Type(j)));
733 }
734 opbtn->select(left_type);
735 opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
736 hbc->add_child(opbtn);
737 opbtn->connect("item_selected", this, "_change_port_type", varray(E->get(), i, true), CONNECT_DEFERRED);
738 }
739
740 Button *rmbtn = memnew(Button);
741 rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons"));
742 hbc->add_child(rmbtn);
743 rmbtn->connect("pressed", this, "_remove_input_port", varray(E->get(), i), CONNECT_DEFERRED);
744 } else {
745 hbc->add_child(memnew(Label(left_name)));
746 }
747
748 if (left_type != Variant::NIL && !script->is_input_value_port_connected(F->get(), E->get(), i)) {
749
750 PropertyInfo pi = node->get_input_value_port_info(i);
751 Button *button = memnew(Button);
752 Variant value = node->get_default_input_value(i);
753 if (value.get_type() != left_type) {
754 //different type? for now convert
755 //not the same, reconvert
756 Variant::CallError ce;
757 const Variant *existingp = &value;
758 value = Variant::construct(left_type, &existingp, 1, ce, false);
759 }
760
761 if (left_type == Variant::COLOR) {
762 button->set_custom_minimum_size(Size2(30, 0) * EDSCALE);
763 button->connect("draw", this, "_draw_color_over_button", varray(button, value));
764 } else if (left_type == Variant::OBJECT && Ref<Resource>(value).is_valid()) {
765
766 Ref<Resource> res = value;
767 Array arr;
768 arr.push_back(button->get_instance_id());
769 arr.push_back(String(value));
770 EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr);
771
772 } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) {
773
774 button->set_text(pi.hint_string.get_slice(",", value));
775 } else {
776
777 button->set_text(value);
778 }
779 button->connect("pressed", this, "_default_value_edited", varray(button, E->get(), i));
780 hbc2->add_child(button);
781 }
782 } else {
783 Control *c = memnew(Control);
784 c->set_custom_minimum_size(Size2(10, 0) * EDSCALE);
785 hbc->add_child(c);
786 }
787
788 hbc->add_spacer();
789 hbc2->add_spacer();
790
791 if (i < mixed_seq_ports) {
792
793 Label *text2 = memnew(Label);
794 text2->set_text(node->get_output_sequence_port_text(i));
795 text2->set_align(Label::ALIGN_RIGHT);
796 hbc->add_child(text2);
797 }
798
799 if (right_ok) {
800
801 if (is_vslist) {
802 Button *rmbtn = memnew(Button);
803 rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons"));
804 hbc->add_child(rmbtn);
805 rmbtn->connect("pressed", this, "_remove_output_port", varray(E->get(), i), CONNECT_DEFERRED);
806
807 if (nd_list->is_output_port_type_editable()) {
808 OptionButton *opbtn = memnew(OptionButton);
809 for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) {
810 opbtn->add_item(Variant::get_type_name(Variant::Type(j)));
811 }
812 opbtn->select(right_type);
813 opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
814 hbc->add_child(opbtn);
815 opbtn->connect("item_selected", this, "_change_port_type", varray(E->get(), i, false), CONNECT_DEFERRED);
816 }
817
818 if (nd_list->is_output_port_name_editable()) {
819 LineEdit *name_box = memnew(LineEdit);
820 hbc->add_child(name_box);
821 name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
822 name_box->set_text(right_name);
823 name_box->set_expand_to_text_length(true);
824 name_box->connect("resized", this, "_update_node_size", varray(E->get()));
825 name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, E->get(), i, false));
826 } else {
827 hbc->add_child(memnew(Label(right_name)));
828 }
829 } else {
830 hbc->add_child(memnew(Label(right_name)));
831 }
832
833 Ref<Texture> t;
834 if (right_type >= 0 && right_type < Variant::VARIANT_MAX) {
835 t = type_icons[right_type];
836 }
837 if (t.is_valid()) {
838 TextureRect *tf = memnew(TextureRect);
839 tf->set_texture(t);
840 tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
841 hbc->add_child(tf);
842 }
843 }
844
845 gnode->add_child(vbc);
846
847 bool dark_theme = get_constant("dark_theme", "Editor");
848 if (i < mixed_seq_ports) {
849 gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), true, TYPE_SEQUENCE, mono_color, Ref<Texture>(), seq_port);
850 } else {
851 gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), right_ok, right_type, _color_from_type(right_type, dark_theme));
852 }
853
854 slot_idx++;
855 }
856
857 graph->add_child(gnode);
858
859 if (gnode->is_comment()) {
860 graph->move_child(gnode, 0);
861 }
862 }
863 }
864 _update_graph_connections();
865 // use default_func instead of default_func for now I think that should be good stop gap solution to ensure not breaking anything
866 graph->call_deferred("set_scroll_ofs", script->get_function_scroll(default_func) * EDSCALE);
867 updating_graph = false;
868 }
869
_change_port_type(int p_select,int p_id,int p_port,bool is_input)870 void VisualScriptEditor::_change_port_type(int p_select, int p_id, int p_port, bool is_input) {
871
872 StringName func = _get_function_of_node(p_id);
873
874 Ref<VisualScriptLists> vsn = script->get_node(func, p_id);
875 if (!vsn.is_valid())
876 return;
877
878 undo_redo->create_action("Change Port Type");
879 if (is_input) {
880 undo_redo->add_do_method(vsn.ptr(), "set_input_data_port_type", p_port, Variant::Type(p_select));
881 undo_redo->add_undo_method(vsn.ptr(), "set_input_data_port_type", p_port, vsn->get_input_value_port_info(p_port).type);
882 } else {
883 undo_redo->add_do_method(vsn.ptr(), "set_output_data_port_type", p_port, Variant::Type(p_select));
884 undo_redo->add_undo_method(vsn.ptr(), "set_output_data_port_type", p_port, vsn->get_output_value_port_info(p_port).type);
885 }
886 undo_redo->commit_action();
887 }
888
_update_node_size(int p_id)889 void VisualScriptEditor::_update_node_size(int p_id) {
890
891 Node *node = graph->get_node(itos(p_id));
892 if (Object::cast_to<Control>(node))
893 Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); //shrink if text is smaller
894 }
_port_name_focus_out(const Node * p_name_box,int p_id,int p_port,bool is_input)895 void VisualScriptEditor::_port_name_focus_out(const Node *p_name_box, int p_id, int p_port, bool is_input) {
896 StringName func = _get_function_of_node(p_id);
897
898 Ref<VisualScriptLists> vsn = script->get_node(func, p_id);
899 if (!vsn.is_valid())
900 return;
901
902 String text;
903
904 if (Object::cast_to<LineEdit>(p_name_box))
905 text = Object::cast_to<LineEdit>(p_name_box)->get_text();
906 else
907 return;
908
909 undo_redo->create_action("Change Port Name");
910 if (is_input) {
911 undo_redo->add_do_method(vsn.ptr(), "set_input_data_port_name", p_port, text);
912 undo_redo->add_undo_method(vsn.ptr(), "set_input_data_port_name", p_port, vsn->get_input_value_port_info(p_port).name);
913 } else {
914 undo_redo->add_do_method(vsn.ptr(), "set_output_data_port_name", p_port, text);
915 undo_redo->add_undo_method(vsn.ptr(), "set_output_data_port_name", p_port, vsn->get_output_value_port_info(p_port).name);
916 }
917 undo_redo->commit_action();
918 }
919
_update_members()920 void VisualScriptEditor::_update_members() {
921 ERR_FAIL_COND(!script.is_valid());
922
923 updating_members = true;
924
925 members->clear();
926 TreeItem *root = members->create_item();
927
928 TreeItem *functions = members->create_item(root);
929 functions->set_selectable(0, false);
930 functions->set_text(0, TTR("Functions:"));
931 functions->add_button(0, Control::get_icon("Override", "EditorIcons"), 1, false, TTR("Override an existing built-in function."));
932 functions->add_button(0, Control::get_icon("Add", "EditorIcons"), 0, false, TTR("Create a new function."));
933 functions->set_custom_color(0, Control::get_color("mono_color", "Editor"));
934
935 List<StringName> func_names;
936 script->get_function_list(&func_names);
937 for (List<StringName>::Element *E = func_names.front(); E; E = E->next()) {
938
939 if (E->get() == default_func) {
940 continue;
941 }
942
943 TreeItem *ti = members->create_item(functions);
944 ti->set_text(0, E->get());
945 ti->set_selectable(0, true);
946 ti->set_metadata(0, E->get());
947 ti->add_button(0, Control::get_icon("Edit", "EditorIcons"), 0);
948 if (selected == E->get())
949 ti->select(0);
950 }
951
952 TreeItem *variables = members->create_item(root);
953 variables->set_selectable(0, false);
954 variables->set_text(0, TTR("Variables:"));
955 variables->add_button(0, Control::get_icon("Add", "EditorIcons"), -1, false, TTR("Create a new variable."));
956 variables->set_custom_color(0, Control::get_color("mono_color", "Editor"));
957
958 Ref<Texture> type_icons[Variant::VARIANT_MAX] = {
959 Control::get_icon("Variant", "EditorIcons"),
960 Control::get_icon("bool", "EditorIcons"),
961 Control::get_icon("int", "EditorIcons"),
962 Control::get_icon("float", "EditorIcons"),
963 Control::get_icon("String", "EditorIcons"),
964 Control::get_icon("Vector2", "EditorIcons"),
965 Control::get_icon("Rect2", "EditorIcons"),
966 Control::get_icon("Vector3", "EditorIcons"),
967 Control::get_icon("Transform2D", "EditorIcons"),
968 Control::get_icon("Plane", "EditorIcons"),
969 Control::get_icon("Quat", "EditorIcons"),
970 Control::get_icon("AABB", "EditorIcons"),
971 Control::get_icon("Basis", "EditorIcons"),
972 Control::get_icon("Transform", "EditorIcons"),
973 Control::get_icon("Color", "EditorIcons"),
974 Control::get_icon("NodePath", "EditorIcons"),
975 Control::get_icon("RID", "EditorIcons"),
976 Control::get_icon("MiniObject", "EditorIcons"),
977 Control::get_icon("Dictionary", "EditorIcons"),
978 Control::get_icon("Array", "EditorIcons"),
979 Control::get_icon("PoolByteArray", "EditorIcons"),
980 Control::get_icon("PoolIntArray", "EditorIcons"),
981 Control::get_icon("PoolRealArray", "EditorIcons"),
982 Control::get_icon("PoolStringArray", "EditorIcons"),
983 Control::get_icon("PoolVector2Array", "EditorIcons"),
984 Control::get_icon("PoolVector3Array", "EditorIcons"),
985 Control::get_icon("PoolColorArray", "EditorIcons")
986 };
987
988 List<StringName> var_names;
989 script->get_variable_list(&var_names);
990 for (List<StringName>::Element *E = var_names.front(); E; E = E->next()) {
991 TreeItem *ti = members->create_item(variables);
992
993 ti->set_text(0, E->get());
994 Variant var = script->get_variable_default_value(E->get());
995 ti->set_suffix(0, "= " + String(var));
996 ti->set_icon(0, type_icons[script->get_variable_info(E->get()).type]);
997
998 ti->set_selectable(0, true);
999 ti->set_editable(0, true);
1000 ti->set_metadata(0, E->get());
1001 if (selected == E->get())
1002 ti->select(0);
1003 }
1004
1005 TreeItem *_signals = members->create_item(root);
1006 _signals->set_selectable(0, false);
1007 _signals->set_text(0, TTR("Signals:"));
1008 _signals->add_button(0, Control::get_icon("Add", "EditorIcons"), -1, false, TTR("Create a new signal."));
1009 _signals->set_custom_color(0, Control::get_color("mono_color", "Editor"));
1010
1011 List<StringName> signal_names;
1012 script->get_custom_signal_list(&signal_names);
1013 for (List<StringName>::Element *E = signal_names.front(); E; E = E->next()) {
1014 TreeItem *ti = members->create_item(_signals);
1015 ti->set_text(0, E->get());
1016 ti->set_selectable(0, true);
1017 ti->set_editable(0, true);
1018 ti->set_metadata(0, E->get());
1019 if (selected == E->get())
1020 ti->select(0);
1021 }
1022
1023 String base_type = script->get_instance_base_type();
1024 String icon_type = base_type;
1025 if (!Control::has_icon(base_type, "EditorIcons")) {
1026 icon_type = "Object";
1027 }
1028
1029 base_type_select->set_text(base_type);
1030 base_type_select->set_icon(Control::get_icon(icon_type, "EditorIcons"));
1031
1032 updating_members = false;
1033 }
1034
_member_selected()1035 void VisualScriptEditor::_member_selected() {
1036
1037 if (updating_members)
1038 return;
1039
1040 TreeItem *ti = members->get_selected();
1041 ERR_FAIL_COND(!ti);
1042
1043 selected = ti->get_metadata(0);
1044
1045 if (ti->get_parent() == members->get_root()->get_children()) {
1046
1047 #ifdef OSX_ENABLED
1048 bool held_ctrl = Input::get_singleton()->is_key_pressed(KEY_META);
1049 #else
1050 bool held_ctrl = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
1051 #endif
1052 if (held_ctrl) {
1053 ERR_FAIL_COND(!script->has_function(selected));
1054 _center_on_node(selected, script->get_function_node_id(selected));
1055 }
1056 }
1057 }
1058
_member_edited()1059 void VisualScriptEditor::_member_edited() {
1060
1061 if (updating_members)
1062 return;
1063
1064 TreeItem *ti = members->get_edited();
1065 ERR_FAIL_COND(!ti);
1066
1067 String name = ti->get_metadata(0);
1068 String new_name = ti->get_text(0);
1069
1070 if (name == new_name)
1071 return;
1072
1073 if (!new_name.is_valid_identifier()) {
1074
1075 EditorNode::get_singleton()->show_warning(TTR("Name is not a valid identifier:") + " " + new_name);
1076 updating_members = true;
1077 ti->set_text(0, name);
1078 updating_members = false;
1079 return;
1080 }
1081
1082 if (script->has_function(new_name) || script->has_variable(new_name) || script->has_custom_signal(new_name)) {
1083
1084 EditorNode::get_singleton()->show_warning(TTR("Name already in use by another func/var/signal:") + " " + new_name);
1085 updating_members = true;
1086 ti->set_text(0, name);
1087 updating_members = false;
1088 return;
1089 }
1090
1091 TreeItem *root = members->get_root();
1092
1093 if (ti->get_parent() == root->get_children()) {
1094
1095 selected = new_name;
1096
1097 int node_id = script->get_function_node_id(name);
1098 Ref<VisualScriptFunction> func;
1099 if (script->has_node(name, node_id)) {
1100 func = script->get_node(name, node_id);
1101 }
1102 undo_redo->create_action(TTR("Rename Function"));
1103 undo_redo->add_do_method(script.ptr(), "rename_function", name, new_name);
1104 undo_redo->add_undo_method(script.ptr(), "rename_function", new_name, name);
1105 if (func.is_valid()) {
1106 undo_redo->add_do_method(func.ptr(), "set_name", new_name);
1107 undo_redo->add_undo_method(func.ptr(), "set_name", name);
1108 }
1109
1110 // also fix all function calls
1111 List<StringName> flst;
1112 script->get_function_list(&flst);
1113 for (List<StringName>::Element *E = flst.front(); E; E = E->next()) {
1114 List<int> lst;
1115 script->get_node_list(E->get(), &lst);
1116 for (List<int>::Element *F = lst.front(); F; F = F->next()) {
1117 Ref<VisualScriptFunctionCall> fncall = script->get_node(E->get(), F->get());
1118 if (!fncall.is_valid())
1119 continue;
1120 if (fncall->get_function() == name) {
1121 undo_redo->add_do_method(fncall.ptr(), "set_function", new_name);
1122 undo_redo->add_undo_method(fncall.ptr(), "set_function", name);
1123 }
1124 }
1125 }
1126
1127 undo_redo->add_do_method(this, "_update_members");
1128 undo_redo->add_undo_method(this, "_update_members");
1129 undo_redo->add_do_method(this, "_update_graph");
1130 undo_redo->add_undo_method(this, "_update_graph");
1131 undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
1132 undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
1133 undo_redo->commit_action();
1134
1135 return; //or crash because it will become invalid
1136 }
1137
1138 if (ti->get_parent() == root->get_children()->get_next()) {
1139
1140 selected = new_name;
1141 undo_redo->create_action(TTR("Rename Variable"));
1142 undo_redo->add_do_method(script.ptr(), "rename_variable", name, new_name);
1143 undo_redo->add_undo_method(script.ptr(), "rename_variable", new_name, name);
1144 undo_redo->add_do_method(this, "_update_members");
1145 undo_redo->add_undo_method(this, "_update_members");
1146 undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
1147 undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
1148 undo_redo->commit_action();
1149
1150 return; //or crash because it will become invalid
1151 }
1152
1153 if (ti->get_parent() == root->get_children()->get_next()->get_next()) {
1154
1155 selected = new_name;
1156 undo_redo->create_action(TTR("Rename Signal"));
1157 undo_redo->add_do_method(script.ptr(), "rename_custom_signal", name, new_name);
1158 undo_redo->add_undo_method(script.ptr(), "rename_custom_signal", new_name, name);
1159 undo_redo->add_do_method(this, "_update_members");
1160 undo_redo->add_undo_method(this, "_update_members");
1161 undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
1162 undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
1163 undo_redo->commit_action();
1164
1165 return; //or crash because it will become invalid
1166 }
1167 }
1168
_create_function_dialog()1169 void VisualScriptEditor::_create_function_dialog() {
1170 function_create_dialog->popup_centered();
1171 func_name_box->set_text("");
1172 func_name_box->grab_focus();
1173 for (int i = 0; i < func_input_vbox->get_child_count(); i++) {
1174 Node *nd = func_input_vbox->get_child(i);
1175 nd->queue_delete();
1176 }
1177 }
1178
_create_function()1179 void VisualScriptEditor::_create_function() {
1180 String name = _validate_name((func_name_box->get_text() == "") ? "new_func" : func_name_box->get_text());
1181 selected = name;
1182 Vector2 ofs = _get_available_pos();
1183
1184 Ref<VisualScriptFunction> func_node;
1185 func_node.instance();
1186 func_node->set_name(name);
1187
1188 for (int i = 0; i < func_input_vbox->get_child_count(); i++) {
1189 OptionButton *opbtn = Object::cast_to<OptionButton>(func_input_vbox->get_child(i)->get_child(3));
1190 LineEdit *lne = Object::cast_to<LineEdit>(func_input_vbox->get_child(i)->get_child(1));
1191 if (!opbtn || !lne)
1192 continue;
1193 Variant::Type arg_type = Variant::Type(opbtn->get_selected());
1194 String arg_name = lne->get_text();
1195 func_node->add_argument(arg_type, arg_name);
1196 }
1197
1198 undo_redo->create_action(TTR("Add Function"));
1199 undo_redo->add_do_method(script.ptr(), "add_function", name);
1200 undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs);
1201 undo_redo->add_undo_method(script.ptr(), "remove_function", name);
1202 undo_redo->add_do_method(this, "_update_members");
1203 undo_redo->add_undo_method(this, "_update_members");
1204 undo_redo->add_do_method(this, "_update_graph");
1205 undo_redo->add_undo_method(this, "_update_graph");
1206 undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
1207 undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
1208 undo_redo->commit_action();
1209
1210 _update_graph();
1211 }
1212
_add_node_dialog()1213 void VisualScriptEditor::_add_node_dialog() {
1214 _generic_search(script->get_instance_base_type(), graph->get_global_position() + Vector2(55, 80), true);
1215 }
1216
_add_func_input()1217 void VisualScriptEditor::_add_func_input() {
1218 HBoxContainer *hbox = memnew(HBoxContainer);
1219 hbox->set_h_size_flags(SIZE_EXPAND_FILL);
1220
1221 Label *name_label = memnew(Label);
1222 name_label->set_text(TTR("Name:"));
1223 hbox->add_child(name_label);
1224
1225 LineEdit *name_box = memnew(LineEdit);
1226 name_box->set_h_size_flags(SIZE_EXPAND_FILL);
1227 name_box->set_text("input");
1228 name_box->connect("focus_entered", this, "_deselect_input_names");
1229 hbox->add_child(name_box);
1230
1231 Label *type_label = memnew(Label);
1232 type_label->set_text(TTR("Type:"));
1233 hbox->add_child(type_label);
1234
1235 OptionButton *type_box = memnew(OptionButton);
1236 type_box->set_custom_minimum_size(Size2(120 * EDSCALE, 0));
1237 for (int i = Variant::NIL; i < Variant::VARIANT_MAX; i++)
1238 type_box->add_item(Variant::get_type_name(Variant::Type(i)));
1239 type_box->select(1);
1240 hbox->add_child(type_box);
1241
1242 Button *delete_button = memnew(Button);
1243 delete_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons"));
1244 delete_button->set_tooltip(vformat(TTR("Delete input port")));
1245 hbox->add_child(delete_button);
1246
1247 for (int i = 0; i < func_input_vbox->get_child_count(); i++) {
1248 LineEdit *line_edit = (LineEdit *)func_input_vbox->get_child(i)->get_child(1);
1249 line_edit->deselect();
1250 }
1251
1252 func_input_vbox->add_child(hbox);
1253 hbox->set_meta("id", hbox->get_position_in_parent());
1254
1255 delete_button->connect("pressed", this, "_remove_func_input", varray(hbox));
1256
1257 name_box->select_all();
1258 name_box->grab_focus();
1259 }
1260
_remove_func_input(Node * p_node)1261 void VisualScriptEditor::_remove_func_input(Node *p_node) {
1262 func_input_vbox->remove_child(p_node);
1263 p_node->queue_delete();
1264 }
1265
_deselect_input_names()1266 void VisualScriptEditor::_deselect_input_names() {
1267 int cn = func_input_vbox->get_child_count();
1268 for (int i = 0; i < cn; i++) {
1269 LineEdit *lne = Object::cast_to<LineEdit>(func_input_vbox->get_child(i)->get_child(1));
1270 if (lne)
1271 lne->deselect();
1272 }
1273 }
1274
_member_button(Object * p_item,int p_column,int p_button)1275 void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_button) {
1276
1277 TreeItem *ti = Object::cast_to<TreeItem>(p_item);
1278
1279 TreeItem *root = members->get_root();
1280
1281 if (ti->get_parent() == root) {
1282 //main buttons
1283 if (ti == root->get_children()) {
1284 //add function, this one uses menu
1285
1286 if (p_button == 1) {
1287 new_virtual_method_select->select_method_from_base_type(script->get_instance_base_type(), String(), true);
1288
1289 return;
1290 } else if (p_button == 0) {
1291
1292 String name = _validate_name("new_function");
1293 selected = name;
1294 Vector2 ofs = _get_available_pos();
1295
1296 Ref<VisualScriptFunction> func_node;
1297 func_node.instance();
1298 func_node->set_name(name);
1299
1300 undo_redo->create_action(TTR("Add Function"));
1301 undo_redo->add_do_method(script.ptr(), "add_function", name);
1302 undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs);
1303 undo_redo->add_undo_method(script.ptr(), "remove_function", name);
1304 undo_redo->add_do_method(this, "_update_members");
1305 undo_redo->add_undo_method(this, "_update_members");
1306 undo_redo->add_do_method(this, "_update_graph");
1307 undo_redo->add_undo_method(this, "_update_graph");
1308 undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
1309 undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
1310 undo_redo->commit_action();
1311
1312 _update_graph();
1313 }
1314
1315 return; //or crash because it will become invalid
1316 }
1317
1318 if (ti == root->get_children()->get_next()) {
1319 //add variable
1320 String name = _validate_name("new_variable");
1321 selected = name;
1322
1323 undo_redo->create_action(TTR("Add Variable"));
1324 undo_redo->add_do_method(script.ptr(), "add_variable", name);
1325 undo_redo->add_undo_method(script.ptr(), "remove_variable", name);
1326 undo_redo->add_do_method(this, "_update_members");
1327 undo_redo->add_undo_method(this, "_update_members");
1328 undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
1329 undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
1330 undo_redo->commit_action();
1331 return; //or crash because it will become invalid
1332 }
1333
1334 if (ti == root->get_children()->get_next()->get_next()) {
1335 //add variable
1336 String name = _validate_name("new_signal");
1337 selected = name;
1338
1339 undo_redo->create_action(TTR("Add Signal"));
1340 undo_redo->add_do_method(script.ptr(), "add_custom_signal", name);
1341 undo_redo->add_undo_method(script.ptr(), "remove_custom_signal", name);
1342 undo_redo->add_do_method(this, "_update_members");
1343 undo_redo->add_undo_method(this, "_update_members");
1344 undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
1345 undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
1346 undo_redo->commit_action();
1347 return; //or crash because it will become invalid
1348 }
1349 } else if (ti->get_parent() == root->get_children()) {
1350 selected = ti->get_text(0);
1351 function_name_edit->set_position(Input::get_singleton()->get_mouse_position() - Vector2(60, -10));
1352 function_name_edit->popup();
1353 function_name_box->set_text(selected);
1354 function_name_box->select_all();
1355 }
1356 }
1357
_add_input_port(int p_id)1358 void VisualScriptEditor::_add_input_port(int p_id) {
1359
1360 StringName func = _get_function_of_node(p_id);
1361
1362 Ref<VisualScriptLists> vsn = script->get_node(func, p_id);
1363 if (!vsn.is_valid())
1364 return;
1365
1366 updating_graph = true;
1367
1368 undo_redo->create_action(TTR("Add Input Port"), UndoRedo::MERGE_ENDS);
1369 undo_redo->add_do_method(vsn.ptr(), "add_input_data_port", Variant::NIL, "arg", -1);
1370 undo_redo->add_do_method(this, "_update_graph", p_id);
1371
1372 undo_redo->add_undo_method(vsn.ptr(), "remove_input_data_port", vsn->get_input_value_port_count());
1373 undo_redo->add_undo_method(this, "_update_graph", p_id);
1374
1375 updating_graph = false;
1376
1377 undo_redo->commit_action();
1378 }
1379
_add_output_port(int p_id)1380 void VisualScriptEditor::_add_output_port(int p_id) {
1381
1382 StringName func = _get_function_of_node(p_id);
1383
1384 Ref<VisualScriptLists> vsn = script->get_node(func, p_id);
1385 if (!vsn.is_valid())
1386 return;
1387
1388 updating_graph = true;
1389
1390 undo_redo->create_action(TTR("Add Output Port"), UndoRedo::MERGE_ENDS);
1391 undo_redo->add_do_method(vsn.ptr(), "add_output_data_port", Variant::NIL, "arg", -1);
1392 undo_redo->add_do_method(this, "_update_graph", p_id);
1393
1394 undo_redo->add_undo_method(vsn.ptr(), "remove_output_data_port", vsn->get_output_value_port_count());
1395 undo_redo->add_undo_method(this, "_update_graph", p_id);
1396
1397 updating_graph = false;
1398
1399 undo_redo->commit_action();
1400 }
1401
_remove_input_port(int p_id,int p_port)1402 void VisualScriptEditor::_remove_input_port(int p_id, int p_port) {
1403
1404 StringName func = _get_function_of_node(p_id);
1405
1406 Ref<VisualScriptLists> vsn = script->get_node(func, p_id);
1407 if (!vsn.is_valid())
1408 return;
1409
1410 updating_graph = true;
1411
1412 undo_redo->create_action(TTR("Remove Input Port"), UndoRedo::MERGE_ENDS);
1413
1414 int conn_from = -1, conn_port = -1;
1415 script->get_input_value_port_connection_source(func, p_id, p_port, &conn_from, &conn_port);
1416
1417 if (conn_from != -1)
1418 undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_id, p_port);
1419
1420 undo_redo->add_do_method(vsn.ptr(), "remove_input_data_port", p_port);
1421 undo_redo->add_do_method(this, "_update_graph", p_id);
1422
1423 if (conn_from != -1)
1424 undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_id, p_port);
1425
1426 undo_redo->add_undo_method(vsn.ptr(), "add_input_data_port", vsn->get_input_value_port_info(p_port).type, vsn->get_input_value_port_info(p_port).name, p_port);
1427 undo_redo->add_undo_method(this, "_update_graph", p_id);
1428
1429 updating_graph = false;
1430
1431 undo_redo->commit_action();
1432 }
1433
_remove_output_port(int p_id,int p_port)1434 void VisualScriptEditor::_remove_output_port(int p_id, int p_port) {
1435
1436 StringName func = _get_function_of_node(p_id);
1437
1438 Ref<VisualScriptLists> vsn = script->get_node(func, p_id);
1439 if (!vsn.is_valid())
1440 return;
1441
1442 updating_graph = true;
1443
1444 undo_redo->create_action(TTR("Remove Output Port"), UndoRedo::MERGE_ENDS);
1445
1446 List<VisualScript::DataConnection> data_connections;
1447 script->get_data_connection_list(func, &data_connections);
1448
1449 HashMap<int, Set<int> > conn_map;
1450 for (const List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
1451 if (E->get().from_node == p_id && E->get().from_port == p_port) {
1452 // push into the connections map
1453 if (!conn_map.has(E->get().to_node))
1454 conn_map.set(E->get().to_node, Set<int>());
1455 conn_map[E->get().to_node].insert(E->get().to_port);
1456 }
1457 }
1458
1459 undo_redo->add_do_method(vsn.ptr(), "remove_output_data_port", p_port);
1460 undo_redo->add_do_method(this, "_update_graph", p_id);
1461
1462 List<int> keys;
1463 conn_map.get_key_list(&keys);
1464 for (const List<int>::Element *E = keys.front(); E; E = E->next()) {
1465 for (const Set<int>::Element *F = conn_map[E->get()].front(); F; F = F->next()) {
1466 undo_redo->add_undo_method(script.ptr(), "data_connect", func, p_id, p_port, E->get(), F->get());
1467 }
1468 }
1469
1470 undo_redo->add_undo_method(vsn.ptr(), "add_output_data_port", vsn->get_output_value_port_info(p_port).type, vsn->get_output_value_port_info(p_port).name, p_port);
1471 undo_redo->add_undo_method(this, "_update_graph", p_id);
1472
1473 updating_graph = false;
1474
1475 undo_redo->commit_action();
1476 }
1477
_expression_text_changed(const String & p_text,int p_id)1478 void VisualScriptEditor::_expression_text_changed(const String &p_text, int p_id) {
1479
1480 StringName func = _get_function_of_node(p_id);
1481
1482 Ref<VisualScriptExpression> vse = script->get_node(func, p_id);
1483 if (!vse.is_valid())
1484 return;
1485
1486 updating_graph = true;
1487
1488 undo_redo->create_action(TTR("Change Expression"), UndoRedo::MERGE_ENDS);
1489 undo_redo->add_do_property(vse.ptr(), "expression", p_text);
1490 undo_redo->add_undo_property(vse.ptr(), "expression", vse->get("expression"));
1491 undo_redo->add_do_method(this, "_update_graph", p_id);
1492 undo_redo->add_undo_method(this, "_update_graph", p_id);
1493 undo_redo->commit_action();
1494
1495 Node *node = graph->get_node(itos(p_id));
1496 if (Object::cast_to<Control>(node))
1497 Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); //shrink if text is smaller
1498
1499 updating_graph = false;
1500 }
1501
_get_available_pos(bool centered,Vector2 ofs) const1502 Vector2 VisualScriptEditor::_get_available_pos(bool centered, Vector2 ofs) const {
1503 if (centered)
1504 ofs = graph->get_scroll_ofs() + graph->get_size() * 0.5;
1505
1506 if (graph->is_using_snap()) {
1507 int snap = graph->get_snap();
1508 ofs = ofs.snapped(Vector2(snap, snap));
1509 }
1510
1511 ofs /= EDSCALE;
1512
1513 while (true) {
1514 bool exists = false;
1515 List<StringName> all_fn;
1516 script->get_function_list(&all_fn);
1517 for (List<StringName>::Element *F = all_fn.front(); F; F = F->next()) {
1518 StringName curr_fn = F->get();
1519 List<int> existing;
1520 script->get_node_list(curr_fn, &existing);
1521 for (List<int>::Element *E = existing.front(); E; E = E->next()) {
1522 Point2 pos = script->get_node_position(curr_fn, E->get());
1523 if (pos.distance_to(ofs) < 50) {
1524 ofs += Vector2(graph->get_snap(), graph->get_snap());
1525 exists = true;
1526 break;
1527 }
1528 }
1529 }
1530 if (exists)
1531 continue;
1532 break;
1533 }
1534
1535 return ofs;
1536 }
1537
_validate_name(const String & p_name) const1538 String VisualScriptEditor::_validate_name(const String &p_name) const {
1539
1540 String valid = p_name;
1541
1542 int counter = 1;
1543 while (true) {
1544
1545 bool exists = script->has_function(valid) || script->has_variable(valid) || script->has_custom_signal(valid);
1546
1547 if (exists) {
1548 counter++;
1549 valid = p_name + "_" + itos(counter);
1550 continue;
1551 }
1552
1553 break;
1554 }
1555
1556 return valid;
1557 }
1558
_on_nodes_delete()1559 void VisualScriptEditor::_on_nodes_delete() {
1560
1561 // delete all the selected nodes
1562
1563 List<int> to_erase;
1564
1565 for (int i = 0; i < graph->get_child_count(); i++) {
1566 GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
1567 if (gn) {
1568 if (gn->is_selected() && gn->is_close_button_visible()) {
1569 to_erase.push_back(gn->get_name().operator String().to_int());
1570 }
1571 }
1572 }
1573
1574 if (to_erase.empty())
1575 return;
1576
1577 undo_redo->create_action(TTR("Remove VisualScript Nodes"));
1578
1579 for (List<int>::Element *F = to_erase.front(); F; F = F->next()) {
1580
1581 int cr_node = F->get();
1582
1583 StringName func = _get_function_of_node(cr_node);
1584
1585 undo_redo->add_do_method(script.ptr(), "remove_node", func, cr_node);
1586 undo_redo->add_undo_method(script.ptr(), "add_node", func, cr_node, script->get_node(func, cr_node), script->get_node_position(func, cr_node));
1587
1588 List<VisualScript::SequenceConnection> sequence_conns;
1589 script->get_sequence_connection_list(func, &sequence_conns);
1590
1591 for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) {
1592
1593 if (E->get().from_node == cr_node || E->get().to_node == cr_node) {
1594 undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, E->get().from_node, E->get().from_output, E->get().to_node);
1595 }
1596 }
1597
1598 List<VisualScript::DataConnection> data_conns;
1599 script->get_data_connection_list(func, &data_conns);
1600
1601 for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) {
1602
1603 if (E->get().from_node == F->get() || E->get().to_node == F->get()) {
1604 undo_redo->add_undo_method(script.ptr(), "data_connect", func, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
1605 }
1606 }
1607 }
1608 undo_redo->add_do_method(this, "_update_graph");
1609 undo_redo->add_undo_method(this, "_update_graph");
1610
1611 undo_redo->commit_action();
1612 }
1613
_on_nodes_duplicate()1614 void VisualScriptEditor::_on_nodes_duplicate() {
1615
1616 Set<int> to_duplicate;
1617 List<StringName> funcs;
1618
1619 for (int i = 0; i < graph->get_child_count(); i++) {
1620 GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
1621 if (gn) {
1622 if (gn->is_selected() && gn->is_close_button_visible()) {
1623 int id = gn->get_name().operator String().to_int();
1624 to_duplicate.insert(id);
1625 funcs.push_back(_get_function_of_node(id));
1626 }
1627 }
1628 }
1629
1630 if (to_duplicate.empty())
1631 return;
1632
1633 undo_redo->create_action(TTR("Duplicate VisualScript Nodes"));
1634 int idc = script->get_available_id() + 1;
1635
1636 Set<int> to_select;
1637 HashMap<int, int> remap;
1638
1639 for (Set<int>::Element *F = to_duplicate.front(); F; F = F->next()) {
1640
1641 // duplicate from the specific function but place it into the default func as it would lack the connections
1642 StringName func = _get_function_of_node(F->get());
1643 Ref<VisualScriptNode> node = script->get_node(func, F->get());
1644
1645 Ref<VisualScriptNode> dupe = node->duplicate(true);
1646
1647 int new_id = idc++;
1648 remap.set(F->get(), new_id);
1649
1650 to_select.insert(new_id);
1651 undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, dupe, script->get_node_position(func, F->get()) + Vector2(20, 20));
1652 undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
1653 }
1654
1655 for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) {
1656 List<VisualScript::SequenceConnection> seqs;
1657 script->get_sequence_connection_list(F->get(), &seqs);
1658 for (List<VisualScript::SequenceConnection>::Element *E = seqs.front(); E; E = E->next()) {
1659 if (to_duplicate.has(E->get().from_node) && to_duplicate.has(E->get().to_node)) {
1660 undo_redo->add_do_method(script.ptr(), "sequence_connect", default_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]);
1661 }
1662 }
1663
1664 List<VisualScript::DataConnection> data;
1665 script->get_data_connection_list(F->get(), &data);
1666 for (List<VisualScript::DataConnection>::Element *E = data.front(); E; E = E->next()) {
1667 if (to_duplicate.has(E->get().from_node) && to_duplicate.has(E->get().to_node)) {
1668 undo_redo->add_do_method(script.ptr(), "data_connect", default_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port);
1669 }
1670 }
1671 }
1672
1673 undo_redo->add_do_method(this, "_update_graph");
1674 undo_redo->add_undo_method(this, "_update_graph");
1675
1676 undo_redo->commit_action();
1677
1678 for (int i = 0; i < graph->get_child_count(); i++) {
1679 GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
1680 if (gn) {
1681 int id = gn->get_name().operator String().to_int();
1682 gn->set_selected(to_select.has(id));
1683 }
1684 }
1685
1686 if (to_select.size()) {
1687 EditorNode::get_singleton()->push_item(script->get_node(default_func, to_select.front()->get()).ptr());
1688 }
1689 }
1690
_generic_search(String p_base_type,Vector2 pos,bool node_centered)1691 void VisualScriptEditor::_generic_search(String p_base_type, Vector2 pos, bool node_centered) {
1692 if (node_centered)
1693 port_action_pos = graph->get_size() / 2.0f;
1694 else
1695 port_action_pos = graph->get_viewport()->get_mouse_position() - graph->get_global_position();
1696
1697 new_connect_node_select->select_from_visual_script(p_base_type, false, false); // neither connecting nor reset text
1698
1699 // ensure that the dialog fits inside the graph
1700 Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size();
1701 pos.x = pos.x > bounds.x ? bounds.x : pos.x;
1702 pos.y = pos.y > bounds.y ? bounds.y : pos.y;
1703
1704 if (pos != Vector2())
1705 new_connect_node_select->set_position(pos);
1706 }
1707
_input(const Ref<InputEvent> & p_event)1708 void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) {
1709 // GUI input for VS Editor Plugin
1710 Ref<InputEventMouseButton> key = p_event;
1711
1712 if (key.is_valid() && !key->is_pressed()) {
1713 mouse_up_position = Input::get_singleton()->get_mouse_position();
1714 }
1715 }
1716
_graph_gui_input(const Ref<InputEvent> & p_event)1717 void VisualScriptEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
1718 Ref<InputEventMouseButton> key = p_event;
1719
1720 if (key.is_valid() && key->is_pressed() && key->get_button_mask() == BUTTON_RIGHT) {
1721 saved_position = graph->get_local_mouse_position();
1722
1723 Point2 gpos = Input::get_singleton()->get_mouse_position();
1724 _generic_search(script->get_instance_base_type(), gpos);
1725 }
1726 }
1727
_members_gui_input(const Ref<InputEvent> & p_event)1728 void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) {
1729
1730 Ref<InputEventKey> key = p_event;
1731 if (key.is_valid() && key->is_pressed() && !key->is_echo()) {
1732 if (members->has_focus()) {
1733 TreeItem *ti = members->get_selected();
1734 if (ti) {
1735 TreeItem *root = members->get_root();
1736 if (ti->get_parent() == root->get_children()) {
1737 member_type = MEMBER_FUNCTION;
1738 }
1739 if (ti->get_parent() == root->get_children()->get_next()) {
1740 member_type = MEMBER_VARIABLE;
1741 }
1742 if (ti->get_parent() == root->get_children()->get_next()->get_next()) {
1743 member_type = MEMBER_SIGNAL;
1744 }
1745 member_name = ti->get_text(0);
1746 }
1747 if (ED_IS_SHORTCUT("visual_script_editor/delete_selected", p_event)) {
1748 _member_option(MEMBER_REMOVE);
1749 }
1750 if (ED_IS_SHORTCUT("visual_script_editor/edit_member", p_event)) {
1751 _member_option(MEMBER_EDIT);
1752 }
1753 }
1754 }
1755
1756 Ref<InputEventMouseButton> btn = p_event;
1757 if (btn.is_valid() && btn->is_doubleclick()) {
1758 TreeItem *ti = members->get_selected();
1759 if (ti && ti->get_parent() == members->get_root()->get_children()) // to check if it's a function
1760 _center_on_node(ti->get_metadata(0), script->get_function_node_id(ti->get_metadata(0)));
1761 }
1762 }
1763
_rename_function(const String & name,const String & new_name)1764 void VisualScriptEditor::_rename_function(const String &name, const String &new_name) {
1765
1766 if (!new_name.is_valid_identifier()) {
1767
1768 EditorNode::get_singleton()->show_warning(TTR("Name is not a valid identifier:") + " " + new_name);
1769 return;
1770 }
1771
1772 if (script->has_function(new_name) || script->has_variable(new_name) || script->has_custom_signal(new_name)) {
1773
1774 EditorNode::get_singleton()->show_warning(TTR("Name already in use by another func/var/signal:") + " " + new_name);
1775 return;
1776 }
1777
1778 int node_id = script->get_function_node_id(name);
1779 Ref<VisualScriptFunction> func;
1780 if (script->has_node(name, node_id)) {
1781 func = script->get_node(name, node_id);
1782 }
1783 undo_redo->create_action(TTR("Rename Function"));
1784 undo_redo->add_do_method(script.ptr(), "rename_function", name, new_name);
1785 undo_redo->add_undo_method(script.ptr(), "rename_function", new_name, name);
1786 if (func.is_valid()) {
1787 undo_redo->add_do_method(func.ptr(), "set_name", new_name);
1788 undo_redo->add_undo_method(func.ptr(), "set_name", name);
1789 }
1790
1791 // also fix all function calls
1792 List<StringName> flst;
1793 script->get_function_list(&flst);
1794 for (List<StringName>::Element *E = flst.front(); E; E = E->next()) {
1795 List<int> lst;
1796 script->get_node_list(E->get(), &lst);
1797 for (List<int>::Element *F = lst.front(); F; F = F->next()) {
1798 Ref<VisualScriptFunctionCall> fncall = script->get_node(E->get(), F->get());
1799 if (!fncall.is_valid())
1800 continue;
1801 if (fncall->get_function() == name) {
1802 undo_redo->add_do_method(fncall.ptr(), "set_function", new_name);
1803 undo_redo->add_undo_method(fncall.ptr(), "set_function", name);
1804 }
1805 }
1806 }
1807
1808 undo_redo->add_do_method(this, "_update_members");
1809 undo_redo->add_undo_method(this, "_update_members");
1810 undo_redo->add_do_method(this, "_update_graph");
1811 undo_redo->add_undo_method(this, "_update_graph");
1812 undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
1813 undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
1814 undo_redo->commit_action();
1815 }
1816
_fn_name_box_input(const Ref<InputEvent> & p_event)1817 void VisualScriptEditor::_fn_name_box_input(const Ref<InputEvent> &p_event) {
1818
1819 if (!function_name_edit->is_visible())
1820 return;
1821
1822 Ref<InputEventKey> key = p_event;
1823 if (key.is_valid() && key->is_pressed() && key->get_scancode() == KEY_ENTER) {
1824 function_name_edit->hide();
1825 _rename_function(selected, function_name_box->get_text());
1826 function_name_box->clear();
1827 }
1828 }
1829
get_drag_data_fw(const Point2 & p_point,Control * p_from)1830 Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
1831
1832 if (p_from == members) {
1833
1834 TreeItem *it = members->get_item_at_position(p_point);
1835 if (!it)
1836 return Variant();
1837
1838 String type = it->get_metadata(0);
1839
1840 if (type == String())
1841 return Variant();
1842
1843 Dictionary dd;
1844 TreeItem *root = members->get_root();
1845
1846 if (it->get_parent() == root->get_children()) {
1847
1848 dd["type"] = "visual_script_function_drag";
1849 dd["function"] = type;
1850 } else if (it->get_parent() == root->get_children()->get_next()) {
1851
1852 dd["type"] = "visual_script_variable_drag";
1853 dd["variable"] = type;
1854 } else if (it->get_parent() == root->get_children()->get_next()->get_next()) {
1855
1856 dd["type"] = "visual_script_signal_drag";
1857 dd["signal"] = type;
1858
1859 } else {
1860 return Variant();
1861 }
1862
1863 Label *label = memnew(Label);
1864 label->set_text(it->get_text(0));
1865 set_drag_preview(label);
1866 return dd;
1867 }
1868 return Variant();
1869 }
1870
can_drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from) const1871 bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
1872
1873 if (p_from == graph) {
1874
1875 Dictionary d = p_data;
1876 if (d.has("type") &&
1877 (String(d["type"]) == "visual_script_node_drag" ||
1878 String(d["type"]) == "visual_script_function_drag" ||
1879 String(d["type"]) == "visual_script_variable_drag" ||
1880 String(d["type"]) == "visual_script_signal_drag" ||
1881 String(d["type"]) == "obj_property" ||
1882 String(d["type"]) == "resource" ||
1883 String(d["type"]) == "files" ||
1884 String(d["type"]) == "nodes")) {
1885
1886 if (String(d["type"]) == "obj_property") {
1887
1888 #ifdef OSX_ENABLED
1889 const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a Getter. Hold Shift to drop a generic signature."), find_keycode_name(KEY_META)));
1890 #else
1891 const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a Getter. Hold Shift to drop a generic signature."));
1892 #endif
1893 }
1894
1895 if (String(d["type"]) == "nodes") {
1896
1897 #ifdef OSX_ENABLED
1898 const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a simple reference to the node."), find_keycode_name(KEY_META)));
1899 #else
1900 const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a simple reference to the node."));
1901 #endif
1902 }
1903
1904 if (String(d["type"]) == "visual_script_variable_drag") {
1905
1906 #ifdef OSX_ENABLED
1907 const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a Variable Setter."), find_keycode_name(KEY_META)));
1908 #else
1909 const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a Variable Setter."));
1910 #endif
1911 }
1912
1913 return true;
1914 }
1915 }
1916
1917 return false;
1918 }
1919
_find_script_node(Node * p_edited_scene,Node * p_current_node,const Ref<Script> & script)1920 static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) {
1921
1922 if (p_edited_scene != p_current_node && p_current_node->get_owner() != p_edited_scene)
1923 return NULL;
1924
1925 Ref<Script> scr = p_current_node->get_script();
1926
1927 if (scr.is_valid() && scr == script)
1928 return p_current_node;
1929
1930 for (int i = 0; i < p_current_node->get_child_count(); i++) {
1931 Node *n = _find_script_node(p_edited_scene, p_current_node->get_child(i), script);
1932 if (n)
1933 return n;
1934 }
1935
1936 return NULL;
1937 }
1938
drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from)1939 void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
1940
1941 if (p_from != graph) {
1942 return;
1943 }
1944
1945 Dictionary d = p_data;
1946
1947 if (!d.has("type")) {
1948 return;
1949 }
1950
1951 if (String(d["type"]) == "visual_script_node_drag") {
1952 if (!d.has("node_type") || String(d["node_type"]) == "Null") {
1953 return;
1954 }
1955
1956 Vector2 ofs = graph->get_scroll_ofs() + p_point;
1957
1958 if (graph->is_using_snap()) {
1959 int snap = graph->get_snap();
1960 ofs = ofs.snapped(Vector2(snap, snap));
1961 }
1962
1963 ofs /= EDSCALE;
1964
1965 int new_id = _create_new_node_from_name(d["node_type"], ofs, default_func);
1966
1967 Node *node = graph->get_node(itos(new_id));
1968 if (node) {
1969 graph->set_selected(node);
1970 _node_selected(node);
1971 }
1972 }
1973
1974 if (String(d["type"]) == "visual_script_variable_drag") {
1975
1976 #ifdef OSX_ENABLED
1977 bool use_set = Input::get_singleton()->is_key_pressed(KEY_META);
1978 #else
1979 bool use_set = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
1980 #endif
1981 Vector2 ofs = graph->get_scroll_ofs() + p_point;
1982 if (graph->is_using_snap()) {
1983 int snap = graph->get_snap();
1984 ofs = ofs.snapped(Vector2(snap, snap));
1985 }
1986
1987 ofs /= EDSCALE;
1988
1989 Ref<VisualScriptNode> vnode;
1990 if (use_set) {
1991 Ref<VisualScriptVariableSet> vnodes;
1992 vnodes.instance();
1993 vnodes->set_variable(d["variable"]);
1994 vnode = vnodes;
1995 } else {
1996
1997 Ref<VisualScriptVariableGet> vnodeg;
1998 vnodeg.instance();
1999 vnodeg->set_variable(d["variable"]);
2000 vnode = vnodeg;
2001 }
2002
2003 int new_id = script->get_available_id();
2004
2005 undo_redo->create_action(TTR("Add Node"));
2006 undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs);
2007 undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
2008 undo_redo->add_do_method(this, "_update_graph");
2009 undo_redo->add_undo_method(this, "_update_graph");
2010 undo_redo->commit_action();
2011
2012 Node *node = graph->get_node(itos(new_id));
2013 if (node) {
2014 graph->set_selected(node);
2015 _node_selected(node);
2016 }
2017 }
2018
2019 if (String(d["type"]) == "visual_script_function_drag") {
2020
2021 Vector2 ofs = graph->get_scroll_ofs() + p_point;
2022 if (graph->is_using_snap()) {
2023 int snap = graph->get_snap();
2024 ofs = ofs.snapped(Vector2(snap, snap));
2025 }
2026
2027 ofs /= EDSCALE;
2028
2029 Ref<VisualScriptFunctionCall> vnode;
2030 vnode.instance();
2031 vnode->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SELF);
2032
2033 int new_id = script->get_available_id();
2034
2035 undo_redo->create_action(TTR("Add Node"));
2036 undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs);
2037 undo_redo->add_do_method(vnode.ptr(), "set_base_type", script->get_instance_base_type());
2038 undo_redo->add_do_method(vnode.ptr(), "set_function", d["function"]);
2039
2040 undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
2041 undo_redo->add_do_method(this, "_update_graph");
2042 undo_redo->add_undo_method(this, "_update_graph");
2043 undo_redo->commit_action();
2044
2045 Node *node = graph->get_node(itos(new_id));
2046 if (node) {
2047 graph->set_selected(node);
2048 _node_selected(node);
2049 }
2050 }
2051
2052 if (String(d["type"]) == "visual_script_signal_drag") {
2053
2054 Vector2 ofs = graph->get_scroll_ofs() + p_point;
2055 if (graph->is_using_snap()) {
2056 int snap = graph->get_snap();
2057 ofs = ofs.snapped(Vector2(snap, snap));
2058 }
2059
2060 ofs /= EDSCALE;
2061
2062 Ref<VisualScriptEmitSignal> vnode;
2063 vnode.instance();
2064 vnode->set_signal(d["signal"]);
2065
2066 int new_id = script->get_available_id();
2067
2068 undo_redo->create_action(TTR("Add Node"));
2069 undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs);
2070 undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
2071 undo_redo->add_do_method(this, "_update_graph");
2072 undo_redo->add_undo_method(this, "_update_graph");
2073 undo_redo->commit_action();
2074
2075 Node *node = graph->get_node(itos(new_id));
2076 if (node) {
2077 graph->set_selected(node);
2078 _node_selected(node);
2079 }
2080 }
2081
2082 if (String(d["type"]) == "resource") {
2083
2084 Vector2 ofs = graph->get_scroll_ofs() + p_point;
2085 if (graph->is_using_snap()) {
2086 int snap = graph->get_snap();
2087 ofs = ofs.snapped(Vector2(snap, snap));
2088 }
2089
2090 ofs /= EDSCALE;
2091
2092 Ref<VisualScriptPreload> prnode;
2093 prnode.instance();
2094 prnode->set_preload(d["resource"]);
2095
2096 int new_id = script->get_available_id();
2097
2098 undo_redo->create_action(TTR("Add Preload Node"));
2099 undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, prnode, ofs);
2100 undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
2101 undo_redo->add_do_method(this, "_update_graph");
2102 undo_redo->add_undo_method(this, "_update_graph");
2103 undo_redo->commit_action();
2104
2105 Node *node = graph->get_node(itos(new_id));
2106 if (node) {
2107 graph->set_selected(node);
2108 _node_selected(node);
2109 }
2110 }
2111
2112 if (String(d["type"]) == "files") {
2113
2114 Vector2 ofs = graph->get_scroll_ofs() + p_point;
2115 if (graph->is_using_snap()) {
2116 int snap = graph->get_snap();
2117 ofs = ofs.snapped(Vector2(snap, snap));
2118 }
2119
2120 ofs /= EDSCALE;
2121
2122 Array files = d["files"];
2123
2124 List<int> new_ids;
2125 int new_id = script->get_available_id();
2126
2127 if (files.size()) {
2128 undo_redo->create_action(TTR("Add Preload Node"));
2129
2130 for (int i = 0; i < files.size(); i++) {
2131
2132 Ref<Resource> res = ResourceLoader::load(files[i]);
2133 if (!res.is_valid())
2134 continue;
2135
2136 Ref<VisualScriptPreload> prnode;
2137 prnode.instance();
2138 prnode->set_preload(res);
2139
2140 undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, prnode, ofs);
2141 undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
2142 new_ids.push_back(new_id);
2143 new_id++;
2144 ofs += Vector2(20, 20) * EDSCALE;
2145 }
2146
2147 undo_redo->add_do_method(this, "_update_graph");
2148 undo_redo->add_undo_method(this, "_update_graph");
2149 undo_redo->commit_action();
2150 }
2151
2152 for (List<int>::Element *E = new_ids.front(); E; E = E->next()) {
2153
2154 Node *node = graph->get_node(itos(E->get()));
2155 if (node) {
2156 graph->set_selected(node);
2157 _node_selected(node);
2158 }
2159 }
2160 }
2161
2162 if (String(d["type"]) == "nodes") {
2163
2164 Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script);
2165
2166 if (!sn) {
2167 EditorNode::get_singleton()->show_warning(vformat(TTR("Can't drop nodes because script '%s' is not used in this scene."), get_name()));
2168 return;
2169 }
2170
2171 #ifdef OSX_ENABLED
2172 bool use_node = Input::get_singleton()->is_key_pressed(KEY_META);
2173 #else
2174 bool use_node = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
2175 #endif
2176
2177 Array nodes = d["nodes"];
2178
2179 Vector2 ofs = graph->get_scroll_ofs() + p_point;
2180
2181 if (graph->is_using_snap()) {
2182 int snap = graph->get_snap();
2183 ofs = ofs.snapped(Vector2(snap, snap));
2184 }
2185 ofs /= EDSCALE;
2186
2187 undo_redo->create_action(TTR("Add Node(s) From Tree"));
2188 int base_id = script->get_available_id();
2189
2190 if (nodes.size() > 1) {
2191 use_node = true;
2192 }
2193
2194 for (int i = 0; i < nodes.size(); i++) {
2195
2196 NodePath np = nodes[i];
2197 Node *node = get_node(np);
2198 if (!node) {
2199 continue;
2200 }
2201
2202 Ref<VisualScriptNode> n;
2203
2204 if (use_node) {
2205 Ref<VisualScriptSceneNode> scene_node;
2206 scene_node.instance();
2207 scene_node->set_node_path(sn->get_path_to(node));
2208 n = scene_node;
2209 } else {
2210 // ! Doesn't work properly
2211 Ref<VisualScriptFunctionCall> call;
2212 call.instance();
2213 call->set_call_mode(VisualScriptFunctionCall::CALL_MODE_NODE_PATH);
2214 call->set_base_path(sn->get_path_to(node));
2215 call->set_base_type(node->get_class());
2216 n = call;
2217 method_select->select_from_instance(node, "", true, node->get_class());
2218 selecting_method_id = base_id;
2219 }
2220
2221 undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, n, ofs);
2222 undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id);
2223
2224 base_id++;
2225 ofs += Vector2(25, 25);
2226 }
2227 undo_redo->add_do_method(this, "_update_graph");
2228 undo_redo->add_undo_method(this, "_update_graph");
2229 undo_redo->commit_action();
2230 }
2231
2232 if (String(d["type"]) == "obj_property") {
2233
2234 Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script);
2235
2236 if (!sn && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
2237 EditorNode::get_singleton()->show_warning(vformat(TTR("Can't drop properties because script '%s' is not used in this scene.\nDrop holding 'Shift' to just copy the signature."), get_name()));
2238 return;
2239 }
2240
2241 Object *obj = d["object"];
2242
2243 if (!obj)
2244 return;
2245
2246 Node *node = Object::cast_to<Node>(obj);
2247 Vector2 ofs = graph->get_scroll_ofs() + p_point;
2248
2249 if (graph->is_using_snap()) {
2250 int snap = graph->get_snap();
2251 ofs = ofs.snapped(Vector2(snap, snap));
2252 }
2253
2254 ofs /= EDSCALE;
2255 #ifdef OSX_ENABLED
2256 bool use_get = Input::get_singleton()->is_key_pressed(KEY_META);
2257 #else
2258 bool use_get = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
2259 #endif
2260
2261 if (!node || Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
2262
2263 if (use_get)
2264 undo_redo->create_action(TTR("Add Getter Property"));
2265 else
2266 undo_redo->create_action(TTR("Add Setter Property"));
2267
2268 int base_id = script->get_available_id();
2269
2270 Ref<VisualScriptNode> vnode;
2271
2272 if (!use_get) {
2273
2274 Ref<VisualScriptPropertySet> pset;
2275 pset.instance();
2276 pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
2277 pset->set_base_type(obj->get_class());
2278 /*if (use_value) {
2279 pset->set_use_builtin_value(true);
2280 pset->set_builtin_value(d["value"]);
2281 }*/
2282 vnode = pset;
2283 } else {
2284
2285 Ref<VisualScriptPropertyGet> pget;
2286 pget.instance();
2287 pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
2288 pget->set_base_type(obj->get_class());
2289
2290 vnode = pget;
2291 }
2292
2293 undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, vnode, ofs);
2294 undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]);
2295 if (!use_get) {
2296 undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]);
2297 }
2298
2299 undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id);
2300
2301 undo_redo->add_do_method(this, "_update_graph");
2302 undo_redo->add_undo_method(this, "_update_graph");
2303 undo_redo->commit_action();
2304
2305 } else {
2306
2307 if (use_get)
2308 undo_redo->create_action(TTR("Add Getter Property"));
2309 else
2310 undo_redo->create_action(TTR("Add Setter Property"));
2311
2312 int base_id = script->get_available_id();
2313
2314 Ref<VisualScriptNode> vnode;
2315
2316 if (!use_get) {
2317
2318 Ref<VisualScriptPropertySet> pset;
2319 pset.instance();
2320 if (sn == node) {
2321 pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF);
2322 } else {
2323 pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH);
2324 pset->set_base_path(sn->get_path_to(node));
2325 }
2326
2327 vnode = pset;
2328 } else {
2329
2330 Ref<VisualScriptPropertyGet> pget;
2331 pget.instance();
2332 if (sn == node) {
2333 pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF);
2334 } else {
2335 pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH);
2336 pget->set_base_path(sn->get_path_to(node));
2337 }
2338 vnode = pget;
2339 }
2340 undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, vnode, ofs);
2341 undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]);
2342 if (!use_get) {
2343 undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]);
2344 }
2345 undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id);
2346
2347 undo_redo->add_do_method(this, "_update_graph");
2348 undo_redo->add_undo_method(this, "_update_graph");
2349 undo_redo->commit_action();
2350 }
2351 }
2352 }
2353
_selected_method(const String & p_method,const String & p_type,const bool p_connecting)2354 void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type, const bool p_connecting) {
2355
2356 Ref<VisualScriptFunctionCall> vsfc = script->get_node(default_func, selecting_method_id);
2357 if (!vsfc.is_valid())
2358 return;
2359 vsfc->set_function(p_method);
2360 }
2361
_draw_color_over_button(Object * obj,Color p_color)2362 void VisualScriptEditor::_draw_color_over_button(Object *obj, Color p_color) {
2363
2364 Button *button = Object::cast_to<Button>(obj);
2365 if (!button)
2366 return;
2367
2368 Ref<StyleBox> normal = get_stylebox("normal", "Button");
2369 button->draw_rect(Rect2(normal->get_offset(), button->get_size() - normal->get_minimum_size()), p_color);
2370 }
2371
_button_resource_previewed(const String & p_path,const Ref<Texture> & p_preview,const Ref<Texture> & p_small_preview,Variant p_ud)2372 void VisualScriptEditor::_button_resource_previewed(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, Variant p_ud) {
2373
2374 Array ud = p_ud;
2375 ERR_FAIL_COND(ud.size() != 2);
2376
2377 ObjectID id = ud[0];
2378 Object *obj = ObjectDB::get_instance(id);
2379
2380 if (!obj)
2381 return;
2382
2383 Button *b = Object::cast_to<Button>(obj);
2384 ERR_FAIL_COND(!b);
2385
2386 if (p_preview.is_null()) {
2387 b->set_text(ud[1]);
2388 } else {
2389
2390 b->set_icon(p_preview);
2391 }
2392 }
2393
2394 /////////////////////////
2395
apply_code()2396 void VisualScriptEditor::apply_code() {
2397 }
2398
get_edited_resource() const2399 RES VisualScriptEditor::get_edited_resource() const {
2400 return script;
2401 }
2402
set_edited_resource(const RES & p_res)2403 void VisualScriptEditor::set_edited_resource(const RES &p_res) {
2404
2405 script = p_res;
2406 signal_editor->script = script;
2407 signal_editor->undo_redo = undo_redo;
2408 variable_editor->script = script;
2409 variable_editor->undo_redo = undo_redo;
2410
2411 script->connect("node_ports_changed", this, "_node_ports_changed");
2412
2413 default_func = script->get_default_func();
2414
2415 if (!script->has_function(default_func)) // this is the supposed default function
2416 {
2417 script->add_function(default_func);
2418 script->set_edited(true); //so that if a function was added it's saved
2419 }
2420
2421 _update_graph();
2422 _update_members();
2423 }
2424
get_functions()2425 Vector<String> VisualScriptEditor::get_functions() {
2426
2427 return Vector<String>();
2428 }
2429
reload_text()2430 void VisualScriptEditor::reload_text() {
2431 }
2432
get_name()2433 String VisualScriptEditor::get_name() {
2434
2435 String name;
2436
2437 if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) {
2438 name = script->get_path().get_file();
2439 if (is_unsaved()) {
2440 name += "(*)";
2441 }
2442 } else if (script->get_name() != "")
2443 name = script->get_name();
2444 else
2445 name = script->get_class() + "(" + itos(script->get_instance_id()) + ")";
2446
2447 return name;
2448 }
2449
get_icon()2450 Ref<Texture> VisualScriptEditor::get_icon() {
2451
2452 return Control::get_icon("VisualScript", "EditorIcons");
2453 }
2454
is_unsaved()2455 bool VisualScriptEditor::is_unsaved() {
2456
2457 return script->is_edited() || script->are_subnodes_edited();
2458 }
2459
get_edit_state()2460 Variant VisualScriptEditor::get_edit_state() {
2461
2462 Dictionary d;
2463 d["function"] = default_func;
2464 d["scroll"] = graph->get_scroll_ofs();
2465 d["zoom"] = graph->get_zoom();
2466 d["using_snap"] = graph->is_using_snap();
2467 d["snap"] = graph->get_snap();
2468 return d;
2469 }
2470
set_edit_state(const Variant & p_state)2471 void VisualScriptEditor::set_edit_state(const Variant &p_state) {
2472
2473 Dictionary d = p_state;
2474 if (d.has("function")) {
2475 selected = default_func;
2476 }
2477
2478 _update_graph();
2479 _update_members();
2480
2481 if (d.has("scroll")) {
2482 graph->set_scroll_ofs(d["scroll"]);
2483 }
2484 if (d.has("zoom")) {
2485 graph->set_zoom(d["zoom"]);
2486 }
2487 if (d.has("snap")) {
2488 graph->set_snap(d["snap"]);
2489 }
2490 if (d.has("snap_enabled")) {
2491 graph->set_use_snap(d["snap_enabled"]);
2492 }
2493 }
2494
_center_on_node(const StringName & p_func,int p_id)2495 void VisualScriptEditor::_center_on_node(const StringName &p_func, int p_id) {
2496
2497 Node *n = graph->get_node(itos(p_id));
2498 GraphNode *gn = Object::cast_to<GraphNode>(n);
2499
2500 // clear selection
2501 for (int i = 0; i < graph->get_child_count(); i++) {
2502 GraphNode *gnd = Object::cast_to<GraphNode>(graph->get_child(i));
2503 if (gnd)
2504 gnd->set_selected(false);
2505 }
2506
2507 if (gn) {
2508 gn->set_selected(true);
2509 Vector2 new_scroll = gn->get_offset() - graph->get_size() * 0.5 + gn->get_size() * 0.5;
2510 graph->set_scroll_ofs(new_scroll);
2511 script->set_function_scroll(p_func, new_scroll / EDSCALE);
2512 script->set_edited(true);
2513 }
2514 }
2515
goto_line(int p_line,bool p_with_error)2516 void VisualScriptEditor::goto_line(int p_line, bool p_with_error) {
2517
2518 p_line += 1; //add one because script lines begin from 0.
2519
2520 if (p_with_error)
2521 error_line = p_line;
2522
2523 List<StringName> functions;
2524 script->get_function_list(&functions);
2525 for (List<StringName>::Element *E = functions.front(); E; E = E->next()) {
2526
2527 if (script->has_node(E->get(), p_line)) {
2528
2529 _update_graph();
2530 _update_members();
2531
2532 call_deferred("call_deferred", "_center_on_node", E->get(), p_line); //editor might be just created and size might not exist yet
2533 return;
2534 }
2535 }
2536 }
2537
set_executing_line(int p_line)2538 void VisualScriptEditor::set_executing_line(int p_line) {
2539 // todo: add a way to show which node is executing right now.
2540 }
2541
clear_executing_line()2542 void VisualScriptEditor::clear_executing_line() {
2543 // todo: add a way to show which node is executing right now.
2544 }
2545
trim_trailing_whitespace()2546 void VisualScriptEditor::trim_trailing_whitespace() {
2547 }
2548
insert_final_newline()2549 void VisualScriptEditor::insert_final_newline() {
2550 }
2551
convert_indent_to_spaces()2552 void VisualScriptEditor::convert_indent_to_spaces() {
2553 }
2554
convert_indent_to_tabs()2555 void VisualScriptEditor::convert_indent_to_tabs() {
2556 }
2557
ensure_focus()2558 void VisualScriptEditor::ensure_focus() {
2559
2560 graph->grab_focus();
2561 }
2562
tag_saved_version()2563 void VisualScriptEditor::tag_saved_version() {
2564 }
2565
reload(bool p_soft)2566 void VisualScriptEditor::reload(bool p_soft) {
2567 _update_graph();
2568 }
2569
get_breakpoints(List<int> * p_breakpoints)2570 void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) {
2571
2572 List<StringName> functions;
2573 script->get_function_list(&functions);
2574 for (List<StringName>::Element *E = functions.front(); E; E = E->next()) {
2575
2576 List<int> nodes;
2577 script->get_node_list(E->get(), &nodes);
2578 for (List<int>::Element *F = nodes.front(); F; F = F->next()) {
2579
2580 Ref<VisualScriptNode> vsn = script->get_node(E->get(), F->get());
2581 if (vsn->is_breakpoint()) {
2582 p_breakpoints->push_back(F->get() - 1); //subtract 1 because breakpoints in text start from zero
2583 }
2584 }
2585 }
2586 }
2587
add_callback(const String & p_function,PoolStringArray p_args)2588 void VisualScriptEditor::add_callback(const String &p_function, PoolStringArray p_args) {
2589
2590 if (script->has_function(p_function)) {
2591 _update_members();
2592 _update_graph();
2593 _center_on_node(p_function, script->get_function_node_id(p_function));
2594 return;
2595 }
2596
2597 Ref<VisualScriptFunction> func;
2598 func.instance();
2599 for (int i = 0; i < p_args.size(); i++) {
2600
2601 String name = p_args[i];
2602 Variant::Type type = Variant::NIL;
2603
2604 if (name.find(":") != -1) {
2605 String tt = name.get_slice(":", 1);
2606 name = name.get_slice(":", 0);
2607 for (int j = 0; j < Variant::VARIANT_MAX; j++) {
2608
2609 String tname = Variant::get_type_name(Variant::Type(j));
2610 if (tname == tt) {
2611 type = Variant::Type(j);
2612 break;
2613 }
2614 }
2615 }
2616
2617 func->add_argument(type, name);
2618 }
2619
2620 func->set_name(p_function);
2621 script->add_function(p_function);
2622 script->add_node(p_function, script->get_available_id(), func);
2623
2624 _update_members();
2625 _update_graph();
2626
2627 _center_on_node(p_function, script->get_function_node_id(p_function));
2628 }
2629
show_members_overview()2630 bool VisualScriptEditor::show_members_overview() {
2631 return false;
2632 }
2633
update_settings()2634 void VisualScriptEditor::update_settings() {
2635
2636 _update_graph();
2637 }
2638
set_debugger_active(bool p_active)2639 void VisualScriptEditor::set_debugger_active(bool p_active) {
2640 if (!p_active) {
2641 error_line = -1;
2642 _update_graph(); //clear line break
2643 }
2644 }
2645
set_tooltip_request_func(String p_method,Object * p_obj)2646 void VisualScriptEditor::set_tooltip_request_func(String p_method, Object *p_obj) {
2647 }
2648
get_edit_menu()2649 Control *VisualScriptEditor::get_edit_menu() {
2650
2651 return edit_menu;
2652 }
2653
_change_base_type()2654 void VisualScriptEditor::_change_base_type() {
2655
2656 select_base_type->popup_create(true, true);
2657 }
2658
_toggle_tool_script()2659 void VisualScriptEditor::_toggle_tool_script() {
2660 script->set_tool_enabled(!script->is_tool());
2661 }
2662
clear_edit_menu()2663 void VisualScriptEditor::clear_edit_menu() {
2664 memdelete(edit_menu);
2665 memdelete(members_section);
2666 }
2667
_change_base_type_callback()2668 void VisualScriptEditor::_change_base_type_callback() {
2669
2670 String bt = select_base_type->get_selected_type();
2671
2672 ERR_FAIL_COND(bt == String());
2673 undo_redo->create_action(TTR("Change Base Type"));
2674 undo_redo->add_do_method(script.ptr(), "set_instance_base_type", bt);
2675 undo_redo->add_undo_method(script.ptr(), "set_instance_base_type", script->get_instance_base_type());
2676 undo_redo->add_do_method(this, "_update_members");
2677 undo_redo->add_undo_method(this, "_update_members");
2678 undo_redo->commit_action();
2679 }
2680
_node_selected(Node * p_node)2681 void VisualScriptEditor::_node_selected(Node *p_node) {
2682
2683 Ref<VisualScriptNode> vnode = p_node->get_meta("__vnode");
2684 if (vnode.is_null())
2685 return;
2686
2687 EditorNode::get_singleton()->push_item(vnode.ptr()); //edit node in inspector
2688 }
2689
_get_out_slot(const Ref<VisualScriptNode> & p_node,int p_slot,int & r_real_slot,bool & r_sequence)2690 static bool _get_out_slot(const Ref<VisualScriptNode> &p_node, int p_slot, int &r_real_slot, bool &r_sequence) {
2691
2692 if (p_slot < p_node->get_output_sequence_port_count()) {
2693 r_sequence = true;
2694 r_real_slot = p_slot;
2695
2696 return true;
2697 }
2698
2699 r_real_slot = p_slot - p_node->get_output_sequence_port_count();
2700 r_sequence = false;
2701
2702 return (r_real_slot < p_node->get_output_value_port_count());
2703 }
2704
_get_in_slot(const Ref<VisualScriptNode> & p_node,int p_slot,int & r_real_slot,bool & r_sequence)2705 static bool _get_in_slot(const Ref<VisualScriptNode> &p_node, int p_slot, int &r_real_slot, bool &r_sequence) {
2706
2707 if (p_slot == 0 && p_node->has_input_sequence_port()) {
2708 r_sequence = true;
2709 r_real_slot = 0;
2710 return true;
2711 }
2712
2713 r_real_slot = p_slot - (p_node->has_input_sequence_port() ? 1 : 0);
2714 r_sequence = false;
2715
2716 return r_real_slot < p_node->get_input_value_port_count();
2717 }
2718
_begin_node_move()2719 void VisualScriptEditor::_begin_node_move() {
2720
2721 undo_redo->create_action(TTR("Move Node(s)"));
2722 }
2723
_end_node_move()2724 void VisualScriptEditor::_end_node_move() {
2725
2726 undo_redo->commit_action();
2727 }
2728
_move_node(const StringName & p_func,int p_id,const Vector2 & p_to)2729 void VisualScriptEditor::_move_node(const StringName &p_func, int p_id, const Vector2 &p_to) {
2730
2731 if (!script->has_function(p_func))
2732 return;
2733
2734 Node *node = graph->get_node(itos(p_id));
2735
2736 if (Object::cast_to<GraphNode>(node))
2737 Object::cast_to<GraphNode>(node)->set_offset(p_to);
2738
2739 script->set_node_position(p_func, p_id, p_to / EDSCALE);
2740 }
2741
_get_function_of_node(int p_id) const2742 StringName VisualScriptEditor::_get_function_of_node(int p_id) const {
2743
2744 List<StringName> funcs;
2745 script->get_function_list(&funcs);
2746 for (List<StringName>::Element *E = funcs.front(); E; E = E->next()) {
2747 if (script->has_node(E->get(), p_id)) {
2748 return E->get();
2749 }
2750 }
2751
2752 return ""; // this is passed to avoid crash and is tested against later
2753 }
2754
_node_moved(Vector2 p_from,Vector2 p_to,int p_id)2755 void VisualScriptEditor::_node_moved(Vector2 p_from, Vector2 p_to, int p_id) {
2756
2757 StringName func = _get_function_of_node(p_id);
2758
2759 undo_redo->add_do_method(this, "_move_node", func, p_id, p_to);
2760 undo_redo->add_undo_method(this, "_move_node", func, p_id, p_from);
2761 }
2762
_remove_node(int p_id)2763 void VisualScriptEditor::_remove_node(int p_id) {
2764
2765 undo_redo->create_action(TTR("Remove VisualScript Node"));
2766
2767 StringName func = _get_function_of_node(p_id);
2768
2769 undo_redo->add_do_method(script.ptr(), "remove_node", func, p_id);
2770 undo_redo->add_undo_method(script.ptr(), "add_node", func, p_id, script->get_node(func, p_id), script->get_node_position(func, p_id));
2771
2772 List<VisualScript::SequenceConnection> sequence_conns;
2773 script->get_sequence_connection_list(func, &sequence_conns);
2774
2775 for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) {
2776
2777 if (E->get().from_node == p_id || E->get().to_node == p_id) {
2778 undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, E->get().from_node, E->get().from_output, E->get().to_node);
2779 }
2780 }
2781
2782 List<VisualScript::DataConnection> data_conns;
2783 script->get_data_connection_list(func, &data_conns);
2784
2785 for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) {
2786
2787 if (E->get().from_node == p_id || E->get().to_node == p_id) {
2788 undo_redo->add_undo_method(script.ptr(), "data_connect", func, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
2789 }
2790 }
2791
2792 undo_redo->add_do_method(this, "_update_graph");
2793 undo_redo->add_undo_method(this, "_update_graph");
2794
2795 undo_redo->commit_action();
2796 }
2797
_node_ports_changed(const String & p_func,int p_id)2798 void VisualScriptEditor::_node_ports_changed(const String &p_func, int p_id) {
2799
2800 _update_graph(p_id);
2801 }
2802
node_has_sequence_connections(const StringName & p_func,int p_id)2803 bool VisualScriptEditor::node_has_sequence_connections(const StringName &p_func, int p_id) {
2804 List<VisualScript::SequenceConnection> sequence_conns;
2805 script->get_sequence_connection_list(p_func, &sequence_conns);
2806
2807 for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) {
2808 int from = E->get().from_node;
2809 int to = E->get().to_node;
2810
2811 if (to == p_id || from == p_id)
2812 return true;
2813 }
2814
2815 return false;
2816 }
2817
_graph_connected(const String & p_from,int p_from_slot,const String & p_to,int p_to_slot)2818 void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot) {
2819
2820 StringName from_func = _get_function_of_node(p_from.to_int());
2821
2822 Ref<VisualScriptNode> from_node = script->get_node(from_func, p_from.to_int());
2823 ERR_FAIL_COND(!from_node.is_valid());
2824
2825 bool from_seq;
2826 int from_port;
2827
2828 if (!_get_out_slot(from_node, p_from_slot, from_port, from_seq))
2829 return; //can't connect this, it's invalid
2830
2831 StringName to_func = _get_function_of_node(p_to.to_int());
2832
2833 Ref<VisualScriptNode> to_node = script->get_node(to_func, p_to.to_int());
2834 ERR_FAIL_COND(!to_node.is_valid());
2835
2836 bool to_seq;
2837 int to_port;
2838
2839 if (!_get_in_slot(to_node, p_to_slot, to_port, to_seq))
2840 return; //can't connect this, it's invalid
2841
2842 ERR_FAIL_COND(from_seq != to_seq);
2843
2844 // Do all the checks here
2845 StringName func; // this the func where we store the one the nodes at the end of the resolution on having multiple nodes
2846
2847 undo_redo->create_action(TTR("Connect Nodes"));
2848
2849 if (from_func == to_func) {
2850 func = to_func;
2851 } else if (from_seq) {
2852 // this is a sequence connection
2853 _move_nodes_with_rescan(to_func, from_func, p_to.to_int()); // this function moves the nodes from func1 to func2
2854 func = from_func;
2855 } else {
2856 if (node_has_sequence_connections(to_func, p_to.to_int())) {
2857 if (node_has_sequence_connections(from_func, p_from.to_int())) {
2858 ERR_PRINT("Trying to connect between different sequence node trees");
2859 return;
2860 } else {
2861 _move_nodes_with_rescan(from_func, to_func, p_from.to_int());
2862 func = to_func;
2863 }
2864 } else if (node_has_sequence_connections(from_func, p_from.to_int())) {
2865 if (from_func == default_func) {
2866 _move_nodes_with_rescan(from_func, to_func, p_from.to_int());
2867 func = to_func;
2868 } else {
2869 _move_nodes_with_rescan(to_func, from_func, p_to.to_int());
2870 func = from_func;
2871 }
2872 } else {
2873 if (to_func == default_func) {
2874 _move_nodes_with_rescan(to_func, from_func, p_to.to_int());
2875 func = from_func;
2876 } else {
2877 _move_nodes_with_rescan(from_func, to_func, p_from.to_int());
2878 func = to_func;
2879 }
2880 }
2881 }
2882
2883 if (from_seq) {
2884 undo_redo->add_do_method(script.ptr(), "sequence_connect", func, p_from.to_int(), from_port, p_to.to_int());
2885 // this undo error on undo after move can't be removed without painful gymnastics
2886 undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, p_from.to_int(), from_port, p_to.to_int());
2887 } else {
2888 bool converted = false;
2889 int conv_node = -1;
2890
2891 Ref<VisualScriptOperator> oper = to_node;
2892 if (oper.is_valid() && oper->get_typed() == Variant::NIL) {
2893 // it's an operator Node and if the type is already nil
2894 if (from_node->get_output_value_port_info(from_port).type != Variant::NIL) {
2895 oper->set_typed(from_node->get_output_value_port_info(from_port).type);
2896 }
2897 }
2898
2899 Ref<VisualScriptOperator> operf = from_node;
2900 if (operf.is_valid() && operf->get_typed() == Variant::NIL) {
2901 // it's an operator Node and if the type is already nil
2902 if (to_node->get_input_value_port_info(to_port).type != Variant::NIL) {
2903 operf->set_typed(to_node->get_input_value_port_info(to_port).type);
2904 }
2905 }
2906
2907 Variant::Type to_type = to_node->get_input_value_port_info(to_port).type;
2908 Variant::Type from_type = from_node->get_output_value_port_info(from_port).type;
2909
2910 if (to_type != Variant::NIL && from_type != Variant::NIL && to_type != from_type) {
2911 // add a constructor node between the ports
2912 bool exceptions = false; // true if there are any exceptions
2913 exceptions = exceptions || (to_type == Variant::INT && from_type == Variant::REAL);
2914 exceptions = exceptions || (to_type == Variant::REAL && from_type == Variant::INT);
2915 if (Variant::can_convert(from_type, to_type) && !exceptions) {
2916 MethodInfo mi;
2917 mi.name = Variant::get_type_name(to_type);
2918 PropertyInfo pi;
2919 pi.name = "from";
2920 pi.type = from_type;
2921 mi.arguments.push_back(pi);
2922 mi.return_val.type = to_type;
2923 // we know that this is allowed so create a new constructor node
2924 Ref<VisualScriptConstructor> constructor;
2925 constructor.instance();
2926 constructor->set_constructor_type(to_type);
2927 constructor->set_constructor(mi);
2928 // add the new constructor node
2929
2930 GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(p_from));
2931 GraphNode *gn2 = Object::cast_to<GraphNode>(graph->get_node(p_to));
2932 if (gn && gn2) {
2933 Vector2 from_node_size = gn->get_rect().get_size();
2934 Vector2 to_node_size = gn2->get_rect().get_size();
2935 Vector2 to_node_pos = script->get_node_position(func, p_to.to_int());
2936 Vector2 from_node_pos = script->get_node_position(func, p_from.to_int());
2937 Vector2 new_to_node_pos = from_node_pos;
2938 Vector2 constructor_pos;
2939 if ((to_node_pos.x - from_node_pos.x) < 0) {
2940 // to is behind from node
2941 if (to_node_pos.x > (from_node_pos.x - to_node_size.x - 240))
2942 new_to_node_pos.x = from_node_pos.x - to_node_size.x - 240; // approx size of constructor node + padding
2943 else
2944 new_to_node_pos.x = to_node_pos.x;
2945 new_to_node_pos.y = to_node_pos.y;
2946 constructor_pos.x = from_node_pos.x - 210;
2947 constructor_pos.y = to_node_pos.y;
2948 } else {
2949 // to is ahead of from node
2950 if (to_node_pos.x < (from_node_size.x + from_node_pos.x + 240))
2951 new_to_node_pos.x = from_node_size.x + from_node_pos.x + 240; // approx size of constructor node + padding
2952 else
2953 new_to_node_pos.x = to_node_pos.x;
2954 new_to_node_pos.y = to_node_pos.y;
2955 constructor_pos.x = from_node_size.x + from_node_pos.x + 10;
2956 constructor_pos.y = to_node_pos.y;
2957 }
2958 undo_redo->add_do_method(this, "_move_node", func, p_to.to_int(), new_to_node_pos);
2959 undo_redo->add_undo_method(this, "_move_node", func, p_to.to_int(), to_node_pos);
2960 conv_node = script->get_available_id();
2961 undo_redo->add_do_method(script.ptr(), "add_node", func, conv_node, constructor, _get_available_pos(false, constructor_pos));
2962 undo_redo->add_undo_method(script.ptr(), "remove_node", func, conv_node);
2963 converted = true;
2964 }
2965 }
2966 }
2967
2968 // disconnect current, and connect the new one
2969 if (script->is_input_value_port_connected(func, p_to.to_int(), to_port)) {
2970 if (can_swap && data_disconnect_node == p_to.to_int()) {
2971 int conn_from;
2972 int conn_port;
2973 script->get_input_value_port_connection_source(func, p_to.to_int(), to_port, &conn_from, &conn_port);
2974 undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_to.to_int(), to_port);
2975 undo_redo->add_do_method(script.ptr(), "data_connect", func, conn_from, conn_port, data_disconnect_node, data_disconnect_port);
2976 undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, data_disconnect_node, data_disconnect_port);
2977 undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_to.to_int(), to_port);
2978 can_swap = false; // swapped
2979 } else {
2980 int conn_from;
2981 int conn_port;
2982 script->get_input_value_port_connection_source(func, p_to.to_int(), to_port, &conn_from, &conn_port);
2983 undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_to.to_int(), to_port);
2984 undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_to.to_int(), to_port);
2985 }
2986 }
2987 if (!converted) {
2988 undo_redo->add_do_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, p_to.to_int(), to_port);
2989 undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, p_to.to_int(), to_port);
2990 } else {
2991 // this is noice
2992 undo_redo->add_do_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, conv_node, 0);
2993 undo_redo->add_do_method(script.ptr(), "data_connect", func, conv_node, 0, p_to.to_int(), to_port);
2994 // I don't think this is needed but gonna leave it here for now... until I need to finalise it all
2995 undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, conv_node, 0);
2996 undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, conv_node, 0, p_to.to_int(), to_port);
2997 }
2998 //update nodes in graph
2999 if (!converted) {
3000 undo_redo->add_do_method(this, "_update_graph", p_from.to_int());
3001 undo_redo->add_do_method(this, "_update_graph", p_to.to_int());
3002 undo_redo->add_undo_method(this, "_update_graph", p_from.to_int());
3003 undo_redo->add_undo_method(this, "_update_graph", p_to.to_int());
3004 } else {
3005 undo_redo->add_do_method(this, "_update_graph");
3006 undo_redo->add_undo_method(this, "_update_graph");
3007 }
3008 }
3009
3010 undo_redo->add_do_method(this, "_update_graph_connections");
3011 undo_redo->add_undo_method(this, "_update_graph_connections");
3012
3013 undo_redo->commit_action();
3014 }
3015
_graph_disconnected(const String & p_from,int p_from_slot,const String & p_to,int p_to_slot)3016 void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot) {
3017
3018 StringName func = _get_function_of_node(p_from.to_int());
3019 ERR_FAIL_COND(func != _get_function_of_node(p_to.to_int()));
3020
3021 Ref<VisualScriptNode> from_node = script->get_node(func, p_from.to_int());
3022 ERR_FAIL_COND(!from_node.is_valid());
3023
3024 bool from_seq;
3025 int from_port;
3026
3027 if (!_get_out_slot(from_node, p_from_slot, from_port, from_seq))
3028 return; //can't connect this, it's invalid
3029
3030 Ref<VisualScriptNode> to_node = script->get_node(func, p_to.to_int());
3031 ERR_FAIL_COND(!to_node.is_valid());
3032
3033 bool to_seq;
3034 int to_port;
3035
3036 if (!_get_in_slot(to_node, p_to_slot, to_port, to_seq))
3037 return; //can't connect this, it's invalid
3038
3039 ERR_FAIL_COND(from_seq != to_seq);
3040
3041 undo_redo->create_action(TTR("Disconnect Nodes"));
3042
3043 if (from_seq) {
3044 undo_redo->add_do_method(script.ptr(), "sequence_disconnect", func, p_from.to_int(), from_port, p_to.to_int());
3045 undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, p_from.to_int(), from_port, p_to.to_int());
3046 } else {
3047
3048 can_swap = true;
3049 data_disconnect_node = p_to.to_int();
3050 data_disconnect_port = to_port;
3051
3052 undo_redo->add_do_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, p_to.to_int(), to_port);
3053 undo_redo->add_undo_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, p_to.to_int(), to_port);
3054 //update relevant nodes in the graph
3055 undo_redo->add_do_method(this, "_update_graph", p_from.to_int());
3056 undo_redo->add_do_method(this, "_update_graph", p_to.to_int());
3057 undo_redo->add_undo_method(this, "_update_graph", p_from.to_int());
3058 undo_redo->add_undo_method(this, "_update_graph", p_to.to_int());
3059 }
3060 undo_redo->add_do_method(this, "_update_graph_connections");
3061 undo_redo->add_undo_method(this, "_update_graph_connections");
3062
3063 undo_redo->commit_action();
3064 }
3065
_move_nodes_with_rescan(const StringName & p_func_from,const StringName & p_func_to,int p_id)3066 void VisualScriptEditor::_move_nodes_with_rescan(const StringName &p_func_from, const StringName &p_func_to, int p_id) {
3067
3068 Set<int> nodes_to_move;
3069 HashMap<int, Map<int, int> > seqconns_to_move; // from => List(outp, to)
3070 HashMap<int, Map<int, Pair<int, int> > > dataconns_to_move; // to => List(inp_p => from, outp)
3071
3072 nodes_to_move.insert(p_id);
3073 Set<int> sequence_connections;
3074 {
3075 List<VisualScript::SequenceConnection> sequence_conns;
3076 script->get_sequence_connection_list(p_func_from, &sequence_conns);
3077
3078 HashMap<int, Map<int, int> > seqcons; // from => List(out_p => to)
3079
3080 for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) {
3081 int from = E->get().from_node;
3082 int to = E->get().to_node;
3083 int out_p = E->get().from_output;
3084 if (!seqcons.has(from))
3085 seqcons.set(from, Map<int, int>());
3086 seqcons[from].insert(out_p, to);
3087 sequence_connections.insert(to);
3088 sequence_connections.insert(from);
3089 }
3090
3091 int conn = p_id;
3092 List<int> stack;
3093 HashMap<int, Set<int> > seen; // from, outp
3094 while (seqcons.has(conn)) {
3095 for (auto E = seqcons[conn].front(); E; E = E->next()) {
3096 if (seen.has(conn) && seen[conn].has(E->key())) {
3097 if (!E->next()) {
3098 if (stack.size() > 0) {
3099 conn = stack.back()->get();
3100 stack.pop_back();
3101 break;
3102 }
3103 conn = -101;
3104 break;
3105 }
3106 continue;
3107 }
3108 if (!seen.has(conn))
3109 seen.set(conn, Set<int>());
3110 seen[conn].insert(E->key());
3111 stack.push_back(conn);
3112 if (!seqconns_to_move.has(conn))
3113 seqconns_to_move.set(conn, Map<int, int>());
3114 seqconns_to_move[conn].insert(E->key(), E->get());
3115 conn = E->get();
3116 nodes_to_move.insert(conn);
3117 break;
3118 }
3119 if (!seqcons.has(conn) && stack.size() > 0) {
3120 conn = stack.back()->get();
3121 stack.pop_back();
3122 }
3123 }
3124 }
3125
3126 {
3127 List<VisualScript::DataConnection> data_connections;
3128 script->get_data_connection_list(p_func_from, &data_connections);
3129 int func_from_node_id = script->get_function_node_id(p_func_from);
3130
3131 HashMap<int, Map<int, Pair<int, int> > > connections;
3132
3133 for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
3134 int from = E->get().from_node;
3135 int to = E->get().to_node;
3136 int out_p = E->get().from_port;
3137 int in_p = E->get().to_port;
3138
3139 // skip if the from_node is a function node
3140 if (from == func_from_node_id) {
3141 continue;
3142 }
3143
3144 if (!connections.has(to))
3145 connections.set(to, Map<int, Pair<int, int> >());
3146 connections[to].insert(in_p, Pair<int, int>(from, out_p));
3147 }
3148
3149 // go through the HashMap and do all sorts of crazy ass stuff now...
3150 Set<int> nodes_to_be_added;
3151 for (Set<int>::Element *F = nodes_to_move.front(); F; F = F->next()) {
3152 HashMap<int, Set<int> > seen;
3153 List<int> stack;
3154 int id = F->get();
3155 while (connections.has(id)) {
3156 for (auto E = connections[id].front(); E; E = E->next()) {
3157 if (seen.has(id) && seen[id].has(E->key())) {
3158 if (!E->next()) {
3159 if (stack.size() > 0) {
3160 id = stack.back()->get();
3161 stack.pop_back();
3162 break;
3163 }
3164 id = -11; // I assume ids can't be negative should confirm it...
3165 break;
3166 }
3167 continue;
3168 }
3169
3170 if (sequence_connections.has(E->get().first)) {
3171 if (!nodes_to_move.has(E->get().first)) {
3172 if (stack.size() > 0) {
3173 id = stack.back()->get();
3174 stack.pop_back();
3175 break;
3176 }
3177 id = -11; // I assume ids can't be negative should confirm it...
3178 break;
3179 }
3180 }
3181
3182 if (!seen.has(id))
3183 seen.set(id, Set<int>());
3184 seen[id].insert(E->key());
3185 stack.push_back(id);
3186 if (!dataconns_to_move.has(id))
3187 dataconns_to_move.set(id, Map<int, Pair<int, int> >());
3188 dataconns_to_move[id].insert(E->key(), Pair<int, int>(E->get().first, E->get().second));
3189 id = E->get().first;
3190 nodes_to_be_added.insert(id);
3191 break;
3192 }
3193 if (!connections.has(id) && stack.size() > 0) {
3194 id = stack.back()->get();
3195 stack.pop_back();
3196 }
3197 }
3198 }
3199 for (Set<int>::Element *E = nodes_to_be_added.front(); E; E = E->next()) {
3200 nodes_to_move.insert(E->get());
3201 }
3202 }
3203
3204 // * this is primarily for the sake of the having proper undo
3205 List<VisualScript::SequenceConnection> seqext;
3206 List<VisualScript::DataConnection> dataext;
3207
3208 List<VisualScript::SequenceConnection> seq_connections;
3209 script->get_sequence_connection_list(p_func_from, &seq_connections);
3210
3211 for (List<VisualScript::SequenceConnection>::Element *E = seq_connections.front(); E; E = E->next()) {
3212 if (!nodes_to_move.has(E->get().from_node) && nodes_to_move.has(E->get().to_node)) {
3213 seqext.push_back(E->get());
3214 } else if (nodes_to_move.has(E->get().from_node) && !nodes_to_move.has(E->get().to_node)) {
3215 seqext.push_back(E->get());
3216 }
3217 }
3218
3219 List<VisualScript::DataConnection> data_connections;
3220 script->get_data_connection_list(p_func_from, &data_connections);
3221
3222 for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
3223 if (!nodes_to_move.has(E->get().from_node) && nodes_to_move.has(E->get().to_node)) {
3224 dataext.push_back(E->get());
3225 } else if (nodes_to_move.has(E->get().from_node) && !nodes_to_move.has(E->get().to_node)) {
3226 dataext.push_back(E->get());
3227 }
3228 }
3229
3230 // undo_redo->create_action("Rescan Functions");
3231
3232 for (Set<int>::Element *E = nodes_to_move.front(); E; E = E->next()) {
3233 int id = E->get();
3234
3235 undo_redo->add_do_method(script.ptr(), "remove_node", p_func_from, id);
3236 undo_redo->add_do_method(script.ptr(), "add_node", p_func_to, id, script->get_node(p_func_from, id), script->get_node_position(p_func_from, id));
3237
3238 undo_redo->add_undo_method(script.ptr(), "remove_node", p_func_to, id);
3239 undo_redo->add_undo_method(script.ptr(), "add_node", p_func_from, id, script->get_node(p_func_from, id), script->get_node_position(p_func_from, id));
3240 }
3241
3242 List<int> skeys;
3243 seqconns_to_move.get_key_list(&skeys);
3244 for (List<int>::Element *E = skeys.front(); E; E = E->next()) {
3245 int from_node = E->get();
3246 for (Map<int, int>::Element *F = seqconns_to_move[from_node].front(); F; F = F->next()) {
3247 int from_port = F->key();
3248 int to_node = F->get();
3249 undo_redo->add_do_method(script.ptr(), "sequence_connect", p_func_to, from_node, from_port, to_node);
3250 undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_func_from, from_node, from_port, to_node);
3251 }
3252 }
3253
3254 List<int> keys;
3255 dataconns_to_move.get_key_list(&keys);
3256 for (List<int>::Element *E = keys.front(); E; E = E->next()) {
3257 int to_node = E->get(); // to_node
3258 for (Map<int, Pair<int, int> >::Element *F = dataconns_to_move[E->get()].front(); F; F = F->next()) {
3259 int inp_p = F->key();
3260 Pair<int, int> fro = F->get();
3261
3262 undo_redo->add_do_method(script.ptr(), "data_connect", p_func_to, fro.first, fro.second, to_node, inp_p);
3263 undo_redo->add_undo_method(script.ptr(), "data_connect", p_func_from, fro.first, fro.second, to_node, inp_p);
3264 }
3265 }
3266
3267 // this to have proper undo operations
3268 for (List<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) {
3269 undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_func_from, E->get().from_node, E->get().from_output, E->get().to_node);
3270 }
3271 for (List<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) {
3272 undo_redo->add_undo_method(script.ptr(), "data_connect", p_func_from, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
3273 }
3274 // this doesn't need do methods as they are handled by the subsequent do calls implicitly
3275
3276 undo_redo->add_do_method(this, "_update_graph");
3277 undo_redo->add_undo_method(this, "_update_graph");
3278
3279 // undo_redo->commit_action();
3280 }
3281
_graph_connect_to_empty(const String & p_from,int p_from_slot,const Vector2 & p_release_pos)3282 void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_pos) {
3283
3284 Node *node = graph->get_node(p_from);
3285 GraphNode *gn = Object::cast_to<GraphNode>(node);
3286 if (!gn)
3287 return;
3288
3289 StringName func = _get_function_of_node(p_from.to_int());
3290
3291 Ref<VisualScriptNode> vsn = script->get_node(func, p_from.to_int());
3292 if (!vsn.is_valid())
3293 return;
3294
3295 port_action_pos = p_release_pos;
3296
3297 if (p_from_slot < vsn->get_output_sequence_port_count()) {
3298
3299 port_action_node = p_from.to_int();
3300 port_action_output = p_from_slot;
3301 _port_action_menu(CREATE_ACTION, func);
3302 } else {
3303 port_action_output = p_from_slot - vsn->get_output_sequence_port_count();
3304 port_action_node = p_from.to_int();
3305 _port_action_menu(CREATE_CALL_SET_GET, func);
3306 }
3307 }
3308
_guess_output_type(int p_port_action_node,int p_port_action_output,Set<int> & visited_nodes)3309 VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &visited_nodes) {
3310
3311 VisualScriptNode::TypeGuess tg;
3312 tg.type = Variant::NIL;
3313
3314 if (visited_nodes.has(p_port_action_node))
3315 return tg; //no loop
3316
3317 visited_nodes.insert(p_port_action_node);
3318
3319 StringName func = _get_function_of_node(p_port_action_node);
3320
3321 Ref<VisualScriptNode> node = script->get_node(func, p_port_action_node);
3322
3323 if (!node.is_valid()) {
3324
3325 return tg;
3326 }
3327
3328 Vector<VisualScriptNode::TypeGuess> in_guesses;
3329
3330 for (int i = 0; i < node->get_input_value_port_count(); i++) {
3331 PropertyInfo pi = node->get_input_value_port_info(i);
3332 VisualScriptNode::TypeGuess g;
3333 g.type = pi.type;
3334
3335 if (g.type == Variant::NIL || g.type == Variant::OBJECT) {
3336 //any or object input, must further guess what this is
3337 int from_node;
3338 int from_port;
3339
3340 if (script->get_input_value_port_connection_source(func, p_port_action_node, i, &from_node, &from_port)) {
3341
3342 g = _guess_output_type(from_node, from_port, visited_nodes);
3343 } else {
3344 Variant defval = node->get_default_input_value(i);
3345 if (defval.get_type() == Variant::OBJECT) {
3346
3347 Object *obj = defval;
3348
3349 if (obj) {
3350
3351 g.type = Variant::OBJECT;
3352 g.gdclass = obj->get_class();
3353 g.script = obj->get_script();
3354 }
3355 }
3356 }
3357 }
3358
3359 in_guesses.push_back(g);
3360 }
3361
3362 return node->guess_output_type(in_guesses.ptrw(), p_port_action_output);
3363 }
3364
_port_action_menu(int p_option,const StringName & func)3365 void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func) {
3366
3367 Vector2 ofs = graph->get_scroll_ofs() + port_action_pos;
3368 if (graph->is_using_snap()) {
3369 int snap = graph->get_snap();
3370 ofs = ofs.snapped(Vector2(snap, snap));
3371 }
3372 ofs /= EDSCALE;
3373
3374 Set<int> vn;
3375
3376 switch (p_option) {
3377
3378 case CREATE_CALL_SET_GET: {
3379 Ref<VisualScriptFunctionCall> n;
3380 n.instance();
3381
3382 VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
3383
3384 if (tg.gdclass != StringName()) {
3385 n->set_base_type(tg.gdclass);
3386 } else {
3387 n->set_base_type("Object");
3388 }
3389 String type_string;
3390 if (script->get_node(func, port_action_node)->get_output_value_port_count() > 0) {
3391 type_string = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
3392 }
3393 if (tg.type == Variant::OBJECT) {
3394 if (tg.script.is_valid()) {
3395 new_connect_node_select->select_from_script(tg.script, "");
3396 } else if (type_string != String()) {
3397 new_connect_node_select->select_from_base_type(type_string);
3398 } else {
3399 new_connect_node_select->select_from_base_type(n->get_base_type());
3400 }
3401 } else if (tg.type == Variant::NIL) {
3402 new_connect_node_select->select_from_base_type("");
3403 } else {
3404 new_connect_node_select->select_from_basic_type(tg.type);
3405 }
3406 // ensure that the dialog fits inside the graph
3407 Vector2 pos = mouse_up_position;
3408 Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size();
3409 pos.x = pos.x > bounds.x ? bounds.x : pos.x;
3410 pos.y = pos.y > bounds.y ? bounds.y : pos.y;
3411 new_connect_node_select->set_position(pos);
3412 } break;
3413 case CREATE_ACTION: {
3414 VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
3415 PropertyInfo property_info;
3416 if (script->get_node(func, port_action_node)->get_output_value_port_count() > 0) {
3417 property_info = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output);
3418 }
3419 if (tg.type == Variant::OBJECT) {
3420 if (property_info.type == Variant::OBJECT && property_info.hint_string != String()) {
3421 new_connect_node_select->select_from_action(property_info.hint_string);
3422 } else {
3423 new_connect_node_select->select_from_action("");
3424 }
3425 } else if (tg.type == Variant::NIL) {
3426 new_connect_node_select->select_from_action("");
3427 } else {
3428 new_connect_node_select->select_from_action(Variant::get_type_name(tg.type));
3429 }
3430 // ensure that the dialog fits inside the graph
3431 Vector2 pos = mouse_up_position;
3432 Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size();
3433 pos.x = pos.x > bounds.x ? bounds.x : pos.x;
3434 pos.y = pos.y > bounds.y ? bounds.y : pos.y;
3435 new_connect_node_select->set_position(pos);
3436 } break;
3437 }
3438 }
3439
connect_data(Ref<VisualScriptNode> vnode_old,Ref<VisualScriptNode> vnode,int new_id)3440 void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id) {
3441
3442 undo_redo->create_action(TTR("Connect Node Data"));
3443 VisualScriptReturn *vnode_return = Object::cast_to<VisualScriptReturn>(vnode.ptr());
3444 if (vnode_return != NULL && vnode_old->get_output_value_port_count() > 0) {
3445 vnode_return->set_enable_return_value(true);
3446 }
3447 if (vnode_old->get_output_value_port_count() <= 0) {
3448 undo_redo->commit_action();
3449 return;
3450 }
3451 if (vnode->get_input_value_port_count() <= 0) {
3452 undo_redo->commit_action();
3453 return;
3454 }
3455 int port = port_action_output;
3456 int value_count = vnode_old->get_output_value_port_count();
3457 if (port >= value_count) {
3458 port = 0;
3459 }
3460 StringName func = _get_function_of_node(port_action_node);
3461 undo_redo->add_do_method(script.ptr(), "data_connect", func, port_action_node, port, new_id, 0);
3462 undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, port_action_node, port, new_id, 0);
3463 undo_redo->commit_action();
3464 }
3465
_selected_connect_node(const String & p_text,const String & p_category,const bool p_connecting)3466 void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting) {
3467
3468 Vector2 ofs = graph->get_scroll_ofs() + port_action_pos;
3469 if (graph->is_using_snap()) {
3470 int snap = graph->get_snap();
3471 ofs = ofs.snapped(Vector2(snap, snap));
3472 }
3473 ofs /= EDSCALE;
3474 ofs /= graph->get_zoom();
3475
3476 Set<int> vn;
3477
3478 bool port_node_exists = true;
3479
3480 StringName func = _get_function_of_node(port_action_node);
3481 if (func == StringName()) {
3482 func = default_func;
3483 port_node_exists = false;
3484 }
3485
3486 if (p_category == "visualscript") {
3487 Ref<VisualScriptNode> vnode_new = VisualScriptLanguage::singleton->create_node_from_name(p_text);
3488 Ref<VisualScriptNode> vnode_old;
3489 if (port_node_exists)
3490 vnode_old = script->get_node(func, port_action_node);
3491 int new_id = script->get_available_id();
3492
3493 if (Object::cast_to<VisualScriptOperator>(vnode_new.ptr()) && vnode_old.is_valid()) {
3494 Variant::Type type = vnode_old->get_output_value_port_info(port_action_output).type;
3495 Object::cast_to<VisualScriptOperator>(vnode_new.ptr())->set_typed(type);
3496 }
3497
3498 if (Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr()) && vnode_old.is_valid()) {
3499 Variant::Type type = vnode_old->get_output_value_port_info(port_action_output).type;
3500 String hint_name = vnode_old->get_output_value_port_info(port_action_output).hint_string;
3501
3502 if (type == Variant::OBJECT) {
3503 Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(hint_name);
3504 } else if (type == Variant::NIL) {
3505 Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type("");
3506 } else {
3507 Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(Variant::get_type_name(type));
3508 }
3509 }
3510
3511 undo_redo->create_action(TTR("Add Node"));
3512 undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode_new, ofs);
3513 if (vnode_old.is_valid() && p_connecting) {
3514 connect_seq(vnode_old, vnode_new, new_id);
3515 connect_data(vnode_old, vnode_new, new_id);
3516 }
3517
3518 undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id);
3519 undo_redo->add_do_method(this, "_update_graph");
3520 undo_redo->add_undo_method(this, "_update_graph");
3521 undo_redo->commit_action();
3522 return;
3523 }
3524
3525 Ref<VisualScriptNode> vnode;
3526 Ref<VisualScriptPropertySet> script_prop_set;
3527
3528 if (p_category == String("method")) {
3529
3530 Ref<VisualScriptFunctionCall> n;
3531 n.instance();
3532 vnode = n;
3533 } else if (p_category == String("set")) {
3534
3535 Ref<VisualScriptPropertySet> n;
3536 n.instance();
3537 vnode = n;
3538 script_prop_set = n;
3539 } else if (p_category == String("get")) {
3540
3541 Ref<VisualScriptPropertyGet> n;
3542 n.instance();
3543 n->set_property(p_text);
3544 vnode = n;
3545 }
3546
3547 if (p_category == String("action")) {
3548 if (p_text == "VisualScriptCondition") {
3549
3550 Ref<VisualScriptCondition> n;
3551 n.instance();
3552 vnode = n;
3553 }
3554 if (p_text == "VisualScriptSwitch") {
3555
3556 Ref<VisualScriptSwitch> n;
3557 n.instance();
3558 vnode = n;
3559 } else if (p_text == "VisualScriptSequence") {
3560
3561 Ref<VisualScriptSequence> n;
3562 n.instance();
3563 vnode = n;
3564 } else if (p_text == "VisualScriptIterator") {
3565
3566 Ref<VisualScriptIterator> n;
3567 n.instance();
3568 vnode = n;
3569 } else if (p_text == "VisualScriptWhile") {
3570
3571 Ref<VisualScriptWhile> n;
3572 n.instance();
3573 vnode = n;
3574 } else if (p_text == "VisualScriptReturn") {
3575
3576 Ref<VisualScriptReturn> n;
3577 n.instance();
3578 vnode = n;
3579 }
3580 }
3581
3582 int new_id = script->get_available_id();
3583 undo_redo->create_action(TTR("Add Node"));
3584 undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode, ofs);
3585 undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id);
3586 undo_redo->add_do_method(this, "_update_graph", new_id);
3587 undo_redo->add_undo_method(this, "_update_graph", new_id);
3588 undo_redo->commit_action();
3589
3590 if (script_prop_set.is_valid())
3591 script_prop_set->set_property(p_text);
3592
3593 port_action_new_node = new_id;
3594
3595 Ref<VisualScriptNode> vsn = script->get_node(func, port_action_new_node);
3596
3597 if (Object::cast_to<VisualScriptFunctionCall>(vsn.ptr())) {
3598
3599 Ref<VisualScriptFunctionCall> vsfc = vsn;
3600 vsfc->set_function(p_text);
3601
3602 if (port_node_exists && p_connecting) {
3603 VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
3604
3605 if (tg.type == Variant::OBJECT) {
3606 vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
3607 vsfc->set_base_type(String(""));
3608 if (tg.gdclass != StringName()) {
3609 vsfc->set_base_type(tg.gdclass);
3610
3611 } else if (script->get_node(func, port_action_node).is_valid()) {
3612 PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint;
3613 String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
3614
3615 if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
3616 vsfc->set_base_type(base_type);
3617 }
3618 if (p_text == "call" || p_text == "call_deferred") {
3619 vsfc->set_function(String(""));
3620 }
3621 }
3622 if (tg.script.is_valid()) {
3623 vsfc->set_base_script(tg.script->get_path());
3624 }
3625 } else if (tg.type == Variant::NIL) {
3626 vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
3627 vsfc->set_base_type(String(""));
3628 } else {
3629 vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE);
3630 vsfc->set_basic_type(tg.type);
3631 }
3632 }
3633 }
3634
3635 if (port_node_exists && p_connecting) {
3636 if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) {
3637 Ref<VisualScriptPropertySet> vsp = vsn;
3638
3639 VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
3640 if (tg.type == Variant::OBJECT) {
3641 vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
3642 vsp->set_base_type(String(""));
3643 if (tg.gdclass != StringName()) {
3644 vsp->set_base_type(tg.gdclass);
3645
3646 } else if (script->get_node(func, port_action_node).is_valid()) {
3647 PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint;
3648 String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
3649
3650 if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
3651 vsp->set_base_type(base_type);
3652 }
3653 }
3654 if (tg.script.is_valid()) {
3655 vsp->set_base_script(tg.script->get_path());
3656 }
3657 } else if (tg.type == Variant::NIL) {
3658 vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
3659 vsp->set_base_type(String(""));
3660 } else {
3661 vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE);
3662 vsp->set_basic_type(tg.type);
3663 }
3664 }
3665
3666 if (Object::cast_to<VisualScriptPropertyGet>(vsn.ptr())) {
3667 Ref<VisualScriptPropertyGet> vsp = vsn;
3668
3669 VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
3670 if (tg.type == Variant::OBJECT) {
3671 vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
3672 vsp->set_base_type(String(""));
3673 if (tg.gdclass != StringName()) {
3674 vsp->set_base_type(tg.gdclass);
3675
3676 } else if (script->get_node(func, port_action_node).is_valid()) {
3677 PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint;
3678 String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
3679 if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
3680 vsp->set_base_type(base_type);
3681 }
3682 }
3683 if (tg.script.is_valid()) {
3684 vsp->set_base_script(tg.script->get_path());
3685 }
3686 } else if (tg.type == Variant::NIL) {
3687 vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
3688 vsp->set_base_type(String(""));
3689 } else {
3690 vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE);
3691 vsp->set_basic_type(tg.type);
3692 }
3693 }
3694 }
3695 if (port_node_exists) {
3696 Ref<VisualScriptNode> vnode_old = script->get_node(func, port_action_node);
3697 if (vnode_old.is_valid() && p_connecting) {
3698 connect_seq(vnode_old, vnode, port_action_new_node);
3699 connect_data(vnode_old, vnode, port_action_new_node);
3700 }
3701 }
3702 _update_graph(port_action_new_node);
3703 if (port_node_exists)
3704 _update_graph_connections();
3705 }
3706
connect_seq(Ref<VisualScriptNode> vnode_old,Ref<VisualScriptNode> vnode_new,int new_id)3707 void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id) {
3708
3709 VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(vnode_new.ptr());
3710 if (vnode_operator != NULL && !vnode_operator->has_input_sequence_port()) {
3711 return;
3712 }
3713 VisualScriptConstructor *vnode_constructor = Object::cast_to<VisualScriptConstructor>(vnode_new.ptr());
3714 if (vnode_constructor != NULL) {
3715 return;
3716 }
3717 if (vnode_old->get_output_sequence_port_count() <= 0) {
3718 return;
3719 }
3720 if (!vnode_new->has_input_sequence_port()) {
3721 return;
3722 }
3723
3724 StringName func = _get_function_of_node(port_action_node);
3725
3726 undo_redo->create_action(TTR("Connect Node Sequence"));
3727 int pass_port = -vnode_old->get_output_sequence_port_count() + 1;
3728 int return_port = port_action_output - 1;
3729 if (vnode_old->get_output_value_port_info(port_action_output).name == String("pass") &&
3730 !script->get_output_sequence_ports_connected(func, port_action_node).has(pass_port)) {
3731 undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, pass_port, new_id);
3732 undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, pass_port, new_id);
3733 } else if (vnode_old->get_output_value_port_info(port_action_output).name == String("return") &&
3734 !script->get_output_sequence_ports_connected(func, port_action_node).has(return_port)) {
3735 undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, return_port, new_id);
3736 undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, return_port, new_id);
3737 } else {
3738 for (int port = 0; port < vnode_old->get_output_sequence_port_count(); port++) {
3739 int count = vnode_old->get_output_sequence_port_count();
3740 if (port_action_output < count && !script->get_output_sequence_ports_connected(func, port_action_node).has(port_action_output)) {
3741 undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, port_action_output, new_id);
3742 undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, port_action_output, new_id);
3743 break;
3744 } else if (!script->get_output_sequence_ports_connected(func, port_action_node).has(port)) {
3745 undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, port, new_id);
3746 undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, port, new_id);
3747 break;
3748 }
3749 }
3750 }
3751
3752 undo_redo->commit_action();
3753 }
3754
_selected_new_virtual_method(const String & p_text,const String & p_category,const bool p_connecting)3755 void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting) {
3756
3757 String name = p_text;
3758 if (script->has_function(name)) {
3759 EditorNode::get_singleton()->show_warning(vformat(TTR("Script already has function '%s'"), name));
3760 return;
3761 }
3762
3763 MethodInfo minfo;
3764 {
3765 List<MethodInfo> methods;
3766 bool found = false;
3767 ClassDB::get_virtual_methods(script->get_instance_base_type(), &methods);
3768 for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
3769 if (E->get().name == name) {
3770 minfo = E->get();
3771 found = true;
3772 }
3773 }
3774
3775 ERR_FAIL_COND(!found);
3776 }
3777
3778 selected = name;
3779 Ref<VisualScriptFunction> func_node;
3780 func_node.instance();
3781 func_node->set_name(name);
3782
3783 undo_redo->create_action(TTR("Add Function"));
3784 undo_redo->add_do_method(script.ptr(), "add_function", name);
3785
3786 for (int i = 0; i < minfo.arguments.size(); i++) {
3787 func_node->add_argument(minfo.arguments[i].type, minfo.arguments[i].name, -1, minfo.arguments[i].hint, minfo.arguments[i].hint_string);
3788 }
3789
3790 Vector2 ofs = _get_available_pos();
3791
3792 undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs);
3793 if (minfo.return_val.type != Variant::NIL || minfo.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
3794 Ref<VisualScriptReturn> ret_node;
3795 ret_node.instance();
3796 ret_node->set_return_type(minfo.return_val.type);
3797 ret_node->set_enable_return_value(true);
3798 ret_node->set_name(name);
3799 undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id() + 1, ret_node, _get_available_pos(false, ofs + Vector2(500, 0)));
3800 }
3801
3802 undo_redo->add_undo_method(script.ptr(), "remove_function", name);
3803 undo_redo->add_do_method(this, "_update_members");
3804 undo_redo->add_undo_method(this, "_update_members");
3805 undo_redo->add_do_method(this, "_update_graph");
3806 undo_redo->add_undo_method(this, "_update_graph");
3807
3808 undo_redo->commit_action();
3809
3810 _update_graph();
3811 }
3812
_cancel_connect_node()3813 void VisualScriptEditor::_cancel_connect_node() {
3814 // ensure the cancel is done
3815 port_action_new_node = -1;
3816 }
3817
_create_new_node_from_name(const String & p_text,const Vector2 & p_point,const StringName & p_func)3818 int VisualScriptEditor::_create_new_node_from_name(const String &p_text, const Vector2 &p_point, const StringName &p_func) {
3819
3820 StringName func = default_func;
3821 if (p_func != StringName())
3822 func = p_func;
3823
3824 Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(p_text);
3825 int new_id = script->get_available_id();
3826 undo_redo->create_action(TTR("Add Node"));
3827 undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode, p_point);
3828 undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id);
3829 undo_redo->add_do_method(this, "_update_graph");
3830 undo_redo->add_undo_method(this, "_update_graph");
3831 undo_redo->commit_action();
3832 return new_id;
3833 }
3834
_default_value_changed()3835 void VisualScriptEditor::_default_value_changed() {
3836
3837 Ref<VisualScriptNode> vsn = script->get_node(_get_function_of_node(editing_id), editing_id);
3838 if (vsn.is_null())
3839 return;
3840
3841 undo_redo->create_action(TTR("Change Input Value"));
3842 undo_redo->add_do_method(vsn.ptr(), "set_default_input_value", editing_input, default_value_edit->get_variant());
3843 undo_redo->add_undo_method(vsn.ptr(), "set_default_input_value", editing_input, vsn->get_default_input_value(editing_input));
3844
3845 undo_redo->add_do_method(this, "_update_graph", editing_id);
3846 undo_redo->add_undo_method(this, "_update_graph", editing_id);
3847 undo_redo->commit_action();
3848 }
3849
_default_value_edited(Node * p_button,int p_id,int p_input_port)3850 void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_input_port) {
3851
3852 Ref<VisualScriptNode> vsn = script->get_node(_get_function_of_node(p_id), p_id);
3853 if (vsn.is_null())
3854 return;
3855
3856 PropertyInfo pinfo = vsn->get_input_value_port_info(p_input_port);
3857 Variant existing = vsn->get_default_input_value(p_input_port);
3858 if (pinfo.type != Variant::NIL && existing.get_type() != pinfo.type) {
3859
3860 Variant::CallError ce;
3861 const Variant *existingp = &existing;
3862 existing = Variant::construct(pinfo.type, &existingp, 1, ce, false);
3863 }
3864
3865 default_value_edit->set_position(Object::cast_to<Control>(p_button)->get_global_position() + Vector2(0, Object::cast_to<Control>(p_button)->get_size().y));
3866 default_value_edit->set_size(Size2(1, 1));
3867
3868 if (pinfo.type == Variant::NODE_PATH) {
3869
3870 Node *edited_scene = get_tree()->get_edited_scene_root();
3871 if (edited_scene) { // Fixing an old crash bug ( Visual Script Crashes on editing NodePath with an empty scene open)
3872 Node *script_node = _find_script_node(edited_scene, edited_scene, script);
3873
3874 if (script_node) {
3875 //pick a node relative to the script, IF the script exists
3876 pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE;
3877 pinfo.hint_string = script_node->get_path();
3878 } else {
3879 //pick a path relative to edited scene
3880 pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE;
3881 pinfo.hint_string = get_tree()->get_edited_scene_root()->get_path();
3882 }
3883 }
3884 }
3885
3886 if (default_value_edit->edit(NULL, pinfo.name, pinfo.type, existing, pinfo.hint, pinfo.hint_string)) {
3887 if (pinfo.hint == PROPERTY_HINT_MULTILINE_TEXT)
3888 default_value_edit->popup_centered_ratio();
3889 else
3890 default_value_edit->popup();
3891 }
3892
3893 editing_id = p_id;
3894 editing_input = p_input_port;
3895 }
3896
_show_hint(const String & p_hint)3897 void VisualScriptEditor::_show_hint(const String &p_hint) {
3898
3899 hint_text->set_text(p_hint);
3900 hint_text->show();
3901 hint_text_timer->start();
3902 }
3903
_hide_timer()3904 void VisualScriptEditor::_hide_timer() {
3905
3906 hint_text->hide();
3907 }
3908
_notification(int p_what)3909 void VisualScriptEditor::_notification(int p_what) {
3910
3911 switch (p_what) {
3912 case NOTIFICATION_READY: {
3913 variable_editor->connect("changed", this, "_update_members");
3914 signal_editor->connect("changed", this, "_update_members");
3915 FALLTHROUGH;
3916 }
3917 case NOTIFICATION_THEME_CHANGED: {
3918 if (p_what != NOTIFICATION_READY && !is_visible_in_tree()) {
3919 return;
3920 }
3921
3922 edit_variable_edit->add_style_override("bg", get_stylebox("bg", "Tree"));
3923 edit_signal_edit->add_style_override("bg", get_stylebox("bg", "Tree"));
3924 func_input_scroll->add_style_override("bg", get_stylebox("bg", "Tree"));
3925
3926 Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme();
3927
3928 bool dark_theme = tm->get_constant("dark_theme", "Editor");
3929
3930 List<Pair<String, Color> > colors;
3931 if (dark_theme) {
3932 colors.push_back(Pair<String, Color>("flow_control", Color(0.96, 0.96, 0.96)));
3933 colors.push_back(Pair<String, Color>("functions", Color(0.96, 0.52, 0.51)));
3934 colors.push_back(Pair<String, Color>("data", Color(0.5, 0.96, 0.81)));
3935 colors.push_back(Pair<String, Color>("operators", Color(0.67, 0.59, 0.87)));
3936 colors.push_back(Pair<String, Color>("custom", Color(0.5, 0.73, 0.96)));
3937 colors.push_back(Pair<String, Color>("constants", Color(0.96, 0.5, 0.69)));
3938 } else {
3939 colors.push_back(Pair<String, Color>("flow_control", Color(0.26, 0.26, 0.26)));
3940 colors.push_back(Pair<String, Color>("functions", Color(0.95, 0.4, 0.38)));
3941 colors.push_back(Pair<String, Color>("data", Color(0.07, 0.73, 0.51)));
3942 colors.push_back(Pair<String, Color>("operators", Color(0.51, 0.4, 0.82)));
3943 colors.push_back(Pair<String, Color>("custom", Color(0.31, 0.63, 0.95)));
3944 colors.push_back(Pair<String, Color>("constants", Color(0.94, 0.18, 0.49)));
3945 }
3946
3947 for (List<Pair<String, Color> >::Element *E = colors.front(); E; E = E->next()) {
3948 Ref<StyleBoxFlat> sb = tm->get_stylebox("frame", "GraphNode");
3949 if (!sb.is_null()) {
3950 Ref<StyleBoxFlat> frame_style = sb->duplicate();
3951 Color c = sb->get_border_color();
3952 Color cn = E->get().second;
3953 cn.a = c.a;
3954 frame_style->set_border_color(cn);
3955 node_styles[E->get().first] = frame_style;
3956 }
3957 }
3958
3959 if (is_visible_in_tree() && script.is_valid()) {
3960 _update_members();
3961 _update_graph();
3962 }
3963 } break;
3964 case NOTIFICATION_VISIBILITY_CHANGED: {
3965 members_section->set_visible(is_visible_in_tree());
3966 } break;
3967 }
3968 }
3969
_graph_ofs_changed(const Vector2 & p_ofs)3970 void VisualScriptEditor::_graph_ofs_changed(const Vector2 &p_ofs) {
3971
3972 if (updating_graph || !script.is_valid())
3973 return;
3974
3975 updating_graph = true;
3976
3977 // Just use the default func for all the properties that need to be handled for drawing rather than adding to the Visual Script Class
3978 if (script->has_function(default_func)) {
3979 script->set_function_scroll(default_func, graph->get_scroll_ofs() / EDSCALE);
3980 script->set_edited(true);
3981 }
3982 updating_graph = false;
3983 }
3984
_comment_node_resized(const Vector2 & p_new_size,int p_node)3985 void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_node) {
3986
3987 if (updating_graph)
3988 return;
3989
3990 StringName func = _get_function_of_node(p_node);
3991
3992 Ref<VisualScriptComment> vsc = script->get_node(func, p_node);
3993 if (vsc.is_null())
3994 return;
3995
3996 Node *node = graph->get_node(itos(p_node));
3997 GraphNode *gn = Object::cast_to<GraphNode>(node);
3998 if (!gn)
3999 return;
4000
4001 updating_graph = true;
4002
4003 graph->set_block_minimum_size_adjust(true); //faster resize
4004
4005 undo_redo->create_action(TTR("Resize Comment"), UndoRedo::MERGE_ENDS);
4006 undo_redo->add_do_method(vsc.ptr(), "set_size", p_new_size / EDSCALE);
4007 undo_redo->add_undo_method(vsc.ptr(), "set_size", vsc->get_size());
4008 undo_redo->commit_action();
4009
4010 gn->set_custom_minimum_size(p_new_size);
4011 gn->set_size(Size2(1, 1));
4012 graph->set_block_minimum_size_adjust(false);
4013 updating_graph = false;
4014 }
4015
_menu_option(int p_what)4016 void VisualScriptEditor::_menu_option(int p_what) {
4017
4018 switch (p_what) {
4019 case EDIT_DELETE_NODES: {
4020 _on_nodes_delete();
4021 } break;
4022 case EDIT_TOGGLE_BREAKPOINT: {
4023
4024 List<String> reselect;
4025 for (int i = 0; i < graph->get_child_count(); i++) {
4026 GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
4027 if (gn) {
4028 if (gn->is_selected()) {
4029 int id = String(gn->get_name()).to_int();
4030 StringName func = _get_function_of_node(id);
4031 Ref<VisualScriptNode> vsn = script->get_node(func, id);
4032 if (vsn.is_valid()) {
4033 vsn->set_breakpoint(!vsn->is_breakpoint());
4034 reselect.push_back(gn->get_name());
4035 }
4036 }
4037 }
4038 }
4039
4040 _update_graph();
4041
4042 for (List<String>::Element *E = reselect.front(); E; E = E->next()) {
4043 GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(E->get()));
4044 gn->set_selected(true);
4045 }
4046
4047 } break;
4048 case EDIT_FIND_NODE_TYPE: {
4049 _generic_search(script->get_instance_base_type());
4050 } break;
4051 case EDIT_COPY_NODES:
4052 case EDIT_CUT_NODES: {
4053 if (!script->has_function(default_func))
4054 break;
4055
4056 clipboard->nodes.clear();
4057 clipboard->data_connections.clear();
4058 clipboard->sequence_connections.clear();
4059
4060 Set<String> funcs;
4061 for (int i = 0; i < graph->get_child_count(); i++) {
4062 GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
4063 if (gn) {
4064 if (gn->is_selected()) {
4065
4066 int id = String(gn->get_name()).to_int();
4067 StringName func = _get_function_of_node(id);
4068 Ref<VisualScriptNode> node = script->get_node(func, id);
4069 if (Object::cast_to<VisualScriptFunction>(*node)) {
4070 EditorNode::get_singleton()->show_warning(TTR("Can't copy the function node."));
4071 return;
4072 }
4073 if (node.is_valid()) {
4074 clipboard->nodes[id] = node->duplicate(true);
4075 clipboard->nodes_positions[id] = script->get_node_position(func, id);
4076 funcs.insert(String(func));
4077 }
4078 }
4079 }
4080 }
4081
4082 if (clipboard->nodes.empty())
4083 break;
4084
4085 for (Set<String>::Element *F = funcs.front(); F; F = F->next()) {
4086 List<VisualScript::SequenceConnection> sequence_connections;
4087
4088 script->get_sequence_connection_list(F->get(), &sequence_connections);
4089
4090 for (List<VisualScript::SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) {
4091
4092 if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) {
4093
4094 clipboard->sequence_connections.insert(E->get());
4095 }
4096 }
4097
4098 List<VisualScript::DataConnection> data_connections;
4099
4100 script->get_data_connection_list(F->get(), &data_connections);
4101
4102 for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
4103
4104 if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) {
4105
4106 clipboard->data_connections.insert(E->get());
4107 }
4108 }
4109 }
4110 if (p_what == EDIT_CUT_NODES) {
4111 _on_nodes_delete(); // oh yeah, also delete on cut
4112 }
4113
4114 } break;
4115 case EDIT_PASTE_NODES: {
4116 if (!script->has_function(default_func))
4117 break;
4118
4119 if (clipboard->nodes.empty()) {
4120 EditorNode::get_singleton()->show_warning(TTR("Clipboard is empty!"));
4121 break;
4122 }
4123
4124 Map<int, int> remap;
4125
4126 undo_redo->create_action(TTR("Paste VisualScript Nodes"));
4127 int idc = script->get_available_id() + 1;
4128
4129 Set<int> to_select;
4130
4131 Set<Vector2> existing_positions;
4132
4133 {
4134 List<StringName> functions;
4135 script->get_function_list(&functions);
4136 for (List<StringName>::Element *F = functions.front(); F; F = F->next()) {
4137 List<int> nodes;
4138 script->get_node_list(F->get(), &nodes);
4139 for (List<int>::Element *E = nodes.front(); E; E = E->next()) {
4140 Vector2 pos = script->get_node_position(F->get(), E->get()).snapped(Vector2(2, 2));
4141 existing_positions.insert(pos);
4142 }
4143 }
4144 }
4145
4146 for (Map<int, Ref<VisualScriptNode> >::Element *E = clipboard->nodes.front(); E; E = E->next()) {
4147
4148 Ref<VisualScriptNode> node = E->get()->duplicate();
4149
4150 int new_id = idc++;
4151 to_select.insert(new_id);
4152
4153 remap[E->key()] = new_id;
4154
4155 Vector2 paste_pos = clipboard->nodes_positions[E->key()];
4156
4157 while (existing_positions.has(paste_pos.snapped(Vector2(2, 2)))) {
4158 paste_pos += Vector2(20, 20) * EDSCALE;
4159 }
4160
4161 undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, node, paste_pos);
4162 undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
4163 }
4164
4165 for (Set<VisualScript::SequenceConnection>::Element *E = clipboard->sequence_connections.front(); E; E = E->next()) {
4166
4167 undo_redo->add_do_method(script.ptr(), "sequence_connect", default_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]);
4168 undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", default_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]);
4169 }
4170
4171 for (Set<VisualScript::DataConnection>::Element *E = clipboard->data_connections.front(); E; E = E->next()) {
4172
4173 undo_redo->add_do_method(script.ptr(), "data_connect", default_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port);
4174 undo_redo->add_undo_method(script.ptr(), "data_disconnect", default_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port);
4175 }
4176
4177 undo_redo->add_do_method(this, "_update_graph");
4178 undo_redo->add_undo_method(this, "_update_graph");
4179
4180 undo_redo->commit_action();
4181
4182 for (int i = 0; i < graph->get_child_count(); i++) {
4183 GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
4184 if (gn) {
4185 int id = gn->get_name().operator String().to_int();
4186 gn->set_selected(to_select.has(id));
4187 }
4188 }
4189 } break;
4190 case EDIT_CREATE_FUNCTION: {
4191
4192 StringName function = "";
4193 Map<int, Ref<VisualScriptNode> > nodes;
4194 Set<int> selections;
4195 for (int i = 0; i < graph->get_child_count(); i++) {
4196 GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
4197 if (gn) {
4198 if (gn->is_selected()) {
4199 int id = String(gn->get_name()).to_int();
4200 StringName func = _get_function_of_node(id);
4201 Ref<VisualScriptNode> node = script->get_node(func, id);
4202 if (Object::cast_to<VisualScriptFunction>(*node)) {
4203 EditorNode::get_singleton()->show_warning(TTR("Can't create function with a function node."));
4204 return;
4205 }
4206 if (node.is_valid()) {
4207 if (func != function && function != StringName("")) {
4208 EditorNode::get_singleton()->show_warning(TTR("Can't create function of nodes from nodes of multiple functions."));
4209 return;
4210 }
4211 nodes.insert(id, node);
4212 selections.insert(id);
4213 function = func;
4214 }
4215 }
4216 }
4217 }
4218
4219 if (nodes.size() == 0) {
4220 return; // nothing to be done if there are no valid nodes selected
4221 }
4222
4223 Set<VisualScript::SequenceConnection> seqmove;
4224 Set<VisualScript::DataConnection> datamove;
4225
4226 Set<VisualScript::SequenceConnection> seqext;
4227 Set<VisualScript::DataConnection> dataext;
4228
4229 int start_node = -1;
4230 Set<int> end_nodes;
4231 if (nodes.size() == 1) {
4232 Ref<VisualScriptNode> nd = script->get_node(function, nodes.front()->key());
4233 if (nd.is_valid() && nd->has_input_sequence_port())
4234 start_node = nodes.front()->key();
4235 else {
4236 EditorNode::get_singleton()->show_warning(TTR("Select at least one node with sequence port."));
4237 return;
4238 }
4239 } else {
4240 List<VisualScript::SequenceConnection> seqs;
4241 script->get_sequence_connection_list(function, &seqs);
4242
4243 if (seqs.size() == 0) {
4244 // in case there are no sequence connections
4245 // select the top most node cause that's probably how
4246 // the user wants to connect the nodes
4247 int top_nd = -1;
4248 Vector2 top;
4249 for (Map<int, Ref<VisualScriptNode> >::Element *E = nodes.front(); E; E = E->next()) {
4250 Ref<VisualScriptNode> nd = script->get_node(function, E->key());
4251 if (nd.is_valid() && nd->has_input_sequence_port()) {
4252 if (top_nd < 0) {
4253 top_nd = E->key();
4254 top = script->get_node_position(function, top_nd);
4255 }
4256 Vector2 pos = script->get_node_position(function, E->key());
4257 if (top.y > pos.y) {
4258 top_nd = E->key();
4259 top = pos;
4260 }
4261 }
4262 }
4263 Ref<VisualScriptNode> nd = script->get_node(function, top_nd);
4264 if (nd.is_valid() && nd->has_input_sequence_port())
4265 start_node = top_nd;
4266 else {
4267 EditorNode::get_singleton()->show_warning(TTR("Select at least one node with sequence port."));
4268 return;
4269 }
4270 } else {
4271 // pick the node with input sequence
4272 Set<int> nodes_from;
4273 Set<int> nodes_to;
4274 for (List<VisualScript::SequenceConnection>::Element *E = seqs.front(); E; E = E->next()) {
4275 if (nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) {
4276 seqmove.insert(E->get());
4277 nodes_from.insert(E->get().from_node);
4278 } else if (nodes.has(E->get().from_node) && !nodes.has(E->get().to_node)) {
4279 seqext.insert(E->get());
4280 } else if (!nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) {
4281 if (start_node == -1) {
4282 seqext.insert(E->get());
4283 start_node = E->get().to_node;
4284 } else {
4285 EditorNode::get_singleton()->show_warning(TTR("Try to only have one sequence input in selection."));
4286 return;
4287 }
4288 }
4289 nodes_to.insert(E->get().to_node);
4290 }
4291
4292 // to use to add return nodes
4293 _get_ends(start_node, seqs, selections, end_nodes);
4294
4295 if (start_node == -1) {
4296 // if we still don't have a start node then
4297 // run through the nodes and select the first tree node
4298 // ie node without any input sequence but output sequence
4299 for (Set<int>::Element *E = nodes_from.front(); E; E = E->next()) {
4300 if (!nodes_to.has(E->get())) {
4301 start_node = E->get();
4302 }
4303 }
4304 }
4305 }
4306 }
4307
4308 if (start_node == -1) {
4309 return; // this should not happen, but just in case something goes wrong
4310 }
4311
4312 List<Variant::Type> inputs; // input types
4313 List<Pair<int, int> > input_connections;
4314 {
4315 List<VisualScript::DataConnection> dats;
4316 script->get_data_connection_list(function, &dats);
4317 for (List<VisualScript::DataConnection>::Element *E = dats.front(); E; E = E->next()) {
4318 if (nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) {
4319 datamove.insert(E->get());
4320 } else if (!nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) {
4321 // add all these as inputs for the Function
4322 Ref<VisualScriptNode> node = script->get_node(function, E->get().to_node);
4323 if (node.is_valid()) {
4324 dataext.insert(E->get());
4325 PropertyInfo pi = node->get_input_value_port_info(E->get().to_port);
4326 inputs.push_back(pi.type);
4327 input_connections.push_back(Pair<int, int>(E->get().to_node, E->get().to_port));
4328 }
4329 } else if (nodes.has(E->get().from_node) && !nodes.has(E->get().to_node)) {
4330 dataext.insert(E->get());
4331 }
4332 }
4333 }
4334
4335 String new_fn = _validate_name("new_function");
4336
4337 Vector2 ofs = _get_available_pos(false, script->get_node_position(function, start_node) - Vector2(80, 150));
4338
4339 Ref<VisualScriptFunction> func_node;
4340 func_node.instance();
4341 func_node->set_name(new_fn);
4342
4343 undo_redo->create_action(TTR("Create Function"));
4344
4345 undo_redo->add_do_method(script.ptr(), "add_function", new_fn);
4346 int fn_id = script->get_available_id();
4347 undo_redo->add_do_method(script.ptr(), "add_node", new_fn, fn_id, func_node, ofs);
4348 undo_redo->add_undo_method(script.ptr(), "remove_function", new_fn);
4349 undo_redo->add_do_method(this, "_update_members");
4350 undo_redo->add_undo_method(this, "_update_members");
4351 undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
4352 undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
4353
4354 // Move the nodes
4355
4356 for (Map<int, Ref<VisualScriptNode> >::Element *E = nodes.front(); E; E = E->next()) {
4357 undo_redo->add_do_method(script.ptr(), "remove_node", function, E->key());
4358 undo_redo->add_do_method(script.ptr(), "add_node", new_fn, E->key(), E->get(), script->get_node_position(function, E->key()));
4359
4360 // undo_redo->add_undo_method(script.ptr(), "remove_node", new_fn, E->key()); not needed cause we already remove the function :P
4361 undo_redo->add_undo_method(script.ptr(), "add_node", function, E->key(), E->get(), script->get_node_position(function, E->key()));
4362 }
4363
4364 for (Set<VisualScript::SequenceConnection>::Element *E = seqmove.front(); E; E = E->next()) {
4365 undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, E->get().from_node, E->get().from_output, E->get().to_node);
4366 undo_redo->add_undo_method(script.ptr(), "sequence_connect", function, E->get().from_node, E->get().from_output, E->get().to_node);
4367 }
4368
4369 for (Set<VisualScript::DataConnection>::Element *E = datamove.front(); E; E = E->next()) {
4370 undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
4371 undo_redo->add_undo_method(script.ptr(), "data_connect", function, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
4372 }
4373
4374 // Add undo for external connections as well so that it's easier to revert back and forth
4375 // these didn't require do methods as it's already handled internally by other do calls
4376 for (Set<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) {
4377 undo_redo->add_undo_method(script.ptr(), "sequence_connect", function, E->get().from_node, E->get().from_output, E->get().to_node);
4378 }
4379 for (Set<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) {
4380 undo_redo->add_undo_method(script.ptr(), "data_connect", function, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
4381 }
4382
4383 // I don't really think we need support for non sequenced functions at this moment
4384 undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, fn_id, 0, start_node);
4385
4386 // end nodes are mapped to the return nodes with data connections if possible
4387 int m = 1;
4388 for (Set<int>::Element *G = end_nodes.front(); G; G = G->next()) {
4389 Ref<VisualScriptReturn> ret_node;
4390 ret_node.instance();
4391
4392 int ret_id = fn_id + (m++);
4393 selections.insert(ret_id);
4394 Vector2 ofsi = _get_available_pos(false, script->get_node_position(function, G->get()) + Vector2(80, -100));
4395 undo_redo->add_do_method(script.ptr(), "add_node", new_fn, ret_id, ret_node, ofsi);
4396 undo_redo->add_undo_method(script.ptr(), "remove_node", new_fn, ret_id);
4397
4398 undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, G->get(), 0, ret_id);
4399 // add data outputs from each of the end_nodes
4400 Ref<VisualScriptNode> vsn = script->get_node(function, G->get());
4401 if (vsn.is_valid() && vsn->get_output_value_port_count() > 0) {
4402 ret_node->set_enable_return_value(true);
4403 // use the zeroth data port cause that's the likely one that is planned to be used
4404 ret_node->set_return_type(vsn->get_output_value_port_info(0).type);
4405 undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, G->get(), 0, ret_id, 0);
4406 }
4407 }
4408
4409 // * might make the system more intelligent by checking port from info.
4410 int i = 0;
4411 List<Pair<int, int> >::Element *F = input_connections.front();
4412 for (List<Variant::Type>::Element *E = inputs.front(); E && F; E = E->next(), F = F->next()) {
4413 func_node->add_argument(E->get(), "arg_" + String::num_int64(i), i);
4414 undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, fn_id, i, F->get().first, F->get().second);
4415 i++; // increment i
4416 }
4417
4418 undo_redo->add_do_method(this, "_update_graph");
4419 undo_redo->add_undo_method(this, "_update_graph");
4420
4421 undo_redo->commit_action();
4422
4423 // make sure all Nodes get marked for selection so that they can be moved together
4424 selections.insert(fn_id);
4425 for (int k = 0; k < graph->get_child_count(); k++) {
4426 GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(k));
4427 if (gn) {
4428 int id = gn->get_name().operator String().to_int();
4429 gn->set_selected(selections.has(id));
4430 }
4431 }
4432
4433 // Ensure Preview Selection is of newly created function node
4434 if (selections.size()) {
4435 EditorNode::get_singleton()->push_item(func_node.ptr());
4436 }
4437
4438 } break;
4439 case REFRESH_GRAPH: {
4440 _update_graph();
4441 } break;
4442 }
4443 }
4444
4445 // this is likely going to be very slow and I am not sure if I should keep it
4446 // but I hope that it will not be a problem considering that we won't be creating functions so frequently
4447 // and cyclic connections would be a problem but hopefully we won't let them get to this point
_get_ends(int p_node,const List<VisualScript::SequenceConnection> & p_seqs,const Set<int> & p_selected,Set<int> & r_end_nodes)4448 void VisualScriptEditor::_get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes) {
4449 for (const List<VisualScript::SequenceConnection>::Element *E = p_seqs.front(); E; E = E->next()) {
4450 int from = E->get().from_node;
4451 int to = E->get().to_node;
4452
4453 if (from == p_node && p_selected.has(to)) {
4454 // this is an interior connection move forward to the to node
4455 _get_ends(to, p_seqs, p_selected, r_end_nodes);
4456 } else if (from == p_node && !p_selected.has(to)) {
4457 r_end_nodes.insert(from);
4458 }
4459 }
4460 }
4461
_member_rmb_selected(const Vector2 & p_pos)4462 void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) {
4463
4464 TreeItem *ti = members->get_selected();
4465 ERR_FAIL_COND(!ti);
4466
4467 member_popup->clear();
4468 member_popup->set_position(members->get_global_position() + p_pos);
4469 member_popup->set_size(Vector2());
4470
4471 function_name_edit->set_position(members->get_global_position() + p_pos);
4472 function_name_edit->set_size(Vector2());
4473
4474 TreeItem *root = members->get_root();
4475
4476 Ref<Texture> del_icon = Control::get_icon("Remove", "EditorIcons");
4477
4478 Ref<Texture> edit_icon = Control::get_icon("Edit", "EditorIcons");
4479
4480 if (ti->get_parent() == root->get_children()) {
4481
4482 member_type = MEMBER_FUNCTION;
4483 member_name = ti->get_text(0);
4484 member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT);
4485 member_popup->add_separator();
4486 member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("visual_script_editor/delete_selected"), MEMBER_REMOVE);
4487 member_popup->popup();
4488 return;
4489 }
4490
4491 if (ti->get_parent() == root->get_children()->get_next()) {
4492
4493 member_type = MEMBER_VARIABLE;
4494 member_name = ti->get_text(0);
4495 member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT);
4496 member_popup->add_separator();
4497 member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("visual_script_editor/delete_selected"), MEMBER_REMOVE);
4498 member_popup->popup();
4499 return;
4500 }
4501
4502 if (ti->get_parent() == root->get_children()->get_next()->get_next()) {
4503
4504 member_type = MEMBER_SIGNAL;
4505 member_name = ti->get_text(0);
4506 member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT);
4507 member_popup->add_separator();
4508 member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("visual_script_editor/delete_selected"), MEMBER_REMOVE);
4509 member_popup->popup();
4510 return;
4511 }
4512 }
4513
_member_option(int p_option)4514 void VisualScriptEditor::_member_option(int p_option) {
4515
4516 switch (member_type) {
4517 case MEMBER_FUNCTION: {
4518
4519 if (p_option == MEMBER_REMOVE) {
4520 //delete the function
4521 String name = member_name;
4522
4523 undo_redo->create_action(TTR("Remove Function"));
4524 undo_redo->add_do_method(script.ptr(), "remove_function", name);
4525 undo_redo->add_undo_method(script.ptr(), "add_function", name);
4526 List<int> nodes;
4527 script->get_node_list(name, &nodes);
4528 for (List<int>::Element *E = nodes.front(); E; E = E->next()) {
4529 undo_redo->add_undo_method(script.ptr(), "add_node", name, E->get(), script->get_node(name, E->get()), script->get_node_position(name, E->get()));
4530 }
4531
4532 List<VisualScript::SequenceConnection> seq_connections;
4533
4534 script->get_sequence_connection_list(name, &seq_connections);
4535
4536 for (List<VisualScript::SequenceConnection>::Element *E = seq_connections.front(); E; E = E->next()) {
4537 undo_redo->add_undo_method(script.ptr(), "sequence_connect", name, E->get().from_node, E->get().from_output, E->get().to_node);
4538 }
4539
4540 List<VisualScript::DataConnection> data_connections;
4541
4542 script->get_data_connection_list(name, &data_connections);
4543
4544 for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
4545 undo_redo->add_undo_method(script.ptr(), "data_connect", name, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
4546 }
4547
4548 undo_redo->add_do_method(this, "_update_members");
4549 undo_redo->add_undo_method(this, "_update_members");
4550 undo_redo->add_do_method(this, "_update_graph");
4551 undo_redo->add_undo_method(this, "_update_graph");
4552 undo_redo->commit_action();
4553 } else if (p_option == MEMBER_EDIT) {
4554 selected = members->get_selected()->get_text(0);
4555 function_name_edit->popup();
4556 function_name_box->set_text(selected);
4557 function_name_box->select_all();
4558 }
4559 } break;
4560 case MEMBER_VARIABLE: {
4561
4562 String name = member_name;
4563
4564 if (p_option == MEMBER_REMOVE) {
4565 undo_redo->create_action(TTR("Remove Variable"));
4566 undo_redo->add_do_method(script.ptr(), "remove_variable", name);
4567 undo_redo->add_undo_method(script.ptr(), "add_variable", name, script->get_variable_default_value(name));
4568 undo_redo->add_undo_method(script.ptr(), "set_variable_info", name, script->call("get_variable_info", name)); //return as dict
4569 undo_redo->add_do_method(this, "_update_members");
4570 undo_redo->add_undo_method(this, "_update_members");
4571 undo_redo->commit_action();
4572 } else if (p_option == MEMBER_EDIT) {
4573 variable_editor->edit(name);
4574 edit_variable_dialog->set_title(TTR("Editing Variable:") + " " + name);
4575 edit_variable_dialog->popup_centered_minsize(Size2(400, 200) * EDSCALE);
4576 }
4577 } break;
4578 case MEMBER_SIGNAL: {
4579 String name = member_name;
4580
4581 if (p_option == MEMBER_REMOVE) {
4582 undo_redo->create_action(TTR("Remove Signal"));
4583 undo_redo->add_do_method(script.ptr(), "remove_custom_signal", name);
4584 undo_redo->add_undo_method(script.ptr(), "add_custom_signal", name);
4585
4586 for (int i = 0; i < script->custom_signal_get_argument_count(name); i++) {
4587 undo_redo->add_undo_method(script.ptr(), "custom_signal_add_argument", name, script->custom_signal_get_argument_name(name, i), script->custom_signal_get_argument_type(name, i));
4588 }
4589
4590 undo_redo->add_do_method(this, "_update_members");
4591 undo_redo->add_undo_method(this, "_update_members");
4592 undo_redo->commit_action();
4593 } else if (p_option == MEMBER_EDIT) {
4594 signal_editor->edit(name);
4595 edit_signal_dialog->set_title(TTR("Editing Signal:") + " " + name);
4596 edit_signal_dialog->popup_centered_minsize(Size2(400, 300) * EDSCALE);
4597 }
4598 } break;
4599 }
4600 }
4601
add_syntax_highlighter(SyntaxHighlighter * p_highlighter)4602 void VisualScriptEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
4603 }
4604
set_syntax_highlighter(SyntaxHighlighter * p_highlighter)4605 void VisualScriptEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
4606 }
4607
_bind_methods()4608 void VisualScriptEditor::_bind_methods() {
4609
4610 ClassDB::bind_method("_member_button", &VisualScriptEditor::_member_button);
4611 ClassDB::bind_method("_member_edited", &VisualScriptEditor::_member_edited);
4612 ClassDB::bind_method("_member_selected", &VisualScriptEditor::_member_selected);
4613 ClassDB::bind_method("_update_members", &VisualScriptEditor::_update_members);
4614 ClassDB::bind_method("_members_gui_input", &VisualScriptEditor::_members_gui_input);
4615 ClassDB::bind_method("_member_rmb_selected", &VisualScriptEditor::_member_rmb_selected);
4616 ClassDB::bind_method("_member_option", &VisualScriptEditor::_member_option);
4617 ClassDB::bind_method("_fn_name_box_input", &VisualScriptEditor::_fn_name_box_input);
4618
4619 ClassDB::bind_method("_change_base_type", &VisualScriptEditor::_change_base_type);
4620 ClassDB::bind_method("_change_base_type_callback", &VisualScriptEditor::_change_base_type_callback);
4621 ClassDB::bind_method("_toggle_tool_script", &VisualScriptEditor::_toggle_tool_script);
4622 ClassDB::bind_method("_node_selected", &VisualScriptEditor::_node_selected);
4623 ClassDB::bind_method("_node_moved", &VisualScriptEditor::_node_moved);
4624 ClassDB::bind_method("_move_node", &VisualScriptEditor::_move_node);
4625 ClassDB::bind_method("_begin_node_move", &VisualScriptEditor::_begin_node_move);
4626 ClassDB::bind_method("_end_node_move", &VisualScriptEditor::_end_node_move);
4627 ClassDB::bind_method("_remove_node", &VisualScriptEditor::_remove_node);
4628 ClassDB::bind_method("_update_graph", &VisualScriptEditor::_update_graph, DEFVAL(-1));
4629 ClassDB::bind_method("_node_ports_changed", &VisualScriptEditor::_node_ports_changed);
4630
4631 ClassDB::bind_method("_create_function_dialog", &VisualScriptEditor::_create_function_dialog);
4632 ClassDB::bind_method("_create_function", &VisualScriptEditor::_create_function);
4633 ClassDB::bind_method("_add_node_dialog", &VisualScriptEditor::_add_node_dialog);
4634 ClassDB::bind_method("_add_func_input", &VisualScriptEditor::_add_func_input);
4635 ClassDB::bind_method("_remove_func_input", &VisualScriptEditor::_remove_func_input);
4636 ClassDB::bind_method("_deselect_input_names", &VisualScriptEditor::_deselect_input_names);
4637
4638 ClassDB::bind_method("_default_value_edited", &VisualScriptEditor::_default_value_edited);
4639 ClassDB::bind_method("_default_value_changed", &VisualScriptEditor::_default_value_changed);
4640 ClassDB::bind_method("_menu_option", &VisualScriptEditor::_menu_option);
4641 ClassDB::bind_method("_graph_ofs_changed", &VisualScriptEditor::_graph_ofs_changed);
4642 ClassDB::bind_method("_center_on_node", &VisualScriptEditor::_center_on_node);
4643 ClassDB::bind_method("_comment_node_resized", &VisualScriptEditor::_comment_node_resized);
4644 ClassDB::bind_method("_button_resource_previewed", &VisualScriptEditor::_button_resource_previewed);
4645 ClassDB::bind_method("_port_action_menu", &VisualScriptEditor::_port_action_menu);
4646 ClassDB::bind_method("_selected_connect_node", &VisualScriptEditor::_selected_connect_node);
4647 ClassDB::bind_method("_selected_new_virtual_method", &VisualScriptEditor::_selected_new_virtual_method);
4648
4649 ClassDB::bind_method("_cancel_connect_node", &VisualScriptEditor::_cancel_connect_node);
4650 ClassDB::bind_method("_create_new_node_from_name", &VisualScriptEditor::_create_new_node_from_name);
4651 ClassDB::bind_method("_expression_text_changed", &VisualScriptEditor::_expression_text_changed);
4652 ClassDB::bind_method("_add_input_port", &VisualScriptEditor::_add_input_port);
4653 ClassDB::bind_method("_add_output_port", &VisualScriptEditor::_add_output_port);
4654 ClassDB::bind_method("_remove_input_port", &VisualScriptEditor::_remove_input_port);
4655 ClassDB::bind_method("_remove_output_port", &VisualScriptEditor::_remove_output_port);
4656 ClassDB::bind_method("_change_port_type", &VisualScriptEditor::_change_port_type);
4657 ClassDB::bind_method("_update_node_size", &VisualScriptEditor::_update_node_size);
4658 ClassDB::bind_method("_port_name_focus_out", &VisualScriptEditor::_port_name_focus_out);
4659
4660 ClassDB::bind_method("get_drag_data_fw", &VisualScriptEditor::get_drag_data_fw);
4661 ClassDB::bind_method("can_drop_data_fw", &VisualScriptEditor::can_drop_data_fw);
4662 ClassDB::bind_method("drop_data_fw", &VisualScriptEditor::drop_data_fw);
4663
4664 ClassDB::bind_method("_input", &VisualScriptEditor::_input);
4665 ClassDB::bind_method("_graph_gui_input", &VisualScriptEditor::_graph_gui_input);
4666
4667 ClassDB::bind_method("_on_nodes_delete", &VisualScriptEditor::_on_nodes_delete);
4668 ClassDB::bind_method("_on_nodes_duplicate", &VisualScriptEditor::_on_nodes_duplicate);
4669
4670 ClassDB::bind_method("_hide_timer", &VisualScriptEditor::_hide_timer);
4671
4672 ClassDB::bind_method("_graph_connected", &VisualScriptEditor::_graph_connected);
4673 ClassDB::bind_method("_graph_disconnected", &VisualScriptEditor::_graph_disconnected);
4674 ClassDB::bind_method("_graph_connect_to_empty", &VisualScriptEditor::_graph_connect_to_empty);
4675
4676 ClassDB::bind_method("_update_graph_connections", &VisualScriptEditor::_update_graph_connections);
4677
4678 ClassDB::bind_method("_selected_method", &VisualScriptEditor::_selected_method);
4679 ClassDB::bind_method("_draw_color_over_button", &VisualScriptEditor::_draw_color_over_button);
4680
4681 ClassDB::bind_method("_generic_search", &VisualScriptEditor::_generic_search);
4682 }
4683
VisualScriptEditor()4684 VisualScriptEditor::VisualScriptEditor() {
4685
4686 if (!clipboard) {
4687 clipboard = memnew(Clipboard);
4688 }
4689 updating_graph = false;
4690 saved_pos_dirty = false;
4691 saved_position = Vector2(0, 0);
4692
4693 edit_menu = memnew(MenuButton);
4694 edit_menu->set_text(TTR("Edit"));
4695 edit_menu->set_switch_on_hover(true);
4696 edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/delete_selected"), EDIT_DELETE_NODES);
4697 edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/toggle_breakpoint"), EDIT_TOGGLE_BREAKPOINT);
4698 edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/find_node_type"), EDIT_FIND_NODE_TYPE);
4699 edit_menu->get_popup()->add_separator();
4700 edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/copy_nodes"), EDIT_COPY_NODES);
4701 edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/cut_nodes"), EDIT_CUT_NODES);
4702 edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/paste_nodes"), EDIT_PASTE_NODES);
4703 edit_menu->get_popup()->add_separator();
4704 edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/create_function"), EDIT_CREATE_FUNCTION);
4705 edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/refresh_nodes"), REFRESH_GRAPH);
4706 edit_menu->get_popup()->connect("id_pressed", this, "_menu_option");
4707
4708 members_section = memnew(VBoxContainer);
4709 // Add but wait until done setting up this.
4710 ScriptEditor::get_singleton()->get_left_list_split()->call_deferred("add_child", members_section);
4711 members_section->set_v_size_flags(SIZE_EXPAND_FILL);
4712
4713 CheckButton *tool_script_check = memnew(CheckButton);
4714 tool_script_check->set_text(TTR("Make Tool:"));
4715 members_section->add_child(tool_script_check);
4716 tool_script_check->connect("pressed", this, "_toggle_tool_script");
4717
4718 /// Members ///
4719
4720 members = memnew(Tree);
4721 members_section->add_margin_child(TTR("Members:"), members, true);
4722 members->set_custom_minimum_size(Size2(0, 50 * EDSCALE));
4723 members->set_hide_root(true);
4724 members->connect("button_pressed", this, "_member_button");
4725 members->connect("item_edited", this, "_member_edited");
4726 members->connect("cell_selected", this, "_member_selected", varray(), CONNECT_DEFERRED);
4727 members->connect("gui_input", this, "_members_gui_input");
4728 members->connect("item_rmb_selected", this, "_member_rmb_selected");
4729 members->set_allow_rmb_select(true);
4730 members->set_allow_reselect(true);
4731 members->set_hide_folding(true);
4732 members->set_drag_forwarding(this);
4733
4734 member_popup = memnew(PopupMenu);
4735 add_child(member_popup);
4736 member_popup->connect("id_pressed", this, "_member_option");
4737
4738 function_name_edit = memnew(PopupDialog);
4739 function_name_box = memnew(LineEdit);
4740 function_name_edit->add_child(function_name_box);
4741 function_name_edit->set_h_size_flags(SIZE_EXPAND);
4742 function_name_box->connect("gui_input", this, "_fn_name_box_input");
4743 function_name_box->set_expand_to_text_length(true);
4744 add_child(function_name_edit);
4745
4746 /// Actual Graph ///
4747
4748 graph = memnew(GraphEdit);
4749 add_child(graph);
4750 graph->set_v_size_flags(Control::SIZE_EXPAND_FILL);
4751 graph->set_anchors_and_margins_preset(Control::PRESET_WIDE);
4752 graph->connect("node_selected", this, "_node_selected");
4753 graph->connect("_begin_node_move", this, "_begin_node_move");
4754 graph->connect("_end_node_move", this, "_end_node_move");
4755 graph->connect("delete_nodes_request", this, "_on_nodes_delete");
4756 graph->connect("duplicate_nodes_request", this, "_on_nodes_duplicate");
4757 graph->connect("gui_input", this, "_graph_gui_input");
4758 graph->set_drag_forwarding(this);
4759 graph->hide();
4760 graph->connect("scroll_offset_changed", this, "_graph_ofs_changed");
4761
4762 /// Add Buttons to Top Bar/Zoom bar.
4763 HBoxContainer *graph_hbc = graph->get_zoom_hbox();
4764
4765 Label *base_lbl = memnew(Label);
4766 base_lbl->set_text(TTR("Change Base Type:") + " ");
4767 graph_hbc->add_child(base_lbl);
4768
4769 base_type_select = memnew(Button);
4770 base_type_select->connect("pressed", this, "_change_base_type");
4771 graph_hbc->add_child(base_type_select);
4772
4773 Button *add_nds = memnew(Button);
4774 add_nds->set_text(TTR("Add Nodes..."));
4775 graph_hbc->add_child(add_nds);
4776 add_nds->connect("pressed", this, "_add_node_dialog");
4777
4778 Button *fn_btn = memnew(Button);
4779 fn_btn->set_text(TTR("Add Function..."));
4780 graph_hbc->add_child(fn_btn);
4781 fn_btn->connect("pressed", this, "_create_function_dialog");
4782
4783 // Add Function Dialog.
4784 VBoxContainer *function_vb = memnew(VBoxContainer);
4785 function_vb->set_v_size_flags(SIZE_EXPAND_FILL);
4786 function_vb->set_custom_minimum_size(Size2(450, 300) * EDSCALE);
4787
4788 HBoxContainer *func_name_hbox = memnew(HBoxContainer);
4789 function_vb->add_child(func_name_hbox);
4790
4791 Label *func_name_label = memnew(Label);
4792 func_name_label->set_text(TTR("Name:"));
4793 func_name_hbox->add_child(func_name_label);
4794
4795 func_name_box = memnew(LineEdit);
4796 func_name_box->set_h_size_flags(SIZE_EXPAND_FILL);
4797 func_name_box->set_placeholder(TTR("function_name"));
4798 func_name_box->set_text("");
4799 func_name_box->connect("focus_entered", this, "_deselect_input_names");
4800 func_name_hbox->add_child(func_name_box);
4801
4802 // Add minor setting for function if needed, here!
4803
4804 function_vb->add_child(memnew(HSeparator));
4805
4806 Button *add_input_button = memnew(Button);
4807 add_input_button->set_h_size_flags(SIZE_EXPAND_FILL);
4808 add_input_button->set_text(TTR("Add Input"));
4809 add_input_button->connect("pressed", this, "_add_func_input");
4810 function_vb->add_child(add_input_button);
4811
4812 func_input_scroll = memnew(ScrollContainer);
4813 func_input_scroll->set_v_size_flags(SIZE_EXPAND_FILL);
4814 function_vb->add_child(func_input_scroll);
4815
4816 func_input_vbox = memnew(VBoxContainer);
4817 func_input_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
4818 func_input_scroll->add_child(func_input_vbox);
4819
4820 function_create_dialog = memnew(ConfirmationDialog);
4821 function_create_dialog->set_v_size_flags(SIZE_EXPAND_FILL);
4822 function_create_dialog->set_title(TTR("Create Function"));
4823 function_create_dialog->add_child(function_vb);
4824 function_create_dialog->get_ok()->set_text(TTR("Create"));
4825 function_create_dialog->get_ok()->connect("pressed", this, "_create_function");
4826 add_child(function_create_dialog);
4827
4828 select_func_text = memnew(Label);
4829 select_func_text->set_text(TTR("Select or create a function to edit its graph."));
4830 select_func_text->set_align(Label::ALIGN_CENTER);
4831 select_func_text->set_valign(Label::VALIGN_CENTER);
4832 select_func_text->set_h_size_flags(SIZE_EXPAND_FILL);
4833 add_child(select_func_text);
4834
4835 hint_text = memnew(Label);
4836 hint_text->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -100);
4837 hint_text->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0);
4838 hint_text->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0);
4839 hint_text->set_align(Label::ALIGN_CENTER);
4840 hint_text->set_valign(Label::VALIGN_CENTER);
4841 graph->add_child(hint_text);
4842
4843 hint_text_timer = memnew(Timer);
4844 hint_text_timer->set_wait_time(4);
4845 hint_text_timer->connect("timeout", this, "_hide_timer");
4846 add_child(hint_text_timer);
4847
4848 // Allowed casts (connections).
4849 for (int i = 0; i < Variant::VARIANT_MAX; i++) {
4850 graph->add_valid_connection_type(Variant::NIL, i);
4851 graph->add_valid_connection_type(i, Variant::NIL);
4852 for (int j = 0; j < Variant::VARIANT_MAX; j++) {
4853 if (Variant::can_convert(Variant::Type(i), Variant::Type(j))) {
4854 graph->add_valid_connection_type(i, j);
4855 }
4856 }
4857
4858 graph->add_valid_right_disconnect_type(i);
4859 }
4860
4861 graph->add_valid_left_disconnect_type(TYPE_SEQUENCE);
4862
4863 graph->connect("connection_request", this, "_graph_connected");
4864 graph->connect("disconnection_request", this, "_graph_disconnected");
4865 graph->connect("connection_to_empty", this, "_graph_connect_to_empty");
4866
4867 edit_signal_dialog = memnew(AcceptDialog);
4868 edit_signal_dialog->get_ok()->set_text(TTR("Close"));
4869 add_child(edit_signal_dialog);
4870
4871 signal_editor = memnew(VisualScriptEditorSignalEdit);
4872 edit_signal_edit = memnew(EditorInspector);
4873 edit_signal_dialog->add_child(edit_signal_edit);
4874
4875 edit_signal_edit->edit(signal_editor);
4876
4877 edit_variable_dialog = memnew(AcceptDialog);
4878 edit_variable_dialog->get_ok()->set_text(TTR("Close"));
4879 add_child(edit_variable_dialog);
4880
4881 variable_editor = memnew(VisualScriptEditorVariableEdit);
4882 edit_variable_edit = memnew(EditorInspector);
4883 edit_variable_dialog->add_child(edit_variable_edit);
4884
4885 edit_variable_edit->edit(variable_editor);
4886
4887 select_base_type = memnew(CreateDialog);
4888 select_base_type->set_base_type("Object"); // Anything goes.
4889 select_base_type->connect("create", this, "_change_base_type_callback");
4890 add_child(select_base_type);
4891
4892 undo_redo = EditorNode::get_singleton()->get_undo_redo();
4893
4894 updating_members = false;
4895
4896 set_process_input(true);
4897 set_process_unhandled_input(true);
4898
4899 default_value_edit = memnew(CustomPropertyEditor);
4900 add_child(default_value_edit);
4901 default_value_edit->connect("variant_changed", this, "_default_value_changed");
4902
4903 method_select = memnew(VisualScriptPropertySelector);
4904 add_child(method_select);
4905 method_select->connect("selected", this, "_selected_method");
4906 error_line = -1;
4907
4908 new_connect_node_select = memnew(VisualScriptPropertySelector);
4909 add_child(new_connect_node_select);
4910 new_connect_node_select->set_resizable(true);
4911 new_connect_node_select->connect("selected", this, "_selected_connect_node");
4912 new_connect_node_select->get_cancel()->connect("pressed", this, "_cancel_connect_node");
4913
4914 new_virtual_method_select = memnew(VisualScriptPropertySelector);
4915 add_child(new_virtual_method_select);
4916 new_virtual_method_select->connect("selected", this, "_selected_new_virtual_method");
4917 }
4918
~VisualScriptEditor()4919 VisualScriptEditor::~VisualScriptEditor() {
4920
4921 undo_redo->clear_history(); // Avoid crashes.
4922 memdelete(signal_editor);
4923 memdelete(variable_editor);
4924 }
4925
create_editor(const RES & p_resource)4926 static ScriptEditorBase *create_editor(const RES &p_resource) {
4927
4928 if (Object::cast_to<VisualScript>(*p_resource)) {
4929 return memnew(VisualScriptEditor);
4930 }
4931
4932 return NULL;
4933 }
4934
4935 VisualScriptEditor::Clipboard *VisualScriptEditor::clipboard = NULL;
4936
free_clipboard()4937 void VisualScriptEditor::free_clipboard() {
4938 if (clipboard)
4939 memdelete(clipboard);
4940 }
4941
register_editor_callback()4942 static void register_editor_callback() {
4943
4944 ScriptEditor::register_create_script_editor_function(create_editor);
4945
4946 ED_SHORTCUT("visual_script_editor/delete_selected", TTR("Delete Selected"), KEY_DELETE);
4947 ED_SHORTCUT("visual_script_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9);
4948 ED_SHORTCUT("visual_script_editor/find_node_type", TTR("Find Node Type"), KEY_MASK_CMD + KEY_F);
4949 ED_SHORTCUT("visual_script_editor/copy_nodes", TTR("Copy Nodes"), KEY_MASK_CMD + KEY_C);
4950 ED_SHORTCUT("visual_script_editor/cut_nodes", TTR("Cut Nodes"), KEY_MASK_CMD + KEY_X);
4951 ED_SHORTCUT("visual_script_editor/paste_nodes", TTR("Paste Nodes"), KEY_MASK_CMD + KEY_V);
4952 ED_SHORTCUT("visual_script_editor/create_function", TTR("Make Function"), KEY_MASK_CMD + KEY_G);
4953 ED_SHORTCUT("visual_script_editor/refresh_nodes", TTR("Refresh Graph"), KEY_MASK_CMD + KEY_R);
4954 ED_SHORTCUT("visual_script_editor/edit_member", TTR("Edit Member"), KEY_MASK_CMD + KEY_E);
4955 }
4956
register_editor()4957 void VisualScriptEditor::register_editor() {
4958
4959 // Too early to register stuff here, request a callback.
4960 EditorNode::add_plugin_init_callback(register_editor_callback);
4961 }
4962
create_node_custom(const String & p_name)4963 Ref<VisualScriptNode> _VisualScriptEditor::create_node_custom(const String &p_name) {
4964
4965 Ref<VisualScriptCustomNode> node;
4966 node.instance();
4967 node->set_script(singleton->custom_nodes[p_name]);
4968 return node;
4969 }
4970
4971 _VisualScriptEditor *_VisualScriptEditor::singleton = NULL;
4972 Map<String, RefPtr> _VisualScriptEditor::custom_nodes;
4973
_VisualScriptEditor()4974 _VisualScriptEditor::_VisualScriptEditor() {
4975 singleton = this;
4976 }
4977
~_VisualScriptEditor()4978 _VisualScriptEditor::~_VisualScriptEditor() {
4979 custom_nodes.clear();
4980 }
4981
add_custom_node(const String & p_name,const String & p_category,const Ref<Script> & p_script)4982 void _VisualScriptEditor::add_custom_node(const String &p_name, const String &p_category, const Ref<Script> &p_script) {
4983 String node_name = "custom/" + p_category + "/" + p_name;
4984 custom_nodes.insert(node_name, p_script.get_ref_ptr());
4985 VisualScriptLanguage::singleton->add_register_func(node_name, &_VisualScriptEditor::create_node_custom);
4986 emit_signal("custom_nodes_updated");
4987 }
4988
remove_custom_node(const String & p_name,const String & p_category)4989 void _VisualScriptEditor::remove_custom_node(const String &p_name, const String &p_category) {
4990 String node_name = "custom/" + p_category + "/" + p_name;
4991 custom_nodes.erase(node_name);
4992 VisualScriptLanguage::singleton->remove_register_func(node_name);
4993 emit_signal("custom_nodes_updated");
4994 }
4995
_bind_methods()4996 void _VisualScriptEditor::_bind_methods() {
4997 ClassDB::bind_method(D_METHOD("add_custom_node", "name", "category", "script"), &_VisualScriptEditor::add_custom_node);
4998 ClassDB::bind_method(D_METHOD("remove_custom_node", "name", "category"), &_VisualScriptEditor::remove_custom_node);
4999 ADD_SIGNAL(MethodInfo("custom_nodes_updated"));
5000 }
5001
validate()5002 void VisualScriptEditor::validate() {
5003 }
5004 #endif
5005