#include "visual_script_editor.h" #include "tools/editor/editor_node.h" #include "visual_script_nodes.h" #include "visual_script_flow_control.h" #include "visual_script_func_nodes.h" #include "visual_script_expression.h" #include "os/input.h" #include "tools/editor/editor_resource_preview.h" #include "os/keyboard.h" #ifdef TOOLS_ENABLED class VisualScriptEditorSignalEdit : public Object { OBJ_TYPE(VisualScriptEditorSignalEdit,Object) StringName sig; public: UndoRedo *undo_redo; Ref<VisualScript> script; protected: static void _bind_methods() { ObjectTypeDB::bind_method("_sig_changed",&VisualScriptEditorSignalEdit::_sig_changed); } void _sig_changed() { _change_notify(); } bool _set(const StringName& p_name, const Variant& p_value) { if (sig==StringName()) return false; if (p_name=="argument_count") { int new_argc=p_value; int argc = script->custom_signal_get_argument_count(sig); if (argc==new_argc) return true; undo_redo->create_action("Change Signal Arguments"); if (new_argc < argc) { for(int i=new_argc;i<argc;i++) { undo_redo->add_do_method(script.ptr(),"custom_signal_remove_argument",sig,new_argc); undo_redo->add_undo_method(script.ptr(),"custom_signal_add_argument",sig,script->custom_signal_get_argument_name(sig,i),script->custom_signal_get_argument_type(sig,i),-1); } } else if (new_argc>argc) { for(int i=argc;i<new_argc;i++) { undo_redo->add_do_method(script.ptr(),"custom_signal_add_argument",sig,Variant::NIL,"arg"+itos(i+1),-1); undo_redo->add_undo_method(script.ptr(),"custom_signal_remove_argument",sig,argc); } } undo_redo->add_do_method(this,"_sig_changed"); undo_redo->add_undo_method(this,"_sig_changed"); undo_redo->commit_action(); return true; } if (String(p_name).begins_with("argument/")) { int idx = String(p_name).get_slice("/",1).to_int()-1; ERR_FAIL_INDEX_V(idx,script->custom_signal_get_argument_count(sig),false); String what = String(p_name).get_slice("/",2); if (what=="type") { int old_type = script->custom_signal_get_argument_type(sig,idx); int new_type=p_value; undo_redo->create_action("Change Argument Type"); undo_redo->add_do_method(script.ptr(),"custom_signal_set_argument_type",sig,idx,new_type); undo_redo->add_undo_method(script.ptr(),"custom_signal_set_argument_type",sig,idx,old_type); undo_redo->commit_action(); return true; } if (what=="name") { String old_name = script->custom_signal_get_argument_name(sig,idx); String new_name=p_value; undo_redo->create_action("Change Argument name"); undo_redo->add_do_method(script.ptr(),"custom_signal_set_argument_name",sig,idx,new_name); undo_redo->add_undo_method(script.ptr(),"custom_signal_set_argument_name",sig,idx,old_name); undo_redo->commit_action(); return true; } } return false; } bool _get(const StringName& p_name,Variant &r_ret) const { if (sig==StringName()) return false; if (p_name=="argument_count") { r_ret = script->custom_signal_get_argument_count(sig); return true; } if (String(p_name).begins_with("argument/")) { int idx = String(p_name).get_slice("/",1).to_int()-1; ERR_FAIL_INDEX_V(idx,script->custom_signal_get_argument_count(sig),false); String what = String(p_name).get_slice("/",2); if (what=="type") { r_ret = script->custom_signal_get_argument_type(sig,idx); return true; } if (what=="name") { r_ret = script->custom_signal_get_argument_name(sig,idx); return true; } } return false; } void _get_property_list( List<PropertyInfo> *p_list) const { if (sig==StringName()) return; p_list->push_back(PropertyInfo(Variant::INT,"argument_count",PROPERTY_HINT_RANGE,"0,256")); String argt="Variant"; for(int i=1;i<Variant::VARIANT_MAX;i++) { argt+=","+Variant::get_type_name(Variant::Type(i)); } for(int i=0;i<script->custom_signal_get_argument_count(sig);i++) { p_list->push_back(PropertyInfo(Variant::INT,"argument/"+itos(i+1)+"/type",PROPERTY_HINT_ENUM,argt)); p_list->push_back(PropertyInfo(Variant::STRING,"argument/"+itos(i+1)+"/name")); } } public: void edit(const StringName& p_sig) { sig=p_sig; _change_notify(); } VisualScriptEditorSignalEdit() { undo_redo=NULL; } }; class VisualScriptEditorVariableEdit : public Object { OBJ_TYPE(VisualScriptEditorVariableEdit,Object) StringName var; public: UndoRedo *undo_redo; Ref<VisualScript> script; protected: static void _bind_methods() { ObjectTypeDB::bind_method("_var_changed",&VisualScriptEditorVariableEdit::_var_changed); ObjectTypeDB::bind_method("_var_value_changed",&VisualScriptEditorVariableEdit::_var_value_changed); } void _var_changed() { _change_notify(); } void _var_value_changed() { _change_notify("value"); //so the whole tree is not redrawn, makes editing smoother in general } bool _set(const StringName& p_name, const Variant& p_value) { if (var==StringName()) return false; if (String(p_name)=="value") { undo_redo->create_action("Set Variable Default Value"); Variant current=script->get_variable_default_value(var); undo_redo->add_do_method(script.ptr(),"set_variable_default_value",var,p_value); undo_redo->add_undo_method(script.ptr(),"set_variable_default_value",var,current); undo_redo->add_do_method(this,"_var_value_changed"); undo_redo->add_undo_method(this,"_var_value_changed"); undo_redo->commit_action(); return true; } Dictionary d = script->call("get_variable_info",var); if (String(p_name)=="type") { Dictionary dc=d.copy(); dc["type"]=p_value; undo_redo->create_action("Set Variable Type"); undo_redo->add_do_method(script.ptr(),"set_variable_info",var,dc); undo_redo->add_undo_method(script.ptr(),"set_variable_info",var,d); undo_redo->add_do_method(this,"_var_changed"); undo_redo->add_undo_method(this,"_var_changed"); undo_redo->commit_action(); return true; } if (String(p_name)=="hint") { Dictionary dc=d.copy(); dc["hint"]=p_value; undo_redo->create_action("Set Variable Type"); undo_redo->add_do_method(script.ptr(),"set_variable_info",var,dc); undo_redo->add_undo_method(script.ptr(),"set_variable_info",var,d); undo_redo->add_do_method(this,"_var_changed"); undo_redo->add_undo_method(this,"_var_changed"); undo_redo->commit_action(); return true; } if (String(p_name)=="hint_string") { Dictionary dc=d.copy(); dc["hint_string"]=p_value; undo_redo->create_action("Set Variable Type"); undo_redo->add_do_method(script.ptr(),"set_variable_info",var,dc); undo_redo->add_undo_method(script.ptr(),"set_variable_info",var,d); undo_redo->add_do_method(this,"_var_changed"); undo_redo->add_undo_method(this,"_var_changed"); undo_redo->commit_action(); return true; } if (String(p_name)=="export") { script->set_variable_export(var,p_value); return true; } return false; } bool _get(const StringName& p_name,Variant &r_ret) const { if (var==StringName()) return false; if (String(p_name)=="value") { r_ret=script->get_variable_default_value(var); return true; } PropertyInfo pinfo = script->get_variable_info(var); if (String(p_name)=="type") { r_ret=pinfo.type; return true; } if (String(p_name)=="hint") { r_ret=pinfo.hint; return true; } if (String(p_name)=="hint_string") { r_ret=pinfo.hint_string; return true; } if (String(p_name)=="export") { r_ret=script->get_variable_export(var); return true; } return false; } void _get_property_list( List<PropertyInfo> *p_list) const { if (var==StringName()) return; String argt="Variant"; for(int i=1;i<Variant::VARIANT_MAX;i++) { argt+=","+Variant::get_type_name(Variant::Type(i)); } p_list->push_back(PropertyInfo(Variant::INT,"type",PROPERTY_HINT_ENUM,argt)); p_list->push_back(PropertyInfo(script->get_variable_info(var).type,"value",script->get_variable_info(var).hint,script->get_variable_info(var).hint_string,PROPERTY_USAGE_DEFAULT)); p_list->push_back(PropertyInfo(Variant::INT,"hint",PROPERTY_HINT_ENUM,"None,Range,ExpRange,Enum,ExpEasing,Length,SpriteFrame,KeyAccel,BitFlags,AllFlags,File,Dir,GlobalFile,GlobalDir,ResourceType,MultilineText")); p_list->push_back(PropertyInfo(Variant::STRING,"hint_string")); p_list->push_back(PropertyInfo(Variant::BOOL,"export")); } public: void edit(const StringName& p_var) { var=p_var; _change_notify(); } VisualScriptEditorVariableEdit() { undo_redo=NULL; } }; static Color _color_from_type(Variant::Type p_type) { Color color; switch(p_type) { case Variant::NIL: color = Color::html("69ecbd"); break; case Variant::BOOL: color = Color::html("8da6f0"); break; case Variant::INT: color = Color::html("7dc6ef"); break; case Variant::REAL: color = Color::html("61daf4"); break; case Variant::STRING: color = Color::html("6ba7ec"); break; case Variant::VECTOR2: color = Color::html("bd91f1"); break; case Variant::RECT2: color = Color::html("f191a5"); break; case Variant::VECTOR3: color = Color::html("d67dee"); break; case Variant::MATRIX32: color = Color::html("c4ec69"); break; case Variant::PLANE: color = Color::html("f77070"); break; case Variant::QUAT: color = Color::html("ec69a3"); break; case Variant::_AABB: color = Color::html("ee7991"); break; case Variant::MATRIX3: color = Color::html("e3ec69"); break; case Variant::TRANSFORM: color = Color::html("f6a86e"); break; case Variant::COLOR: color = Color::html("9dff70"); break; case Variant::IMAGE: color = Color::html("93f1b9"); break; case Variant::NODE_PATH: color = Color::html("6993ec"); break; case Variant::_RID: color = Color::html("69ec9a"); break; case Variant::OBJECT: color = Color::html("79f3e8"); break; case Variant::INPUT_EVENT: color = Color::html("adf18f"); break; case Variant::DICTIONARY: color = Color::html("77edb1"); break; case Variant::ARRAY: color = Color::html("e0e0e0"); break; case Variant::RAW_ARRAY: color = Color::html("aaf4c8"); break; case Variant::INT_ARRAY: color = Color::html("afdcf5"); break; case Variant::REAL_ARRAY: color = Color::html("97e7f8"); break; case Variant::STRING_ARRAY: color = Color::html("9dc4f2"); break; case Variant::VECTOR2_ARRAY: color = Color::html("d1b3f5"); break; case Variant::VECTOR3_ARRAY: color = Color::html("df9bf2"); break; case Variant::COLOR_ARRAY: color = Color::html("e9ff97"); break; default: color.set_hsv(p_type/float(Variant::VARIANT_MAX),0.7,0.7); } return color; } 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()) { graph->connect_node(itos(E->get().from_node),E->get().from_output,itos(E->get().to_node),0); } 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()) { VisualScript::DataConnection dc=E->get(); 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); 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); } } void VisualScriptEditor::_update_graph(int p_only_id) { if (updating_graph) return; updating_graph=true; //byebye all nodes if (p_only_id>=0) { if (graph->has_node(itos(p_only_id))) { Node* gid = graph->get_node(itos(p_only_id)); if (gid) memdelete(gid); } } else { for(int i=0;i<graph->get_child_count();i++) { if (graph->get_child(i)->cast_to<GraphNode>()) { memdelete(graph->get_child(i)); i--; } } } if (!script->has_function(edited_func)) { graph->hide(); select_func_text->show(); updating_graph=false; return; } graph->show(); select_func_text->hide(); Ref<Texture> type_icons[Variant::VARIANT_MAX]={ Control::get_icon("MiniVariant","EditorIcons"), Control::get_icon("MiniBoolean","EditorIcons"), Control::get_icon("MiniInteger","EditorIcons"), Control::get_icon("MiniFloat","EditorIcons"), Control::get_icon("MiniString","EditorIcons"), Control::get_icon("MiniVector2","EditorIcons"), Control::get_icon("MiniRect2","EditorIcons"), Control::get_icon("MiniVector3","EditorIcons"), Control::get_icon("MiniMatrix32","EditorIcons"), Control::get_icon("MiniPlane","EditorIcons"), Control::get_icon("MiniQuat","EditorIcons"), Control::get_icon("MiniAabb","EditorIcons"), Control::get_icon("MiniMatrix3","EditorIcons"), Control::get_icon("MiniTransform","EditorIcons"), Control::get_icon("MiniColor","EditorIcons"), Control::get_icon("MiniImage","EditorIcons"), Control::get_icon("MiniPath","EditorIcons"), Control::get_icon("MiniRid","EditorIcons"), Control::get_icon("MiniObject","EditorIcons"), Control::get_icon("MiniInput","EditorIcons"), Control::get_icon("MiniDictionary","EditorIcons"), Control::get_icon("MiniArray","EditorIcons"), Control::get_icon("MiniRawArray","EditorIcons"), Control::get_icon("MiniIntArray","EditorIcons"), Control::get_icon("MiniFloatArray","EditorIcons"), Control::get_icon("MiniStringArray","EditorIcons"), Control::get_icon("MiniVector2Array","EditorIcons"), Control::get_icon("MiniVector3Array","EditorIcons"), Control::get_icon("MiniColorArray","EditorIcons") }; 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<int>::Element *E=ids.front();E;E=E->next()) { if (p_only_id>=0 && p_only_id!=E->get()) continue; Ref<VisualScriptNode> node = script->get_node(edited_func,E->get()); Vector2 pos = script->get_node_pos(edited_func,E->get()); GraphNode *gnode = memnew( GraphNode ); gnode->set_title(node->get_caption()); if (error_line==E->get()) { gnode->set_overlay(GraphNode::OVERLAY_POSITION); } else if (node->is_breakpoint()) { gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT); } if (EditorSettings::get_singleton()->has("visual_script_editor/color_"+node->get_category())) { gnode->set_modulate(EditorSettings::get_singleton()->get("visual_script_editor/color_"+node->get_category())); } 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); if (E->get()!=script->get_function_node_id(edited_func)) { //function can't be erased gnode->set_show_close_button(true); } if (node->cast_to<VisualScriptExpression>()) { 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 { Label *text = memnew( Label ); text->set_text(node->get_text()); gnode->add_child(text); } if (node->cast_to<VisualScriptComment>()) { Ref<VisualScriptComment> vsc=node; gnode->set_comment(true); gnode->set_resizeable(true); gnode->set_custom_minimum_size(vsc->get_size()*EDSCALE); gnode->connect("resize_request",this,"_comment_node_resized",varray(E->get())); } int slot_idx=0; bool single_seq_output = node->get_output_sequence_port_count()==1 && node->get_output_sequence_port_text(0)==String(); gnode->set_slot(0,node->has_input_sequence_port(),TYPE_SEQUENCE,Color(1,1,1,1),single_seq_output,TYPE_SEQUENCE,Color(1,1,1,1),seq_port,seq_port); gnode->set_offset(pos*EDSCALE); slot_idx++; int mixed_seq_ports=0; if (!single_seq_output) { 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,Color(1,1,1,1),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++) { bool left_ok=false; Variant::Type left_type=Variant::NIL; String left_name; 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; } bool right_ok=false; Variant::Type right_type=Variant::NIL; String right_name; 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; } HBoxContainer *hbc = memnew( HBoxContainer); if (left_ok) { Ref<Texture> t; if (left_type>=0 && left_type<Variant::VARIANT_MAX) { t=type_icons[left_type]; } if (t.is_valid()) { TextureFrame *tf = memnew(TextureFrame); tf->set_texture(t); tf->set_stretch_mode(TextureFrame::STRETCH_KEEP_CENTERED); hbc->add_child(tf); } hbc->add_child(memnew(Label(left_name))); if (left_type!=Variant::NIL && !script->is_input_value_port_connected(edited_func,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); } 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()) { 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); } else if (pi.type==Variant::INT && pi.hint==PROPERTY_HINT_ENUM){ 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)); hbc->add_child(button); } } else { Control *c = memnew(Control); c->set_custom_minimum_size(Size2(10,0)*EDSCALE); hbc->add_child(c); } hbc->add_spacer(); 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); } if (right_ok) { hbc->add_child(memnew(Label(right_name))); Ref<Texture> t; if (right_type>=0 && right_type<Variant::VARIANT_MAX) { t=type_icons[right_type]; } if (t.is_valid()) { TextureFrame *tf = memnew(TextureFrame); tf->set_texture(t); tf->set_stretch_mode(TextureFrame::STRETCH_KEEP_CENTERED); hbc->add_child(tf); } } gnode->add_child(hbc); if (i<mixed_seq_ports) { gnode->set_slot(slot_idx,left_ok,left_type,_color_from_type(left_type),true,TYPE_SEQUENCE,Color(1,1,1,1),Ref<Texture>(),seq_port); } else { gnode->set_slot(slot_idx,left_ok,left_type,_color_from_type(left_type),right_ok,right_type,_color_from_type(right_type)); } slot_idx++; } graph->add_child(gnode); if (gnode->is_comment()) { graph->move_child(gnode,0); } } _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_members() { updating_members=true; members->clear(); TreeItem *root = members->create_item(); TreeItem *functions = members->create_item(root); functions->set_selectable(0,false); functions->set_text(0,TTR("Functions:")); functions->add_button(0,Control::get_icon("Override","EditorIcons"),1); functions->add_button(0,Control::get_icon("Add","EditorIcons"),0); functions->set_custom_bg_color(0,Control::get_color("prop_section","Editor")); List<StringName> func_names; script->get_function_list(&func_names); for (List<StringName>::Element *E=func_names.front();E;E=E->next()) { TreeItem *ti = members->create_item(functions) ; ti->set_text(0,E->get()); ti->set_selectable(0,true); ti->set_editable(0,true); //ti->add_button(0,Control::get_icon("Edit","EditorIcons"),0); function arguments are in the node now ti->add_button(0,Control::get_icon("Del","EditorIcons"),1); ti->set_metadata(0,E->get()); if (E->get()==edited_func) { ti->set_custom_bg_color(0,get_color("prop_category","Editor")); ti->set_custom_color(0,Color(1,1,1,1)); } if (selected==E->get()) ti->select(0); } TreeItem *variables = members->create_item(root); variables->set_selectable(0,false); variables->set_text(0,TTR("Variables:")); variables->add_button(0,Control::get_icon("Add","EditorIcons")); variables->set_custom_bg_color(0,Control::get_color("prop_section","Editor")); Ref<Texture> type_icons[Variant::VARIANT_MAX]={ Control::get_icon("MiniVariant","EditorIcons"), Control::get_icon("MiniBoolean","EditorIcons"), Control::get_icon("MiniInteger","EditorIcons"), Control::get_icon("MiniFloat","EditorIcons"), Control::get_icon("MiniString","EditorIcons"), Control::get_icon("MiniVector2","EditorIcons"), Control::get_icon("MiniRect2","EditorIcons"), Control::get_icon("MiniVector3","EditorIcons"), Control::get_icon("MiniMatrix32","EditorIcons"), Control::get_icon("MiniPlane","EditorIcons"), Control::get_icon("MiniQuat","EditorIcons"), Control::get_icon("MiniAabb","EditorIcons"), Control::get_icon("MiniMatrix3","EditorIcons"), Control::get_icon("MiniTransform","EditorIcons"), Control::get_icon("MiniColor","EditorIcons"), Control::get_icon("MiniImage","EditorIcons"), Control::get_icon("MiniPath","EditorIcons"), Control::get_icon("MiniRid","EditorIcons"), Control::get_icon("MiniObject","EditorIcons"), Control::get_icon("MiniInput","EditorIcons"), Control::get_icon("MiniDictionary","EditorIcons"), Control::get_icon("MiniArray","EditorIcons"), Control::get_icon("MiniRawArray","EditorIcons"), Control::get_icon("MiniIntArray","EditorIcons"), Control::get_icon("MiniFloatArray","EditorIcons"), Control::get_icon("MiniStringArray","EditorIcons"), Control::get_icon("MiniVector2Array","EditorIcons"), Control::get_icon("MiniVector3Array","EditorIcons"), Control::get_icon("MiniColorArray","EditorIcons") }; List<StringName> var_names; script->get_variable_list(&var_names); for (List<StringName>::Element *E=var_names.front();E;E=E->next()) { TreeItem *ti = members->create_item(variables); ti->set_text(0,E->get()); Variant var = script->get_variable_default_value(E->get()); ti->set_suffix(0,"="+String(var)); ti->set_icon(0,type_icons[script->get_variable_info(E->get()).type]); ti->set_selectable(0,true); ti->set_editable(0,true); ti->add_button(0,Control::get_icon("Edit","EditorIcons"),0); ti->add_button(0,Control::get_icon("Del","EditorIcons"),1); ti->set_metadata(0,E->get()); if (selected==E->get()) ti->select(0); } TreeItem *_signals = members->create_item(root); _signals->set_selectable(0,false); _signals->set_text(0,TTR("Signals:")); _signals->add_button(0,Control::get_icon("Add","EditorIcons")); _signals->set_custom_bg_color(0,Control::get_color("prop_section","Editor")); List<StringName> signal_names; script->get_custom_signal_list(&signal_names); for (List<StringName>::Element *E=signal_names.front();E;E=E->next()) { TreeItem *ti = members->create_item(_signals); ti->set_text(0,E->get()); ti->set_selectable(0,true); ti->set_editable(0,true); ti->add_button(0,Control::get_icon("Edit","EditorIcons"),0); ti->add_button(0,Control::get_icon("Del","EditorIcons"),1); ti->set_metadata(0,E->get()); if (selected==E->get()) ti->select(0); } String base_type=script->get_instance_base_type(); String icon_type=base_type; if (!Control::has_icon(base_type,"EditorIcons")) { icon_type="Object"; } base_type_select->set_text(base_type); base_type_select->set_icon(Control::get_icon(icon_type,"EditorIcons")); updating_members=false; } void VisualScriptEditor::_member_selected() { if (updating_members) return; TreeItem *ti=members->get_selected(); ERR_FAIL_COND(!ti); selected=ti->get_metadata(0); // print_line("selected: "+String(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(); } return; //or crash because it will become invalid } } void VisualScriptEditor::_member_edited() { if (updating_members) return; TreeItem *ti=members->get_edited(); ERR_FAIL_COND(!ti); String name = ti->get_metadata(0); String new_name = ti->get_text(0); if (name==new_name) return; if (!new_name.is_valid_identifier()) { EditorNode::get_singleton()->show_warning(TTR("Name is not a valid identifier:")+" "+new_name); updating_members=true; ti->set_text(0,name); updating_members=false; return; } if (script->has_function(new_name) || script->has_variable(new_name) || script->has_custom_signal(new_name)) { EditorNode::get_singleton()->show_warning(TTR("Name already in use by another func/var/signal:")+" "+new_name); updating_members=true; ti->set_text(0,name); updating_members=false; return; } TreeItem *root=members->get_root(); if (ti->get_parent()==root->get_children()) { if (edited_func==selected) { edited_func=new_name; } selected=new_name; _update_graph(); 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); undo_redo->add_do_method(this,"_update_members"); undo_redo->add_undo_method(this,"_update_members"); undo_redo->commit_action(); return; //or crash because it will become invalid } if (ti->get_parent()==root->get_children()->get_next()) { selected=new_name; undo_redo->create_action(TTR("Rename Variable")); undo_redo->add_do_method(script.ptr(),"rename_variable",name,new_name); undo_redo->add_undo_method(script.ptr(),"rename_variable",new_name,name); undo_redo->add_do_method(this,"_update_members"); undo_redo->add_undo_method(this,"_update_members"); undo_redo->commit_action(); return; //or crash because it will become invalid } if (ti->get_parent()==root->get_children()->get_next()->get_next()) { selected=new_name; undo_redo->create_action(TTR("Rename Signal")); undo_redo->add_do_method(script.ptr(),"rename_custom_signal",name,new_name); undo_redo->add_undo_method(script.ptr(),"rename_custom_signal",new_name,name); undo_redo->add_do_method(this,"_update_members"); undo_redo->add_undo_method(this,"_update_members"); undo_redo->commit_action(); return; //or crash because it will become invalid } } void VisualScriptEditor::_override_pressed(int p_id) { //override a virtual function or method from base type ERR_FAIL_COND(!virtuals_in_menu.has(p_id)); VirtualInMenu vim=virtuals_in_menu[p_id]; String name = _validate_name(vim.name); selected=name; edited_func=selected; Ref<VisualScriptFunction> func_node; func_node.instance(); func_node->set_name(vim.name); undo_redo->create_action(TTR("Add Function")); undo_redo->add_do_method(script.ptr(),"add_function",name); for(int i=0;i<vim.args.size();i++) { func_node->add_argument(vim.args[i].first,vim.args[i].second); } undo_redo->add_do_method(script.ptr(),"add_node",name,script->get_available_id(),func_node); if (vim.ret!=Variant::NIL || vim.ret_variant) { Ref<VisualScriptReturn> ret_node; ret_node.instance(); ret_node->set_return_type(vim.ret); ret_node->set_enable_return_value(true); ret_node->set_name(vim.name); undo_redo->add_do_method(script.ptr(),"add_node",name,script->get_available_id()+1,ret_node,Vector2(500,0)); } 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->commit_action(); _update_graph(); } void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_button) { TreeItem *ti=p_item->cast_to<TreeItem>(); TreeItem *root=members->get_root(); if (ti->get_parent()==root) { //main buttons if (ti==root->get_children()) { //add function, this one uses menu if (p_button==1) { new_function_menu->clear(); new_function_menu->set_size(Size2(0,0)); int idx=0; virtuals_in_menu.clear(); List<MethodInfo> mi; ObjectTypeDB::get_method_list(script->get_instance_base_type(),&mi); for (List<MethodInfo>::Element *E=mi.front();E;E=E->next()) { MethodInfo mi=E->get(); if (mi.flags&METHOD_FLAG_VIRTUAL) { VirtualInMenu vim; vim.name=mi.name; vim.ret=mi.return_val.type; if (mi.return_val.name!=String()) vim.ret_variant=true; else vim.ret_variant=false; String desc; if (mi.return_val.type==Variant::NIL) desc="var"; else desc=Variant::get_type_name(mi.return_val.type); desc+=" "+mi.name+" ( "; for(int i=0;i<mi.arguments.size();i++) { if (i>0) desc+=", "; if (mi.arguments[i].type==Variant::NIL) desc+="var "; else desc+=Variant::get_type_name(mi.arguments[i].type)+" "; desc+=mi.arguments[i].name; Pair<Variant::Type,String> p; p.first=mi.arguments[i].type; p.second=mi.arguments[i].name; vim.args.push_back( p ); } desc+=" )"; virtuals_in_menu[idx]=vim; new_function_menu->add_item(desc,idx); idx++; } } Rect2 pos = members->get_item_rect(ti); new_function_menu->set_pos(members->get_global_pos()+pos.pos+Vector2(0,pos.size.y)); new_function_menu->popup(); return; } else if (p_button==0) { String name = _validate_name("new_function"); selected=name; edited_func=selected; Ref<VisualScriptFunction> func_node; func_node.instance(); func_node->set_name(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); 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->commit_action(); _update_graph(); } return; //or crash because it will become invalid } if (ti==root->get_children()->get_next()) { //add variable String name = _validate_name("new_variable"); selected=name; undo_redo->create_action(TTR("Add Variable")); undo_redo->add_do_method(script.ptr(),"add_variable",name); undo_redo->add_undo_method(script.ptr(),"remove_variable",name); undo_redo->add_do_method(this,"_update_members"); undo_redo->add_undo_method(this,"_update_members"); undo_redo->commit_action(); return; //or crash because it will become invalid } if (ti==root->get_children()->get_next()->get_next()) { //add variable String name = _validate_name("new_signal"); selected=name; undo_redo->create_action(TTR("Add Signal")); undo_redo->add_do_method(script.ptr(),"add_custom_signal",name); undo_redo->add_undo_method(script.ptr(),"remove_custom_signal",name); undo_redo->add_do_method(this,"_update_members"); undo_redo->add_undo_method(this,"_update_members"); undo_redo->commit_action(); return; //or crash because it will become invalid } } else { if (ti->get_parent()==root->get_children()) { //edit/remove function String name = ti->get_metadata(0); if (p_button==1) { //delete the function undo_redo->create_action(TTR("Remove Function")); undo_redo->add_do_method(script.ptr(),"remove_function",name); undo_redo->add_undo_method(script.ptr(),"add_function",name); List<int> nodes; script->get_node_list(name,&nodes); for (List<int>::Element *E=nodes.front();E;E=E->next()) { undo_redo->add_undo_method(script.ptr(),"add_node",name,E->get(),script->get_node(name,E->get()),script->get_node_pos(name,E->get())); } List<VisualScript::SequenceConnection> seq_connections; script->get_sequence_connection_list(name,&seq_connections); for (List<VisualScript::SequenceConnection>::Element *E=seq_connections.front();E;E=E->next()) { undo_redo->add_undo_method(script.ptr(),"sequence_connect",name,E->get().from_node,E->get().from_output,E->get().to_node); } List<VisualScript::DataConnection> data_connections; script->get_data_connection_list(name,&data_connections); for (List<VisualScript::DataConnection>::Element *E=data_connections.front();E;E=E->next()) { undo_redo->add_undo_method(script.ptr(),"data_connect",name,E->get().from_node,E->get().from_port,E->get().to_node,E->get().to_port); } //for(int i=0;i<script->function_get_argument_count(name);i++) { //// undo_redo->add_undo_method(script.ptr(),"function_add_argument",name,script->function_get_argument_name(name,i),script->function_get_argument_type(name,i)); //} 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->commit_action(); } else if (p_button==0) { } return; //or crash because it will become invalid } if (ti->get_parent()==root->get_children()->get_next()) { //edit/remove variable String name = ti->get_metadata(0); if (p_button==1) { undo_redo->create_action(TTR("Remove Variable")); undo_redo->add_do_method(script.ptr(),"remove_variable",name); undo_redo->add_undo_method(script.ptr(),"add_variable",name,script->get_variable_default_value(name)); undo_redo->add_undo_method(script.ptr(),"set_variable_info",name,script->call("get_variable_info",name)); //return as dict undo_redo->add_do_method(this,"_update_members"); undo_redo->add_undo_method(this,"_update_members"); undo_redo->commit_action(); return; //or crash because it will become invalid } else if (p_button==0) { variable_editor->edit(name); edit_variable_dialog->set_title(TTR("Editing Variable:")+" "+name); edit_variable_dialog->popup_centered_minsize(Size2(400,200)*EDSCALE); } } if (ti->get_parent()==root->get_children()->get_next()->get_next()) { //edit/remove variable String name = ti->get_metadata(0); if (p_button==1) { undo_redo->create_action(TTR("Remove Signal")); undo_redo->add_do_method(script.ptr(),"remove_custom_signal",name); undo_redo->add_undo_method(script.ptr(),"add_custom_signal",name); for(int i=0;i<script->custom_signal_get_argument_count(name);i++) { undo_redo->add_undo_method(script.ptr(),"custom_signal_add_argument",name,script->custom_signal_get_argument_name(name,i),script->custom_signal_get_argument_type(name,i)); } undo_redo->add_do_method(this,"_update_members"); undo_redo->add_undo_method(this,"_update_members"); undo_redo->commit_action(); } else if (p_button==0) { signal_editor->edit(name); edit_signal_dialog->set_title(TTR("Editing Signal:")+" "+name); edit_signal_dialog->popup_centered_minsize(Size2(400,300)*EDSCALE); } return; //or crash because it will become invalid } } } void VisualScriptEditor::_expression_text_changed(const String& p_text,int p_id) { Ref<VisualScriptExpression> vse = script->get_node(edited_func,p_id); if (!vse.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->add_do_method(this,"_update_graph",p_id); undo_redo->add_undo_method(this,"_update_graph",p_id); undo_redo->commit_action(); Node *node = graph->get_node(itos(p_id)); if (node->cast_to<Control>()) node->cast_to<Control>()->set_size(Vector2(1,1)); //shrink if text is smaller updating_graph=false; } void VisualScriptEditor::_available_node_doubleclicked() { TreeItem *item = nodes->get_selected(); if (!item) return; String which = item->get_metadata(0); if (which==String()) 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)); } ofs/=EDSCALE; 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_pos(edited_func,E->get()); if (pos.distance_to(ofs)<15) { ofs+=Vector2(graph->get_snap(),graph->get_snap()); exists=true; break; } } if (exists) continue; break; } Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(which); 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(); Node* node = graph->get_node(itos(new_id)); if (node) { graph->set_selected(node); _node_selected(node); } } void VisualScriptEditor::_update_available_nodes() { nodes->clear(); TreeItem *root = nodes->create_item(); Map<String,TreeItem*> path_cache; String filter = node_filter->get_text(); List<String> fnodes; VisualScriptLanguage::singleton->get_registered_node_names(&fnodes); for (List<String>::Element *E=fnodes.front();E;E=E->next()) { Vector<String> path = E->get().split("/"); if (filter!=String() && path.size() && path[path.size()-1].findn(filter)==-1) continue; 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 } } 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()); } } String VisualScriptEditor::_validate_name(const String& p_name) const { String valid=p_name; int counter=1; while(true) { bool exists = script->has_function(valid) || script->has_variable(valid) || script->has_custom_signal(valid); if (exists) { counter++; valid=p_name+"_"+itos(counter); continue; } break; } return valid; } void VisualScriptEditor::_on_nodes_delete() { List<int> to_erase; for(int i=0;i<graph->get_child_count();i++) { GraphNode *gn = graph->get_child(i)->cast_to<GraphNode>(); if (gn) { if (gn->is_selected() && gn->is_close_button_visible()) { to_erase.push_back(gn->get_name().operator String().to_int()); } } } if (to_erase.empty()) return; undo_redo->create_action("Remove VisualScript Nodes"); 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_pos(edited_func,F->get())); 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()) { 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); } } 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()) { 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_do_method(this,"_update_graph"); undo_redo->add_undo_method(this,"_update_graph"); undo_redo->commit_action(); } void VisualScriptEditor::_on_nodes_duplicate() { List<int> to_duplicate; for(int i=0;i<graph->get_child_count();i++) { GraphNode *gn = graph->get_child(i)->cast_to<GraphNode>(); if (gn) { if (gn->is_selected() && gn->is_close_button_visible()) { to_duplicate.push_back(gn->get_name().operator String().to_int()); } } } if (to_duplicate.empty()) return; undo_redo->create_action("Duplicate VisualScript Nodes"); int idc=script->get_available_id()+1; Set<int> to_select; for(List<int>::Element*F=to_duplicate.front();F;F=F->next()) { Ref<VisualScriptNode> node = script->get_node(edited_func,F->get()); Ref<VisualScriptNode> dupe = node->duplicate(); int new_id = idc++; to_select.insert(new_id); undo_redo->add_do_method(script.ptr(),"add_node",edited_func,new_id,dupe,script->get_node_pos(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(this,"_update_graph"); undo_redo->add_undo_method(this,"_update_graph"); undo_redo->commit_action(); for(int i=0;i<graph->get_child_count();i++) { GraphNode *gn = graph->get_child(i)->cast_to<GraphNode>(); if (gn) { int id = gn->get_name().operator String().to_int(); gn->set_selected(to_select.has(id)); } } if (to_select.size()) { EditorNode::get_singleton()->push_item(script->get_node(edited_func,to_select.front()->get()).ptr()); } } void VisualScriptEditor::_input(const InputEvent& p_event) { if (p_event.type==InputEvent::MOUSE_BUTTON && !p_event.mouse_button.pressed && p_event.mouse_button.button_index==BUTTON_LEFT) { revert_on_drag=String(); //so we can still drag functions } } Variant VisualScriptEditor::get_drag_data_fw(const Point2& p_point,Control* p_from) { if (p_from==nodes) { TreeItem *it = nodes->get_item_at_pos(p_point); if (!it) return Variant(); String type=it->get_metadata(0); if (type==String()) return Variant(); Dictionary dd; dd["type"]="visual_script_node_drag"; dd["node_type"]=type; Label *label = memnew(Label); label->set_text(it->get_text(0)); set_drag_preview(label); return dd; } if (p_from==members) { TreeItem *it = members->get_item_at_pos(p_point); if (!it) return Variant(); String type=it->get_metadata(0); if (type==String()) return Variant(); Dictionary dd; TreeItem *root=members->get_root(); if (it->get_parent()==root->get_children()) { 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"; dd["variable"]=type; } else if (it->get_parent()==root->get_children()->get_next()->get_next()) { dd["type"]="visual_script_signal_drag"; dd["signal"]=type; } else { return Variant(); } Label *label = memnew(Label); label->set_text(it->get_text(0)); set_drag_preview(label); return dd; } return Variant(); } bool VisualScriptEditor::can_drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from) const{ if (p_from==graph) { Dictionary d = p_data; if (d.has("type") && ( String(d["type"])=="visual_script_node_drag" || String(d["type"])=="visual_script_function_drag" || String(d["type"])=="visual_script_variable_drag" || String(d["type"])=="visual_script_signal_drag" || String(d["type"])=="obj_property" || String(d["type"])=="resource" || String(d["type"])=="files" || String(d["type"])=="nodes" ) ) { if (String(d["type"])=="obj_property") { #ifdef OSX_ENABLED const_cast<VisualScriptEditor*>(this)->_show_hint(TTR("Hold Meta to drop a Getter. Hold Shift to drop a generic signature.")); #else const_cast<VisualScriptEditor*>(this)->_show_hint(TTR("Hold Ctrl to drop a Getter. Hold Shift to drop a generic signature.")); #endif } if (String(d["type"])=="nodes") { #ifdef OSX_ENABLED const_cast<VisualScriptEditor*>(this)->_show_hint(TTR("Hold Meta to drop a simple reference to the node.")); #else const_cast<VisualScriptEditor*>(this)->_show_hint(TTR("Hold Ctrl to drop a simple reference to the node.")); #endif } if (String(d["type"])=="visual_script_variable_drag") { #ifdef OSX_ENABLED const_cast<VisualScriptEditor*>(this)->_show_hint(TTR("Hold Meta to drop a Variable Setter.")); #else const_cast<VisualScriptEditor*>(this)->_show_hint(TTR("Hold Ctrl to drop a Variable Setter.")); #endif } return true; } } return false; } #ifdef TOOLS_ENABLED static Node* _find_script_node(Node* p_edited_scene,Node* p_current_node,const Ref<Script> &script) { if (p_edited_scene!=p_current_node && p_current_node->get_owner()!=p_edited_scene) return NULL; Ref<Script> scr = p_current_node->get_script(); if (scr.is_valid() && scr==script) return p_current_node; for(int i=0;i<p_current_node->get_child_count();i++) { Node *n = _find_script_node(p_edited_scene,p_current_node->get_child(i),script); if (n) return n; } return NULL; } #endif void VisualScriptEditor::drop_data_fw(const Point2& p_point,const Variant& p_data,Control* p_from){ if (p_from==graph) { Dictionary d = p_data; if (d.has("type") && String(d["type"])=="visual_script_node_drag") { 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; 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(); Node* node = graph->get_node(itos(new_id)); if (node) { graph->set_selected(node); _node_selected(node); } } if (d.has("type") && String(d["type"])=="visual_script_variable_drag") { #ifdef OSX_ENABLED bool use_set = Input::get_singleton()->is_key_pressed(KEY_META); #else bool use_set = Input::get_singleton()->is_key_pressed(KEY_CONTROL); #endif 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; Ref<VisualScriptNode> vnode; if (use_set) { Ref<VisualScriptVariableSet> vnodes; vnodes.instance(); vnodes->set_variable(d["variable"]); vnode=vnodes; } else { Ref<VisualScriptVariableGet> vnodeg; vnodeg.instance(); vnodeg->set_variable(d["variable"]); vnode=vnodeg; } 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(); Node* node = graph->get_node(itos(new_id)); if (node) { graph->set_selected(node); _node_selected(node); } } if (d.has("type") && String(d["type"])=="visual_script_function_drag") { 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; Ref<VisualScriptFunctionCall> vnode; vnode.instance(); vnode->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SELF); 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(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_do_method(this,"_update_graph"); undo_redo->add_undo_method(this,"_update_graph"); undo_redo->commit_action(); Node* node = graph->get_node(itos(new_id)); if (node) { graph->set_selected(node); _node_selected(node); } } if (d.has("type") && String(d["type"])=="visual_script_signal_drag") { 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; Ref<VisualScriptEmitSignal> vnode; vnode.instance(); vnode->set_signal(d["signal"]); 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(); Node* node = graph->get_node(itos(new_id)); if (node) { graph->set_selected(node); _node_selected(node); } } if (d.has("type") && String(d["type"])=="resource") { 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; Ref<VisualScriptPreload> prnode; prnode.instance(); prnode->set_preload(d["resource"]); 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(this,"_update_graph"); undo_redo->add_undo_method(this,"_update_graph"); undo_redo->commit_action(); Node* node = graph->get_node(itos(new_id)); if (node) { graph->set_selected(node); _node_selected(node); } } if (d.has("type") && String(d["type"])=="files") { 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; Array files = d["files"]; List<int> new_ids; int new_id = script->get_available_id(); if (files.size()) { undo_redo->create_action(TTR("Add Preload Node")); for(int i=0;i<files.size();i++) { Ref<Resource> res = ResourceLoader::load(files[i]); if (!res.is_valid()) continue; Ref<VisualScriptPreload> prnode; 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); new_ids.push_back(new_id); new_id++; ofs+=Vector2(20,20)*EDSCALE; } undo_redo->add_do_method(this,"_update_graph"); undo_redo->add_undo_method(this,"_update_graph"); undo_redo->commit_action(); } for(List<int>::Element *E=new_ids.front();E;E=E->next()) { Node* node = graph->get_node(itos(E->get())); if (node) { graph->set_selected(node); _node_selected(node); } } } if (d.has("type") && String(d["type"])=="nodes") { 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."); return; } #ifdef OSX_ENABLED bool use_node = Input::get_singleton()->is_key_pressed(KEY_META); #else bool use_node = Input::get_singleton()->is_key_pressed(KEY_CONTROL); #endif Array nodes = d["nodes"]; 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; undo_redo->create_action(TTR("Add Node(s) From Tree")); int base_id = script->get_available_id(); if (nodes.size()>1) { use_node=true; } for(int i=0;i<nodes.size();i++) { NodePath np = nodes[i]; Node *node = get_node(np); if (!node) { continue; } Ref<VisualScriptNode> n; if (use_node) { Ref<VisualScriptSceneNode> scene_node; scene_node.instance(); scene_node->set_node_path(sn->get_path_to(node)); n=scene_node; } else { 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_type()); n=call; method_select->select_method_from_instance(node); 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); base_id++; ofs+=Vector2(25,25); } undo_redo->add_do_method(this,"_update_graph"); undo_redo->add_undo_method(this,"_update_graph"); undo_redo->commit_action(); } if (d.has("type") && String(d["type"])=="obj_property") { 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."); return; } Object *obj=d["object"]; if (!obj) return; Node *node = obj->cast_to<Node>(); 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; #ifdef OSX_ENABLED bool use_get = Input::get_singleton()->is_key_pressed(KEY_META); #else bool use_get = Input::get_singleton()->is_key_pressed(KEY_CONTROL); #endif if (!node || Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { if (use_get) undo_redo->create_action(TTR("Add Getter Property")); else undo_redo->create_action(TTR("Add Setter Property")); int base_id = script->get_available_id(); Ref<VisualScriptNode> vnode; if (!use_get) { Ref<VisualScriptPropertySet> pset; pset.instance(); pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); pset->set_base_type(obj->get_type()); /*if (use_value) { pset->set_use_builtin_value(true); pset->set_builtin_value(d["value"]); }*/ vnode=pset; } else { Ref<VisualScriptPropertyGet> pget; pget.instance(); pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); pget->set_base_type(obj->get_type()); vnode=pget; } undo_redo->add_do_method(script.ptr(),"add_node",edited_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_do_method(this,"_update_graph"); undo_redo->add_undo_method(this,"_update_graph"); undo_redo->commit_action(); } else { if (use_get) undo_redo->create_action(TTR("Add Getter Property")); else undo_redo->create_action(TTR("Add Setter Property")); int base_id = script->get_available_id(); Ref<VisualScriptNode> vnode; if (!use_get) { Ref<VisualScriptPropertySet> pset; pset.instance(); if (sn==node) { pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF); } else { pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH); pset->set_base_path(sn->get_path_to(node)); } vnode=pset; } else { Ref<VisualScriptPropertyGet> pget; pget.instance(); if (sn==node) { pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF); } else { pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH); pget->set_base_path(sn->get_path_to(node)); } vnode=pget; } undo_redo->add_do_method(script.ptr(),"add_node",edited_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_do_method(this,"_update_graph"); undo_redo->add_undo_method(this,"_update_graph"); undo_redo->commit_action(); } } } } void VisualScriptEditor::_selected_method(const String& p_method) { Ref<VisualScriptFunctionCall> vsfc = script->get_node(edited_func,selecting_method_id); if (!vsfc.is_valid()) return; vsfc->set_function(p_method); } void VisualScriptEditor::_draw_color_over_button(Object* obj,Color p_color) { Button *button = obj->cast_to<Button>(); if (!button) return; Ref<StyleBox> normal = get_stylebox("normal","Button" ); button->draw_rect(Rect2(normal->get_offset(),button->get_size()-normal->get_minimum_size()),p_color); } void VisualScriptEditor::_button_resource_previewed(const String& p_path,const Ref<Texture>& p_preview,Variant p_ud) { Array ud=p_ud; ERR_FAIL_COND(ud.size()!=2); ObjectID id = ud[0]; Object *obj = ObjectDB::get_instance(id); if (!obj) return; Button *b = obj->cast_to<Button>(); ERR_FAIL_COND(!b); if (p_preview.is_null()) { b->set_text(ud[1]); } else { b->set_icon(p_preview); } } ///////////////////////// void VisualScriptEditor::apply_code() { } Ref<Script> VisualScriptEditor::get_edited_script() const{ return script; } Vector<String> VisualScriptEditor::get_functions(){ return Vector<String>(); } void VisualScriptEditor::set_edited_script(const Ref<Script>& p_script){ script=p_script; signal_editor->script=p_script; signal_editor->undo_redo=undo_redo; variable_editor->script=p_script; variable_editor->undo_redo=undo_redo; script->connect("node_ports_changed",this,"_node_ports_changed"); _update_members(); _update_available_nodes(); } void VisualScriptEditor::reload_text(){ } String VisualScriptEditor::get_name(){ String name; if (script->get_path().find("local://")==-1 && script->get_path().find("::")==-1) { name=script->get_path().get_file(); if (is_unsaved()) { name+="(*)"; } } else if (script->get_name()!="") name=script->get_name(); else name=script->get_type()+"("+itos(script->get_instance_ID())+")"; return name; } Ref<Texture> VisualScriptEditor::get_icon(){ return Control::get_icon("VisualScript","EditorIcons"); } bool VisualScriptEditor::is_unsaved(){ #ifdef TOOLS_ENABLED return script->is_edited() || script->are_subnodes_edited(); #else return false; #endif } Variant VisualScriptEditor::get_edit_state(){ Dictionary d; d["function"]=edited_func; d["scroll"]=graph->get_scroll_ofs(); d["zoom"]=graph->get_zoom(); d["using_snap"]=graph->is_using_snap(); d["snap"]=graph->get_snap(); return d; } void VisualScriptEditor::set_edit_state(const Variant& p_state){ Dictionary d = p_state; if (d.has("function")) { edited_func=p_state; selected=edited_func; } _update_graph(); _update_members(); if (d.has("scroll")) { graph->set_scroll_ofs(d["scroll"]); } if (d.has("zoom")) { graph->set_zoom(d["zoom"]); } if (d.has("snap")) { graph->set_snap(d["snap"]); } if (d.has("snap_enabled")) { graph->set_use_snap(d["snap_enabled"]); } } void VisualScriptEditor::_center_on_node(int p_id) { Node *n = graph->get_node(itos(p_id)); if (!n) return; GraphNode *gn = n->cast_to<GraphNode>(); 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 } } void VisualScriptEditor::goto_line(int p_line, bool p_with_error){ p_line+=1; //add one because script lines begin from 0. if (p_with_error) error_line=p_line; List<StringName> functions; script->get_function_list(&functions); for (List<StringName>::Element *E=functions.front();E;E=E->next()) { if (script->has_node(E->get(),p_line)) { edited_func=E->get(); selected=edited_func; _update_graph(); _update_members(); call_deferred("_center_on_node",p_line); //editor might be just created and size might not exist yet return; } } } void VisualScriptEditor::trim_trailing_whitespace(){ } void VisualScriptEditor::ensure_focus(){ graph->grab_focus(); } void VisualScriptEditor::tag_saved_version(){ } void VisualScriptEditor::reload(bool p_soft){ } void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints){ List<StringName> functions; script->get_function_list(&functions); for (List<StringName>::Element *E=functions.front();E;E=E->next()) { List<int> nodes; script->get_node_list(E->get(),&nodes); for (List<int>::Element *F=nodes.front();F;F=F->next()) { Ref<VisualScriptNode> vsn = script->get_node(E->get(),F->get()); if (vsn->is_breakpoint()) { p_breakpoints->push_back(F->get()-1); //subtract 1 because breakpoints in text start from zero } } } } bool VisualScriptEditor::goto_method(const String& p_method){ if (!script->has_function(p_method)) return false; edited_func=p_method; selected=edited_func; _update_members(); _update_graph(); return true; } void VisualScriptEditor::add_callback(const String& p_function,StringArray p_args){ if (script->has_function(p_function)) { edited_func=p_function; selected=edited_func; _update_members(); _update_graph(); return; } Ref<VisualScriptFunction> func; func.instance(); for(int i=0;i<p_args.size();i++) { String name = p_args[i]; Variant::Type type=Variant::NIL; if (name.find(":")!=-1) { String tt = name.get_slice(":",1); name=name.get_slice(":",0); for(int j=0;j<Variant::VARIANT_MAX;j++) { String tname = Variant::get_type_name(Variant::Type(j)); if (tname==tt) { type=Variant::Type(j); break; } } } func->add_argument(type,name); } func->set_name(p_function); 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(); } void VisualScriptEditor::update_settings(){ _update_graph(); } void VisualScriptEditor::set_debugger_active(bool p_active) { if (!p_active) { error_line=-1; _update_graph(); //clear line break } } void VisualScriptEditor::set_tooltip_request_func(String p_method,Object* p_obj){ } Control *VisualScriptEditor::get_edit_menu(){ return edit_menu; } void VisualScriptEditor::_change_base_type() { select_base_type->popup(true); } void VisualScriptEditor::_change_base_type_callback() { String bt = select_base_type->get_selected_type(); ERR_FAIL_COND(bt==String()); undo_redo->create_action("Change Base Type"); undo_redo->add_do_method(script.ptr(),"set_instance_base_type",bt); undo_redo->add_undo_method(script.ptr(),"set_instance_base_type",script->get_instance_base_type()); undo_redo->add_do_method(this,"_update_members"); undo_redo->add_undo_method(this,"_update_members"); undo_redo->commit_action(); } void VisualScriptEditor::_node_selected(Node* p_node) { Ref<VisualScriptNode> vnode = p_node->get_meta("__vnode"); if (vnode.is_null()) return; EditorNode::get_singleton()->push_item(vnode.ptr()); //edit node in inspector } static bool _get_out_slot(const Ref<VisualScriptNode>& p_node,int p_slot,int& r_real_slot,bool& r_sequence) { if (p_slot<p_node->get_output_sequence_port_count()) { r_sequence=true; r_real_slot=p_slot; return true; } r_real_slot=p_slot-p_node->get_output_sequence_port_count(); r_sequence=false; return (r_real_slot<p_node->get_output_value_port_count()); } static bool _get_in_slot(const Ref<VisualScriptNode>& p_node,int p_slot,int& r_real_slot,bool& r_sequence) { if (p_slot==0 && p_node->has_input_sequence_port()) { r_sequence=true; r_real_slot=0; return true; } r_real_slot=p_slot-(p_node->has_input_sequence_port()?1:0); r_sequence=false; return r_real_slot<p_node->get_input_value_port_count(); } void VisualScriptEditor::_begin_node_move() { undo_redo->create_action("Move Node(s)"); } void VisualScriptEditor::_end_node_move() { undo_redo->commit_action(); } void VisualScriptEditor::_move_node(String func,int p_id,const Vector2& p_to) { if (func==String(edited_func)) { Node* node = graph->get_node(itos(p_id)); if (node && node->cast_to<GraphNode>()) node->cast_to<GraphNode>()->set_offset(p_to); } script->set_node_pos(edited_func,p_id,p_to/EDSCALE); } 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); } void VisualScriptEditor::_remove_node(int p_id) { undo_redo->create_action("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_pos(edited_func,p_id)); 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()) { 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); } } 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()) { 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_do_method(this,"_update_graph"); undo_redo->add_undo_method(this,"_update_graph"); undo_redo->commit_action(); } void VisualScriptEditor::_node_ports_changed(const String& p_func,int p_id) { if (p_func!=String(edited_func)) return; _update_graph(p_id); } 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()); ERR_FAIL_COND(!from_node.is_valid()); bool from_seq; int from_port; 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()); ERR_FAIL_COND(!to_node.is_valid()); bool to_seq; int to_port; if (!_get_in_slot(to_node,p_to_slot,to_port,to_seq)) return; //can't connect this, it' s invalid ERR_FAIL_COND(from_seq!=to_seq); undo_redo->create_action("Connect Nodes"); 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()); } else { 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()); } undo_redo->add_do_method(this,"_update_graph_connections"); undo_redo->add_undo_method(this,"_update_graph_connections"); undo_redo->commit_action(); } 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()); ERR_FAIL_COND(!from_node.is_valid()); bool from_seq; int from_port; 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()); ERR_FAIL_COND(!to_node.is_valid()); bool to_seq; int to_port; if (!_get_in_slot(to_node,p_to_slot,to_port,to_seq)) return; //can't connect this, it' s invalid ERR_FAIL_COND(from_seq!=to_seq); undo_redo->create_action("Connect 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()); } 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 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()); } undo_redo->add_do_method(this,"_update_graph_connections"); undo_redo->add_undo_method(this,"_update_graph_connections"); 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); if (!node) return; GraphNode *gn = node->cast_to<GraphNode>(); if (!gn) return; Ref<VisualScriptNode> vsn = script->get_node(edited_func,p_from.to_int()); if (!vsn.is_valid()) return; if (p_from_slot<vsn->get_output_sequence_port_count()) { port_action_popup->clear(); port_action_popup->add_item(TTR("Condition"),CREATE_COND); port_action_popup->add_item(TTR("Sequence"),CREATE_SEQUENCE); port_action_popup->add_item(TTR("Switch"),CREATE_SWITCH); port_action_popup->add_item(TTR("Iterator"),CREATE_ITERATOR); port_action_popup->add_item(TTR("While"),CREATE_WHILE); port_action_popup->add_item(TTR("Return"),CREATE_RETURN); port_action_node=p_from.to_int(); port_action_output=p_from_slot; } else { port_action_popup->clear(); port_action_popup->add_item(TTR("Call"),CREATE_CALL); port_action_popup->add_item(TTR("Get"),CREATE_GET); port_action_popup->add_item(TTR("Set"),CREATE_SET); port_action_output=p_from_slot-vsn->get_output_sequence_port_count(); port_action_node=p_from.to_int(); } port_action_pos=p_release_pos; port_action_popup->set_size(Size2(1,1)); port_action_popup->set_pos(graph->get_global_pos()+p_release_pos); port_action_popup->popup(); } VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_node,int p_output,Set<int> &visited_nodes) { VisualScriptNode::TypeGuess tg; tg.type=Variant::NIL; if (visited_nodes.has(p_node)) return tg; //no loop visited_nodes.insert(p_node); Ref<VisualScriptNode> node = script->get_node(edited_func,p_node); if (!node.is_valid()) { return tg; } Vector<VisualScriptNode::TypeGuess> in_guesses; for(int i=0;i<node->get_input_value_port_count();i++) { PropertyInfo pi = node->get_input_value_port_info(i); VisualScriptNode::TypeGuess g; g.type=pi.type; if (g.type==Variant::NIL || g.type==Variant::OBJECT) { //any or object input, must further guess what this is int from_node; int from_port; if (script->get_input_value_port_connection_source(edited_func,p_node,i,&from_node,&from_port)) { g = _guess_output_type(from_node,from_port,visited_nodes); } else { Variant defval = node->get_default_input_value(i); if (defval.get_type()==Variant::OBJECT) { Object *obj = defval; if (obj) { g.type=Variant::OBJECT; g.obj_type=obj->get_type(); g.script=obj->get_script(); } } } } in_guesses.push_back(g); } return node->guess_output_type(in_guesses.ptr(),p_output); } void VisualScriptEditor::_port_action_menu(int p_option) { Vector2 ofs = graph->get_scroll_ofs() + port_action_pos; if (graph->is_using_snap()) { int snap = graph->get_snap(); ofs = ofs.snapped(Vector2(snap,snap)); } ofs/=EDSCALE; bool seq_connect=false; Ref<VisualScriptNode> vnode; Set<int> vn; switch(p_option) { case CREATE_CALL: { Ref<VisualScriptFunctionCall> n; n.instance(); vnode=n; VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node,port_action_output,vn); if (tg.type==Variant::OBJECT) { n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); if (tg.obj_type!=StringName()) { n->set_base_type(tg.obj_type); } else { n->set_base_type("Object"); } if (tg.script.is_valid()) { n->set_base_script(tg.script->get_path()); new_connect_node_select->select_method_from_script(tg.script); } else { new_connect_node_select->select_method_from_base_type(n->get_base_type()); } } else { n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE); n->set_basic_type(tg.type); new_connect_node_select->select_method_from_basic_type(tg.type); } } break; case CREATE_SET: { Ref<VisualScriptPropertySet> n; n.instance(); vnode=n; VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node,port_action_output,vn); if (tg.type==Variant::OBJECT) { n->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); if (tg.obj_type!=StringName()) { n->set_base_type(tg.obj_type); } else { n->set_base_type("Object"); } if (tg.script.is_valid()) { n->set_base_script(tg.script->get_path()); new_connect_node_select->select_property_from_script(tg.script); } else { new_connect_node_select->select_property_from_base_type(n->get_base_type()); } } else { n->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE); n->set_basic_type(tg.type); new_connect_node_select->select_property_from_basic_type(tg.type,tg.ev_type); } } break; case CREATE_GET: { Ref<VisualScriptPropertyGet> n; n.instance(); vnode=n; VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node,port_action_output,vn); if (tg.type==Variant::OBJECT) { n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); if (tg.obj_type!=StringName()) { n->set_base_type(tg.obj_type); } else { n->set_base_type("Object"); } if (tg.script.is_valid()) { n->set_base_script(tg.script->get_path()); new_connect_node_select->select_property_from_script(tg.script); } else { new_connect_node_select->select_property_from_base_type(n->get_base_type()); } } else { n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE); n->set_basic_type(tg.type); new_connect_node_select->select_property_from_basic_type(tg.type,tg.ev_type); } } break; case CREATE_COND: { Ref<VisualScriptCondition> n; n.instance(); vnode=n; seq_connect=true; } break; case CREATE_SEQUENCE: { Ref<VisualScriptSequence> n; n.instance(); vnode=n; seq_connect=true; } break; case CREATE_SWITCH: { Ref<VisualScriptSwitch> n; n.instance(); vnode=n; seq_connect=true; } break; case CREATE_ITERATOR: { Ref<VisualScriptIterator> n; n.instance(); vnode=n; seq_connect=true; } break; case CREATE_WHILE: { Ref<VisualScriptWhile> n; n.instance(); vnode=n; seq_connect=true; } break; case CREATE_RETURN: { Ref<VisualScriptReturn> n; n.instance(); vnode=n; seq_connect=true; } break; } 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); if (seq_connect) { 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(),"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::_selected_connect_node_method_or_setget(const String& p_text) { Ref<VisualScriptNode> vsn = script->get_node(edited_func,port_action_new_node); if (vsn->cast_to<VisualScriptFunctionCall>()) { Ref<VisualScriptFunctionCall> vsfc = vsn; vsfc->set_function(p_text); script->data_connect(edited_func,port_action_node,port_action_output,port_action_new_node,0); } if (vsn->cast_to<VisualScriptPropertySet>()) { Ref<VisualScriptPropertySet> vsp = vsn; vsp->set_property(p_text); script->data_connect(edited_func,port_action_node,port_action_output,port_action_new_node,0); } if (vsn->cast_to<VisualScriptPropertyGet>()) { Ref<VisualScriptPropertyGet> vsp = vsn; vsp->set_property(p_text); script->data_connect(edited_func,port_action_node,port_action_output,port_action_new_node,0); } _update_graph(port_action_new_node); _update_graph_connections(); } void VisualScriptEditor::_default_value_changed() { Ref<VisualScriptNode> vsn = script->get_node(edited_func,editing_id); if (vsn.is_null()) return; undo_redo->create_action("Change Input Value"); undo_redo->add_do_method(vsn.ptr(),"set_default_input_value",editing_input,default_value_edit->get_variant()); undo_redo->add_undo_method(vsn.ptr(),"set_default_input_value",editing_input,vsn->get_default_input_value(editing_input)); undo_redo->add_do_method(this,"_update_graph",editing_id); undo_redo->add_undo_method(this,"_update_graph",editing_id); undo_redo->commit_action(); } 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); if (vsn.is_null()) return; PropertyInfo pinfo = vsn->get_input_value_port_info(p_input_port); Variant existing = vsn->get_default_input_value(p_input_port); if (pinfo.type!=Variant::NIL && existing.get_type()!=pinfo.type) { Variant::CallError ce; const Variant *existingp=&existing; existing = Variant::construct(pinfo.type,&existingp,1,ce,false); } default_value_edit->set_pos(p_button->cast_to<Control>()->get_global_pos()+Vector2(0,p_button->cast_to<Control>()->get_size().y)); default_value_edit->set_size(Size2(1,1)); if (default_value_edit->edit(NULL,pinfo.name,pinfo.type,existing,pinfo.hint,pinfo.hint_string)) { if (pinfo.hint==PROPERTY_HINT_MULTILINE_TEXT) default_value_edit->popup_centered_ratio(); else default_value_edit->popup(); } editing_id = p_id; editing_input=p_input_port; } void VisualScriptEditor::_show_hint(const String& p_hint) { hint_text->set_text(p_hint); hint_text->show(); hint_text_timer->start(); } 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) { node_filter_icon->set_texture(Control::get_icon("Zoom","EditorIcons")); } } void VisualScriptEditor::_graph_ofs_changed(const Vector2& p_ofs) { if (updating_graph) return; updating_graph=true; if (script->has_function(edited_func)) { script->set_function_scroll(edited_func,graph->get_scroll_ofs()/EDSCALE); script->set_edited(true); } updating_graph=false; } void VisualScriptEditor::_comment_node_resized(const Vector2& p_new_size,int p_node) { if (updating_graph) return; Ref<VisualScriptComment> vsc = script->get_node(edited_func,p_node); if (vsc.is_null()) return; Node *node = graph->get_node(itos(p_node)); if (!node) return; GraphNode *gn = node->cast_to<GraphNode>(); if (!gn) return; updating_graph=true; graph->set_block_minimum_size_adjust(true); //faster resize undo_redo->create_action("Resize Comment",UndoRedo::MERGE_ENDS); undo_redo->add_do_method(vsc.ptr(),"set_size",p_new_size/EDSCALE); 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_size(Size2(1,1)); graph->set_block_minimum_size_adjust(false); updating_graph=false; } void VisualScriptEditor::_menu_option(int p_what) { switch(p_what) { case EDIT_DELETE_NODES: { _on_nodes_delete(); } break; case EDIT_TOGGLE_BREAKPOINT: { List<String> reselect; for(int i=0;i<graph->get_child_count();i++) { GraphNode *gn = graph->get_child(i)->cast_to<GraphNode>(); if (gn) { if (gn->is_selected()) { int id = String(gn->get_name()).to_int(); Ref<VisualScriptNode> vsn = script->get_node(edited_func,id); if (vsn.is_valid()) { vsn->set_breakpoint(!vsn->is_breakpoint()); reselect.push_back(gn->get_name()); } } } } _update_graph(); for(List<String>::Element *E=reselect.front();E;E=E->next()) { GraphNode *gn = graph->get_node(E->get())->cast_to<GraphNode>(); gn->set_selected(true); } } break; case EDIT_FIND_NODE_TYPE: { //popup disappearing grabs focus to owner, so use call deferred node_filter->call_deferred("grab_focus"); node_filter->call_deferred("select_all"); } break; case EDIT_COPY_NODES: case EDIT_CUT_NODES: { if (!script->has_function(edited_func)) break; clipboard->nodes.clear(); clipboard->data_connections.clear(); clipboard->sequence_connections.clear(); for(int i=0;i<graph->get_child_count();i++) { GraphNode *gn = graph->get_child(i)->cast_to<GraphNode>(); if (gn) { if (gn->is_selected()) { int id = String(gn->get_name()).to_int(); Ref<VisualScriptNode> node = script->get_node(edited_func,id); if (node->cast_to<VisualScriptFunction>()) { EditorNode::get_singleton()->show_warning("Can't copy the function node."); return; } if (node.is_valid()) { clipboard->nodes[id]=node->duplicate(); clipboard->nodes_positions[id]=script->get_node_pos(edited_func,id); } } } } if (clipboard->nodes.empty()) break; List<VisualScript::SequenceConnection> sequence_connections; script->get_sequence_connection_list(edited_func,&sequence_connections); 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)) { clipboard->sequence_connections.insert(E->get()); } } List<VisualScript::DataConnection> data_connections; script->get_data_connection_list(edited_func,&data_connections); 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)) { 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)) break; if (clipboard->nodes.empty()) { EditorNode::get_singleton()->show_warning("Clipboard is empty!"); break; } Map<int,int> remap; undo_redo->create_action("Paste VisualScript Nodes"); int idc=script->get_available_id()+1; Set<int> to_select; 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_pos(edited_func,E->get()).snapped(Vector2(2,2)); existing_positions.insert(pos); } } for (Map<int,Ref<VisualScriptNode> >::Element *E=clipboard->nodes.front();E;E=E->next()) { Ref<VisualScriptNode> node = E->get()->duplicate(); int new_id = idc++; to_select.insert(new_id); remap[E->key()]=new_id; Vector2 paste_pos = clipboard->nodes_positions[E->key()]; while(existing_positions.has(paste_pos.snapped(Vector2(2,2)))) { 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); } 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]); } 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(this,"_update_graph"); undo_redo->add_undo_method(this,"_update_graph"); undo_redo->commit_action(); for(int i=0;i<graph->get_child_count();i++) { GraphNode *gn = graph->get_child(i)->cast_to<GraphNode>(); if (gn) { int id = gn->get_name().operator String().to_int(); gn->set_selected(to_select.has(id)); } } } break; } } void VisualScriptEditor::_bind_methods() { ObjectTypeDB::bind_method("_member_button",&VisualScriptEditor::_member_button); ObjectTypeDB::bind_method("_member_edited",&VisualScriptEditor::_member_edited); ObjectTypeDB::bind_method("_member_selected",&VisualScriptEditor::_member_selected); ObjectTypeDB::bind_method("_update_members",&VisualScriptEditor::_update_members); ObjectTypeDB::bind_method("_change_base_type",&VisualScriptEditor::_change_base_type); ObjectTypeDB::bind_method("_change_base_type_callback",&VisualScriptEditor::_change_base_type_callback); ObjectTypeDB::bind_method("_override_pressed",&VisualScriptEditor::_override_pressed); ObjectTypeDB::bind_method("_node_selected",&VisualScriptEditor::_node_selected); ObjectTypeDB::bind_method("_node_moved",&VisualScriptEditor::_node_moved); ObjectTypeDB::bind_method("_move_node",&VisualScriptEditor::_move_node); ObjectTypeDB::bind_method("_begin_node_move",&VisualScriptEditor::_begin_node_move); ObjectTypeDB::bind_method("_end_node_move",&VisualScriptEditor::_end_node_move); ObjectTypeDB::bind_method("_remove_node",&VisualScriptEditor::_remove_node); ObjectTypeDB::bind_method("_update_graph",&VisualScriptEditor::_update_graph,DEFVAL(-1)); ObjectTypeDB::bind_method("_node_ports_changed",&VisualScriptEditor::_node_ports_changed); ObjectTypeDB::bind_method("_available_node_doubleclicked",&VisualScriptEditor::_available_node_doubleclicked); ObjectTypeDB::bind_method("_default_value_edited",&VisualScriptEditor::_default_value_edited); ObjectTypeDB::bind_method("_default_value_changed",&VisualScriptEditor::_default_value_changed); ObjectTypeDB::bind_method("_menu_option",&VisualScriptEditor::_menu_option); ObjectTypeDB::bind_method("_graph_ofs_changed",&VisualScriptEditor::_graph_ofs_changed); ObjectTypeDB::bind_method("_center_on_node",&VisualScriptEditor::_center_on_node); ObjectTypeDB::bind_method("_comment_node_resized",&VisualScriptEditor::_comment_node_resized); ObjectTypeDB::bind_method("_button_resource_previewed",&VisualScriptEditor::_button_resource_previewed); ObjectTypeDB::bind_method("_port_action_menu",&VisualScriptEditor::_port_action_menu); ObjectTypeDB::bind_method("_selected_connect_node_method_or_setget",&VisualScriptEditor::_selected_connect_node_method_or_setget); ObjectTypeDB::bind_method("_expression_text_changed",&VisualScriptEditor::_expression_text_changed); ObjectTypeDB::bind_method("get_drag_data_fw",&VisualScriptEditor::get_drag_data_fw); ObjectTypeDB::bind_method("can_drop_data_fw",&VisualScriptEditor::can_drop_data_fw); ObjectTypeDB::bind_method("drop_data_fw",&VisualScriptEditor::drop_data_fw); ObjectTypeDB::bind_method("_input",&VisualScriptEditor::_input); ObjectTypeDB::bind_method("_on_nodes_delete",&VisualScriptEditor::_on_nodes_delete); ObjectTypeDB::bind_method("_on_nodes_duplicate",&VisualScriptEditor::_on_nodes_duplicate); ObjectTypeDB::bind_method("_hide_timer",&VisualScriptEditor::_hide_timer); ObjectTypeDB::bind_method("_graph_connected",&VisualScriptEditor::_graph_connected); ObjectTypeDB::bind_method("_graph_disconnected",&VisualScriptEditor::_graph_disconnected); ObjectTypeDB::bind_method("_graph_connect_to_empty",&VisualScriptEditor::_graph_connect_to_empty); ObjectTypeDB::bind_method("_update_graph_connections",&VisualScriptEditor::_update_graph_connections); ObjectTypeDB::bind_method("_node_filter_changed",&VisualScriptEditor::_node_filter_changed); ObjectTypeDB::bind_method("_selected_method",&VisualScriptEditor::_selected_method); ObjectTypeDB::bind_method("_draw_color_over_button",&VisualScriptEditor::_draw_color_over_button); } VisualScriptEditor::VisualScriptEditor() { if (!clipboard) { clipboard = memnew( Clipboard ); } updating_graph=false; edit_menu = memnew( MenuButton ); edit_menu->set_text(TTR("Edit")); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/delete_selected"), EDIT_DELETE_NODES); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/toggle_breakpoint"), EDIT_TOGGLE_BREAKPOINT); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/find_node_type"), EDIT_FIND_NODE_TYPE); edit_menu->get_popup()->add_separator(); 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()->connect("item_pressed",this,"_menu_option"); main_hsplit = memnew( HSplitContainer ); add_child(main_hsplit); main_hsplit->set_area_as_parent_rect(); left_vsplit = memnew( VSplitContainer ); main_hsplit->add_child(left_vsplit); 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); 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 = memnew( Tree ); left_vb->add_margin_child(TTR("Members:"),members,true); 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->set_single_select_cell_editing_only_when_already_selected(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); node_filter_icon = memnew( TextureFrame ); node_filter_icon->set_stretch_mode(TextureFrame::STRETCH_KEEP_CENTERED); hbc_nodes->add_child(node_filter_icon); vbc_nodes->add_child(hbc_nodes); nodes = memnew( Tree ); vbc_nodes->add_child(nodes); nodes->set_v_size_flags(SIZE_EXPAND_FILL); left_vb2->add_margin_child(TTR("Available Nodes:"),vbc_nodes,true); nodes->set_hide_root(true); nodes->connect("item_activated",this,"_available_node_doubleclicked"); nodes->set_drag_forwarding(this); graph = memnew( GraphEdit ); main_hsplit->add_child(graph); graph->set_h_size_flags(SIZE_EXPAND_FILL); graph->connect("node_selected",this,"_node_selected"); graph->connect("_begin_node_move",this,"_begin_node_move"); 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->set_drag_forwarding(this); graph->hide(); graph->connect("scroll_offset_changed",this,"_graph_ofs_changed"); select_func_text = memnew( Label ); select_func_text->set_text(TTR("Select or create a function to edit graph")); select_func_text->set_align(Label::ALIGN_CENTER); select_func_text->set_valign(Label::VALIGN_CENTER); select_func_text->set_h_size_flags(SIZE_EXPAND_FILL); main_hsplit->add_child(select_func_text); hint_text = memnew( Label ); hint_text->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,100); hint_text->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,0); hint_text->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,0); hint_text->set_align(Label::ALIGN_CENTER); hint_text->set_valign(Label::VALIGN_CENTER); graph->add_child(hint_text); hint_text_timer = memnew( Timer ); hint_text_timer->set_wait_time(4); hint_text_timer->connect("timeout",this,"_hide_timer"); add_child(hint_text_timer); //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); for(int j=0;j<Variant::VARIANT_MAX;j++) { if (Variant::can_convert(Variant::Type(i),Variant::Type(j))) { graph->add_valid_connection_type(i,j); } } graph->add_valid_right_disconnect_type(i); } graph->add_valid_left_disconnect_type(TYPE_SEQUENCE); graph->connect("connection_request",this,"_graph_connected"); graph->connect("disconnection_request",this,"_graph_disconnected"); graph->connect("connection_to_empty",this,"_graph_connect_to_empty"); edit_signal_dialog = memnew( AcceptDialog ); edit_signal_dialog->get_ok()->set_text(TTR("Close")); add_child(edit_signal_dialog); edit_signal_dialog->set_title(TTR("Edit Signal Arguments:")); signal_editor = memnew( VisualScriptEditorSignalEdit ); edit_signal_edit = memnew( PropertyEditor ); edit_signal_edit->hide_top_label(); edit_signal_dialog->add_child(edit_signal_edit); edit_signal_dialog->set_child_rect(edit_signal_edit); edit_signal_edit->edit(signal_editor); edit_variable_dialog = memnew( AcceptDialog ); edit_variable_dialog->get_ok()->set_text(TTR("Close")); add_child(edit_variable_dialog); edit_variable_dialog->set_title(TTR("Edit Variable:")); variable_editor = memnew( VisualScriptEditorVariableEdit ); edit_variable_edit = memnew( PropertyEditor ); edit_variable_edit->hide_top_label(); edit_variable_dialog->add_child(edit_variable_edit); edit_variable_dialog->set_child_rect(edit_variable_edit); edit_variable_edit->edit(variable_editor); select_base_type=memnew(CreateDialog); select_base_type->set_base_type("Object"); //anything goes select_base_type->connect("create",this,"_change_base_type_callback"); select_base_type->get_ok()->set_text(TTR("Change")); add_child(select_base_type); undo_redo = EditorNode::get_singleton()->get_undo_redo(); new_function_menu = memnew( PopupMenu ); new_function_menu->connect("item_pressed",this,"_override_pressed"); add_child(new_function_menu); updating_members=false; set_process_input(true); //for revert on drag set_process_unhandled_input(true); //for revert on drag default_value_edit= memnew( CustomPropertyEditor); add_child(default_value_edit); default_value_edit->connect("variant_changed",this,"_default_value_changed"); method_select = memnew( PropertySelector ); add_child(method_select); method_select->connect("selected",this,"_selected_method"); error_line=-1; new_connect_node_select = memnew( PropertySelector ); add_child(new_connect_node_select); new_connect_node_select->connect("selected",this,"_selected_connect_node_method_or_setget"); port_action_popup = memnew( PopupMenu ); add_child(port_action_popup); port_action_popup->connect("item_pressed",this,"_port_action_menu"); } VisualScriptEditor::~VisualScriptEditor() { undo_redo->clear_history(); //avoid crashes memdelete(signal_editor); memdelete(variable_editor); } static ScriptEditorBase * create_editor(const Ref<Script>& p_script) { if (p_script->cast_to<VisualScript>()) { return memnew( VisualScriptEditor ); } return NULL; } VisualScriptEditor::Clipboard *VisualScriptEditor::clipboard=NULL; void VisualScriptEditor::free_clipboard() { if (clipboard) memdelete(clipboard); } static void register_editor_callback() { ScriptEditor::register_create_script_editor_function(create_editor); EditorSettings::get_singleton()->set("visual_script_editor/color_functions",Color(1,0.9,0.9)); EditorSettings::get_singleton()->set("visual_script_editor/color_data",Color(0.9,1.0,0.9)); EditorSettings::get_singleton()->set("visual_script_editor/color_operators",Color(0.9,0.9,1.0)); EditorSettings::get_singleton()->set("visual_script_editor/color_flow_control",Color(1.0,1.0,0.8)); EditorSettings::get_singleton()->set("visual_script_editor/color_custom",Color(0.8,1.0,1.0)); EditorSettings::get_singleton()->set("visual_script_editor/color_constants",Color(1.0,0.8,1.0)); ED_SHORTCUT("visual_script_editor/delete_selected", TTR("Delete Selected")); ED_SHORTCUT("visual_script_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9); ED_SHORTCUT("visual_script_editor/find_node_type", TTR("Find Node Type"), KEY_MASK_CMD+KEY_F); 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); } void VisualScriptEditor::register_editor() { //too early to register stuff here, request a callback EditorNode::add_plugin_init_callback(register_editor_callback); } #endif