godot/modules/visual_script/visual_script_editor.cpp
Juan Linietsky 118eed485e ObjectTypeDB was renamed to ClassDB. Types are meant to be more generic to Variant.
All usages of "type" to refer to classes were renamed to "class"
ClassDB has been exposed to GDScript.
OBJ_TYPE() macro is now GDCLASS()
2017-01-02 23:03:46 -03:00

3530 lines
99 KiB
C++

#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 {
GDCLASS(VisualScriptEditorSignalEdit,Object)
StringName sig;
public:
UndoRedo *undo_redo;
Ref<VisualScript> script;
protected:
static void _bind_methods() {
ClassDB::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 {
GDCLASS(VisualScriptEditorVariableEdit,Object)
StringName var;
public:
UndoRedo *undo_redo;
Ref<VisualScript> script;
protected:
static void _bind_methods() {
ClassDB::bind_method("_var_changed",&VisualScriptEditorVariableEdit::_var_changed);
ClassDB::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;
ClassDB::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_class());
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_class());
/*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_class());
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_class()+"("+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.GDCLASS=obj->get_class();
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.GDCLASS!=StringName()) {
n->set_base_type(tg.GDCLASS);
} 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.GDCLASS!=StringName()) {
n->set_base_type(tg.GDCLASS);
} 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.GDCLASS!=StringName()) {
n->set_base_type(tg.GDCLASS);
} 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() {
ClassDB::bind_method("_member_button",&VisualScriptEditor::_member_button);
ClassDB::bind_method("_member_edited",&VisualScriptEditor::_member_edited);
ClassDB::bind_method("_member_selected",&VisualScriptEditor::_member_selected);
ClassDB::bind_method("_update_members",&VisualScriptEditor::_update_members);
ClassDB::bind_method("_change_base_type",&VisualScriptEditor::_change_base_type);
ClassDB::bind_method("_change_base_type_callback",&VisualScriptEditor::_change_base_type_callback);
ClassDB::bind_method("_override_pressed",&VisualScriptEditor::_override_pressed);
ClassDB::bind_method("_node_selected",&VisualScriptEditor::_node_selected);
ClassDB::bind_method("_node_moved",&VisualScriptEditor::_node_moved);
ClassDB::bind_method("_move_node",&VisualScriptEditor::_move_node);
ClassDB::bind_method("_begin_node_move",&VisualScriptEditor::_begin_node_move);
ClassDB::bind_method("_end_node_move",&VisualScriptEditor::_end_node_move);
ClassDB::bind_method("_remove_node",&VisualScriptEditor::_remove_node);
ClassDB::bind_method("_update_graph",&VisualScriptEditor::_update_graph,DEFVAL(-1));
ClassDB::bind_method("_node_ports_changed",&VisualScriptEditor::_node_ports_changed);
ClassDB::bind_method("_available_node_doubleclicked",&VisualScriptEditor::_available_node_doubleclicked);
ClassDB::bind_method("_default_value_edited",&VisualScriptEditor::_default_value_edited);
ClassDB::bind_method("_default_value_changed",&VisualScriptEditor::_default_value_changed);
ClassDB::bind_method("_menu_option",&VisualScriptEditor::_menu_option);
ClassDB::bind_method("_graph_ofs_changed",&VisualScriptEditor::_graph_ofs_changed);
ClassDB::bind_method("_center_on_node",&VisualScriptEditor::_center_on_node);
ClassDB::bind_method("_comment_node_resized",&VisualScriptEditor::_comment_node_resized);
ClassDB::bind_method("_button_resource_previewed",&VisualScriptEditor::_button_resource_previewed);
ClassDB::bind_method("_port_action_menu",&VisualScriptEditor::_port_action_menu);
ClassDB::bind_method("_selected_connect_node_method_or_setget",&VisualScriptEditor::_selected_connect_node_method_or_setget);
ClassDB::bind_method("_expression_text_changed",&VisualScriptEditor::_expression_text_changed);
ClassDB::bind_method("get_drag_data_fw",&VisualScriptEditor::get_drag_data_fw);
ClassDB::bind_method("can_drop_data_fw",&VisualScriptEditor::can_drop_data_fw);
ClassDB::bind_method("drop_data_fw",&VisualScriptEditor::drop_data_fw);
ClassDB::bind_method("_input",&VisualScriptEditor::_input);
ClassDB::bind_method("_on_nodes_delete",&VisualScriptEditor::_on_nodes_delete);
ClassDB::bind_method("_on_nodes_duplicate",&VisualScriptEditor::_on_nodes_duplicate);
ClassDB::bind_method("_hide_timer",&VisualScriptEditor::_hide_timer);
ClassDB::bind_method("_graph_connected",&VisualScriptEditor::_graph_connected);
ClassDB::bind_method("_graph_disconnected",&VisualScriptEditor::_graph_disconnected);
ClassDB::bind_method("_graph_connect_to_empty",&VisualScriptEditor::_graph_connect_to_empty);
ClassDB::bind_method("_update_graph_connections",&VisualScriptEditor::_update_graph_connections);
ClassDB::bind_method("_node_filter_changed",&VisualScriptEditor::_node_filter_changed);
ClassDB::bind_method("_selected_method",&VisualScriptEditor::_selected_method);
ClassDB::bind_method("_draw_color_over_button",&VisualScriptEditor::_draw_color_over_button);
}
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