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);  |