diff options
Diffstat (limited to 'modules/visual_script')
-rw-r--r-- | modules/visual_script/config.py | 2 | ||||
-rw-r--r-- | modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml | 4 | ||||
-rw-r--r-- | modules/visual_script/doc_classes/VisualScriptComposeArray.xml | 15 | ||||
-rw-r--r-- | modules/visual_script/doc_classes/VisualScriptLists.xml | 95 | ||||
-rw-r--r-- | modules/visual_script/register_types.cpp | 2 | ||||
-rw-r--r-- | modules/visual_script/visual_script.cpp | 69 | ||||
-rw-r--r-- | modules/visual_script/visual_script.h | 3 | ||||
-rw-r--r-- | modules/visual_script/visual_script_editor.cpp | 2561 | ||||
-rw-r--r-- | modules/visual_script/visual_script_editor.h | 69 | ||||
-rw-r--r-- | modules/visual_script/visual_script_func_nodes.cpp | 13 | ||||
-rw-r--r-- | modules/visual_script/visual_script_nodes.cpp | 455 | ||||
-rw-r--r-- | modules/visual_script/visual_script_nodes.h | 97 | ||||
-rw-r--r-- | modules/visual_script/visual_script_property_selector.cpp | 109 | ||||
-rw-r--r-- | modules/visual_script/visual_script_property_selector.h | 14 |
14 files changed, 2762 insertions, 746 deletions
diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py index 04e1a40b81..087a13a200 100644 --- a/modules/visual_script/config.py +++ b/modules/visual_script/config.py @@ -11,6 +11,7 @@ def get_doc_classes(): "VisualScriptBuiltinFunc", "VisualScriptClassConstant", "VisualScriptComment", + "VisualScriptComposeArray", "VisualScriptCondition", "VisualScriptConstant", "VisualScriptConstructor", @@ -28,6 +29,7 @@ def get_doc_classes(): "VisualScriptIndexSet", "VisualScriptInputAction", "VisualScriptIterator", + "VisualScriptLists", "VisualScriptLocalVarSet", "VisualScriptLocalVar", "VisualScriptMathConstant", diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 9e3670ec35..b5b452ee47 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -217,7 +217,9 @@ </constant> <constant name="MATH_LERP_ANGLE" value="66" enum="BuiltinFunc"> </constant> - <constant name="FUNC_MAX" value="67" enum="BuiltinFunc"> + <constant name="TEXT_ORD" value="67" enum="BuiltinFunc"> + </constant> + <constant name="FUNC_MAX" value="68" enum="BuiltinFunc"> Represents the size of the [enum BuiltinFunc] enum. </constant> </constants> diff --git a/modules/visual_script/doc_classes/VisualScriptComposeArray.xml b/modules/visual_script/doc_classes/VisualScriptComposeArray.xml new file mode 100644 index 0000000000..92efbc51d1 --- /dev/null +++ b/modules/visual_script/doc_classes/VisualScriptComposeArray.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualScriptComposeArray" inherits="VisualScriptLists" category="Core" version="3.2"> + <brief_description> + A Visual Script Node used to create array from a list of items. + </brief_description> + <description> + A Visual Script Node used to compose array from the list of elements provided with custom in-graph UI hard coded in the VisualScript Editor. + </description> + <tutorials> + </tutorials> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/visual_script/doc_classes/VisualScriptLists.xml b/modules/visual_script/doc_classes/VisualScriptLists.xml new file mode 100644 index 0000000000..8cf3eb1d38 --- /dev/null +++ b/modules/visual_script/doc_classes/VisualScriptLists.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualScriptLists" inherits="VisualScriptNode" category="Core" version="3.2"> + <brief_description> + A Visual Script virtual class for in-graph editable nodes. + </brief_description> + <description> + A Visual Script virtual class that defines the shape and the default behaviour of the nodes that have to be in-graph editable nodes. + </description> + <tutorials> + </tutorials> + <methods> + <method name="add_input_data_port"> + <return type="void"> + </return> + <argument index="0" name="type" type="int" enum="Variant.Type"> + </argument> + <argument index="1" name="name" type="String"> + </argument> + <argument index="2" name="index" type="int"> + </argument> + <description> + </description> + </method> + <method name="add_output_data_port"> + <return type="void"> + </return> + <argument index="0" name="type" type="int" enum="Variant.Type"> + </argument> + <argument index="1" name="name" type="String"> + </argument> + <argument index="2" name="index" type="int"> + </argument> + <description> + </description> + </method> + <method name="remove_input_data_port"> + <return type="void"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <description> + </description> + </method> + <method name="remove_output_data_port"> + <return type="void"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <description> + </description> + </method> + <method name="set_input_data_port_name"> + <return type="void"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <argument index="1" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="set_input_data_port_type"> + <return type="void"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <argument index="1" name="type" type="int" enum="Variant.Type"> + </argument> + <description> + </description> + </method> + <method name="set_output_data_port_name"> + <return type="void"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <argument index="1" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="set_output_data_port_type"> + <return type="void"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <argument index="1" name="type" type="int" enum="Variant.Type"> + </argument> + <description> + </description> + </method> + </methods> + <constants> + </constants> +</class> diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp index 24b96223d7..49272345fe 100644 --- a/modules/visual_script/register_types.cpp +++ b/modules/visual_script/register_types.cpp @@ -56,6 +56,8 @@ void register_visual_script_types() { ClassDB::register_virtual_class<VisualScriptNode>(); ClassDB::register_class<VisualScriptFunctionState>(); ClassDB::register_class<VisualScriptFunction>(); + ClassDB::register_virtual_class<VisualScriptLists>(); + ClassDB::register_class<VisualScriptComposeArray>(); ClassDB::register_class<VisualScriptOperator>(); ClassDB::register_class<VisualScriptVariableSet>(); ClassDB::register_class<VisualScriptVariableGet>(); diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 6bed1742eb..0cacd0f0b5 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -1014,17 +1014,16 @@ void VisualScript::get_script_method_list(List<MethodInfo> *p_list) const { Ref<VisualScriptFunction> func = E->get().nodes[E->get().function_id].node; if (func.is_valid()) { - for (int i = 0; i < func->get_argument_count(); i++) { PropertyInfo arg; arg.name = func->get_argument_name(i); arg.type = func->get_argument_type(i); mi.arguments.push_back(arg); } + + p_list->push_back(mi); } } - - p_list->push_back(mi); } } @@ -1137,6 +1136,9 @@ void VisualScript::_set_data(const Dictionary &p_data) { Array funcs = d["functions"]; functions.clear(); + Vector2 last_pos = Vector2(-100 * funcs.size(), -100 * funcs.size()); // this is the center of the last fn box + Vector2 last_size = Vector2(0.0, 0.0); + for (int i = 0; i < funcs.size(); i++) { Dictionary func = funcs[i]; @@ -1149,11 +1151,42 @@ void VisualScript::_set_data(const Dictionary &p_data) { Array nodes = func["nodes"]; - for (int j = 0; j < nodes.size(); j += 3) { + if (!d.has("vs_unify") && nodes.size() > 0) { + Vector2 top_left = nodes[1]; + Vector2 bottom_right = nodes[1]; - add_node(name, nodes[j], nodes[j + 2], nodes[j + 1]); - } + for (int j = 0; j < nodes.size(); j += 3) { + Point2 pos = nodes[j + 1]; + if (pos.y > top_left.y) { + top_left.y = pos.y; + } + if (pos.y < bottom_right.y) { + bottom_right.y = pos.y; + } + if (pos.x > bottom_right.x) { + bottom_right.x = pos.x; + } + if (pos.x < top_left.x) { + top_left.x = pos.x; + } + } + + Vector2 size = Vector2(bottom_right.x - top_left.x, top_left.y - bottom_right.y); + + Vector2 offset = last_pos + (last_size / 2.0) + (size / 2.0); // dunno I might just keep it in one axis but diagonal feels better.... + last_pos = offset; + last_size = size; + + for (int j = 0; j < nodes.size(); j += 3) { + add_node(name, nodes[j], nodes[j + 2], offset + nodes[j + 1]); // also add an additional buffer if you want to + } + + } else { + for (int j = 0; j < nodes.size(); j += 3) { + add_node(name, nodes[j], nodes[j + 2], nodes[j + 1]); + } + } Array sequence_connections = func["sequence_connections"]; for (int j = 0; j < sequence_connections.size(); j += 3) { @@ -1254,8 +1287,8 @@ Dictionary VisualScript::_get_data() const { } d["functions"] = funcs; - d["is_tool_script"] = is_tool_script; + d["vs_unify"] = true; return d; } @@ -1330,6 +1363,10 @@ VisualScript::VisualScript() { base_type = "Object"; } +StringName VisualScript::get_default_func() const { + return StringName("f_312843592"); +} + Set<int> VisualScript::get_output_sequence_ports_connected(const String &edited_func, int from_node) { List<VisualScript::SequenceConnection> *sc = memnew(List<VisualScript::SequenceConnection>); get_sequence_connection_list(edited_func, sc); @@ -1403,6 +1440,10 @@ void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const { for (const Map<StringName, VisualScript::Function>::Element *E = script->functions.front(); E; E = E->next()) { + if (E->key() == script->get_default_func()) { + continue; + } + MethodInfo mi; mi.name = E->key(); if (E->get().function_id >= 0 && E->get().nodes.has(E->get().function_id)) { @@ -1421,8 +1462,6 @@ void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const { if (!vsf->is_sequenced()) { //assumed constant if not sequenced mi.flags |= METHOD_FLAG_CONST; } - - //vsf->Get_ for now at least it does not return.. } } @@ -1431,6 +1470,9 @@ void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const { } bool VisualScriptInstance::has_method(const StringName &p_method) const { + if (p_method == script->get_default_func()) + return false; + return script->functions.has(p_method); } @@ -2002,6 +2044,9 @@ Ref<Script> VisualScriptInstance::get_script() const { MultiplayerAPI::RPCMode VisualScriptInstance::get_rpc_mode(const StringName &p_method) const { + if (p_method == script->get_default_func()) + return MultiplayerAPI::RPC_MODE_DISABLED; + const Map<StringName, VisualScript::Function>::Element *E = script->functions.find(p_method); if (!E) { return MultiplayerAPI::RPC_MODE_DISABLED; @@ -2050,11 +2095,14 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o for (const Map<StringName, VisualScript::Variable>::Element *E = script->variables.front(); E; E = E->next()) { variables[E->key()] = E->get().default_value; - //no hacer que todo exporte, solo las que queres! } for (const Map<StringName, VisualScript::Function>::Element *E = script->functions.front(); E; E = E->next()) { + if (E->key() == script->get_default_func()) { + continue; + } + Function function; function.node = E->get().function_id; function.max_stack = 0; @@ -2091,6 +2139,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o for (const Map<int, VisualScript::Function::NodeData>::Element *F = E->get().nodes.front(); F; F = F->next()) { Ref<VisualScriptNode> node = F->get().node; + VisualScriptNodeInstance *instance = node->instance(this); //create instance ERR_FAIL_COND(!instance); diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index 14927c4363..a035f6d42d 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -239,6 +239,7 @@ private: PropertyInfo info; Variant default_value; bool _export; + // add getter & setter options here }; Map<StringName, Function> functions; @@ -267,6 +268,8 @@ protected: static void _bind_methods(); public: + // TODO: Remove it in future when breaking changes are acceptable + StringName get_default_func() const; void add_function(const StringName &p_name); bool has_function(const StringName &p_name) const; void remove_function(const StringName &p_name); diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 7262dde359..093901ad07 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -421,31 +421,42 @@ void VisualScriptEditor::_update_graph_connections() { graph->clear_connections(); - List<VisualScript::SequenceConnection> sequence_conns; - script->get_sequence_connection_list(edited_func, &sequence_conns); - - for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { + List<StringName> funcs; + script->get_function_list(&funcs); - graph->connect_node(itos(E->get().from_node), E->get().from_output, itos(E->get().to_node), 0); + if (funcs.size() <= 0) { + updating_graph = false; + return; } - List<VisualScript::DataConnection> data_conns; - script->get_data_connection_list(edited_func, &data_conns); - - for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) { + for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { - VisualScript::DataConnection dc = E->get(); + List<VisualScript::SequenceConnection> sequence_conns; + script->get_sequence_connection_list(F->get(), &sequence_conns); - Ref<VisualScriptNode> from_node = script->get_node(edited_func, E->get().from_node); - Ref<VisualScriptNode> to_node = script->get_node(edited_func, E->get().to_node); + for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { - if (to_node->has_input_sequence_port()) { - dc.to_port++; + graph->connect_node(itos(E->get().from_node), E->get().from_output, itos(E->get().to_node), 0); } - dc.from_port += from_node->get_output_sequence_port_count(); + List<VisualScript::DataConnection> data_conns; + script->get_data_connection_list(F->get(), &data_conns); - graph->connect_node(itos(E->get().from_node), dc.from_port, itos(E->get().to_node), dc.to_port); + for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) { + + VisualScript::DataConnection dc = E->get(); + + Ref<VisualScriptNode> from_node = script->get_node(F->get(), E->get().from_node); + Ref<VisualScriptNode> to_node = script->get_node(F->get(), E->get().to_node); + + if (to_node->has_input_sequence_port()) { + dc.to_port++; + } + + dc.from_port += from_node->get_output_sequence_port_count(); + + graph->connect_node(itos(E->get().from_node), dc.from_port, itos(E->get().to_node), dc.to_port); + } } } @@ -474,7 +485,10 @@ void VisualScriptEditor::_update_graph(int p_only_id) { } } - if (!script->has_function(edited_func)) { + List<StringName> funcs; + script->get_function_list(&funcs); + + if (funcs.size() <= 0) { graph->hide(); select_func_text->show(); updating_graph = false; @@ -516,254 +530,390 @@ void VisualScriptEditor::_update_graph(int p_only_id) { Ref<Texture> seq_port = Control::get_icon("VisualShaderPort", "EditorIcons"); - List<int> ids; - script->get_node_list(edited_func, &ids); - StringName editor_icons = "EditorIcons"; + for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { // loop through all the functions - for (List<int>::Element *E = ids.front(); E; E = E->next()) { + List<int> ids; + script->get_node_list(F->get(), &ids); + StringName editor_icons = "EditorIcons"; - if (p_only_id >= 0 && p_only_id != E->get()) - continue; + for (List<int>::Element *E = ids.front(); E; E = E->next()) { - Ref<VisualScriptNode> node = script->get_node(edited_func, E->get()); - Vector2 pos = script->get_node_position(edited_func, E->get()); + if (p_only_id >= 0 && p_only_id != E->get()) + continue; - GraphNode *gnode = memnew(GraphNode); - gnode->set_title(node->get_caption()); - gnode->set_offset(pos * EDSCALE); - if (error_line == E->get()) { - gnode->set_overlay(GraphNode::OVERLAY_POSITION); - } else if (node->is_breakpoint()) { - gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT); - } + Ref<VisualScriptNode> node = script->get_node(F->get(), E->get()); + Vector2 pos = script->get_node_position(F->get(), E->get()); - gnode->set_meta("__vnode", node); - gnode->set_name(itos(E->get())); - gnode->connect("dragged", this, "_node_moved", varray(E->get())); - gnode->connect("close_request", this, "_remove_node", varray(E->get()), CONNECT_DEFERRED); + GraphNode *gnode = memnew(GraphNode); + gnode->set_title(node->get_caption()); + gnode->set_offset(pos * EDSCALE); + if (error_line == E->get()) { + gnode->set_overlay(GraphNode::OVERLAY_POSITION); + } else if (node->is_breakpoint()) { + gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT); + } - if (E->get() != script->get_function_node_id(edited_func)) { - //function can't be erased - gnode->set_show_close_button(true); - } + gnode->set_meta("__vnode", node); + gnode->set_name(itos(E->get())); + gnode->connect("dragged", this, "_node_moved", varray(E->get())); + gnode->connect("close_request", this, "_remove_node", varray(E->get()), CONNECT_DEFERRED); - bool has_gnode_text = false; + if (E->get() != script->get_function_node_id(F->get())) { + //function can't be erased + gnode->set_show_close_button(true); + } - if (Object::cast_to<VisualScriptExpression>(node.ptr())) { - has_gnode_text = true; - LineEdit *line_edit = memnew(LineEdit); - line_edit->set_text(node->get_text()); - line_edit->set_expand_to_text_length(true); - line_edit->add_font_override("font", get_font("source", "EditorFonts")); - gnode->add_child(line_edit); - line_edit->connect("text_changed", this, "_expression_text_changed", varray(E->get())); - } else { - String text = node->get_text(); - if (!text.empty()) { + bool has_gnode_text = false; + + Ref<VisualScriptLists> nd_list = node; + bool is_vslist = nd_list.is_valid(); + if (is_vslist) { + HBoxContainer *hbnc = memnew(HBoxContainer); + if (nd_list->is_input_port_editable()) { + has_gnode_text = true; + Button *btn = memnew(Button); + btn->set_text("Add Input Port"); + hbnc->add_child(btn); + btn->connect("pressed", this, "_add_input_port", varray(E->get())); + } + if (nd_list->is_output_port_editable()) { + if (nd_list->is_input_port_editable()) + hbnc->add_spacer(); + has_gnode_text = true; + Button *btn = memnew(Button); + btn->set_text("Add Output Port"); + hbnc->add_child(btn); + btn->connect("pressed", this, "_add_output_port", varray(E->get())); + } + gnode->add_child(hbnc); + } else if (Object::cast_to<VisualScriptExpression>(node.ptr())) { has_gnode_text = true; - Label *label = memnew(Label); - label->set_text(text); - gnode->add_child(label); + LineEdit *line_edit = memnew(LineEdit); + line_edit->set_text(node->get_text()); + line_edit->set_expand_to_text_length(true); + line_edit->add_font_override("font", get_font("source", "EditorFonts")); + gnode->add_child(line_edit); + line_edit->connect("text_changed", this, "_expression_text_changed", varray(E->get())); + } else { + String text = node->get_text(); + if (!text.empty()) { + has_gnode_text = true; + Label *label = memnew(Label); + label->set_text(text); + gnode->add_child(label); + } } - } - - if (Object::cast_to<VisualScriptComment>(node.ptr())) { - Ref<VisualScriptComment> vsc = node; - gnode->set_comment(true); - gnode->set_resizable(true); - gnode->set_custom_minimum_size(vsc->get_size() * EDSCALE); - gnode->connect("resize_request", this, "_comment_node_resized", varray(E->get())); - } - if (node_styles.has(node->get_category())) { - Ref<StyleBoxFlat> sbf = node_styles[node->get_category()]; - if (gnode->is_comment()) - sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox("comment", "GraphNode"); - - Color c = sbf->get_border_color(); - c.a = 1; - if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { - 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); - mono_color.a = 0.85; - c = mono_color; + if (Object::cast_to<VisualScriptComment>(node.ptr())) { + Ref<VisualScriptComment> vsc = node; + gnode->set_comment(true); + gnode->set_resizable(true); + gnode->set_custom_minimum_size(vsc->get_size() * EDSCALE); + gnode->connect("resize_request", this, "_comment_node_resized", varray(E->get())); } - gnode->add_color_override("title_color", c); - c.a = 0.7; - gnode->add_color_override("close_color", c); - gnode->add_color_override("resizer_color", c); - gnode->add_style_override("frame", sbf); - } - - const Color mono_color = get_color("mono_color", "Editor"); - - int slot_idx = 0; - - bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String(); - if ((node->has_input_sequence_port() || single_seq_output) || has_gnode_text) { - // IF has_gnode_text is true BUT we have no sequence ports to draw (in here), - // we still draw the disabled default ones to shift up the slots by one, - // so the slots DON'T start with the content text. - - // IF has_gnode_text is false, but we DO want to draw default sequence ports, - // we draw a dummy text to take up the position of the sequence nodes, so all the other ports are still aligned correctly. - if (!has_gnode_text) { - Label *dummy = memnew(Label); - dummy->set_text(" "); - gnode->add_child(dummy); + if (node_styles.has(node->get_category())) { + Ref<StyleBoxFlat> sbf = node_styles[node->get_category()]; + if (gnode->is_comment()) + sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox("comment", "GraphNode"); + + Color c = sbf->get_border_color(); + c.a = 1; + if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { + 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); + mono_color.a = 0.85; + c = mono_color; + } + gnode->add_color_override("title_color", c); + c.a = 0.7; + gnode->add_color_override("close_color", c); + gnode->add_color_override("resizer_color", c); + gnode->add_style_override("frame", sbf); } - gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port); - slot_idx++; - } - int mixed_seq_ports = 0; + const Color mono_color = get_color("mono_color", "Editor"); - if (!single_seq_output) { + int slot_idx = 0; - if (node->has_mixed_input_and_sequence_ports()) { - mixed_seq_ports = node->get_output_sequence_port_count(); - } else { - for (int i = 0; i < node->get_output_sequence_port_count(); i++) { + bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String(); + if ((node->has_input_sequence_port() || single_seq_output) || has_gnode_text) { + // IF has_gnode_text is true BUT we have no sequence ports to draw (in here), + // we still draw the disabled default ones to shift up the slots by one, + // so the slots DON'T start with the content text. - Label *text2 = memnew(Label); - text2->set_text(node->get_output_sequence_port_text(i)); - text2->set_align(Label::ALIGN_RIGHT); - gnode->add_child(text2); - gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, mono_color, seq_port, seq_port); - slot_idx++; + // IF has_gnode_text is false, but we DO want to draw default sequence ports, + // we draw a dummy text to take up the position of the sequence nodes, so all the other ports are still aligned correctly. + if (!has_gnode_text) { + Label *dummy = memnew(Label); + dummy->set_text(" "); + gnode->add_child(dummy); } + gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port); + slot_idx++; } - } - for (int i = 0; i < MAX(node->get_output_value_port_count(), MAX(mixed_seq_ports, node->get_input_value_port_count())); i++) { + int mixed_seq_ports = 0; - bool left_ok = false; - Variant::Type left_type = Variant::NIL; - String left_name; + if (!single_seq_output) { - if (i < node->get_input_value_port_count()) { - PropertyInfo pi = node->get_input_value_port_info(i); - left_ok = true; - left_type = pi.type; - left_name = pi.name; + if (node->has_mixed_input_and_sequence_ports()) { + mixed_seq_ports = node->get_output_sequence_port_count(); + } else { + for (int i = 0; i < node->get_output_sequence_port_count(); i++) { + + Label *text2 = memnew(Label); + text2->set_text(node->get_output_sequence_port_text(i)); + text2->set_align(Label::ALIGN_RIGHT); + gnode->add_child(text2); + gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, mono_color, seq_port, seq_port); + slot_idx++; + } + } } - bool right_ok = false; - Variant::Type right_type = Variant::NIL; - String right_name; + for (int i = 0; i < MAX(node->get_output_value_port_count(), MAX(mixed_seq_ports, node->get_input_value_port_count())); i++) { - if (i >= mixed_seq_ports && i < node->get_output_value_port_count() + mixed_seq_ports) { - PropertyInfo pi = node->get_output_value_port_info(i - mixed_seq_ports); - right_ok = true; - right_type = pi.type; - right_name = pi.name; - } + bool left_ok = false; + Variant::Type left_type = Variant::NIL; + String left_name; - HBoxContainer *hbc = memnew(HBoxContainer); + if (i < node->get_input_value_port_count()) { + PropertyInfo pi = node->get_input_value_port_info(i); + left_ok = true; + left_type = pi.type; + left_name = pi.name; + } - if (left_ok) { + bool right_ok = false; + Variant::Type right_type = Variant::NIL; + String right_name; - Ref<Texture> t; - if (left_type >= 0 && left_type < Variant::VARIANT_MAX) { - t = type_icons[left_type]; - } - if (t.is_valid()) { - TextureRect *tf = memnew(TextureRect); - tf->set_texture(t); - tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); - hbc->add_child(tf); + if (i >= mixed_seq_ports && i < node->get_output_value_port_count() + mixed_seq_ports) { + PropertyInfo pi = node->get_output_value_port_info(i - mixed_seq_ports); + right_ok = true; + right_type = pi.type; + right_name = pi.name; } + VBoxContainer *vbc = memnew(VBoxContainer); + HBoxContainer *hbc = memnew(HBoxContainer); + HBoxContainer *hbc2 = memnew(HBoxContainer); + vbc->add_child(hbc); + vbc->add_child(hbc2); + if (left_ok) { + + Ref<Texture> t; + if (left_type >= 0 && left_type < Variant::VARIANT_MAX) { + t = type_icons[left_type]; + } + if (t.is_valid()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(t); + tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + hbc->add_child(tf); + } - hbc->add_child(memnew(Label(left_name))); + if (is_vslist) { + if (nd_list->is_input_port_name_editable()) { + LineEdit *name_box = memnew(LineEdit); + hbc->add_child(name_box); + name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); + name_box->set_text(left_name); + name_box->set_expand_to_text_length(true); + name_box->connect("resized", this, "_update_node_size", varray(E->get())); + name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, E->get(), i, true)); + } else { + hbc->add_child(memnew(Label(left_name))); + } - if (left_type != Variant::NIL && !script->is_input_value_port_connected(edited_func, E->get(), i)) { + if (nd_list->is_input_port_type_editable()) { + OptionButton *opbtn = memnew(OptionButton); + for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) { + opbtn->add_item(Variant::get_type_name(Variant::Type(j))); + } + opbtn->select(left_type); + opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + hbc->add_child(opbtn); + opbtn->connect("item_selected", this, "_change_port_type", varray(E->get(), i, true), CONNECT_DEFERRED); + } - PropertyInfo pi = node->get_input_value_port_info(i); - Button *button = memnew(Button); - Variant value = node->get_default_input_value(i); - if (value.get_type() != left_type) { - //different type? for now convert - //not the same, reconvert - Variant::CallError ce; - const Variant *existingp = &value; - value = Variant::construct(left_type, &existingp, 1, ce, false); + Button *rmbtn = memnew(Button); + rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons")); + hbc->add_child(rmbtn); + rmbtn->connect("pressed", this, "_remove_input_port", varray(E->get(), i), CONNECT_DEFERRED); + } else { + hbc->add_child(memnew(Label(left_name))); } - if (left_type == Variant::COLOR) { - button->set_custom_minimum_size(Size2(30, 0) * EDSCALE); - button->connect("draw", this, "_draw_color_over_button", varray(button, value)); - } else if (left_type == Variant::OBJECT && Ref<Resource>(value).is_valid()) { + if (left_type != Variant::NIL && !script->is_input_value_port_connected(F->get(), E->get(), i)) { + + PropertyInfo pi = node->get_input_value_port_info(i); + Button *button = memnew(Button); + Variant value = node->get_default_input_value(i); + if (value.get_type() != left_type) { + //different type? for now convert + //not the same, reconvert + Variant::CallError ce; + const Variant *existingp = &value; + value = Variant::construct(left_type, &existingp, 1, ce, false); + } - Ref<Resource> res = value; - Array arr; - arr.push_back(button->get_instance_id()); - arr.push_back(String(value)); - EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr); + if (left_type == Variant::COLOR) { + button->set_custom_minimum_size(Size2(30, 0) * EDSCALE); + button->connect("draw", this, "_draw_color_over_button", varray(button, value)); + } else if (left_type == Variant::OBJECT && Ref<Resource>(value).is_valid()) { - } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) { + Ref<Resource> res = value; + Array arr; + arr.push_back(button->get_instance_id()); + arr.push_back(String(value)); + EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr); - button->set_text(pi.hint_string.get_slice(",", value)); - } else { + } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) { - button->set_text(value); + button->set_text(pi.hint_string.get_slice(",", value)); + } else { + + button->set_text(value); + } + button->connect("pressed", this, "_default_value_edited", varray(button, E->get(), i)); + hbc2->add_child(button); } - button->connect("pressed", this, "_default_value_edited", varray(button, E->get(), i)); - hbc->add_child(button); + } else { + Control *c = memnew(Control); + c->set_custom_minimum_size(Size2(10, 0) * EDSCALE); + hbc->add_child(c); } - } else { - Control *c = memnew(Control); - c->set_custom_minimum_size(Size2(10, 0) * EDSCALE); - hbc->add_child(c); - } - hbc->add_spacer(); + hbc->add_spacer(); + hbc2->add_spacer(); - if (i < mixed_seq_ports) { + if (i < mixed_seq_ports) { - Label *text2 = memnew(Label); - text2->set_text(node->get_output_sequence_port_text(i)); - text2->set_align(Label::ALIGN_RIGHT); - hbc->add_child(text2); - } + Label *text2 = memnew(Label); + text2->set_text(node->get_output_sequence_port_text(i)); + text2->set_align(Label::ALIGN_RIGHT); + hbc->add_child(text2); + } - if (right_ok) { + if (right_ok) { + + if (is_vslist) { + Button *rmbtn = memnew(Button); + rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons")); + hbc->add_child(rmbtn); + rmbtn->connect("pressed", this, "_remove_output_port", varray(E->get(), i), CONNECT_DEFERRED); + + if (nd_list->is_output_port_type_editable()) { + OptionButton *opbtn = memnew(OptionButton); + for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) { + opbtn->add_item(Variant::get_type_name(Variant::Type(j))); + } + opbtn->select(right_type); + opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + hbc->add_child(opbtn); + opbtn->connect("item_selected", this, "_change_port_type", varray(E->get(), i, false), CONNECT_DEFERRED); + } - hbc->add_child(memnew(Label(right_name))); + if (nd_list->is_output_port_name_editable()) { + LineEdit *name_box = memnew(LineEdit); + hbc->add_child(name_box); + name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); + name_box->set_text(right_name); + name_box->set_expand_to_text_length(true); + name_box->connect("resized", this, "_update_node_size", varray(E->get())); + name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, E->get(), i, false)); + } else { + hbc->add_child(memnew(Label(right_name))); + } + } else { + hbc->add_child(memnew(Label(right_name))); + } - Ref<Texture> t; - if (right_type >= 0 && right_type < Variant::VARIANT_MAX) { - t = type_icons[right_type]; + Ref<Texture> t; + if (right_type >= 0 && right_type < Variant::VARIANT_MAX) { + t = type_icons[right_type]; + } + if (t.is_valid()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(t); + tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + hbc->add_child(tf); + } } - if (t.is_valid()) { - TextureRect *tf = memnew(TextureRect); - tf->set_texture(t); - tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); - hbc->add_child(tf); + + gnode->add_child(vbc); + + bool dark_theme = get_constant("dark_theme", "Editor"); + if (i < mixed_seq_ports) { + 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); + } else { + 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)); } + + slot_idx++; } - gnode->add_child(hbc); + graph->add_child(gnode); - bool dark_theme = get_constant("dark_theme", "Editor"); - if (i < mixed_seq_ports) { - 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); - } else { - 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)); + if (gnode->is_comment()) { + graph->move_child(gnode, 0); } - - slot_idx++; } + } + _update_graph_connections(); + // use default_func instead of default_func for now I think that should be good stop gap solution to ensure not breaking anything + graph->call_deferred("set_scroll_ofs", script->get_function_scroll(default_func) * EDSCALE); + updating_graph = false; +} - graph->add_child(gnode); +void VisualScriptEditor::_change_port_type(int p_select, int p_id, int p_port, bool is_input) { - if (gnode->is_comment()) { - graph->move_child(gnode, 0); - } + StringName func = _get_function_of_node(p_id); + + Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + if (!vsn.is_valid()) + return; + + undo_redo->create_action("Change Port Type"); + if (is_input) { + undo_redo->add_do_method(vsn.ptr(), "set_input_data_port_type", p_port, Variant::Type(p_select)); + undo_redo->add_undo_method(vsn.ptr(), "set_input_data_port_type", p_port, vsn->get_input_value_port_info(p_port).type); + } else { + undo_redo->add_do_method(vsn.ptr(), "set_output_data_port_type", p_port, Variant::Type(p_select)); + undo_redo->add_undo_method(vsn.ptr(), "set_output_data_port_type", p_port, vsn->get_output_value_port_info(p_port).type); } + undo_redo->commit_action(); +} - _update_graph_connections(); - graph->call_deferred("set_scroll_ofs", script->get_function_scroll(edited_func) * EDSCALE); //may need to adapt a bit, let it do so - updating_graph = false; +void VisualScriptEditor::_update_node_size(int p_id) { + + Node *node = graph->get_node(itos(p_id)); + if (Object::cast_to<Control>(node)) + Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); //shrink if text is smaller +} +void VisualScriptEditor::_port_name_focus_out(const Node *p_name_box, int p_id, int p_port, bool is_input) { + StringName func = _get_function_of_node(p_id); + + Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + if (!vsn.is_valid()) + return; + + String text; + + if (Object::cast_to<LineEdit>(p_name_box)) + text = Object::cast_to<LineEdit>(p_name_box)->get_text(); + else + return; + + undo_redo->create_action("Change Port Name"); + if (is_input) { + undo_redo->add_do_method(vsn.ptr(), "set_input_data_port_name", p_port, text); + undo_redo->add_undo_method(vsn.ptr(), "set_input_data_port_name", p_port, vsn->get_input_value_port_info(p_port).name); + } else { + undo_redo->add_do_method(vsn.ptr(), "set_output_data_port_name", p_port, text); + undo_redo->add_undo_method(vsn.ptr(), "set_output_data_port_name", p_port, vsn->get_output_value_port_info(p_port).name); + } + undo_redo->commit_action(); } void VisualScriptEditor::_update_members() { @@ -784,11 +934,16 @@ void VisualScriptEditor::_update_members() { List<StringName> func_names; script->get_function_list(&func_names); for (List<StringName>::Element *E = func_names.front(); E; E = E->next()) { + + if (E->get() == default_func) { + continue; + } + TreeItem *ti = members->create_item(functions); ti->set_text(0, E->get()); ti->set_selectable(0, true); - ti->set_editable(0, true); ti->set_metadata(0, E->get()); + ti->add_button(0, Control::get_icon("Edit", "EditorIcons"), 0); if (selected == E->get()) ti->select(0); } @@ -888,15 +1043,15 @@ void VisualScriptEditor::_member_selected() { if (ti->get_parent() == members->get_root()->get_children()) { - if (edited_func != selected) { - - revert_on_drag = edited_func; - edited_func = selected; - _update_members(); - _update_graph(); +#ifdef OSX_ENABLED + bool held_ctrl = Input::get_singleton()->is_key_pressed(KEY_META); +#else + bool held_ctrl = Input::get_singleton()->is_key_pressed(KEY_CONTROL); +#endif + if (held_ctrl) { + ERR_FAIL_COND(!script->has_function(selected)); + _center_on_node(selected, script->get_function_node_id(selected)); } - - return; //or crash because it will become invalid } } @@ -936,9 +1091,6 @@ void VisualScriptEditor::_member_edited() { if (ti->get_parent() == root->get_children()) { - if (edited_func == selected) { - edited_func = new_name; - } selected = new_name; int node_id = script->get_function_node_id(name); @@ -950,10 +1102,27 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_do_method(script.ptr(), "rename_function", name, new_name); undo_redo->add_undo_method(script.ptr(), "rename_function", new_name, name); if (func.is_valid()) { - undo_redo->add_do_method(func.ptr(), "set_name", new_name); undo_redo->add_undo_method(func.ptr(), "set_name", name); } + + // also fix all function calls + List<StringName> flst; + script->get_function_list(&flst); + for (List<StringName>::Element *E = flst.front(); E; E = E->next()) { + List<int> lst; + script->get_node_list(E->get(), &lst); + for (List<int>::Element *F = lst.front(); F; F = F->next()) { + Ref<VisualScriptFunctionCall> fncall = script->get_node(E->get(), F->get()); + if (!fncall.is_valid()) + continue; + if (fncall->get_function() == name) { + undo_redo->add_do_method(fncall.ptr(), "set_function", new_name); + undo_redo->add_undo_method(fncall.ptr(), "set_function", name); + } + } + } + undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); undo_redo->add_do_method(this, "_update_graph"); @@ -962,8 +1131,6 @@ void VisualScriptEditor::_member_edited() { undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); undo_redo->commit_action(); - // _update_graph(); - return; //or crash because it will become invalid } @@ -998,6 +1165,112 @@ void VisualScriptEditor::_member_edited() { } } +void VisualScriptEditor::_create_function_dialog() { + function_create_dialog->popup_centered(); + func_name_box->set_text(""); + func_name_box->grab_focus(); + for (int i = 0; i < func_input_vbox->get_child_count(); i++) { + Node *nd = func_input_vbox->get_child(i); + nd->queue_delete(); + } +} + +void VisualScriptEditor::_create_function() { + String name = _validate_name((func_name_box->get_text() == "") ? "new_func" : func_name_box->get_text()); + selected = name; + Vector2 ofs = _get_available_pos(); + + Ref<VisualScriptFunction> func_node; + func_node.instance(); + func_node->set_name(name); + + for (int i = 0; i < func_input_vbox->get_child_count(); i++) { + OptionButton *opbtn = Object::cast_to<OptionButton>(func_input_vbox->get_child(i)->get_child(3)); + LineEdit *lne = Object::cast_to<LineEdit>(func_input_vbox->get_child(i)->get_child(1)); + if (!opbtn || !lne) + continue; + Variant::Type arg_type = Variant::Type(opbtn->get_selected()); + String arg_name = lne->get_text(); + func_node->add_argument(arg_type, arg_name); + } + + undo_redo->create_action(TTR("Add Function")); + undo_redo->add_do_method(script.ptr(), "add_function", name); + undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_function", name); + undo_redo->add_do_method(this, "_update_members"); + undo_redo->add_undo_method(this, "_update_members"); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); + undo_redo->commit_action(); + + _update_graph(); +} + +void VisualScriptEditor::_add_node_dialog() { + _generic_search(script->get_instance_base_type(), graph->get_global_position() + Vector2(55, 80), true); +} + +void VisualScriptEditor::_add_func_input() { + HBoxContainer *hbox = memnew(HBoxContainer); + hbox->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *name_label = memnew(Label); + name_label->set_text(TTR("Name:")); + hbox->add_child(name_label); + + LineEdit *name_box = memnew(LineEdit); + name_box->set_h_size_flags(SIZE_EXPAND_FILL); + name_box->set_text("input"); + name_box->connect("focus_entered", this, "_deselect_input_names"); + hbox->add_child(name_box); + + Label *type_label = memnew(Label); + type_label->set_text(TTR("Type:")); + hbox->add_child(type_label); + + OptionButton *type_box = memnew(OptionButton); + type_box->set_custom_minimum_size(Size2(120 * EDSCALE, 0)); + for (int i = Variant::NIL; i < Variant::VARIANT_MAX; i++) + type_box->add_item(Variant::get_type_name(Variant::Type(i))); + type_box->select(1); + hbox->add_child(type_box); + + Button *delete_button = memnew(Button); + delete_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons")); + delete_button->set_tooltip(vformat(TTR("Delete input port"))); + hbox->add_child(delete_button); + + for (int i = 0; i < func_input_vbox->get_child_count(); i++) { + LineEdit *line_edit = (LineEdit *)func_input_vbox->get_child(i)->get_child(1); + line_edit->deselect(); + } + + func_input_vbox->add_child(hbox); + hbox->set_meta("id", hbox->get_position_in_parent()); + + delete_button->connect("pressed", this, "_remove_func_input", varray(hbox)); + + name_box->select_all(); + name_box->grab_focus(); +} + +void VisualScriptEditor::_remove_func_input(Node *p_node) { + func_input_vbox->remove_child(p_node); + p_node->queue_delete(); +} + +void VisualScriptEditor::_deselect_input_names() { + int cn = func_input_vbox->get_child_count(); + for (int i = 0; i < cn; i++) { + LineEdit *lne = Object::cast_to<LineEdit>(func_input_vbox->get_child(i)->get_child(1)); + if (lne) + lne->deselect(); + } +} + void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_button) { TreeItem *ti = Object::cast_to<TreeItem>(p_item); @@ -1010,7 +1283,6 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt //add function, this one uses menu if (p_button == 1) { - new_virtual_method_select->select_method_from_base_type(script->get_instance_base_type(), String(), true); return; @@ -1018,7 +1290,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt String name = _validate_name("new_function"); selected = name; - edited_func = selected; + Vector2 ofs = _get_available_pos(); Ref<VisualScriptFunction> func_node; func_node.instance(); @@ -1026,7 +1298,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt undo_redo->create_action(TTR("Add Function")); undo_redo->add_do_method(script.ptr(), "add_function", name); - undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node); + undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs); undo_redo->add_undo_method(script.ptr(), "remove_function", name); undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); @@ -1073,135 +1345,193 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt undo_redo->commit_action(); return; //or crash because it will become invalid } + } else if (ti->get_parent() == root->get_children()) { + selected = ti->get_text(0); + function_name_edit->set_position(Input::get_singleton()->get_mouse_position() - Vector2(60, -10)); + function_name_edit->popup(); + function_name_box->set_text(selected); + function_name_box->select_all(); } } -void VisualScriptEditor::_expression_text_changed(const String &p_text, int p_id) { +void VisualScriptEditor::_add_input_port(int p_id) { - Ref<VisualScriptExpression> vse = script->get_node(edited_func, p_id); - if (!vse.is_valid()) + StringName func = _get_function_of_node(p_id); + + Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + if (!vsn.is_valid()) return; updating_graph = true; - undo_redo->create_action(TTR("Change Expression"), UndoRedo::MERGE_ENDS); - undo_redo->add_do_property(vse.ptr(), "expression", p_text); - undo_redo->add_undo_property(vse.ptr(), "expression", vse->get("expression")); + undo_redo->create_action(TTR("Add Input Port"), UndoRedo::MERGE_ENDS); + undo_redo->add_do_method(vsn.ptr(), "add_input_data_port", Variant::NIL, "arg", -1); undo_redo->add_do_method(this, "_update_graph", p_id); + + undo_redo->add_undo_method(vsn.ptr(), "remove_input_data_port", vsn->get_input_value_port_count()); undo_redo->add_undo_method(this, "_update_graph", p_id); + + updating_graph = false; + undo_redo->commit_action(); +} - Node *node = graph->get_node(itos(p_id)); - if (Object::cast_to<Control>(node)) - Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); //shrink if text is smaller +void VisualScriptEditor::_add_output_port(int p_id) { + + StringName func = _get_function_of_node(p_id); + + Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + if (!vsn.is_valid()) + return; + + updating_graph = true; + + undo_redo->create_action(TTR("Add Output Port"), UndoRedo::MERGE_ENDS); + undo_redo->add_do_method(vsn.ptr(), "add_output_data_port", Variant::NIL, "arg", -1); + undo_redo->add_do_method(this, "_update_graph", p_id); + + undo_redo->add_undo_method(vsn.ptr(), "remove_output_data_port", vsn->get_output_value_port_count()); + undo_redo->add_undo_method(this, "_update_graph", p_id); updating_graph = false; + + undo_redo->commit_action(); } -void VisualScriptEditor::_available_node_doubleclicked() { +void VisualScriptEditor::_remove_input_port(int p_id, int p_port) { + + StringName func = _get_function_of_node(p_id); - if (edited_func == String()) + Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + if (!vsn.is_valid()) return; - TreeItem *item = nodes->get_selected(); + updating_graph = true; - if (!item) - return; + undo_redo->create_action(TTR("Remove Input Port"), UndoRedo::MERGE_ENDS); + + int conn_from = -1, conn_port = -1; + script->get_input_value_port_connection_source(func, p_id, p_port, &conn_from, &conn_port); + + if (conn_from != -1) + undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_id, p_port); + + undo_redo->add_do_method(vsn.ptr(), "remove_input_data_port", p_port); + undo_redo->add_do_method(this, "_update_graph", p_id); + + if (conn_from != -1) + undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_id, p_port); + + 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); + undo_redo->add_undo_method(this, "_update_graph", p_id); + + updating_graph = false; + + undo_redo->commit_action(); +} - String which = item->get_metadata(0); - if (which == String()) +void VisualScriptEditor::_remove_output_port(int p_id, int p_port) { + + StringName func = _get_function_of_node(p_id); + + Ref<VisualScriptLists> vsn = script->get_node(func, p_id); + if (!vsn.is_valid()) return; - Vector2 ofs = graph->get_scroll_ofs() + graph->get_size() * 0.5; - if (graph->is_using_snap()) { - int snap = graph->get_snap(); - ofs = ofs.snapped(Vector2(snap, snap)); - } + updating_graph = true; - ofs /= EDSCALE; + undo_redo->create_action(TTR("Remove Output Port"), UndoRedo::MERGE_ENDS); - while (true) { - bool exists = false; - List<int> existing; - script->get_node_list(edited_func, &existing); - for (List<int>::Element *E = existing.front(); E; E = E->next()) { - Point2 pos = script->get_node_position(edited_func, E->get()); - if (pos.distance_to(ofs) < 15) { - ofs += Vector2(graph->get_snap(), graph->get_snap()); - exists = true; - break; - } + List<VisualScript::DataConnection> data_connections; + script->get_data_connection_list(func, &data_connections); + + HashMap<int, Set<int> > conn_map; + for (const List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { + if (E->get().from_node == p_id && E->get().from_port == p_port) { + // push into the connections map + if (!conn_map.has(E->get().to_node)) + conn_map.set(E->get().to_node, Set<int>()); + conn_map[E->get().to_node].insert(E->get().to_port); } + } - if (exists) - continue; - break; + undo_redo->add_do_method(vsn.ptr(), "remove_output_data_port", p_port); + undo_redo->add_do_method(this, "_update_graph", p_id); + + List<int> keys; + conn_map.get_key_list(&keys); + for (const List<int>::Element *E = keys.front(); E; E = E->next()) { + for (const Set<int>::Element *F = conn_map[E->get()].front(); F; F = F->next()) { + undo_redo->add_undo_method(script.ptr(), "data_connect", func, p_id, p_port, E->get(), F->get()); + } } - Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(which); - int new_id = script->get_available_id(); + 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); + undo_redo->add_undo_method(this, "_update_graph", p_id); - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + updating_graph = false; - Node *node = graph->get_node(itos(new_id)); - if (node) { - graph->set_selected(node); - _node_selected(node); - } + undo_redo->commit_action(); } -void VisualScriptEditor::_update_available_nodes() { +void VisualScriptEditor::_expression_text_changed(const String &p_text, int p_id) { - nodes->clear(); + StringName func = _get_function_of_node(p_id); - TreeItem *root = nodes->create_item(); + Ref<VisualScriptExpression> vse = script->get_node(func, p_id); + if (!vse.is_valid()) + return; - Map<String, TreeItem *> path_cache; + updating_graph = true; - String filter = node_filter->get_text(); + undo_redo->create_action(TTR("Change Expression"), UndoRedo::MERGE_ENDS); + undo_redo->add_do_property(vse.ptr(), "expression", p_text); + undo_redo->add_undo_property(vse.ptr(), "expression", vse->get("expression")); + undo_redo->add_do_method(this, "_update_graph", p_id); + undo_redo->add_undo_method(this, "_update_graph", p_id); + undo_redo->commit_action(); - List<String> fnodes; - VisualScriptLanguage::singleton->get_registered_node_names(&fnodes); + Node *node = graph->get_node(itos(p_id)); + if (Object::cast_to<Control>(node)) + Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); //shrink if text is smaller - for (List<String>::Element *E = fnodes.front(); E; E = E->next()) { + updating_graph = false; +} - Vector<String> path = E->get().split("/"); +Vector2 VisualScriptEditor::_get_available_pos(bool centered, Vector2 ofs) const { + if (centered) + ofs = graph->get_scroll_ofs() + graph->get_size() * 0.5; - if (filter != String() && path.size() && path[path.size() - 1].findn(filter) == -1) - continue; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } + + ofs /= EDSCALE; - String sp; - TreeItem *parent = root; - - for (int i = 0; i < path.size() - 1; i++) { - - if (i > 0) - sp += ","; - sp += path[i]; - if (!path_cache.has(sp)) { - TreeItem *pathn = nodes->create_item(parent); - pathn->set_selectable(0, false); - pathn->set_text(0, path[i].capitalize()); - path_cache[sp] = pathn; - parent = pathn; - if (filter == String()) { - pathn->set_collapsed(true); //should remember state + while (true) { + bool exists = false; + List<StringName> all_fn; + script->get_function_list(&all_fn); + for (List<StringName>::Element *F = all_fn.front(); F; F = F->next()) { + StringName curr_fn = F->get(); + List<int> existing; + script->get_node_list(curr_fn, &existing); + for (List<int>::Element *E = existing.front(); E; E = E->next()) { + Point2 pos = script->get_node_position(curr_fn, E->get()); + if (pos.distance_to(ofs) < 50) { + ofs += Vector2(graph->get_snap(), graph->get_snap()); + exists = true; + break; } - } else { - parent = path_cache[sp]; } } - - TreeItem *item = nodes->create_item(parent); - item->set_text(0, path[path.size() - 1].capitalize()); - item->set_selectable(0, true); - item->set_metadata(0, E->get()); + if (exists) + continue; + break; } + + return ofs; } String VisualScriptEditor::_validate_name(const String &p_name) const { @@ -1227,6 +1557,8 @@ String VisualScriptEditor::_validate_name(const String &p_name) const { void VisualScriptEditor::_on_nodes_delete() { + // delete all the selected nodes + List<int> to_erase; for (int i = 0; i < graph->get_child_count(); i++) { @@ -1245,26 +1577,30 @@ void VisualScriptEditor::_on_nodes_delete() { for (List<int>::Element *F = to_erase.front(); F; F = F->next()) { - undo_redo->add_do_method(script.ptr(), "remove_node", edited_func, F->get()); - undo_redo->add_undo_method(script.ptr(), "add_node", edited_func, F->get(), script->get_node(edited_func, F->get()), script->get_node_position(edited_func, F->get())); + int cr_node = F->get(); + + StringName func = _get_function_of_node(cr_node); + + undo_redo->add_do_method(script.ptr(), "remove_node", func, cr_node); + 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)); List<VisualScript::SequenceConnection> sequence_conns; - script->get_sequence_connection_list(edited_func, &sequence_conns); + script->get_sequence_connection_list(func, &sequence_conns); for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { - if (E->get().from_node == F->get() || E->get().to_node == F->get()) { - undo_redo->add_undo_method(script.ptr(), "sequence_connect", edited_func, E->get().from_node, E->get().from_output, E->get().to_node); + if (E->get().from_node == cr_node || E->get().to_node == cr_node) { + undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, E->get().from_node, E->get().from_output, E->get().to_node); } } List<VisualScript::DataConnection> data_conns; - script->get_data_connection_list(edited_func, &data_conns); + script->get_data_connection_list(func, &data_conns); for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) { if (E->get().from_node == F->get() || E->get().to_node == F->get()) { - undo_redo->add_undo_method(script.ptr(), "data_connect", edited_func, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + 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); } } } @@ -1276,13 +1612,16 @@ void VisualScriptEditor::_on_nodes_delete() { void VisualScriptEditor::_on_nodes_duplicate() { - List<int> to_duplicate; + Set<int> to_duplicate; + List<StringName> funcs; for (int i = 0; i < graph->get_child_count(); i++) { GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); if (gn) { if (gn->is_selected() && gn->is_close_button_visible()) { - to_duplicate.push_back(gn->get_name().operator String().to_int()); + int id = gn->get_name().operator String().to_int(); + to_duplicate.insert(id); + funcs.push_back(_get_function_of_node(id)); } } } @@ -1294,18 +1633,42 @@ void VisualScriptEditor::_on_nodes_duplicate() { int idc = script->get_available_id() + 1; Set<int> to_select; + HashMap<int, int> remap; - for (List<int>::Element *F = to_duplicate.front(); F; F = F->next()) { + for (Set<int>::Element *F = to_duplicate.front(); F; F = F->next()) { - Ref<VisualScriptNode> node = script->get_node(edited_func, F->get()); + // duplicate from the specifc function but place it into the default func as it would lack the connections + StringName func = _get_function_of_node(F->get()); + Ref<VisualScriptNode> node = script->get_node(func, F->get()); Ref<VisualScriptNode> dupe = node->duplicate(true); int new_id = idc++; + remap.set(F->get(), new_id); + to_select.insert(new_id); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, dupe, script->get_node_position(edited_func, F->get()) + Vector2(20, 20)); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, dupe, script->get_node_position(func, F->get()) + Vector2(20, 20)); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); + } + + for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { + List<VisualScript::SequenceConnection> seqs; + script->get_sequence_connection_list(F->get(), &seqs); + for (List<VisualScript::SequenceConnection>::Element *E = seqs.front(); E; E = E->next()) { + if (to_duplicate.has(E->get().from_node) && to_duplicate.has(E->get().to_node)) { + 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]); + } + } + + List<VisualScript::DataConnection> data; + script->get_data_connection_list(F->get(), &data); + for (List<VisualScript::DataConnection>::Element *E = data.front(); E; E = E->next()) { + if (to_duplicate.has(E->get().from_node) && to_duplicate.has(E->get().to_node)) { + 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); + } + } } + undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -1320,22 +1683,45 @@ void VisualScriptEditor::_on_nodes_duplicate() { } if (to_select.size()) { - EditorNode::get_singleton()->push_item(script->get_node(edited_func, to_select.front()->get()).ptr()); + EditorNode::get_singleton()->push_item(script->get_node(default_func, to_select.front()->get()).ptr()); } } -void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) { +void VisualScriptEditor::_generic_search(String p_base_type, Vector2 pos, bool node_centered) { + if (node_centered) + port_action_pos = graph->get_size() / 2.0f; + else + port_action_pos = graph->get_viewport()->get_mouse_position() - graph->get_global_position(); + + new_connect_node_select->select_from_visual_script(p_base_type, false, false); // neither connecting nor reset text - Ref<InputEventMouseButton> mb = p_event; + // ensure that the dialog fits inside the graph + Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size(); + pos.x = pos.x > bounds.x ? bounds.x : pos.x; + pos.y = pos.y > bounds.y ? bounds.y : pos.y; - if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - revert_on_drag = String(); //so we can still drag functions + if (pos != Vector2()) + new_connect_node_select->set_position(pos); +} + +void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) { + // GUI input for VS Editor Plugin + Ref<InputEventMouseButton> key = p_event; + + if (key.is_valid() && !key->is_pressed()) { + mouse_up_position = Input::get_singleton()->get_mouse_position(); } } -void VisualScriptEditor::_generic_search(String p_base_type) { - port_action_pos = graph->get_viewport()->get_mouse_position() - graph->get_global_position(); - new_connect_node_select->select_from_visual_script(p_base_type, false); +void VisualScriptEditor::_graph_gui_input(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> key = p_event; + + if (key.is_valid() && key->is_pressed() && key->get_button_mask() == BUTTON_RIGHT) { + saved_position = graph->get_local_mouse_position(); + + Point2 gpos = Input::get_singleton()->get_mouse_position(); + _generic_search(script->get_instance_base_type(), gpos); + } } void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) { @@ -1365,28 +1751,82 @@ void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) { } } } + + Ref<InputEventMouseButton> btn = p_event; + if (btn.is_valid() && btn->is_doubleclick()) { + TreeItem *ti = members->get_selected(); + if (ti && ti->get_parent() == members->get_root()->get_children()) // to check if it's a function + _center_on_node(ti->get_metadata(0), script->get_function_node_id(ti->get_metadata(0))); + } } -Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { +void VisualScriptEditor::_rename_function(const String &name, const String &new_name) { - if (p_from == nodes) { + if (!new_name.is_valid_identifier()) { - TreeItem *it = nodes->get_item_at_position(p_point); - if (!it) - return Variant(); - String type = it->get_metadata(0); - if (type == String()) - return Variant(); + EditorNode::get_singleton()->show_warning(TTR("Name is not a valid identifier:") + " " + new_name); + return; + } - Dictionary dd; - dd["type"] = "visual_script_node_drag"; - dd["node_type"] = type; + if (script->has_function(new_name) || script->has_variable(new_name) || script->has_custom_signal(new_name)) { - Label *label = memnew(Label); - label->set_text(it->get_text(0)); - set_drag_preview(label); - return dd; + EditorNode::get_singleton()->show_warning(TTR("Name already in use by another func/var/signal:") + " " + new_name); + return; + } + + int node_id = script->get_function_node_id(name); + Ref<VisualScriptFunction> func; + if (script->has_node(name, node_id)) { + func = script->get_node(name, node_id); + } + undo_redo->create_action(TTR("Rename Function")); + undo_redo->add_do_method(script.ptr(), "rename_function", name, new_name); + undo_redo->add_undo_method(script.ptr(), "rename_function", new_name, name); + if (func.is_valid()) { + undo_redo->add_do_method(func.ptr(), "set_name", new_name); + undo_redo->add_undo_method(func.ptr(), "set_name", name); + } + + // also fix all function calls + List<StringName> flst; + script->get_function_list(&flst); + for (List<StringName>::Element *E = flst.front(); E; E = E->next()) { + List<int> lst; + script->get_node_list(E->get(), &lst); + for (List<int>::Element *F = lst.front(); F; F = F->next()) { + Ref<VisualScriptFunctionCall> fncall = script->get_node(E->get(), F->get()); + if (!fncall.is_valid()) + continue; + if (fncall->get_function() == name) { + undo_redo->add_do_method(fncall.ptr(), "set_function", new_name); + undo_redo->add_undo_method(fncall.ptr(), "set_function", name); + } + } + } + + undo_redo->add_do_method(this, "_update_members"); + undo_redo->add_undo_method(this, "_update_members"); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); + undo_redo->commit_action(); +} + +void VisualScriptEditor::_fn_name_box_input(const Ref<InputEvent> &p_event) { + + if (!function_name_edit->is_visible()) + return; + + Ref<InputEventKey> key = p_event; + if (key.is_valid() && key->is_pressed() && key->get_scancode() == KEY_ENTER) { + function_name_edit->hide(); + _rename_function(selected, function_name_box->get_text()); + function_name_box->clear(); } +} + +Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { if (p_from == members) { @@ -1406,11 +1846,6 @@ Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_f dd["type"] = "visual_script_function_drag"; dd["function"] = type; - if (revert_on_drag != String()) { - edited_func = revert_on_drag; //revert so function does not change - revert_on_drag = String(); - _update_graph(); - } } else if (it->get_parent() == root->get_children()->get_next()) { dd["type"] = "visual_script_variable_drag"; @@ -1530,15 +1965,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da ofs /= EDSCALE; - Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(d["node_type"]); - int new_id = script->get_available_id(); - - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph"); - undo_redo->add_undo_method(this, "_update_graph"); - undo_redo->commit_action(); + int new_id = _create_new_node_from_name(d["node_type"], ofs, default_func); Node *node = graph->get_node(itos(new_id)); if (node) { @@ -1579,8 +2006,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -1609,11 +2036,11 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs); undo_redo->add_do_method(vnode.ptr(), "set_base_type", script->get_instance_base_type()); undo_redo->add_do_method(vnode.ptr(), "set_function", d["function"]); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -1642,8 +2069,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -1672,8 +2099,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Preload Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, prnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, prnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -1713,8 +2140,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da prnode.instance(); prnode->set_preload(res); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, prnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, prnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); new_ids.push_back(new_id); new_id++; ofs += Vector2(20, 20) * EDSCALE; @@ -1740,7 +2167,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script); if (!sn) { - EditorNode::get_singleton()->show_warning("Can't drop nodes because script '" + get_name() + "' is not used in this scene."); + EditorNode::get_singleton()->show_warning(TTR("Can't drop nodes because script '" + get_name() + "' is not used in this scene.")); return; } @@ -1782,20 +2209,20 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da scene_node.instance(); scene_node->set_node_path(sn->get_path_to(node)); n = scene_node; - } else { + // ! Doesn't work properly Ref<VisualScriptFunctionCall> call; call.instance(); call->set_call_mode(VisualScriptFunctionCall::CALL_MODE_NODE_PATH); call->set_base_path(sn->get_path_to(node)); call->set_base_type(node->get_class()); n = call; - method_select->select_from_instance(node); + method_select->select_from_instance(node, "", true, node->get_class()); selecting_method_id = base_id; } - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, base_id, n, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, base_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, n, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id); base_id++; ofs += Vector2(25, 25); @@ -1810,7 +2237,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script); if (!sn && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { - EditorNode::get_singleton()->show_warning("Can't drop properties because script '" + get_name() + "' is not used in this scene.\nDrop holding 'Shift' to just copy the signature."); + EditorNode::get_singleton()->show_warning(TTR("Can't drop properties because script '" + get_name() + "' is not used in this scene.\nDrop holding 'Shift' to just copy the signature.")); return; } @@ -1866,13 +2293,13 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da vnode = pget; } - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, base_id, vnode, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, vnode, ofs); undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]); if (!use_get) { undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]); } - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, base_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -1913,12 +2340,12 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } vnode = pget; } - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, base_id, vnode, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, vnode, ofs); undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]); if (!use_get) { undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]); } - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, base_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -1929,7 +2356,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type, const bool p_connecting) { - Ref<VisualScriptFunctionCall> vsfc = script->get_node(edited_func, selecting_method_id); + Ref<VisualScriptFunctionCall> vsfc = script->get_node(default_func, selecting_method_id); if (!vsfc.is_valid()) return; vsfc->set_function(p_method); @@ -1986,8 +2413,16 @@ void VisualScriptEditor::set_edited_resource(const RES &p_res) { script->connect("node_ports_changed", this, "_node_ports_changed"); + default_func = script->get_default_func(); + + if (!script->has_function(default_func)) // this is the supposed default function + { + script->add_function(default_func); + script->set_edited(true); //so that if a function was added it's saved + } + + _update_graph(); _update_members(); - _update_available_nodes(); } Vector<String> VisualScriptEditor::get_functions() { @@ -2032,7 +2467,7 @@ bool VisualScriptEditor::is_unsaved() { Variant VisualScriptEditor::get_edit_state() { Dictionary d; - d["function"] = edited_func; + d["function"] = default_func; d["scroll"] = graph->get_scroll_ofs(); d["zoom"] = graph->get_zoom(); d["using_snap"] = graph->is_using_snap(); @@ -2044,8 +2479,7 @@ void VisualScriptEditor::set_edit_state(const Variant &p_state) { Dictionary d = p_state; if (d.has("function")) { - edited_func = d["function"]; - selected = edited_func; + selected = default_func; } _update_graph(); @@ -2065,16 +2499,24 @@ void VisualScriptEditor::set_edit_state(const Variant &p_state) { } } -void VisualScriptEditor::_center_on_node(int p_id) { +void VisualScriptEditor::_center_on_node(const StringName &p_func, int p_id) { Node *n = graph->get_node(itos(p_id)); GraphNode *gn = Object::cast_to<GraphNode>(n); + + // clear selection + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gnd = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gnd) + gnd->set_selected(false); + } + if (gn) { gn->set_selected(true); Vector2 new_scroll = gn->get_offset() - graph->get_size() * 0.5 + gn->get_size() * 0.5; graph->set_scroll_ofs(new_scroll); - script->set_function_scroll(edited_func, new_scroll / EDSCALE); - script->set_edited(true); //so it's saved + script->set_function_scroll(p_func, new_scroll / EDSCALE); + script->set_edited(true); } } @@ -2091,13 +2533,10 @@ void VisualScriptEditor::goto_line(int p_line, bool p_with_error) { if (script->has_node(E->get(), p_line)) { - edited_func = E->get(); - selected = edited_func; _update_graph(); _update_members(); - call_deferred("call_deferred", "_center_on_node", p_line); //editor might be just created and size might not exist yet - + call_deferred("call_deferred", "_center_on_node", E->get(), p_line); //editor might be just created and size might not exist yet return; } } @@ -2132,6 +2571,7 @@ void VisualScriptEditor::tag_saved_version() { } void VisualScriptEditor::reload(bool p_soft) { + _update_graph(); } void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) { @@ -2155,10 +2595,9 @@ void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) { void VisualScriptEditor::add_callback(const String &p_function, PoolStringArray p_args) { if (script->has_function(p_function)) { - edited_func = p_function; - selected = edited_func; _update_members(); _update_graph(); + _center_on_node(p_function, script->get_function_node_id(p_function)); return; } @@ -2189,13 +2628,10 @@ void VisualScriptEditor::add_callback(const String &p_function, PoolStringArray script->add_function(p_function); script->add_node(p_function, script->get_available_id(), func); - edited_func = p_function; - selected = edited_func; _update_members(); _update_graph(); - graph->call_deferred("set_scroll_ofs", script->get_function_scroll(edited_func)); //for first time it might need to be later - //undo_redo->clear_history(); + _center_on_node(p_function, script->get_function_node_id(p_function)); } bool VisualScriptEditor::show_members_overview() { @@ -2233,7 +2669,7 @@ void VisualScriptEditor::_toggle_tool_script() { void VisualScriptEditor::clear_edit_menu() { memdelete(edit_menu); - memdelete(left_vsplit); + memdelete(members_section); } void VisualScriptEditor::_change_base_type_callback() { @@ -2297,46 +2733,66 @@ void VisualScriptEditor::_end_node_move() { undo_redo->commit_action(); } -void VisualScriptEditor::_move_node(String func, int p_id, const Vector2 &p_to) { +void VisualScriptEditor::_move_node(const StringName &p_func, int p_id, const Vector2 &p_to) { + + if (!script->has_function(p_func)) + return; + + Node *node = graph->get_node(itos(p_id)); + + if (Object::cast_to<GraphNode>(node)) + Object::cast_to<GraphNode>(node)->set_offset(p_to); - if (func == String(edited_func)) { - Node *node = graph->get_node(itos(p_id)); - if (Object::cast_to<GraphNode>(node)) - Object::cast_to<GraphNode>(node)->set_offset(p_to); + script->set_node_position(p_func, p_id, p_to / EDSCALE); +} + +StringName VisualScriptEditor::_get_function_of_node(int p_id) const { + + List<StringName> funcs; + script->get_function_list(&funcs); + for (List<StringName>::Element *E = funcs.front(); E; E = E->next()) { + if (script->has_node(E->get(), p_id)) { + return E->get(); + } } - script->set_node_position(edited_func, p_id, p_to / EDSCALE); + + return ""; // this is passed to avoid crash and is tested against later } void VisualScriptEditor::_node_moved(Vector2 p_from, Vector2 p_to, int p_id) { - undo_redo->add_do_method(this, "_move_node", String(edited_func), p_id, p_to); - undo_redo->add_undo_method(this, "_move_node", String(edited_func), p_id, p_from); + StringName func = _get_function_of_node(p_id); + + undo_redo->add_do_method(this, "_move_node", func, p_id, p_to); + undo_redo->add_undo_method(this, "_move_node", func, p_id, p_from); } void VisualScriptEditor::_remove_node(int p_id) { undo_redo->create_action(TTR("Remove VisualScript Node")); - undo_redo->add_do_method(script.ptr(), "remove_node", edited_func, p_id); - undo_redo->add_undo_method(script.ptr(), "add_node", edited_func, p_id, script->get_node(edited_func, p_id), script->get_node_position(edited_func, p_id)); + StringName func = _get_function_of_node(p_id); + + undo_redo->add_do_method(script.ptr(), "remove_node", func, p_id); + 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)); List<VisualScript::SequenceConnection> sequence_conns; - script->get_sequence_connection_list(edited_func, &sequence_conns); + script->get_sequence_connection_list(func, &sequence_conns); for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { if (E->get().from_node == p_id || E->get().to_node == p_id) { - undo_redo->add_undo_method(script.ptr(), "sequence_connect", edited_func, E->get().from_node, E->get().from_output, E->get().to_node); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, E->get().from_node, E->get().from_output, E->get().to_node); } } List<VisualScript::DataConnection> data_conns; - script->get_data_connection_list(edited_func, &data_conns); + script->get_data_connection_list(func, &data_conns); for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) { if (E->get().from_node == p_id || E->get().to_node == p_id) { - undo_redo->add_undo_method(script.ptr(), "data_connect", edited_func, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + 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); } } @@ -2348,15 +2804,29 @@ void VisualScriptEditor::_remove_node(int p_id) { void VisualScriptEditor::_node_ports_changed(const String &p_func, int p_id) { - if (p_func != String(edited_func)) - return; - _update_graph(p_id); } +bool VisualScriptEditor::node_has_sequence_connections(const StringName &p_func, int p_id) { + List<VisualScript::SequenceConnection> sequence_conns; + script->get_sequence_connection_list(p_func, &sequence_conns); + + for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { + int from = E->get().from_node; + int to = E->get().to_node; + + if (to == p_id || from == p_id) + return true; + } + + return false; +} + void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot) { - Ref<VisualScriptNode> from_node = script->get_node(edited_func, p_from.to_int()); + StringName from_func = _get_function_of_node(p_from.to_int()); + + Ref<VisualScriptNode> from_node = script->get_node(from_func, p_from.to_int()); ERR_FAIL_COND(!from_node.is_valid()); bool from_seq; @@ -2365,7 +2835,9 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, if (!_get_out_slot(from_node, p_from_slot, from_port, from_seq)) return; //can't connect this, it's invalid - Ref<VisualScriptNode> to_node = script->get_node(edited_func, p_to.to_int()); + StringName to_func = _get_function_of_node(p_to.to_int()); + + Ref<VisualScriptNode> to_node = script->get_node(to_func, p_to.to_int()); ERR_FAIL_COND(!to_node.is_valid()); bool to_seq; @@ -2376,29 +2848,170 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, ERR_FAIL_COND(from_seq != to_seq); + // Do all the checks here + StringName func; // this the func where we store the one the nodes at the end of the resolution on having multiple nodes + undo_redo->create_action(TTR("Connect Nodes")); + if (from_func == to_func) { + func = to_func; + } else if (from_seq) { + // this is a sequence connection + _move_nodes_with_rescan(to_func, from_func, p_to.to_int()); // this function moves the nodes from func1 to func2 + func = from_func; + } else { + if (node_has_sequence_connections(to_func, p_to.to_int())) { + if (node_has_sequence_connections(from_func, p_from.to_int())) { + ERR_PRINT("Trying to connect between different sequence node trees"); + return; + } else { + _move_nodes_with_rescan(from_func, to_func, p_from.to_int()); + func = to_func; + } + } else if (node_has_sequence_connections(from_func, p_from.to_int())) { + if (from_func == default_func) { + _move_nodes_with_rescan(from_func, to_func, p_from.to_int()); + func = to_func; + } else { + _move_nodes_with_rescan(to_func, from_func, p_to.to_int()); + func = from_func; + } + } else { + if (to_func == default_func) { + _move_nodes_with_rescan(to_func, from_func, p_to.to_int()); + func = from_func; + } else { + _move_nodes_with_rescan(from_func, to_func, p_from.to_int()); + func = to_func; + } + } + } + if (from_seq) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, p_from.to_int(), from_port, p_to.to_int()); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int()); + undo_redo->add_do_method(script.ptr(), "sequence_connect", func, p_from.to_int(), from_port, p_to.to_int()); + // this undo error on undo after move can't be removed without painful gymnastics + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, p_from.to_int(), from_port, p_to.to_int()); } else { + bool converted = false; + int conv_node = -1; + + Ref<VisualScriptOperator> oper = to_node; + if (oper.is_valid() && oper->get_typed() == Variant::NIL) { + // it's an operator Node and if the type is already nil + if (from_node->get_output_value_port_info(from_port).type != Variant::NIL) { + oper->set_typed(from_node->get_output_value_port_info(from_port).type); + } + } - // disconnect current, and connect the new one - if (script->is_input_value_port_connected(edited_func, p_to.to_int(), to_port)) { - int conn_from; - int conn_port; - script->get_input_value_port_connection_source(edited_func, p_to.to_int(), to_port, &conn_from, &conn_port); - undo_redo->add_do_method(script.ptr(), "data_disconnect", edited_func, conn_from, conn_port, p_to.to_int(), to_port); - undo_redo->add_undo_method(script.ptr(), "data_connect", edited_func, conn_from, conn_port, p_to.to_int(), to_port); + Ref<VisualScriptOperator> operf = from_node; + if (operf.is_valid() && operf->get_typed() == Variant::NIL) { + // it's an operator Node and if the type is already nil + if (to_node->get_input_value_port_info(to_port).type != Variant::NIL) { + operf->set_typed(to_node->get_input_value_port_info(to_port).type); + } } - undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, p_from.to_int(), from_port, p_to.to_int(), to_port); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int(), to_port); - //update nodes in sgraph - undo_redo->add_do_method(this, "_update_graph", p_from.to_int()); - undo_redo->add_do_method(this, "_update_graph", p_to.to_int()); - undo_redo->add_undo_method(this, "_update_graph", p_from.to_int()); - undo_redo->add_undo_method(this, "_update_graph", p_to.to_int()); + Variant::Type to_type = to_node->get_input_value_port_info(to_port).type; + Variant::Type from_type = from_node->get_output_value_port_info(from_port).type; + + if (to_type != Variant::NIL && from_type != Variant::NIL && to_type != from_type) { + // add a constructor node between the ports + bool exceptions = false; // true if there are any exceptions + exceptions = exceptions || (to_type == Variant::INT && from_type == Variant::REAL); + exceptions = exceptions || (to_type == Variant::REAL && from_type == Variant::INT); + if (Variant::can_convert(from_type, to_type) && !exceptions) { + MethodInfo mi; + mi.name = Variant::get_type_name(to_type); + PropertyInfo pi; + pi.name = "from"; + pi.type = from_type; + mi.arguments.push_back(pi); + mi.return_val.type = to_type; + // we know that this is allowed so create a new constructor node + Ref<VisualScriptConstructor> constructor; + constructor.instance(); + constructor->set_constructor_type(to_type); + constructor->set_constructor(mi); + // add the new constructor node + + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(p_from)); + GraphNode *gn2 = Object::cast_to<GraphNode>(graph->get_node(p_to)); + if (gn && gn2) { + Vector2 from_node_size = gn->get_rect().get_size(); + Vector2 to_node_size = gn2->get_rect().get_size(); + Vector2 to_node_pos = script->get_node_position(func, p_to.to_int()); + Vector2 from_node_pos = script->get_node_position(func, p_from.to_int()); + Vector2 new_to_node_pos = from_node_pos; + Vector2 constructor_pos; + if ((to_node_pos.x - from_node_pos.x) < 0) { + // to is behind from node + if (to_node_pos.x > (from_node_pos.x - to_node_size.x - 240)) + new_to_node_pos.x = from_node_pos.x - to_node_size.x - 240; // approx size of construtor node + padding + else + new_to_node_pos.x = to_node_pos.x; + new_to_node_pos.y = to_node_pos.y; + constructor_pos.x = from_node_pos.x - 210; + constructor_pos.y = to_node_pos.y; + } else { + // to is ahead of from node + if (to_node_pos.x < (from_node_size.x + from_node_pos.x + 240)) + new_to_node_pos.x = from_node_size.x + from_node_pos.x + 240; // approx size of construtor node + padding + else + new_to_node_pos.x = to_node_pos.x; + new_to_node_pos.y = to_node_pos.y; + constructor_pos.x = from_node_size.x + from_node_pos.x + 10; + constructor_pos.y = to_node_pos.y; + } + undo_redo->add_do_method(this, "_move_node", func, p_to.to_int(), new_to_node_pos); + undo_redo->add_undo_method(this, "_move_node", func, p_to.to_int(), to_node_pos); + conv_node = script->get_available_id(); + undo_redo->add_do_method(script.ptr(), "add_node", func, conv_node, constructor, _get_available_pos(false, constructor_pos)); + undo_redo->add_undo_method(script.ptr(), "remove_node", func, conv_node); + converted = true; + } + } + } + + // disconnect current, and connect the new one + if (script->is_input_value_port_connected(func, p_to.to_int(), to_port)) { + if (can_swap && data_disconnect_node == p_to.to_int()) { + int conn_from; + int conn_port; + script->get_input_value_port_connection_source(func, p_to.to_int(), to_port, &conn_from, &conn_port); + undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_to.to_int(), to_port); + undo_redo->add_do_method(script.ptr(), "data_connect", func, conn_from, conn_port, data_disconnect_node, data_disconnect_port); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, data_disconnect_node, data_disconnect_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_to.to_int(), to_port); + can_swap = false; // swapped + } else { + int conn_from; + int conn_port; + script->get_input_value_port_connection_source(func, p_to.to_int(), to_port, &conn_from, &conn_port); + undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_to.to_int(), to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_to.to_int(), to_port); + } + } + if (!converted) { + undo_redo->add_do_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, p_to.to_int(), to_port); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, p_to.to_int(), to_port); + } else { + // this is noice + undo_redo->add_do_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, conv_node, 0); + undo_redo->add_do_method(script.ptr(), "data_connect", func, conv_node, 0, p_to.to_int(), to_port); + // I don't think this is needed but gonna leave it here for now... until I need to finalise it all + undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, conv_node, 0); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, conv_node, 0, p_to.to_int(), to_port); + } + //update nodes in graph + if (!converted) { + undo_redo->add_do_method(this, "_update_graph", p_from.to_int()); + undo_redo->add_do_method(this, "_update_graph", p_to.to_int()); + undo_redo->add_undo_method(this, "_update_graph", p_from.to_int()); + undo_redo->add_undo_method(this, "_update_graph", p_to.to_int()); + } else { + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + } } undo_redo->add_do_method(this, "_update_graph_connections"); @@ -2409,7 +3022,10 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot) { - Ref<VisualScriptNode> from_node = script->get_node(edited_func, p_from.to_int()); + StringName func = _get_function_of_node(p_from.to_int()); + ERR_FAIL_COND(func != _get_function_of_node(p_to.to_int())); + + Ref<VisualScriptNode> from_node = script->get_node(func, p_from.to_int()); ERR_FAIL_COND(!from_node.is_valid()); bool from_seq; @@ -2418,7 +3034,7 @@ void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_sl if (!_get_out_slot(from_node, p_from_slot, from_port, from_seq)) return; //can't connect this, it's invalid - Ref<VisualScriptNode> to_node = script->get_node(edited_func, p_to.to_int()); + Ref<VisualScriptNode> to_node = script->get_node(func, p_to.to_int()); ERR_FAIL_COND(!to_node.is_valid()); bool to_seq; @@ -2429,15 +3045,20 @@ void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_sl ERR_FAIL_COND(from_seq != to_seq); - undo_redo->create_action(TTR("Connect Nodes")); + undo_redo->create_action(TTR("Disconnect Nodes")); if (from_seq) { - undo_redo->add_do_method(script.ptr(), "sequence_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int()); - undo_redo->add_undo_method(script.ptr(), "sequence_connect", edited_func, p_from.to_int(), from_port, p_to.to_int()); + undo_redo->add_do_method(script.ptr(), "sequence_disconnect", func, p_from.to_int(), from_port, p_to.to_int()); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, p_from.to_int(), from_port, p_to.to_int()); } else { - undo_redo->add_do_method(script.ptr(), "data_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int(), to_port); - undo_redo->add_undo_method(script.ptr(), "data_connect", edited_func, p_from.to_int(), from_port, p_to.to_int(), to_port); - //update nodes in sgraph + + can_swap = true; + data_disconnect_node = p_to.to_int(); + data_disconnect_port = to_port; + + undo_redo->add_do_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, p_to.to_int(), to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, p_to.to_int(), to_port); + //update relevant nodes in the graph undo_redo->add_do_method(this, "_update_graph", p_from.to_int()); undo_redo->add_do_method(this, "_update_graph", p_to.to_int()); undo_redo->add_undo_method(this, "_update_graph", p_from.to_int()); @@ -2449,6 +3070,216 @@ void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_sl undo_redo->commit_action(); } +void VisualScriptEditor::_move_nodes_with_rescan(const StringName &p_func_from, const StringName &p_func_to, int p_id) { + + Set<int> nodes_to_move; + HashMap<int, Map<int, int> > seqconns_to_move; // from => List(outp, to) + HashMap<int, Map<int, Pair<int, int> > > dataconns_to_move; // to => List(inp_p => from, outp) + + nodes_to_move.insert(p_id); + Set<int> sequence_connections; + { + List<VisualScript::SequenceConnection> sequence_conns; + script->get_sequence_connection_list(p_func_from, &sequence_conns); + + HashMap<int, Map<int, int> > seqcons; // from => List(out_p => to) + + for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) { + int from = E->get().from_node; + int to = E->get().to_node; + int out_p = E->get().from_output; + if (!seqcons.has(from)) + seqcons.set(from, Map<int, int>()); + seqcons[from].insert(out_p, to); + sequence_connections.insert(to); + sequence_connections.insert(from); + } + + int conn = p_id; + List<int> stack; + HashMap<int, Set<int> > seen; // from, outp + while (seqcons.has(conn)) { + for (auto E = seqcons[conn].front(); E; E = E->next()) { + if (seen.has(conn) && seen[conn].has(E->key())) { + if (!E->next()) { + if (stack.size() > 0) { + conn = stack.back()->get(); + stack.pop_back(); + break; + } + conn = -101; + break; + } + continue; + } + if (!seen.has(conn)) + seen.set(conn, Set<int>()); + seen[conn].insert(E->key()); + stack.push_back(conn); + if (!seqconns_to_move.has(conn)) + seqconns_to_move.set(conn, Map<int, int>()); + seqconns_to_move[conn].insert(E->key(), E->get()); + conn = E->get(); + nodes_to_move.insert(conn); + break; + } + if (!seqcons.has(conn) && stack.size() > 0) { + conn = stack.back()->get(); + stack.pop_back(); + } + } + } + + { + List<VisualScript::DataConnection> data_connections; + script->get_data_connection_list(p_func_from, &data_connections); + + HashMap<int, Map<int, Pair<int, int> > > connections; + + for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { + int from = E->get().from_node; + int to = E->get().to_node; + int out_p = E->get().from_port; + int in_p = E->get().to_port; + + if (!connections.has(to)) + connections.set(to, Map<int, Pair<int, int> >()); + connections[to].insert(in_p, Pair<int, int>(from, out_p)); + } + + // go through the HashMap and do all sorts of crazy ass stuff now... + Set<int> nodes_to_be_added; + for (Set<int>::Element *F = nodes_to_move.front(); F; F = F->next()) { + HashMap<int, Set<int> > seen; + List<int> stack; + int id = F->get(); + while (connections.has(id)) { + for (auto E = connections[id].front(); E; E = E->next()) { + if (seen.has(id) && seen[id].has(E->key())) { + if (!E->next()) { + if (stack.size() > 0) { + id = stack.back()->get(); + stack.pop_back(); + break; + } + id = -11; // I assume ids can't be negative should confirm it... + break; + } + continue; + } + + if (sequence_connections.has(E->get().first)) { + if (!nodes_to_move.has(E->get().first)) { + if (stack.size() > 0) { + id = stack.back()->get(); + stack.pop_back(); + break; + } + id = -11; // I assume ids can't be negative should confirm it... + break; + } + } + + if (!seen.has(id)) + seen.set(id, Set<int>()); + seen[id].insert(E->key()); + stack.push_back(id); + if (!dataconns_to_move.has(id)) + dataconns_to_move.set(id, Map<int, Pair<int, int> >()); + dataconns_to_move[id].insert(E->key(), Pair<int, int>(E->get().first, E->get().second)); + id = E->get().first; + nodes_to_be_added.insert(id); + break; + } + if (!connections.has(id) && stack.size() > 0) { + id = stack.back()->get(); + stack.pop_back(); + } + } + } + for (Set<int>::Element *E = nodes_to_be_added.front(); E; E = E->next()) { + nodes_to_move.insert(E->get()); + } + } + + // * this is primarily for the sake of the having proper undo + List<VisualScript::SequenceConnection> seqext; + List<VisualScript::DataConnection> dataext; + + List<VisualScript::SequenceConnection> seq_connections; + script->get_sequence_connection_list(p_func_from, &seq_connections); + + for (List<VisualScript::SequenceConnection>::Element *E = seq_connections.front(); E; E = E->next()) { + if (!nodes_to_move.has(E->get().from_node) && nodes_to_move.has(E->get().to_node)) { + seqext.push_back(E->get()); + } else if (nodes_to_move.has(E->get().from_node) && !nodes_to_move.has(E->get().to_node)) { + seqext.push_back(E->get()); + } + } + + List<VisualScript::DataConnection> data_connections; + script->get_data_connection_list(p_func_from, &data_connections); + + for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { + if (!nodes_to_move.has(E->get().from_node) && nodes_to_move.has(E->get().to_node)) { + dataext.push_back(E->get()); + } else if (nodes_to_move.has(E->get().from_node) && !nodes_to_move.has(E->get().to_node)) { + dataext.push_back(E->get()); + } + } + + // undo_redo->create_action("Rescan Functions"); + + for (Set<int>::Element *E = nodes_to_move.front(); E; E = E->next()) { + int id = E->get(); + + undo_redo->add_do_method(script.ptr(), "remove_node", p_func_from, id); + 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)); + + undo_redo->add_undo_method(script.ptr(), "remove_node", p_func_to, id); + 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)); + } + + List<int> skeys; + seqconns_to_move.get_key_list(&skeys); + for (List<int>::Element *E = skeys.front(); E; E = E->next()) { + int from_node = E->get(); + for (Map<int, int>::Element *F = seqconns_to_move[from_node].front(); F; F = F->next()) { + int from_port = F->key(); + int to_node = F->get(); + undo_redo->add_do_method(script.ptr(), "sequence_connect", p_func_to, from_node, from_port, to_node); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_func_from, from_node, from_port, to_node); + } + } + + List<int> keys; + dataconns_to_move.get_key_list(&keys); + for (List<int>::Element *E = keys.front(); E; E = E->next()) { + int to_node = E->get(); // to_node + for (Map<int, Pair<int, int> >::Element *F = dataconns_to_move[E->get()].front(); F; F = F->next()) { + int inp_p = F->key(); + Pair<int, int> fro = F->get(); + + undo_redo->add_do_method(script.ptr(), "data_connect", p_func_to, fro.first, fro.second, to_node, inp_p); + undo_redo->add_undo_method(script.ptr(), "data_connect", p_func_from, fro.first, fro.second, to_node, inp_p); + } + } + + // this to have proper undo operations + for (List<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) { + undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_func_from, E->get().from_node, E->get().from_output, E->get().to_node); + } + for (List<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) { + 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); + } + // this doesn't need do methods as they are handled by the subsequent do calls implicitly + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + + // undo_redo->commit_action(); +} + void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_pos) { Node *node = graph->get_node(p_from); @@ -2456,7 +3287,9 @@ void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_fro if (!gn) return; - Ref<VisualScriptNode> vsn = script->get_node(edited_func, p_from.to_int()); + StringName func = _get_function_of_node(p_from.to_int()); + + Ref<VisualScriptNode> vsn = script->get_node(func, p_from.to_int()); if (!vsn.is_valid()) return; @@ -2466,12 +3299,11 @@ void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_fro port_action_node = p_from.to_int(); port_action_output = p_from_slot; - _port_action_menu(CREATE_ACTION); + _port_action_menu(CREATE_ACTION, func); } else { - port_action_output = p_from_slot - vsn->get_output_sequence_port_count(); port_action_node = p_from.to_int(); - _port_action_menu(CREATE_CALL_SET_GET); + _port_action_menu(CREATE_CALL_SET_GET, func); } } @@ -2485,7 +3317,9 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac visited_nodes.insert(p_port_action_node); - Ref<VisualScriptNode> node = script->get_node(edited_func, p_port_action_node); + StringName func = _get_function_of_node(p_port_action_node); + + Ref<VisualScriptNode> node = script->get_node(func, p_port_action_node); if (!node.is_valid()) { @@ -2504,7 +3338,7 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac int from_node; int from_port; - if (script->get_input_value_port_connection_source(edited_func, p_port_action_node, i, &from_node, &from_port)) { + if (script->get_input_value_port_connection_source(func, p_port_action_node, i, &from_node, &from_port)) { g = _guess_output_type(from_node, from_port, visited_nodes); } else { @@ -2529,7 +3363,7 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac return node->guess_output_type(in_guesses.ptrw(), p_port_action_output); } -void VisualScriptEditor::_port_action_menu(int p_option) { +void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func) { Vector2 ofs = graph->get_scroll_ofs() + port_action_pos; if (graph->is_using_snap()) { @@ -2553,8 +3387,10 @@ void VisualScriptEditor::_port_action_menu(int p_option) { } else { n->set_base_type("Object"); } - - String type_string = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + String type_string; + if (script->get_node(func, port_action_node)->get_output_value_port_count() > 0) { + type_string = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + } if (tg.type == Variant::OBJECT) { if (tg.script.is_valid()) { new_connect_node_select->select_from_script(tg.script, ""); @@ -2568,10 +3404,19 @@ void VisualScriptEditor::_port_action_menu(int p_option) { } else { new_connect_node_select->select_from_basic_type(tg.type); } + // ensure that the dialog fits inside the graph + Vector2 pos = mouse_up_position; + Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size(); + pos.x = pos.x > bounds.x ? bounds.x : pos.x; + pos.y = pos.y > bounds.y ? bounds.y : pos.y; + new_connect_node_select->set_position(pos); } break; case CREATE_ACTION: { VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); - PropertyInfo property_info = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output); + PropertyInfo property_info; + if (script->get_node(func, port_action_node)->get_output_value_port_count() > 0) { + property_info = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output); + } if (tg.type == Variant::OBJECT) { if (property_info.type == Variant::OBJECT && property_info.hint_string != String()) { new_connect_node_select->select_from_action(property_info.hint_string); @@ -2583,25 +3428,18 @@ void VisualScriptEditor::_port_action_menu(int p_option) { } else { new_connect_node_select->select_from_action(Variant::get_type_name(tg.type)); } + // ensure that the dialog fits inside the graph + Vector2 pos = mouse_up_position; + Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size(); + pos.x = pos.x > bounds.x ? bounds.x : pos.x; + pos.y = pos.y > bounds.y ? bounds.y : pos.y; + new_connect_node_select->set_position(pos); } break; } } -void VisualScriptEditor::new_node(Ref<VisualScriptNode> vnode, Vector2 ofs) { - Set<int> vn; - Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); - int new_id = script->get_available_id(); - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph", new_id); - undo_redo->add_undo_method(this, "_update_graph", new_id); - undo_redo->commit_action(); - - port_action_new_node = new_id; -} - void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id) { + undo_redo->create_action(TTR("Connect Node Data")); VisualScriptReturn *vnode_return = Object::cast_to<VisualScriptReturn>(vnode.ptr()); if (vnode_return != NULL && vnode_old->get_output_value_port_count() > 0) { @@ -2620,12 +3458,14 @@ void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<Visua if (port >= value_count) { port = 0; } - undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, port_action_node, port, new_id, 0); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, port_action_node, port, new_id, 0); + StringName func = _get_function_of_node(port_action_node); + undo_redo->add_do_method(script.ptr(), "data_connect", func, port_action_node, port, new_id, 0); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, port_action_node, port, new_id, 0); undo_redo->commit_action(); } void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting) { + Vector2 ofs = graph->get_scroll_ofs() + port_action_pos; if (graph->is_using_snap()) { int snap = graph->get_snap(); @@ -2635,19 +3475,29 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri Set<int> vn; + bool port_node_exists = true; + + StringName func = _get_function_of_node(port_action_node); + if (func == StringName()) { + func = default_func; + port_node_exists = false; + } + if (p_category == "visualscript") { Ref<VisualScriptNode> vnode_new = VisualScriptLanguage::singleton->create_node_from_name(p_text); - Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); + Ref<VisualScriptNode> vnode_old; + if (port_node_exists) + vnode_old = script->get_node(func, port_action_node); int new_id = script->get_available_id(); - if (Object::cast_to<VisualScriptOperator>(vnode_new.ptr()) && script->get_node(edited_func, port_action_node).is_valid()) { - Variant::Type type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).type; + if (Object::cast_to<VisualScriptOperator>(vnode_new.ptr()) && vnode_old.is_valid()) { + Variant::Type type = vnode_old->get_output_value_port_info(port_action_output).type; Object::cast_to<VisualScriptOperator>(vnode_new.ptr())->set_typed(type); } - if (Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr()) && script->get_node(edited_func, port_action_node).is_valid()) { - Variant::Type type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).type; - String hint_name = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + if (Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr()) && vnode_old.is_valid()) { + Variant::Type type = vnode_old->get_output_value_port_info(port_action_output).type; + String hint_name = vnode_old->get_output_value_port_info(port_action_output).hint_string; if (type == Variant::OBJECT) { Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(hint_name); @@ -2657,14 +3507,15 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(Variant::get_type_name(type)); } } + undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode_new, ofs); + undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode_new, ofs); if (vnode_old.is_valid() && p_connecting) { connect_seq(vnode_old, vnode_new, new_id); connect_data(vnode_old, vnode_new, new_id); } - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -2727,16 +3578,24 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } } - new_node(vnode, ofs); + int new_id = script->get_available_id(); + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id); + undo_redo->add_do_method(this, "_update_graph", new_id); + undo_redo->add_undo_method(this, "_update_graph", new_id); + undo_redo->commit_action(); + + port_action_new_node = new_id; - Ref<VisualScriptNode> vsn = script->get_node(edited_func, port_action_new_node); + Ref<VisualScriptNode> vsn = script->get_node(func, port_action_new_node); if (Object::cast_to<VisualScriptFunctionCall>(vsn.ptr())) { Ref<VisualScriptFunctionCall> vsfc = vsn; vsfc->set_function(p_text); - if (p_connecting) { + if (port_node_exists && p_connecting) { VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); if (tg.type == Variant::OBJECT) { @@ -2745,9 +3604,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri if (tg.gdclass != StringName()) { vsfc->set_base_type(tg.gdclass); - } else if (script->get_node(edited_func, port_action_node).is_valid()) { - PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; - String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + } else if (script->get_node(func, port_action_node).is_valid()) { + PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { vsfc->set_base_type(base_type); @@ -2769,8 +3628,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } } - // if connecting from another node the call mode shouldn't be self - if (p_connecting) { + if (port_node_exists && p_connecting) { if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) { Ref<VisualScriptPropertySet> vsp = vsn; @@ -2781,9 +3639,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri if (tg.gdclass != StringName()) { vsp->set_base_type(tg.gdclass); - } else if (script->get_node(edited_func, port_action_node).is_valid()) { - PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; - String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + } else if (script->get_node(func, port_action_node).is_valid()) { + PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { vsp->set_base_type(base_type); @@ -2811,9 +3669,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri if (tg.gdclass != StringName()) { vsp->set_base_type(tg.gdclass); - } else if (script->get_node(edited_func, port_action_node).is_valid()) { - PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; - String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + } else if (script->get_node(func, port_action_node).is_valid()) { + PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { vsp->set_base_type(base_type); } @@ -2830,16 +3688,20 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } } } - Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); - if (vnode_old.is_valid() && p_connecting) { - connect_seq(vnode_old, vnode, port_action_new_node); - connect_data(vnode_old, vnode, port_action_new_node); + if (port_node_exists) { + Ref<VisualScriptNode> vnode_old = script->get_node(func, port_action_node); + if (vnode_old.is_valid() && p_connecting) { + connect_seq(vnode_old, vnode, port_action_new_node); + connect_data(vnode_old, vnode, port_action_new_node); + } } _update_graph(port_action_new_node); - _update_graph_connections(); + if (port_node_exists) + _update_graph_connections(); } void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id) { + VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(vnode_new.ptr()); if (vnode_operator != NULL && !vnode_operator->has_input_sequence_port()) { return; @@ -2855,27 +3717,29 @@ void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<Visual return; } + StringName func = _get_function_of_node(port_action_node); + undo_redo->create_action(TTR("Connect Node Sequence")); int pass_port = -vnode_old->get_output_sequence_port_count() + 1; int return_port = port_action_output - 1; if (vnode_old->get_output_value_port_info(port_action_output).name == String("pass") && - !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(pass_port)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, pass_port, new_id); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, pass_port, new_id); + !script->get_output_sequence_ports_connected(func, port_action_node).has(pass_port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, pass_port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, pass_port, new_id); } else if (vnode_old->get_output_value_port_info(port_action_output).name == String("return") && - !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(return_port)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, return_port, new_id); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, return_port, new_id); + !script->get_output_sequence_ports_connected(func, port_action_node).has(return_port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, return_port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, return_port, new_id); } else { for (int port = 0; port < vnode_old->get_output_sequence_port_count(); port++) { int count = vnode_old->get_output_sequence_port_count(); - if (port_action_output < count && !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(port_action_output)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port_action_output, new_id); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, port_action_output, new_id); + if (port_action_output < count && !script->get_output_sequence_ports_connected(func, port_action_node).has(port_action_output)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, port_action_output, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, port_action_output, new_id); break; - } else if (!script->get_output_sequence_ports_connected(edited_func, port_action_node).has(port)) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port, new_id); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, port, new_id); + } else if (!script->get_output_sequence_ports_connected(func, port_action_node).has(port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, port, new_id); break; } } @@ -2908,7 +3772,6 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, cons } selected = name; - edited_func = selected; Ref<VisualScriptFunction> func_node; func_node.instance(); func_node->set_name(name); @@ -2920,14 +3783,16 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, cons func_node->add_argument(minfo.arguments[i].type, minfo.arguments[i].name, -1, minfo.arguments[i].hint, minfo.arguments[i].hint_string); } - undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node); + Vector2 ofs = _get_available_pos(); + + undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs); if (minfo.return_val.type != Variant::NIL || minfo.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { Ref<VisualScriptReturn> ret_node; ret_node.instance(); ret_node->set_return_type(minfo.return_val.type); ret_node->set_enable_return_value(true); ret_node->set_name(name); - undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id() + 1, ret_node, Vector2(500, 0)); + 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))); } undo_redo->add_undo_method(script.ptr(), "remove_function", name); @@ -2942,31 +3807,30 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, cons } void VisualScriptEditor::_cancel_connect_node() { - // Causes crashes - //script->remove_node(edited_func, port_action_new_node); - _update_graph(); + // ensure the cancel is done + port_action_new_node = -1; } -void VisualScriptEditor::_create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point) { - Vector2 ofs = graph->get_scroll_ofs() + p_point; - if (graph->is_using_snap()) { - int snap = graph->get_snap(); - ofs = ofs.snapped(Vector2(snap, snap)); - } - ofs /= EDSCALE; +int VisualScriptEditor::_create_new_node_from_name(const String &p_text, const Vector2 &p_point, const StringName &p_func) { + + StringName func = default_func; + if (p_func != StringName()) + func = p_func; + Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(p_text); int new_id = script->get_available_id(); undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode, p_point); + undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); + return new_id; } void VisualScriptEditor::_default_value_changed() { - Ref<VisualScriptNode> vsn = script->get_node(edited_func, editing_id); + Ref<VisualScriptNode> vsn = script->get_node(_get_function_of_node(editing_id), editing_id); if (vsn.is_null()) return; @@ -2981,7 +3845,7 @@ void VisualScriptEditor::_default_value_changed() { void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_input_port) { - Ref<VisualScriptNode> vsn = script->get_node(edited_func, p_id); + Ref<VisualScriptNode> vsn = script->get_node(_get_function_of_node(p_id), p_id); if (vsn.is_null()) return; @@ -3000,16 +3864,18 @@ void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_i if (pinfo.type == Variant::NODE_PATH) { Node *edited_scene = get_tree()->get_edited_scene_root(); - Node *script_node = _find_script_node(edited_scene, edited_scene, script); + if (edited_scene) { // Fixing an old crash bug ( Visual Script Crashes on editing NodePath with an empty scene open) + Node *script_node = _find_script_node(edited_scene, edited_scene, script); - if (script_node) { - //pick a node relative to the script, IF the script exists - pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE; - pinfo.hint_string = script_node->get_path(); - } else { - //pick a path relative to edited scene - pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE; - pinfo.hint_string = get_tree()->get_edited_scene_root()->get_path(); + if (script_node) { + //pick a node relative to the script, IF the script exists + pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE; + pinfo.hint_string = script_node->get_path(); + } else { + //pick a path relative to edited scene + pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE; + pinfo.hint_string = get_tree()->get_edited_scene_root()->get_path(); + } } } @@ -3036,62 +3902,64 @@ void VisualScriptEditor::_hide_timer() { hint_text->hide(); } -void VisualScriptEditor::_node_filter_changed(const String &p_text) { - - _update_available_nodes(); -} - void VisualScriptEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_READY || (p_what == NOTIFICATION_THEME_CHANGED && is_visible_in_tree())) { - - node_filter->set_right_icon(Control::get_icon("Search", "EditorIcons")); - node_filter->set_clear_button_enabled(true); - - if (p_what == NOTIFICATION_READY) { + switch (p_what) { + case NOTIFICATION_READY: { variable_editor->connect("changed", this, "_update_members"); signal_editor->connect("changed", this, "_update_members"); + FALLTHROUGH; } + case NOTIFICATION_THEME_CHANGED: { + if (p_what != NOTIFICATION_READY && !is_visible_in_tree()) { + return; + } - Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme(); + edit_variable_edit->add_style_override("bg", get_stylebox("bg", "Tree")); + edit_signal_edit->add_style_override("bg", get_stylebox("bg", "Tree")); + func_input_scroll->add_style_override("bg", get_stylebox("bg", "Tree")); - bool dark_theme = tm->get_constant("dark_theme", "Editor"); + Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme(); - List<Pair<String, Color> > colors; - if (dark_theme) { - colors.push_back(Pair<String, Color>("flow_control", Color(0.96, 0.96, 0.96))); - colors.push_back(Pair<String, Color>("functions", Color(0.96, 0.52, 0.51))); - colors.push_back(Pair<String, Color>("data", Color(0.5, 0.96, 0.81))); - colors.push_back(Pair<String, Color>("operators", Color(0.67, 0.59, 0.87))); - colors.push_back(Pair<String, Color>("custom", Color(0.5, 0.73, 0.96))); - colors.push_back(Pair<String, Color>("constants", Color(0.96, 0.5, 0.69))); - } else { - colors.push_back(Pair<String, Color>("flow_control", Color(0.26, 0.26, 0.26))); - colors.push_back(Pair<String, Color>("functions", Color(0.95, 0.4, 0.38))); - colors.push_back(Pair<String, Color>("data", Color(0.07, 0.73, 0.51))); - colors.push_back(Pair<String, Color>("operators", Color(0.51, 0.4, 0.82))); - colors.push_back(Pair<String, Color>("custom", Color(0.31, 0.63, 0.95))); - colors.push_back(Pair<String, Color>("constants", Color(0.94, 0.18, 0.49))); - } + bool dark_theme = tm->get_constant("dark_theme", "Editor"); - for (List<Pair<String, Color> >::Element *E = colors.front(); E; E = E->next()) { - Ref<StyleBoxFlat> sb = tm->get_stylebox("frame", "GraphNode"); - if (!sb.is_null()) { - Ref<StyleBoxFlat> frame_style = sb->duplicate(); - Color c = sb->get_border_color(); - Color cn = E->get().second; - cn.a = c.a; - frame_style->set_border_color(cn); - node_styles[E->get().first] = frame_style; + List<Pair<String, Color> > colors; + if (dark_theme) { + colors.push_back(Pair<String, Color>("flow_control", Color(0.96, 0.96, 0.96))); + colors.push_back(Pair<String, Color>("functions", Color(0.96, 0.52, 0.51))); + colors.push_back(Pair<String, Color>("data", Color(0.5, 0.96, 0.81))); + colors.push_back(Pair<String, Color>("operators", Color(0.67, 0.59, 0.87))); + colors.push_back(Pair<String, Color>("custom", Color(0.5, 0.73, 0.96))); + colors.push_back(Pair<String, Color>("constants", Color(0.96, 0.5, 0.69))); + } else { + colors.push_back(Pair<String, Color>("flow_control", Color(0.26, 0.26, 0.26))); + colors.push_back(Pair<String, Color>("functions", Color(0.95, 0.4, 0.38))); + colors.push_back(Pair<String, Color>("data", Color(0.07, 0.73, 0.51))); + colors.push_back(Pair<String, Color>("operators", Color(0.51, 0.4, 0.82))); + colors.push_back(Pair<String, Color>("custom", Color(0.31, 0.63, 0.95))); + colors.push_back(Pair<String, Color>("constants", Color(0.94, 0.18, 0.49))); } - } - if (is_visible_in_tree() && script.is_valid()) { - _update_members(); - _update_graph(); - } - } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { - left_vsplit->set_visible(is_visible_in_tree()); + for (List<Pair<String, Color> >::Element *E = colors.front(); E; E = E->next()) { + Ref<StyleBoxFlat> sb = tm->get_stylebox("frame", "GraphNode"); + if (!sb.is_null()) { + Ref<StyleBoxFlat> frame_style = sb->duplicate(); + Color c = sb->get_border_color(); + Color cn = E->get().second; + cn.a = c.a; + frame_style->set_border_color(cn); + node_styles[E->get().first] = frame_style; + } + } + + if (is_visible_in_tree() && script.is_valid()) { + _update_members(); + _update_graph(); + } + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + members_section->set_visible(is_visible_in_tree()); + } break; } } @@ -3102,8 +3970,9 @@ void VisualScriptEditor::_graph_ofs_changed(const Vector2 &p_ofs) { updating_graph = true; - if (script->has_function(edited_func)) { - script->set_function_scroll(edited_func, graph->get_scroll_ofs() / EDSCALE); + // Just use the default func for all the properties that need to be handled for drawing rather than adding to the Visual Script Class + if (script->has_function(default_func)) { + script->set_function_scroll(default_func, graph->get_scroll_ofs() / EDSCALE); script->set_edited(true); } updating_graph = false; @@ -3114,7 +3983,9 @@ void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_ if (updating_graph) return; - Ref<VisualScriptComment> vsc = script->get_node(edited_func, p_node); + StringName func = _get_function_of_node(p_node); + + Ref<VisualScriptComment> vsc = script->get_node(func, p_node); if (vsc.is_null()) return; @@ -3132,7 +4003,7 @@ void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_ undo_redo->add_undo_method(vsc.ptr(), "set_size", vsc->get_size()); undo_redo->commit_action(); - gn->set_custom_minimum_size(p_new_size); //for this time since graph update is blocked + gn->set_custom_minimum_size(p_new_size); gn->set_size(Size2(1, 1)); graph->set_block_minimum_size_adjust(false); updating_graph = false; @@ -3152,7 +4023,8 @@ void VisualScriptEditor::_menu_option(int p_what) { if (gn) { if (gn->is_selected()) { int id = String(gn->get_name()).to_int(); - Ref<VisualScriptNode> vsn = script->get_node(edited_func, id); + StringName func = _get_function_of_node(id); + Ref<VisualScriptNode> vsn = script->get_node(func, id); if (vsn.is_valid()) { vsn->set_breakpoint(!vsn->is_breakpoint()); reselect.push_back(gn->get_name()); @@ -3174,28 +4046,30 @@ void VisualScriptEditor::_menu_option(int p_what) { } break; case EDIT_COPY_NODES: case EDIT_CUT_NODES: { - - if (!script->has_function(edited_func)) + if (!script->has_function(default_func)) break; clipboard->nodes.clear(); clipboard->data_connections.clear(); clipboard->sequence_connections.clear(); + Set<String> funcs; for (int i = 0; i < graph->get_child_count(); i++) { GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); if (gn) { if (gn->is_selected()) { int id = String(gn->get_name()).to_int(); - Ref<VisualScriptNode> node = script->get_node(edited_func, id); + StringName func = _get_function_of_node(id); + Ref<VisualScriptNode> node = script->get_node(func, id); if (Object::cast_to<VisualScriptFunction>(*node)) { EditorNode::get_singleton()->show_warning(TTR("Can't copy the function node.")); return; } if (node.is_valid()) { clipboard->nodes[id] = node->duplicate(true); - clipboard->nodes_positions[id] = script->get_node_position(edited_func, id); + clipboard->nodes_positions[id] = script->get_node_position(func, id); + funcs.insert(String(func)); } } } @@ -3204,37 +4078,38 @@ void VisualScriptEditor::_menu_option(int p_what) { if (clipboard->nodes.empty()) break; - List<VisualScript::SequenceConnection> sequence_connections; + for (Set<String>::Element *F = funcs.front(); F; F = F->next()) { + List<VisualScript::SequenceConnection> sequence_connections; - script->get_sequence_connection_list(edited_func, &sequence_connections); + script->get_sequence_connection_list(F->get(), &sequence_connections); - for (List<VisualScript::SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) { + for (List<VisualScript::SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) { - if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) { + if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) { - clipboard->sequence_connections.insert(E->get()); + clipboard->sequence_connections.insert(E->get()); + } } - } - List<VisualScript::DataConnection> data_connections; + List<VisualScript::DataConnection> data_connections; - script->get_data_connection_list(edited_func, &data_connections); + script->get_data_connection_list(F->get(), &data_connections); - for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { + for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) { - if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) { + if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) { - clipboard->data_connections.insert(E->get()); + clipboard->data_connections.insert(E->get()); + } } } - if (p_what == EDIT_CUT_NODES) { _on_nodes_delete(); // oh yeah, also delete on cut } } break; case EDIT_PASTE_NODES: { - if (!script->has_function(edited_func)) + if (!script->has_function(default_func)) break; if (clipboard->nodes.empty()) { @@ -3252,11 +4127,15 @@ void VisualScriptEditor::_menu_option(int p_what) { Set<Vector2> existing_positions; { - List<int> nodes; - script->get_node_list(edited_func, &nodes); - for (List<int>::Element *E = nodes.front(); E; E = E->next()) { - Vector2 pos = script->get_node_position(edited_func, E->get()).snapped(Vector2(2, 2)); - existing_positions.insert(pos); + List<StringName> functions; + script->get_function_list(&functions); + for (List<StringName>::Element *F = functions.front(); F; F = F->next()) { + List<int> nodes; + script->get_node_list(F->get(), &nodes); + for (List<int>::Element *E = nodes.front(); E; E = E->next()) { + Vector2 pos = script->get_node_position(F->get(), E->get()).snapped(Vector2(2, 2)); + existing_positions.insert(pos); + } } } @@ -3275,20 +4154,20 @@ void VisualScriptEditor::_menu_option(int p_what) { paste_pos += Vector2(20, 20) * EDSCALE; } - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, node, paste_pos); - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, node, paste_pos); + undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id); } for (Set<VisualScript::SequenceConnection>::Element *E = clipboard->sequence_connections.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); - undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]); + 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]); + 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]); } for (Set<VisualScript::DataConnection>::Element *E = clipboard->data_connections.front(); E; E = E->next()) { - undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); - undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port); + 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); + 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); } undo_redo->add_do_method(this, "_update_graph"); @@ -3304,6 +4183,275 @@ void VisualScriptEditor::_menu_option(int p_what) { } } } break; + case EDIT_CREATE_FUNCTION: { + + StringName function = ""; + Map<int, Ref<VisualScriptNode> > nodes; + Set<int> selections; + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + if (gn->is_selected()) { + int id = String(gn->get_name()).to_int(); + StringName func = _get_function_of_node(id); + Ref<VisualScriptNode> node = script->get_node(func, id); + if (Object::cast_to<VisualScriptFunction>(*node)) { + EditorNode::get_singleton()->show_warning(TTR("Can't create function with a function node.")); + return; + } + if (node.is_valid()) { + if (func != function && function != StringName("")) { + EditorNode::get_singleton()->show_warning(TTR("Can't create function of nodes from nodes of multiple functions.")); + return; + } + nodes.insert(id, node); + selections.insert(id); + function = func; + } + } + } + } + + if (nodes.size() == 0) { + return; // nothing to be done if there are no valid nodes selected + } + + Set<VisualScript::SequenceConnection> seqmove; + Set<VisualScript::DataConnection> datamove; + + Set<VisualScript::SequenceConnection> seqext; + Set<VisualScript::DataConnection> dataext; + + int start_node = -1; + Set<int> end_nodes; + if (nodes.size() == 1) { + Ref<VisualScriptNode> nd = script->get_node(function, nodes.front()->key()); + if (nd.is_valid() && nd->has_input_sequence_port()) + start_node = nodes.front()->key(); + else { + EditorNode::get_singleton()->show_warning(TTR("Select atleast one node with sequence port.")); + return; + } + } else { + List<VisualScript::SequenceConnection> seqs; + script->get_sequence_connection_list(function, &seqs); + + if (seqs.size() == 0) { + // in case there are no sequence connections + // select the top most node cause that's probably how + // the user wants to connect the nodes + int top_nd = -1; + Vector2 top; + for (Map<int, Ref<VisualScriptNode> >::Element *E = nodes.front(); E; E = E->next()) { + Ref<VisualScriptNode> nd = script->get_node(function, E->key()); + if (nd.is_valid() && nd->has_input_sequence_port()) { + if (top_nd < 0) { + top_nd = E->key(); + top = script->get_node_position(function, top_nd); + } + Vector2 pos = script->get_node_position(function, E->key()); + if (top.y > pos.y) { + top_nd = E->key(); + top = pos; + } + } + } + Ref<VisualScriptNode> nd = script->get_node(function, top_nd); + if (nd.is_valid() && nd->has_input_sequence_port()) + start_node = top_nd; + else { + EditorNode::get_singleton()->show_warning(TTR("Select atleast one node with sequence port.")); + return; + } + } else { + // pick the node with input sequence + Set<int> nodes_from; + Set<int> nodes_to; + for (List<VisualScript::SequenceConnection>::Element *E = seqs.front(); E; E = E->next()) { + if (nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) { + seqmove.insert(E->get()); + nodes_from.insert(E->get().from_node); + } else if (nodes.has(E->get().from_node) && !nodes.has(E->get().to_node)) { + seqext.insert(E->get()); + } else if (!nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) { + if (start_node == -1) { + seqext.insert(E->get()); + start_node = E->get().to_node; + } else { + EditorNode::get_singleton()->show_warning(TTR("Try to only have one sequence input in selection.")); + return; + } + } + nodes_to.insert(E->get().to_node); + } + + // to use to add return nodes + _get_ends(start_node, seqs, selections, end_nodes); + + if (start_node == -1) { + // if we still don't have a start node then + // run through the nodes and select the first tree node + // ie node without any input sequence but output sequence + for (Set<int>::Element *E = nodes_from.front(); E; E = E->next()) { + if (!nodes_to.has(E->get())) { + start_node = E->get(); + } + } + } + } + } + + if (start_node == -1) { + return; // this should not happen, but just in case something goes wrong + } + + List<Variant::Type> inputs; // input types + List<Pair<int, int> > input_connections; + { + List<VisualScript::DataConnection> dats; + script->get_data_connection_list(function, &dats); + for (List<VisualScript::DataConnection>::Element *E = dats.front(); E; E = E->next()) { + if (nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) { + datamove.insert(E->get()); + } else if (!nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) { + // add all these as inputs for the Function + Ref<VisualScriptNode> node = script->get_node(function, E->get().to_node); + if (node.is_valid()) { + dataext.insert(E->get()); + PropertyInfo pi = node->get_input_value_port_info(E->get().to_port); + inputs.push_back(pi.type); + input_connections.push_back(Pair<int, int>(E->get().to_node, E->get().to_port)); + } + } else if (nodes.has(E->get().from_node) && !nodes.has(E->get().to_node)) { + dataext.insert(E->get()); + } + } + } + + String new_fn = _validate_name("new_function"); + + Vector2 ofs = _get_available_pos(false, script->get_node_position(function, start_node) - Vector2(80, 150)); + + Ref<VisualScriptFunction> func_node; + func_node.instance(); + func_node->set_name(new_fn); + + undo_redo->create_action(TTR("Create Function")); + + undo_redo->add_do_method(script.ptr(), "add_function", new_fn); + int fn_id = script->get_available_id(); + undo_redo->add_do_method(script.ptr(), "add_node", new_fn, fn_id, func_node, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_function", new_fn); + undo_redo->add_do_method(this, "_update_members"); + undo_redo->add_undo_method(this, "_update_members"); + undo_redo->add_do_method(this, "emit_signal", "edited_script_changed"); + undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed"); + + // Move the nodes + + for (Map<int, Ref<VisualScriptNode> >::Element *E = nodes.front(); E; E = E->next()) { + undo_redo->add_do_method(script.ptr(), "remove_node", function, E->key()); + undo_redo->add_do_method(script.ptr(), "add_node", new_fn, E->key(), E->get(), script->get_node_position(function, E->key())); + + // undo_redo->add_undo_method(script.ptr(), "remove_node", new_fn, E->key()); not needed cause we already remove the function :P + undo_redo->add_undo_method(script.ptr(), "add_node", function, E->key(), E->get(), script->get_node_position(function, E->key())); + } + + for (Set<VisualScript::SequenceConnection>::Element *E = seqmove.front(); E; E = E->next()) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, E->get().from_node, E->get().from_output, E->get().to_node); + undo_redo->add_undo_method(script.ptr(), "sequence_connect", function, E->get().from_node, E->get().from_output, E->get().to_node); + } + + for (Set<VisualScript::DataConnection>::Element *E = datamove.front(); E; E = E->next()) { + 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); + 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); + } + + // Add undo for external connections as well so that it's easier to revert back and forth + // these didn't require do methods as it's already handled internally by other do calls + for (Set<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) { + undo_redo->add_undo_method(script.ptr(), "sequence_connect", function, E->get().from_node, E->get().from_output, E->get().to_node); + } + for (Set<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) { + 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); + } + + // I don't really think we need support for non sequenced functions at this moment + undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, fn_id, 0, start_node); + + // end nodes are mapped to the return nodes with data connections if possible + int m = 1; + for (Set<int>::Element *G = end_nodes.front(); G; G = G->next()) { + Ref<VisualScriptReturn> ret_node; + ret_node.instance(); + + int ret_id = fn_id + (m++); + selections.insert(ret_id); + Vector2 ofsi = _get_available_pos(false, script->get_node_position(function, G->get()) + Vector2(80, -100)); + undo_redo->add_do_method(script.ptr(), "add_node", new_fn, ret_id, ret_node, ofsi); + undo_redo->add_undo_method(script.ptr(), "remove_node", new_fn, ret_id); + + undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, G->get(), 0, ret_id); + // add data outputs from each of the end_nodes + Ref<VisualScriptNode> vsn = script->get_node(function, G->get()); + if (vsn.is_valid() && vsn->get_output_value_port_count() > 0) { + ret_node->set_enable_return_value(true); + // use the zeroth data port cause that's the likely one that is planned to be used + ret_node->set_return_type(vsn->get_output_value_port_info(0).type); + undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, G->get(), 0, ret_id, 0); + } + } + + // * might make the system more intelligent by checking port from info. + int i = 0; + List<Pair<int, int> >::Element *F = input_connections.front(); + for (List<Variant::Type>::Element *E = inputs.front(); E && F; E = E->next(), F = F->next()) { + func_node->add_argument(E->get(), "arg_" + String::num_int64(i), i); + undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, fn_id, i, F->get().first, F->get().second); + i++; // increment i + } + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + + undo_redo->commit_action(); + + // make sure all Nodes get marked for selection so that they can be moved together + selections.insert(fn_id); + for (int k = 0; k < graph->get_child_count(); k++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(k)); + if (gn) { + int id = gn->get_name().operator String().to_int(); + gn->set_selected(selections.has(id)); + } + } + + // Ensure Preview Selection is of newly created function node + if (selections.size()) { + EditorNode::get_singleton()->push_item(func_node.ptr()); + } + + } break; + case REFRESH_GRAPH: { + _update_graph(); + } break; + } +} + +// this is likely going to be very slow and I am not sure if I should keep it +// but I hope that it will not be a problem considering that we won't be creating functions so frequently +// and cyclic connections would be a problem but hopefully we won't let them get to this point +void VisualScriptEditor::_get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes) { + for (const List<VisualScript::SequenceConnection>::Element *E = p_seqs.front(); E; E = E->next()) { + int from = E->get().from_node; + int to = E->get().to_node; + + if (from == p_node && p_selected.has(to)) { + // this is an interior connection move forward to the to node + _get_ends(to, p_seqs, p_selected, r_end_nodes); + } else if (from == p_node && !p_selected.has(to)) { + r_end_nodes.insert(from); + } } } @@ -3316,6 +4464,9 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) { member_popup->set_position(members->get_global_position() + p_pos); member_popup->set_size(Vector2()); + function_name_edit->set_position(members->get_global_position() + p_pos); + function_name_edit->set_size(Vector2()); + TreeItem *root = members->get_root(); Ref<Texture> del_icon = Control::get_icon("Remove", "EditorIcons"); @@ -3326,6 +4477,8 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) { member_type = MEMBER_FUNCTION; member_name = ti->get_text(0); + member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT); + member_popup->add_separator(); member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("visual_script_editor/delete_selected"), MEMBER_REMOVE); member_popup->popup(); return; @@ -3393,6 +4546,11 @@ void VisualScriptEditor::_member_option(int p_option) { undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); + } else if (p_option == MEMBER_EDIT) { + selected = members->get_selected()->get_text(0); + function_name_edit->popup(); + function_name_box->set_text(selected); + function_name_box->select_all(); } } break; case MEMBER_VARIABLE: { @@ -3429,7 +4587,6 @@ void VisualScriptEditor::_member_option(int p_option) { undo_redo->add_undo_method(this, "_update_members"); undo_redo->commit_action(); } else if (p_option == MEMBER_EDIT) { - signal_editor->edit(name); edit_signal_dialog->set_title(TTR("Editing Signal:") + " " + name); edit_signal_dialog->popup_centered_minsize(Size2(400, 300) * EDSCALE); @@ -3450,6 +4607,11 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_member_edited", &VisualScriptEditor::_member_edited); ClassDB::bind_method("_member_selected", &VisualScriptEditor::_member_selected); ClassDB::bind_method("_update_members", &VisualScriptEditor::_update_members); + ClassDB::bind_method("_members_gui_input", &VisualScriptEditor::_members_gui_input); + ClassDB::bind_method("_member_rmb_selected", &VisualScriptEditor::_member_rmb_selected); + ClassDB::bind_method("_member_option", &VisualScriptEditor::_member_option); + ClassDB::bind_method("_fn_name_box_input", &VisualScriptEditor::_fn_name_box_input); + ClassDB::bind_method("_change_base_type", &VisualScriptEditor::_change_base_type); ClassDB::bind_method("_change_base_type_callback", &VisualScriptEditor::_change_base_type_callback); ClassDB::bind_method("_toggle_tool_script", &VisualScriptEditor::_toggle_tool_script); @@ -3461,7 +4623,14 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_remove_node", &VisualScriptEditor::_remove_node); ClassDB::bind_method("_update_graph", &VisualScriptEditor::_update_graph, DEFVAL(-1)); ClassDB::bind_method("_node_ports_changed", &VisualScriptEditor::_node_ports_changed); - ClassDB::bind_method("_available_node_doubleclicked", &VisualScriptEditor::_available_node_doubleclicked); + + ClassDB::bind_method("_create_function_dialog", &VisualScriptEditor::_create_function_dialog); + ClassDB::bind_method("_create_function", &VisualScriptEditor::_create_function); + ClassDB::bind_method("_add_node_dialog", &VisualScriptEditor::_add_node_dialog); + ClassDB::bind_method("_add_func_input", &VisualScriptEditor::_add_func_input); + ClassDB::bind_method("_remove_func_input", &VisualScriptEditor::_remove_func_input); + ClassDB::bind_method("_deselect_input_names", &VisualScriptEditor::_deselect_input_names); + ClassDB::bind_method("_default_value_edited", &VisualScriptEditor::_default_value_edited); ClassDB::bind_method("_default_value_changed", &VisualScriptEditor::_default_value_changed); ClassDB::bind_method("_menu_option", &VisualScriptEditor::_menu_option); @@ -3474,15 +4643,23 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_selected_new_virtual_method", &VisualScriptEditor::_selected_new_virtual_method); ClassDB::bind_method("_cancel_connect_node", &VisualScriptEditor::_cancel_connect_node); - ClassDB::bind_method("_create_new_node", &VisualScriptEditor::_create_new_node); + ClassDB::bind_method("_create_new_node_from_name", &VisualScriptEditor::_create_new_node_from_name); ClassDB::bind_method("_expression_text_changed", &VisualScriptEditor::_expression_text_changed); + ClassDB::bind_method("_add_input_port", &VisualScriptEditor::_add_input_port); + ClassDB::bind_method("_add_output_port", &VisualScriptEditor::_add_output_port); + ClassDB::bind_method("_remove_input_port", &VisualScriptEditor::_remove_input_port); + ClassDB::bind_method("_remove_output_port", &VisualScriptEditor::_remove_output_port); + ClassDB::bind_method("_change_port_type", &VisualScriptEditor::_change_port_type); + ClassDB::bind_method("_update_node_size", &VisualScriptEditor::_update_node_size); + ClassDB::bind_method("_port_name_focus_out", &VisualScriptEditor::_port_name_focus_out); ClassDB::bind_method("get_drag_data_fw", &VisualScriptEditor::get_drag_data_fw); ClassDB::bind_method("can_drop_data_fw", &VisualScriptEditor::can_drop_data_fw); ClassDB::bind_method("drop_data_fw", &VisualScriptEditor::drop_data_fw); ClassDB::bind_method("_input", &VisualScriptEditor::_input); - ClassDB::bind_method("_members_gui_input", &VisualScriptEditor::_members_gui_input); + ClassDB::bind_method("_graph_gui_input", &VisualScriptEditor::_graph_gui_input); + ClassDB::bind_method("_on_nodes_delete", &VisualScriptEditor::_on_nodes_delete); ClassDB::bind_method("_on_nodes_duplicate", &VisualScriptEditor::_on_nodes_duplicate); @@ -3493,17 +4670,10 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_graph_connect_to_empty", &VisualScriptEditor::_graph_connect_to_empty); ClassDB::bind_method("_update_graph_connections", &VisualScriptEditor::_update_graph_connections); - ClassDB::bind_method("_node_filter_changed", &VisualScriptEditor::_node_filter_changed); ClassDB::bind_method("_selected_method", &VisualScriptEditor::_selected_method); ClassDB::bind_method("_draw_color_over_button", &VisualScriptEditor::_draw_color_over_button); - ClassDB::bind_method("_member_rmb_selected", &VisualScriptEditor::_member_rmb_selected); - - ClassDB::bind_method("_member_option", &VisualScriptEditor::_member_option); - - ClassDB::bind_method("_update_available_nodes", &VisualScriptEditor::_update_available_nodes); - ClassDB::bind_method("_generic_search", &VisualScriptEditor::_generic_search); } @@ -3513,6 +4683,8 @@ VisualScriptEditor::VisualScriptEditor() { clipboard = memnew(Clipboard); } updating_graph = false; + saved_pos_dirty = false; + saved_position = Vector2(0, 0); edit_menu = memnew(MenuButton); edit_menu->set_text(TTR("Edit")); @@ -3524,61 +4696,50 @@ VisualScriptEditor::VisualScriptEditor() { edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/copy_nodes"), EDIT_COPY_NODES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/cut_nodes"), EDIT_CUT_NODES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/paste_nodes"), EDIT_PASTE_NODES); - + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/create_function"), EDIT_CREATE_FUNCTION); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/refresh_nodes"), REFRESH_GRAPH); edit_menu->get_popup()->connect("id_pressed", this, "_menu_option"); - left_vsplit = memnew(VSplitContainer); - ScriptEditor::get_singleton()->get_left_list_split()->call_deferred("add_child", left_vsplit); //add but wait until done settig up this - left_vsplit->set_v_size_flags(SIZE_EXPAND_FILL); - left_vsplit->set_stretch_ratio(2); - left_vsplit->hide(); - - VBoxContainer *left_vb = memnew(VBoxContainer); - left_vsplit->add_child(left_vb); - left_vb->set_v_size_flags(SIZE_EXPAND_FILL); - //left_vb->set_custom_minimum_size(Size2(230, 1) * EDSCALE); + members_section = memnew(VBoxContainer); + // Add but wait until done setting up this. + ScriptEditor::get_singleton()->get_left_list_split()->call_deferred("add_child", members_section); + members_section->set_v_size_flags(SIZE_EXPAND_FILL); CheckButton *tool_script_check = memnew(CheckButton); tool_script_check->set_text(TTR("Make Tool:")); - left_vb->add_child(tool_script_check); + members_section->add_child(tool_script_check); tool_script_check->connect("pressed", this, "_toggle_tool_script"); - base_type_select = memnew(Button); - left_vb->add_margin_child(TTR("Base Type:"), base_type_select); - base_type_select->connect("pressed", this, "_change_base_type"); + /// Members /// members = memnew(Tree); - left_vb->add_margin_child(TTR("Members:"), members, true); + members_section->add_margin_child(TTR("Members:"), members, true); + members->set_custom_minimum_size(Size2(0, 50 * EDSCALE)); members->set_hide_root(true); members->connect("button_pressed", this, "_member_button"); members->connect("item_edited", this, "_member_edited"); members->connect("cell_selected", this, "_member_selected", varray(), CONNECT_DEFERRED); members->connect("gui_input", this, "_members_gui_input"); + members->connect("item_rmb_selected", this, "_member_rmb_selected"); + members->set_allow_rmb_select(true); members->set_allow_reselect(true); members->set_hide_folding(true); members->set_drag_forwarding(this); - VBoxContainer *left_vb2 = memnew(VBoxContainer); - left_vsplit->add_child(left_vb2); - left_vb2->set_v_size_flags(SIZE_EXPAND_FILL); - - VBoxContainer *vbc_nodes = memnew(VBoxContainer); - HBoxContainer *hbc_nodes = memnew(HBoxContainer); - node_filter = memnew(LineEdit); - node_filter->connect("text_changed", this, "_node_filter_changed"); - hbc_nodes->add_child(node_filter); - node_filter->set_h_size_flags(SIZE_EXPAND_FILL); - vbc_nodes->add_child(hbc_nodes); - - nodes = memnew(Tree); - vbc_nodes->add_child(nodes); - nodes->set_v_size_flags(SIZE_EXPAND_FILL); + member_popup = memnew(PopupMenu); + add_child(member_popup); + member_popup->connect("id_pressed", this, "_member_option"); - left_vb2->add_margin_child(TTR("Available Nodes:"), vbc_nodes, true); + function_name_edit = memnew(PopupDialog); + function_name_box = memnew(LineEdit); + function_name_edit->add_child(function_name_box); + function_name_edit->set_h_size_flags(SIZE_EXPAND); + function_name_box->connect("gui_input", this, "_fn_name_box_input"); + function_name_box->set_expand_to_text_length(true); + add_child(function_name_edit); - nodes->set_hide_root(true); - nodes->connect("item_activated", this, "_available_node_doubleclicked"); - nodes->set_drag_forwarding(this); + /// Actual Graph /// graph = memnew(GraphEdit); add_child(graph); @@ -3589,10 +4750,77 @@ VisualScriptEditor::VisualScriptEditor() { graph->connect("_end_node_move", this, "_end_node_move"); graph->connect("delete_nodes_request", this, "_on_nodes_delete"); graph->connect("duplicate_nodes_request", this, "_on_nodes_duplicate"); + graph->connect("gui_input", this, "_graph_gui_input"); graph->set_drag_forwarding(this); graph->hide(); graph->connect("scroll_offset_changed", this, "_graph_ofs_changed"); + /// Add Buttons to Top Bar/Zoom bar. + HBoxContainer *graph_hbc = graph->get_zoom_hbox(); + + Label *base_lbl = memnew(Label); + base_lbl->set_text("Change Base Type: "); + graph_hbc->add_child(base_lbl); + + base_type_select = memnew(Button); + base_type_select->connect("pressed", this, "_change_base_type"); + graph_hbc->add_child(base_type_select); + + Button *add_nds = memnew(Button); + add_nds->set_text("Add Nodes..."); + graph_hbc->add_child(add_nds); + add_nds->connect("pressed", this, "_add_node_dialog"); + + Button *fn_btn = memnew(Button); + fn_btn->set_text("Add Function..."); + graph_hbc->add_child(fn_btn); + fn_btn->connect("pressed", this, "_create_function_dialog"); + + // Add Function Dialog. + VBoxContainer *function_vb = memnew(VBoxContainer); + function_vb->set_v_size_flags(SIZE_EXPAND_FILL); + + HBoxContainer *func_name_hbox = memnew(HBoxContainer); + function_vb->add_child(func_name_hbox); + + Label *func_name_label = memnew(Label); + func_name_label->set_text(TTR("Name:")); + func_name_hbox->add_child(func_name_label); + + func_name_box = memnew(LineEdit); + func_name_box->set_h_size_flags(SIZE_EXPAND_FILL); + func_name_box->set_placeholder(TTR("function_name")); + func_name_box->set_text(""); + func_name_box->connect("focus_entered", this, "_deselect_input_names"); + func_name_hbox->add_child(func_name_box); + + // Add minor setting for function if needed, here! + + function_vb->add_child(memnew(HSeparator)); + + Button *add_input_button = memnew(Button); + add_input_button->set_h_size_flags(SIZE_EXPAND_FILL); + add_input_button->set_text(TTR("Add Input")); + add_input_button->connect("pressed", this, "_add_func_input"); + function_vb->add_child(add_input_button); + + func_input_scroll = memnew(ScrollContainer); + func_input_scroll->set_v_size_flags(SIZE_EXPAND_FILL); + function_vb->add_child(func_input_scroll); + + func_input_vbox = memnew(VBoxContainer); + func_input_vbox->set_h_size_flags(SIZE_EXPAND_FILL); + func_input_scroll->add_child(func_input_vbox); + + function_create_dialog = memnew(ConfirmationDialog); + function_create_dialog->set_custom_minimum_size(Size2(450, 300) * EDSCALE); + function_create_dialog->set_v_size_flags(SIZE_EXPAND_FILL); + function_create_dialog->set_title(TTR("Create Function")); + function_create_dialog->add_child(function_vb); + function_create_dialog->get_ok()->set_text(TTR("Create")); + function_create_dialog->get_ok()->connect("pressed", this, "_create_function"); + add_child(function_create_dialog); + select_func_text = memnew(Label); select_func_text->set_text(TTR("Select or create a function to edit its graph.")); select_func_text->set_align(Label::ALIGN_CENTER); @@ -3613,7 +4841,7 @@ VisualScriptEditor::VisualScriptEditor() { hint_text_timer->connect("timeout", this, "_hide_timer"); add_child(hint_text_timer); - //allowed casts (connections) + // Allowed casts (connections). for (int i = 0; i < Variant::VARIANT_MAX; i++) { graph->add_valid_connection_type(Variant::NIL, i); graph->add_valid_connection_type(i, Variant::NIL); @@ -3653,7 +4881,7 @@ VisualScriptEditor::VisualScriptEditor() { edit_variable_edit->edit(variable_editor); select_base_type = memnew(CreateDialog); - select_base_type->set_base_type("Object"); //anything goes + select_base_type->set_base_type("Object"); // Anything goes. select_base_type->connect("create", this, "_change_base_type_callback"); add_child(select_base_type); @@ -3661,8 +4889,8 @@ VisualScriptEditor::VisualScriptEditor() { updating_members = false; - set_process_input(true); //for revert on drag - set_process_unhandled_input(true); //for revert on drag + set_process_input(true); + set_process_unhandled_input(true); default_value_edit = memnew(CustomPropertyEditor); add_child(default_value_edit); @@ -3675,25 +4903,18 @@ VisualScriptEditor::VisualScriptEditor() { new_connect_node_select = memnew(VisualScriptPropertySelector); add_child(new_connect_node_select); + new_connect_node_select->set_resizable(true); new_connect_node_select->connect("selected", this, "_selected_connect_node"); new_connect_node_select->get_cancel()->connect("pressed", this, "_cancel_connect_node"); new_virtual_method_select = memnew(VisualScriptPropertySelector); add_child(new_virtual_method_select); new_virtual_method_select->connect("selected", this, "_selected_new_virtual_method"); - - member_popup = memnew(PopupMenu); - add_child(member_popup); - members->connect("item_rmb_selected", this, "_member_rmb_selected"); - members->set_allow_rmb_select(true); - member_popup->connect("id_pressed", this, "_member_option"); - - _VisualScriptEditor::get_singleton()->connect("custom_nodes_updated", this, "_update_available_nodes"); } VisualScriptEditor::~VisualScriptEditor() { - undo_redo->clear_history(); //avoid crashes + undo_redo->clear_history(); // Avoid crashes. memdelete(signal_editor); memdelete(variable_editor); } @@ -3724,12 +4945,14 @@ static void register_editor_callback() { ED_SHORTCUT("visual_script_editor/copy_nodes", TTR("Copy Nodes"), KEY_MASK_CMD + KEY_C); ED_SHORTCUT("visual_script_editor/cut_nodes", TTR("Cut Nodes"), KEY_MASK_CMD + KEY_X); ED_SHORTCUT("visual_script_editor/paste_nodes", TTR("Paste Nodes"), KEY_MASK_CMD + KEY_V); + ED_SHORTCUT("visual_script_editor/create_function", TTR("Make Function"), KEY_MASK_CMD + KEY_G); + ED_SHORTCUT("visual_script_editor/refresh_nodes", TTR("Refresh Graph"), KEY_MASK_CMD + KEY_R); ED_SHORTCUT("visual_script_editor/edit_member", TTR("Edit Member"), KEY_MASK_CMD + KEY_E); } void VisualScriptEditor::register_editor() { - //too early to register stuff here, request a callback + // Too early to register stuff here, request a callback. EditorNode::add_plugin_init_callback(register_editor_callback); } diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 5df9b1a004..5a00469eea 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -59,6 +59,8 @@ class VisualScriptEditor : public ScriptEditorBase { EDIT_COPY_NODES, EDIT_CUT_NODES, EDIT_PASTE_NODES, + EDIT_CREATE_FUNCTION, + REFRESH_GRAPH }; enum PortAction { @@ -79,17 +81,19 @@ class VisualScriptEditor : public ScriptEditorBase { MEMBER_SIGNAL }; - VSplitContainer *left_vsplit; + VBoxContainer *members_section; MenuButton *edit_menu; Ref<VisualScript> script; Button *base_type_select; - GraphEdit *graph; + LineEdit *func_name_box; + ScrollContainer *func_input_scroll; + VBoxContainer *func_input_vbox; + ConfirmationDialog *function_create_dialog; - LineEdit *node_filter; - TextureRect *node_filter_icon; + GraphEdit *graph; VisualScriptEditorSignalEdit *signal_editor; @@ -110,7 +114,8 @@ class VisualScriptEditor : public ScriptEditorBase { UndoRedo *undo_redo; Tree *members; - Tree *nodes; + PopupDialog *function_name_edit; + LineEdit *function_name_box; Label *hint_text; Timer *hint_text_timer; @@ -133,6 +138,7 @@ class VisualScriptEditor : public ScriptEditorBase { HashMap<StringName, Ref<StyleBox> > node_styles; StringName edited_func; + StringName default_func; void _update_graph_connections(); void _update_graph(int p_only_id = -1); @@ -165,9 +171,13 @@ class VisualScriptEditor : public ScriptEditorBase { int port_action_output; Vector2 port_action_pos; int port_action_new_node; - void _port_action_menu(int p_option); - void new_node(Ref<VisualScriptNode> vnode, Vector2 ofs); + bool saved_pos_dirty; + Vector2 saved_position; + + Vector2 mouse_up_position; + + void _port_action_menu(int p_option, const StringName &p_func); void connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id); @@ -175,13 +185,13 @@ class VisualScriptEditor : public ScriptEditorBase { void connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id); void _cancel_connect_node(); - void _create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point); + int _create_new_node_from_name(const String &p_text, const Vector2 &p_point, const StringName &p_func = StringName()); void _selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting); int error_line; void _node_selected(Node *p_node); - void _center_on_node(int p_id); + void _center_on_node(const StringName &p_func, int p_id); void _node_filter_changed(const String &p_text); void _change_base_type_callback(); @@ -192,7 +202,9 @@ class VisualScriptEditor : public ScriptEditorBase { void _begin_node_move(); void _end_node_move(); - void _move_node(String func, int p_id, const Vector2 &p_to); + void _move_node(const StringName &p_func, int p_id, const Vector2 &p_to); + + void _get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes); void _node_moved(Vector2 p_from, Vector2 p_to, int p_id); void _remove_node(int p_id); @@ -201,21 +213,44 @@ class VisualScriptEditor : public ScriptEditorBase { void _graph_connect_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_pos); void _node_ports_changed(const String &p_func, int p_id); - void _available_node_doubleclicked(); + void _node_create(); void _update_available_nodes(); void _member_button(Object *p_item, int p_column, int p_button); void _expression_text_changed(const String &p_text, int p_id); + void _add_input_port(int p_id); + void _add_output_port(int p_id); + void _remove_input_port(int p_id, int p_port); + void _remove_output_port(int p_id, int p_port); + void _change_port_type(int p_select, int p_id, int p_port, bool is_input); + void _update_node_size(int p_id); + void _port_name_focus_out(const Node *p_name_box, int p_id, int p_port, bool is_input); - String revert_on_drag; + Vector2 _get_available_pos(bool centered = true, Vector2 pos = Vector2()) const; + StringName _get_function_of_node(int p_id) const; - void _input(const Ref<InputEvent> &p_event); + void _move_nodes_with_rescan(const StringName &p_func_from, const StringName &p_func_to, int p_id); + bool node_has_sequence_connections(const StringName &p_func, int p_id); - void _generic_search(String p_base_type = ""); + void _generic_search(String p_base_type = "", Vector2 pos = Vector2(), bool node_centered = false); + void _input(const Ref<InputEvent> &p_event); + void _graph_gui_input(const Ref<InputEvent> &p_event); void _members_gui_input(const Ref<InputEvent> &p_event); + void _fn_name_box_input(const Ref<InputEvent> &p_event); + void _rename_function(const String &p_name, const String &p_new_name); + + void _create_function_dialog(); + void _create_function(); + void _add_func_input(); + void _remove_func_input(Node *p_node); + void _deselect_input_names(); + void _add_node_dialog(); + void _node_item_selected(); + void _node_item_unselected(); + void _on_nodes_delete(); void _on_nodes_duplicate(); @@ -226,6 +261,10 @@ class VisualScriptEditor : public ScriptEditorBase { int editing_id; int editing_input; + bool can_swap; + int data_disconnect_node; + int data_disconnect_port; + void _default_value_changed(); void _default_value_edited(Node *p_button, int p_id, int p_input_port); @@ -240,7 +279,7 @@ class VisualScriptEditor : public ScriptEditorBase { void _draw_color_over_button(Object *obj, Color p_color); void _button_resource_previewed(const String &p_path, const Ref<Texture> &p_preview, const Ref<Texture> &p_small_preview, Variant p_ud); - VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &visited_nodes); + VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &p_visited_nodes); void _member_rmb_selected(const Vector2 &p_pos); void _member_option(int p_option); diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index c330fa1bc0..7f36549ae4 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -133,10 +133,12 @@ int VisualScriptFunctionCall::get_input_value_port_count() const { MethodBind *mb = ClassDB::get_method(_get_base_type(), function); if (mb) { - return mb->get_argument_count() + (call_mode == CALL_MODE_INSTANCE ? 1 : 0) + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) - use_default_args; + int defaulted_args = mb->get_argument_count() < use_default_args ? mb->get_argument_count() : use_default_args; + return mb->get_argument_count() + (call_mode == CALL_MODE_INSTANCE ? 1 : 0) + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) - defaulted_args; } - return method_cache.arguments.size() + (call_mode == CALL_MODE_INSTANCE ? 1 : 0) + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) - use_default_args; + int defaulted_args = method_cache.arguments.size() < use_default_args ? method_cache.arguments.size() : use_default_args; + return method_cache.arguments.size() + (call_mode == CALL_MODE_INSTANCE ? 1 : 0) + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) - defaulted_args; } } int VisualScriptFunctionCall::get_output_value_port_count() const { @@ -1056,13 +1058,6 @@ PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) cons if (call_mode == CALL_MODE_BASIC_TYPE) { return PropertyInfo(basic_type, "out"); } else if (call_mode == CALL_MODE_INSTANCE) { - List<PropertyInfo> props; - ClassDB::get_property_list(_get_base_type(), &props, true); - for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - if (E->get().name == property) { - return PropertyInfo(E->get().type, "pass", PROPERTY_HINT_TYPE_STRING, E->get().hint_string); - } - } return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type()); } else { return PropertyInfo(); diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 65820b4c15..957127fe61 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -356,6 +356,441 @@ int VisualScriptFunction::get_stack_size() const { } ////////////////////////////////////////// +/////////////////LISTS//////////////////// +////////////////////////////////////////// + +int VisualScriptLists::get_output_sequence_port_count() const { + if (sequenced) + return 1; + return 0; +} +bool VisualScriptLists::has_input_sequence_port() const { + return sequenced; +} + +String VisualScriptLists::get_output_sequence_port_text(int p_port) const { + return ""; +} + +int VisualScriptLists::get_input_value_port_count() const { + return inputports.size(); +} +int VisualScriptLists::get_output_value_port_count() const { + return outputports.size(); +} + +PropertyInfo VisualScriptLists::get_input_value_port_info(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, inputports.size(), PropertyInfo()); + + PropertyInfo pi; + pi.name = inputports[p_idx].name; + pi.type = inputports[p_idx].type; + return pi; +} +PropertyInfo VisualScriptLists::get_output_value_port_info(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, outputports.size(), PropertyInfo()); + + PropertyInfo pi; + pi.name = outputports[p_idx].name; + pi.type = outputports[p_idx].type; + return pi; +} + +bool VisualScriptLists::is_input_port_editable() const { + return ((flags & INPUT_EDITABLE) == INPUT_EDITABLE); +} +bool VisualScriptLists::is_input_port_name_editable() const { + return ((flags & INPUT_NAME_EDITABLE) == INPUT_NAME_EDITABLE); +} +bool VisualScriptLists::is_input_port_type_editable() const { + return ((flags & INPUT_TYPE_EDITABLE) == INPUT_TYPE_EDITABLE); +} + +bool VisualScriptLists::is_output_port_editable() const { + return ((flags & OUTPUT_EDITABLE) == OUTPUT_EDITABLE); +} +bool VisualScriptLists::is_output_port_name_editable() const { + return ((flags & INPUT_NAME_EDITABLE) == INPUT_NAME_EDITABLE); +} +bool VisualScriptLists::is_output_port_type_editable() const { + return ((flags & INPUT_TYPE_EDITABLE) == INPUT_TYPE_EDITABLE); +} + +// for the inspector +bool VisualScriptLists::_set(const StringName &p_name, const Variant &p_value) { + + if (p_name == "input_count" && is_input_port_editable()) { + + int new_argc = p_value; + int argc = inputports.size(); + if (argc == new_argc) + return true; + + inputports.resize(new_argc); + + for (int i = argc; i < new_argc; i++) { + inputports.write[i].name = "arg" + itos(i + 1); + inputports.write[i].type = Variant::NIL; + } + ports_changed_notify(); + _change_notify(); + return true; + } + if (String(p_name).begins_with("input_") && is_input_port_editable()) { + int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1; + ERR_FAIL_INDEX_V(idx, inputports.size(), false); + String what = String(p_name).get_slice("/", 1); + if (what == "type") { + + Variant::Type new_type = Variant::Type(int(p_value)); + inputports.write[idx].type = new_type; + ports_changed_notify(); + + return true; + } + + if (what == "name") { + + inputports.write[idx].name = p_value; + ports_changed_notify(); + return true; + } + } + + if (p_name == "output_count" && is_output_port_editable()) { + + int new_argc = p_value; + int argc = outputports.size(); + if (argc == new_argc) + return true; + + outputports.resize(new_argc); + + for (int i = argc; i < new_argc; i++) { + outputports.write[i].name = "arg" + itos(i + 1); + outputports.write[i].type = Variant::NIL; + } + ports_changed_notify(); + _change_notify(); + return true; + } + if (String(p_name).begins_with("output_") && is_output_port_editable()) { + int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1; + ERR_FAIL_INDEX_V(idx, outputports.size(), false); + String what = String(p_name).get_slice("/", 1); + if (what == "type") { + + Variant::Type new_type = Variant::Type(int(p_value)); + outputports.write[idx].type = new_type; + ports_changed_notify(); + + return true; + } + + if (what == "name") { + + outputports.write[idx].name = p_value; + ports_changed_notify(); + return true; + } + } + + if (p_name == "sequenced/sequenced") { + sequenced = p_value; + ports_changed_notify(); + return true; + } + + return false; +} +bool VisualScriptLists::_get(const StringName &p_name, Variant &r_ret) const { + + if (p_name == "input_count" && is_input_port_editable()) { + r_ret = inputports.size(); + return true; + } + if (String(p_name).begins_with("input_") && is_input_port_editable()) { + int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1; + ERR_FAIL_INDEX_V(idx, inputports.size(), false); + String what = String(p_name).get_slice("/", 1); + if (what == "type") { + r_ret = inputports[idx].type; + return true; + } + if (what == "name") { + r_ret = inputports[idx].name; + return true; + } + } + + if (p_name == "output_count" && is_output_port_editable()) { + r_ret = outputports.size(); + return true; + } + if (String(p_name).begins_with("output_") && is_output_port_editable()) { + int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1; + ERR_FAIL_INDEX_V(idx, outputports.size(), false); + String what = String(p_name).get_slice("/", 1); + if (what == "type") { + r_ret = outputports[idx].type; + return true; + } + if (what == "name") { + r_ret = outputports[idx].name; + return true; + } + } + + if (p_name == "sequenced/sequenced") { + r_ret = sequenced; + return true; + } + + return false; +} +void VisualScriptLists::_get_property_list(List<PropertyInfo> *p_list) const { + + if (is_input_port_editable()) { + p_list->push_back(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,256")); + String argt = "Any"; + for (int i = 1; i < Variant::VARIANT_MAX; i++) { + argt += "," + Variant::get_type_name(Variant::Type(i)); + } + + for (int i = 0; i < inputports.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, "input_" + itos(i + 1) + "/type", PROPERTY_HINT_ENUM, argt)); + p_list->push_back(PropertyInfo(Variant::STRING, "input_" + itos(i + 1) + "/name")); + } + } + + if (is_output_port_editable()) { + p_list->push_back(PropertyInfo(Variant::INT, "output_count", PROPERTY_HINT_RANGE, "0,256")); + String argt = "Any"; + for (int i = 1; i < Variant::VARIANT_MAX; i++) { + argt += "," + Variant::get_type_name(Variant::Type(i)); + } + + for (int i = 0; i < outputports.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, "output_" + itos(i + 1) + "/type", PROPERTY_HINT_ENUM, argt)); + p_list->push_back(PropertyInfo(Variant::STRING, "output_" + itos(i + 1) + "/name")); + } + } + p_list->push_back(PropertyInfo(Variant::BOOL, "sequenced/sequenced")); +} + +// input data port interaction +void VisualScriptLists::add_input_data_port(Variant::Type p_type, const String &p_name, int p_index) { + + if (!is_input_port_editable()) + return; + + Port inp; + inp.name = p_name; + inp.type = p_type; + if (p_index >= 0) + inputports.insert(p_index, inp); + else + inputports.push_back(inp); + + ports_changed_notify(); + _change_notify(); +} +void VisualScriptLists::set_input_data_port_type(int p_idx, Variant::Type p_type) { + + if (!is_input_port_type_editable()) + return; + + ERR_FAIL_INDEX(p_idx, inputports.size()); + + inputports.write[p_idx].type = p_type; + ports_changed_notify(); + _change_notify(); +} +void VisualScriptLists::set_input_data_port_name(int p_idx, const String &p_name) { + + if (!is_input_port_name_editable()) + return; + + ERR_FAIL_INDEX(p_idx, inputports.size()); + + inputports.write[p_idx].name = p_name; + ports_changed_notify(); + _change_notify(); +} +void VisualScriptLists::remove_input_data_port(int p_argidx) { + + if (!is_input_port_editable()) + return; + + ERR_FAIL_INDEX(p_argidx, inputports.size()); + + inputports.remove(p_argidx); + + ports_changed_notify(); + _change_notify(); +} + +// output data port interaction +void VisualScriptLists::add_output_data_port(Variant::Type p_type, const String &p_name, int p_index) { + + if (!is_output_port_editable()) + return; + + Port out; + out.name = p_name; + out.type = p_type; + if (p_index >= 0) + outputports.insert(p_index, out); + else + outputports.push_back(out); + + ports_changed_notify(); + _change_notify(); +} +void VisualScriptLists::set_output_data_port_type(int p_idx, Variant::Type p_type) { + + if (!is_output_port_type_editable()) + return; + + ERR_FAIL_INDEX(p_idx, outputports.size()); + + outputports.write[p_idx].type = p_type; + ports_changed_notify(); + _change_notify(); +} +void VisualScriptLists::set_output_data_port_name(int p_idx, const String &p_name) { + + if (!is_output_port_name_editable()) + return; + + ERR_FAIL_INDEX(p_idx, outputports.size()); + + outputports.write[p_idx].name = p_name; + ports_changed_notify(); + _change_notify(); +} +void VisualScriptLists::remove_output_data_port(int p_argidx) { + + if (!is_output_port_editable()) + return; + + ERR_FAIL_INDEX(p_argidx, outputports.size()); + + outputports.remove(p_argidx); + + ports_changed_notify(); + _change_notify(); +} + +// sequences +void VisualScriptLists::set_sequenced(bool p_enable) { + if (sequenced == p_enable) + return; + sequenced = p_enable; + ports_changed_notify(); +} +bool VisualScriptLists::is_sequenced() const { + return sequenced; +} + +VisualScriptLists::VisualScriptLists() { + // initialize + sequenced = false; + flags = 0; +} + +void VisualScriptLists::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_input_data_port", "type", "name", "index"), &VisualScriptLists::add_input_data_port); + ClassDB::bind_method(D_METHOD("set_input_data_port_name", "index", "name"), &VisualScriptLists::set_input_data_port_name); + ClassDB::bind_method(D_METHOD("set_input_data_port_type", "index", "type"), &VisualScriptLists::set_input_data_port_type); + ClassDB::bind_method(D_METHOD("remove_input_data_port", "index"), &VisualScriptLists::remove_input_data_port); + + ClassDB::bind_method(D_METHOD("add_output_data_port", "type", "name", "index"), &VisualScriptLists::add_output_data_port); + ClassDB::bind_method(D_METHOD("set_output_data_port_name", "index", "name"), &VisualScriptLists::set_output_data_port_name); + ClassDB::bind_method(D_METHOD("set_output_data_port_type", "index", "type"), &VisualScriptLists::set_output_data_port_type); + ClassDB::bind_method(D_METHOD("remove_output_data_port", "index"), &VisualScriptLists::remove_output_data_port); +} + +////////////////////////////////////////// +//////////////COMPOSEARRAY//////////////// +////////////////////////////////////////// + +int VisualScriptComposeArray::get_output_sequence_port_count() const { + if (sequenced) + return 1; + return 0; +} +bool VisualScriptComposeArray::has_input_sequence_port() const { + return sequenced; +} + +String VisualScriptComposeArray::get_output_sequence_port_text(int p_port) const { + return ""; +} + +int VisualScriptComposeArray::get_input_value_port_count() const { + return inputports.size(); +} +int VisualScriptComposeArray::get_output_value_port_count() const { + return 1; +} + +PropertyInfo VisualScriptComposeArray::get_input_value_port_info(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, inputports.size(), PropertyInfo()); + + PropertyInfo pi; + pi.name = inputports[p_idx].name; + pi.type = inputports[p_idx].type; + return pi; +} +PropertyInfo VisualScriptComposeArray::get_output_value_port_info(int p_idx) const { + PropertyInfo pi; + pi.name = "out"; + pi.type = Variant::ARRAY; + return pi; +} + +String VisualScriptComposeArray::get_caption() const { + return "Compose Array"; +} +String VisualScriptComposeArray::get_text() const { + return ""; +} + +class VisualScriptComposeArrayNode : public VisualScriptNodeInstance { +public: + int input_count = 0; + virtual int get_working_memory_size() const { return 0; } + + virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Variant::CallError &r_error, String &r_error_str) { + + if (input_count > 0) { + Array arr; + for (int i = 0; i < input_count; i++) + arr.push_back((*p_inputs[i])); + Variant va = Variant(arr); + + *p_outputs[0] = va; + } + + return 0; + } +}; + +VisualScriptNodeInstance *VisualScriptComposeArray::instance(VisualScriptInstance *p_instance) { + + VisualScriptComposeArrayNode *instance = memnew(VisualScriptComposeArrayNode); + instance->input_count = inputports.size(); + return instance; +} + +VisualScriptComposeArray::VisualScriptComposeArray() { + // initialize stuff here + sequenced = false; + flags = INPUT_EDITABLE; +} + +////////////////////////////////////////// ////////////////OPERATOR////////////////// ////////////////////////////////////////// @@ -3640,6 +4075,14 @@ VisualScriptDeconstruct::VisualScriptDeconstruct() { type = Variant::NIL; } +template <Variant::Type T> +static Ref<VisualScriptNode> create_node_deconst_typed(const String &p_name) { + Ref<VisualScriptDeconstruct> node; + node.instance(); + node->set_deconstruct_type(T); + return node; +} + void register_visual_script_nodes() { VisualScriptLanguage::singleton->add_register_func("data/set_variable", create_node_generic<VisualScriptVariableSet>); @@ -3697,7 +4140,17 @@ void register_visual_script_nodes() { VisualScriptLanguage::singleton->add_register_func("operators/logic/in", create_op_node<Variant::OP_IN>); VisualScriptLanguage::singleton->add_register_func("operators/logic/select", create_node_generic<VisualScriptSelect>); - VisualScriptLanguage::singleton->add_register_func("functions/deconstruct", create_node_generic<VisualScriptDeconstruct>); + VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR2), create_node_deconst_typed<Variant::Type::VECTOR2>); + VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR3), create_node_deconst_typed<Variant::Type::VECTOR3>); + VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::COLOR), create_node_deconst_typed<Variant::Type::COLOR>); + VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::RECT2), create_node_deconst_typed<Variant::Type::RECT2>); + VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::TRANSFORM2D), create_node_deconst_typed<Variant::Type::TRANSFORM2D>); + VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::PLANE), create_node_deconst_typed<Variant::Type::PLANE>); + VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::QUAT), create_node_deconst_typed<Variant::Type::QUAT>); + VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::AABB), create_node_deconst_typed<Variant::Type::AABB>); + VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::BASIS), create_node_deconst_typed<Variant::Type::BASIS>); + VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::TRANSFORM), create_node_deconst_typed<Variant::Type::TRANSFORM>); + VisualScriptLanguage::singleton->add_register_func("functions/compose_array", create_node_generic<VisualScriptComposeArray>); for (int i = 1; i < Variant::VARIANT_MAX; i++) { diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index 762a1bdfb6..c7354cb0d8 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -103,6 +103,103 @@ public: VisualScriptFunction(); }; +class VisualScriptLists : public VisualScriptNode { + + GDCLASS(VisualScriptLists, VisualScriptNode) + + struct Port { + String name; + Variant::Type type; + }; + +protected: + Vector<Port> inputports; + Vector<Port> outputports; + + enum { + OUTPUT_EDITABLE = 0x0001, + OUTPUT_NAME_EDITABLE = 0x0002, + OUTPUT_TYPE_EDITABLE = 0x0004, + INPUT_EDITABLE = 0x0008, + INPUT_NAME_EDITABLE = 0x000F, + INPUT_TYPE_EDITABLE = 0x0010, + }; + + int flags; + + bool sequenced; + + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + + static void _bind_methods(); + +public: + virtual bool is_output_port_editable() const; + virtual bool is_output_port_name_editable() const; + virtual bool is_output_port_type_editable() const; + + virtual bool is_input_port_editable() const; + virtual bool is_input_port_name_editable() const; + virtual bool is_input_port_type_editable() const; + + virtual int get_output_sequence_port_count() const; + virtual bool has_input_sequence_port() const; + + virtual String get_output_sequence_port_text(int p_port) const; + + virtual int get_input_value_port_count() const; + virtual int get_output_value_port_count() const; + + virtual PropertyInfo get_input_value_port_info(int p_idx) const; + virtual PropertyInfo get_output_value_port_info(int p_idx) const; + + virtual String get_caption() const = 0; + virtual String get_text() const = 0; + virtual String get_category() const = 0; + + void add_input_data_port(Variant::Type p_type, const String &p_name, int p_index = -1); + void set_input_data_port_type(int p_idx, Variant::Type p_type); + void set_input_data_port_name(int p_idx, const String &p_name); + void remove_input_data_port(int p_argidx); + + void add_output_data_port(Variant::Type p_type, const String &p_name, int p_index = -1); + void set_output_data_port_type(int p_idx, Variant::Type p_type); + void set_output_data_port_name(int p_idx, const String &p_name); + void remove_output_data_port(int p_argidx); + + void set_sequenced(bool p_enable); + bool is_sequenced() const; + + VisualScriptLists(); +}; + +class VisualScriptComposeArray : public VisualScriptLists { + + GDCLASS(VisualScriptComposeArray, VisualScriptLists) + +public: + virtual int get_output_sequence_port_count() const; + virtual bool has_input_sequence_port() const; + + virtual String get_output_sequence_port_text(int p_port) const; + + virtual int get_input_value_port_count() const; + virtual int get_output_value_port_count() const; + + virtual PropertyInfo get_input_value_port_info(int p_idx) const; + virtual PropertyInfo get_output_value_port_info(int p_idx) const; + + virtual String get_caption() const; + virtual String get_text() const; + virtual String get_category() const { return "functions"; } + + virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance); + + VisualScriptComposeArray(); +}; + class VisualScriptOperator : public VisualScriptNode { GDCLASS(VisualScriptOperator, VisualScriptNode); diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp index 764807cffd..42d4c5e209 100644 --- a/modules/visual_script/visual_script_property_selector.cpp +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -200,13 +200,10 @@ void VisualScriptPropertySelector::_update_search() { Object *obj = ObjectDB::get_instance(script); if (Object::cast_to<Script>(obj)) { - methods.push_back(MethodInfo("*Script Methods")); Object::cast_to<Script>(obj)->get_script_method_list(&methods); - - } else { - methods.push_back(MethodInfo("*" + String(E->get()))); - ClassDB::get_method_list(E->get(), &methods, true, true); } + + ClassDB::get_method_list(E->get(), &methods, true, true); } } for (List<MethodInfo>::Element *M = methods.front(); M; M = M->next()) { @@ -274,6 +271,7 @@ void VisualScriptPropertySelector::_update_search() { get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box); get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box); get_visual_node_names("functions/by_type/" + Variant::get_type_name(type), Set<String>(), found, root, search_box); + get_visual_node_names("functions/deconstruct/" + Variant::get_type_name(type), Set<String>(), found, root, search_box); get_visual_node_names("operators/compare/", Set<String>(), found, root, search_box); if (type == Variant::INT) { get_visual_node_names("operators/bitwise/", Set<String>(), found, root, search_box); @@ -327,7 +325,7 @@ void VisualScriptPropertySelector::create_visualscript_item(const String &name, } } -void VisualScriptPropertySelector::get_visual_node_names(const String &root_filter, const Set<String> &filter, bool &found, TreeItem *const root, LineEdit *const search_box) { +void VisualScriptPropertySelector::get_visual_node_names(const String &root_filter, const Set<String> &p_modifiers, bool &found, TreeItem *const root, LineEdit *const search_box) { Map<String, TreeItem *> path_cache; List<String> fnodes; @@ -338,37 +336,59 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt continue; } Vector<String> path = E->get().split("/"); - bool is_filter = false; - for (Set<String>::Element *F = filter.front(); F; F = F->next()) { - if (path.size() >= 2 && path[1].findn(F->get()) != -1) { - is_filter = true; + + // check if the name has the filter + bool in_filter = false; + Vector<String> tx_filters = search_box->get_text().split(" "); + for (int i = 0; i < tx_filters.size(); i++) { + if (tx_filters[i] == "") { + in_filter = true; + } else { + in_filter = false; + } + if (E->get().findn(tx_filters[i]) != -1) { + in_filter = true; break; } } - if (is_filter) { + if (!in_filter) { continue; } - if (search_box->get_text() != String() && E->get().findn(search_box->get_text()) == -1) { + bool in_modifier = false | p_modifiers.empty(); + for (Set<String>::Element *F = p_modifiers.front(); F && in_modifier; F = F->next()) { + if (E->get().findn(F->get()) != -1) + in_modifier = true; + } + if (!in_modifier) { continue; } + TreeItem *item = search_options->create_item(root); - VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); + Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(E->get()); + Ref<VisualScriptOperator> vnode_operator = vnode; String type_name; - if (vnode_operator != NULL) { + if (vnode_operator.is_valid()) { String type; if (path.size() >= 2) { type = path[1]; } type_name = type.capitalize() + " "; } - VisualScriptFunctionCall *vnode_function_call = Object::cast_to<VisualScriptFunctionCall>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); - if (vnode_function_call != NULL) { + Ref<VisualScriptFunctionCall> vnode_function_call = vnode; + if (vnode_function_call.is_valid()) { String basic_type = Variant::get_type_name(vnode_function_call->get_basic_type()); type_name = basic_type.capitalize() + " "; } - - Vector<String> desc = path[path.size() - 1].replace("(", "( ").replace(")", " )").replace(",", ", ").split(" "); + Ref<VisualScriptConstructor> vnode_constructor = vnode; + if (vnode_constructor.is_valid()) { + type_name = "Construct "; + } + Ref<VisualScriptDeconstruct> vnode_deconstruct = vnode; + if (vnode_deconstruct.is_valid()) { + type_name = "Deconstruct "; + } + Vector<String> desc = path[path.size() - 1].replace("(", " ").replace(")", " ").replace(",", " ").split(" "); for (int i = 0; i < desc.size(); i++) { desc.write[i] = desc[i].capitalize(); if (desc[i].ends_with(",")) { @@ -504,7 +524,7 @@ void VisualScriptPropertySelector::_notification(int p_what) { } } -void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, const bool p_virtuals_only, const bool p_connecting) { +void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, const bool p_virtuals_only, const bool p_connecting, bool clear_text) { base_type = p_base; selected = p_current; @@ -515,7 +535,10 @@ void VisualScriptPropertySelector::select_method_from_base_type(const String &p_ virtuals_only = p_virtuals_only; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); connecting = p_connecting; @@ -526,7 +549,7 @@ void VisualScriptPropertySelector::set_type_filter(const Vector<Variant::Type> & type_filter = p_type_filter; } -void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only, bool p_seq_connect, const bool p_connecting) { +void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only, bool p_seq_connect, const bool p_connecting, bool clear_text) { base_type = p_base; selected = p_current; @@ -538,7 +561,10 @@ void VisualScriptPropertySelector::select_from_base_type(const String &p_base, c virtuals_only = p_virtuals_only; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); seq_connect = p_seq_connect; connecting = p_connecting; @@ -546,7 +572,7 @@ void VisualScriptPropertySelector::select_from_base_type(const String &p_base, c _update_search(); } -void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const String &p_current, const bool p_connecting) { +void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const String &p_current, const bool p_connecting, bool clear_text) { ERR_FAIL_COND(p_script.is_null()); base_type = p_script->get_instance_base_type(); @@ -559,7 +585,10 @@ void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_scrip virtuals_only = false; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); seq_connect = false; connecting = p_connecting; @@ -567,7 +596,7 @@ void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_scrip _update_search(); } -void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const String &p_current, const bool p_connecting) { +void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const String &p_current, const bool p_connecting, bool clear_text) { ERR_FAIL_COND(p_type == Variant::NIL); base_type = ""; selected = p_current; @@ -579,7 +608,10 @@ void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, virtuals_only = false; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); seq_connect = false; connecting = p_connecting; @@ -587,7 +619,7 @@ void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, _update_search(); } -void VisualScriptPropertySelector::select_from_action(const String &p_type, const String &p_current, const bool p_connecting) { +void VisualScriptPropertySelector::select_from_action(const String &p_type, const String &p_current, const bool p_connecting, bool clear_text) { base_type = p_type; selected = p_current; type = Variant::NIL; @@ -598,7 +630,10 @@ void VisualScriptPropertySelector::select_from_action(const String &p_type, cons virtuals_only = false; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); seq_connect = true; connecting = p_connecting; @@ -606,8 +641,8 @@ void VisualScriptPropertySelector::select_from_action(const String &p_type, cons _update_search(); } -void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const String &p_current, const bool p_connecting) { - base_type = ""; +void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const String &p_current, const bool p_connecting, const String &p_basetype, bool clear_text) { + base_type = p_basetype; selected = p_current; type = Variant::NIL; script = 0; @@ -617,7 +652,10 @@ void VisualScriptPropertySelector::select_from_instance(Object *p_instance, cons virtuals_only = false; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); seq_connect = false; connecting = p_connecting; @@ -625,7 +663,7 @@ void VisualScriptPropertySelector::select_from_instance(Object *p_instance, cons _update_search(); } -void VisualScriptPropertySelector::select_from_visual_script(const String &p_base, const bool p_connecting) { +void VisualScriptPropertySelector::select_from_visual_script(const String &p_base, const bool p_connecting, bool clear_text) { base_type = p_base; selected = ""; type = Variant::NIL; @@ -635,7 +673,10 @@ void VisualScriptPropertySelector::select_from_visual_script(const String &p_bas instance = NULL; virtuals_only = false; show_window(.5f); - search_box->set_text(""); + if (clear_text) + search_box->set_text(""); + else + search_box->select_all(); search_box->grab_focus(); connecting = p_connecting; @@ -646,7 +687,7 @@ void VisualScriptPropertySelector::show_window(float p_screen_ratio) { Rect2 rect; Point2 window_size = get_viewport_rect().size; rect.size = (window_size * p_screen_ratio).floor(); - rect.size.x = rect.size.x / 1.25f; + rect.size.x = rect.size.x / 2.2f; rect.position = ((window_size - rect.size) / 2.0f).floor(); popup(rect); } diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h index 6235e4ba1d..13ce9bdca2 100644 --- a/modules/visual_script/visual_script_property_selector.h +++ b/modules/visual_script/visual_script_property_selector.h @@ -74,13 +74,13 @@ protected: static void _bind_methods(); public: - void select_method_from_base_type(const String &p_base, const String &p_current = "", const bool p_virtuals_only = false, const bool p_connecting = true); - void select_from_base_type(const String &p_base, const String &p_current = "", bool p_virtuals_only = false, bool p_seq_connect = false, const bool p_connecting = true); - void select_from_script(const Ref<Script> &p_script, const String &p_current = "", const bool p_connecting = true); - void select_from_basic_type(Variant::Type p_type, const String &p_current = "", const bool p_connecting = true); - void select_from_action(const String &p_type, const String &p_current = "", const bool p_connecting = true); - void select_from_instance(Object *p_instance, const String &p_current = "", const bool p_connecting = true); - void select_from_visual_script(const String &p_base, const bool p_connecting = true); + void select_method_from_base_type(const String &p_base, const String &p_current = "", const bool p_virtuals_only = false, const bool p_connecting = true, bool clear_text = true); + void select_from_base_type(const String &p_base, const String &p_current = "", bool p_virtuals_only = false, bool p_seq_connect = false, const bool p_connecting = true, bool clear_text = true); + void select_from_script(const Ref<Script> &p_script, const String &p_current = "", const bool p_connecting = true, bool clear_text = true); + void select_from_basic_type(Variant::Type p_type, const String &p_current = "", const bool p_connecting = true, bool clear_text = true); + void select_from_action(const String &p_type, const String &p_current = "", const bool p_connecting = true, bool clear_text = true); + void select_from_instance(Object *p_instance, const String &p_current = "", const bool p_connecting = true, const String &p_basetype = "", bool clear_text = true); + void select_from_visual_script(const String &p_base, const bool p_connecting = true, bool clear_text = true); void show_window(float p_screen_ratio); |