added ability to define signals in script

closes #2175
This commit is contained in:
Juan Linietsky 2015-06-24 13:29:23 -03:00
parent 199ad16bbc
commit 48f1d02da4
12 changed files with 271 additions and 65 deletions

View file

@ -1302,6 +1302,10 @@ Array Object::_get_signal_connection_list(const String& p_signal) const{
void Object::get_signal_list(List<MethodInfo> *p_signals ) const {
if (!script.is_null()) {
Ref<Script>(script)->get_script_signal_list(p_signals);
}
ObjectTypeDB::get_signal_list(get_type_name(),p_signals);
//find maybe usersignals?
const StringName *S=NULL;
@ -1313,6 +1317,7 @@ void Object::get_signal_list(List<MethodInfo> *p_signals ) const {
p_signals->push_back(signal_map[*S].user);
}
}
}
@ -1351,6 +1356,10 @@ Error Object::connect(const StringName& p_signal, Object *p_to_object, const Str
Signal *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ObjectTypeDB::has_signal(get_type_name(),p_signal);
//check in script
if (!signal_is_valid && !script.is_null() && Ref<Script>(script)->has_script_signal(p_signal))
signal_is_valid=true;
if (!signal_is_valid) {
ERR_EXPLAIN("Attempt to connect nonexistent signal '"+p_signal+"' to method '"+p_to_method+"'");
ERR_FAIL_COND_V(!signal_is_valid,ERR_INVALID_PARAMETER);

View file

@ -94,6 +94,10 @@ public:
virtual ScriptLanguage *get_language() const=0;
virtual bool has_script_signal(const StringName& p_signal) const=0;
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const=0;
virtual void update_exports() {} //editor tool

View file

@ -45,3 +45,4 @@ func _on_shot_body_enter( body ):
#hit the tilemap
_hit_something()
pass # replace with function body

Binary file not shown.

View file

@ -28,15 +28,6 @@
/*************************************************************************/
#include "gd_compiler.h"
#include "gd_script.h"
/* TODO:
*AND and OR need early abort
-Inheritance properly process (done?)
*create built in initializer and constructor
*assign operators
*build arrays and dictionaries
*call parent constructor
*/
void GDCompiler::_set_error(const String& p_error,const GDParser::Node *p_node) {
@ -1397,13 +1388,14 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
int index_from=0;
Ref<GDNativeClass> native;
if (p_class->extends_used) {
//do inheritance
String path = p_class->extends_file;
Ref<GDScript> script;
Ref<GDNativeClass> native;
if (path!="") {
//path (and optionally subclasses)
@ -1573,7 +1565,35 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
//p_script->constants[constant->value].make_const();
}
for(int i=0;i<p_class->_signals.size();i++) {
StringName name = p_class->_signals[i].name;
GDScript *c = p_script;
while(c) {
if (c->_signals.has(name)) {
_set_error("Signal '"+name+"' redefined (in current or parent class)",p_class);
return ERR_ALREADY_EXISTS;
}
if (c->base.is_valid()) {
c=c->base.ptr();
} else {
c=NULL;
}
}
if (native.is_valid()) {
if (ObjectTypeDB::has_signal(native->get_name(),name)) {
_set_error("Signal '"+name+"' redefined (original in native class '"+String(native->get_name())+"')",p_class);
return ERR_ALREADY_EXISTS;
}
}
p_script->_signals[name]=p_class->_signals[i].arguments;
}
//parse sub-classes
for(int i=0;i<p_class->subclasses.size();i++) {

View file

@ -1520,8 +1520,10 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
op->arguments.push_back(assigned);
p_block->statements.push_back(op);
_end_statement();
if (!_end_statement()) {
_set_error("Expected end of statement (var)");
return;
}
} break;
case GDTokenizer::TK_CF_IF: {
@ -1946,8 +1948,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
_parse_extends(p_class);
if (error_set)
return;
_end_statement();
if (!_end_statement()) {
_set_error("Expected end of statement after extends");
return;
}
} break;
case GDTokenizer::TK_PR_TOOL: {
@ -2227,6 +2231,53 @@ void GDParser::_parse_class(ClassNode *p_class) {
//arguments
} break;
case GDTokenizer::TK_PR_SIGNAL: {
tokenizer->advance();
if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
_set_error("Expected identifier after 'signal'.");
return;
}
ClassNode::Signal sig;
sig.name = tokenizer->get_token_identifier();
tokenizer->advance();
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
tokenizer->advance();
while(true) {
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
tokenizer->advance();
break;
}
if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
_set_error("Expected identifier in signal argument.");
return;
}
sig.arguments.push_back(tokenizer->get_token_identifier());
tokenizer->advance();
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
tokenizer->advance();
} else if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
_set_error("Expected ',' or ')' after signal parameter identifier.");
return;
}
}
}
p_class->_signals.push_back(sig);
if (!_end_statement()) {
_set_error("Expected end of statement (signal)");
return;
}
} break;
case GDTokenizer::TK_PR_EXPORT: {
tokenizer->advance();
@ -2644,8 +2695,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
p_class->variables.push_back(member);
_end_statement();
if (!_end_statement()) {
_set_error("Expected end of statement (continue)");
return;
}
} break;
case GDTokenizer::TK_PR_CONST: {
//variale declaration and (eventual) initialization
@ -2682,8 +2735,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
p_class->constant_expressions.push_back(constant);
_end_statement();
if (!_end_statement()) {
_set_error("Expected end of statement (constant)");
return;
}
} break;

View file

@ -76,6 +76,7 @@ public:
StringName extends_file;
Vector<StringName> extends_class;
struct Member {
PropertyInfo _export;
#ifdef TOOLS_ENABLED
@ -92,11 +93,17 @@ public:
Node *expression;
};
struct Signal {
StringName name;
Vector<StringName> arguments;
};
Vector<ClassNode*> subclasses;
Vector<Member> variables;
Vector<Constant> constant_expressions;
Vector<FunctionNode*> functions;
Vector<FunctionNode*> static_functions;
Vector<Signal> _signals;
BlockNode *initializer;
ClassNode *owner;
//Vector<Node*> initializers;

View file

@ -1756,6 +1756,12 @@ bool GDScript::_update_exports() {
//print_line("found "+c->variables[i]._export.name);
member_default_values_cache[c->variables[i].identifier]=c->variables[i].default_value;
}
_signals.clear();
for(int i=0;i<c->_signals.size();i++) {
_signals[c->_signals[i].name]=c->_signals[i].arguments;
}
}
} else {
//print_line("unchaged is "+get_path());
@ -2100,6 +2106,47 @@ Ref<GDScript> GDScript::get_base() const {
return base;
}
bool GDScript::has_script_signal(const StringName& p_signal) const {
if (_signals.has(p_signal))
return true;
if (base.is_valid()) {
return base->has_script_signal(p_signal);
}
#ifdef TOOLS_ENABLED
else if (base_cache.is_valid()){
return base_cache->has_script_signal(p_signal);
}
#endif
return false;
}
void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
for(const Map<StringName,Vector<StringName> >::Element *E=_signals.front();E;E=E->next()) {
MethodInfo mi;
mi.name=E->key();
for(int i=0;i<E->get().size();i++) {
PropertyInfo arg;
arg.name=E->get()[i];
mi.arguments.push_back(arg);
}
r_signals->push_back(mi);
}
if (base.is_valid()) {
base->get_script_signal_list(r_signals);
}
#ifdef TOOLS_ENABLED
else if (base_cache.is_valid()){
base_cache->get_script_signal_list(r_signals);
}
#endif
}
GDScript::GDScript() {
@ -2594,6 +2641,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"static",
"float",
"int",
"signal",
0};

View file

@ -260,6 +260,7 @@ friend class GDScriptLanguage;
Map<StringName,GDFunction> member_functions;
Map<StringName,MemberInfo> member_indices; //members are just indices to the instanced script.
Map<StringName,Ref<GDScript> > subclasses;
Map<StringName,Vector<StringName> > _signals;
#ifdef TOOLS_ENABLED
@ -318,6 +319,9 @@ public:
const Map<StringName,GDFunction>& get_member_functions() const { return member_functions; }
const Ref<GDNativeClass>& get_native() const { return native; }
virtual bool has_script_signal(const StringName& p_signal) const;
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
bool is_tool() const { return tool; }
Ref<GDScript> get_base() const;

View file

@ -856,6 +856,7 @@ void GDTokenizerText::_advance() {
{TK_PR_PRELOAD,"preload"},
{TK_PR_ASSERT,"assert"},
{TK_PR_YIELD,"yield"},
{TK_PR_SIGNAL,"signal"},
{TK_PR_CONST,"const"},
//controlflow
{TK_CF_IF,"if"},

View file

@ -104,6 +104,7 @@ public:
TK_PR_PRELOAD,
TK_PR_ASSERT,
TK_PR_YIELD,
TK_PR_SIGNAL,
TK_BRACKET_OPEN,
TK_BRACKET_CLOSE,
TK_CURLY_BRACKET_OPEN,

View file

@ -632,74 +632,130 @@ void ConnectionsDialog::update_tree() {
node->get_signal_list(&node_signals);
//node_signals.sort_custom<_ConnectionsDialogMethodInfoSort>();
bool did_script=false;
StringName base = node->get_type();
for(List<MethodInfo>::Element *E=node_signals.front();E;E=E->next()) {
while(base) {
List<MethodInfo> node_signals;
Ref<Texture> icon;
String name;
MethodInfo &mi =E->get();
if (!did_script) {
String signaldesc;
signaldesc=mi.name+"(";
StringArray argnames;
if (mi.arguments.size()) {
signaldesc+=" ";
for(int i=0;i<mi.arguments.size();i++) {
PropertyInfo &pi = mi.arguments[i];
if (i>0)
signaldesc+=", ";
signaldesc+=Variant::get_type_name(pi.type)+" "+(pi.name==""?String("arg "+itos(i)):pi.name);
argnames.push_back(pi.name);
Ref<Script> scr = node->get_script();
if (scr.is_valid()) {
scr->get_script_signal_list(&node_signals);
if (scr->get_path().is_resource_file())
name=scr->get_path().get_file();
else
name=scr->get_type();
if (has_icon(scr->get_type(),"EditorIcons")) {
icon=get_icon(scr->get_type(),"EditorIcons");
}
}
signaldesc+=" ";
} else {
ObjectTypeDB::get_signal_list(base,&node_signals,true);
if (has_icon(base,"EditorIcons")) {
icon=get_icon(base,"EditorIcons");
}
name=base;
}
signaldesc+=")";
TreeItem *item=tree->create_item(root);
item->set_text(0,signaldesc);
Dictionary sinfo;
sinfo["name"]=mi.name;
sinfo["args"]=argnames;
item->set_metadata(0,sinfo);
item->set_icon(0,get_icon("Signal","EditorIcons"));
TreeItem *pitem = NULL;
List<Object::Connection> connections;
node->get_signal_connection_list(mi.name,&connections);
if (node_signals.size()) {
pitem=tree->create_item(root);
pitem->set_text(0,name);
pitem->set_icon(0,icon);
pitem->set_selectable(0,false);
pitem->set_editable(0,false);
pitem->set_custom_bg_color(0,get_color("prop_subsection","Editor"));
node_signals.sort();
}
for(List<Object::Connection>::Element *F=connections.front();F;F=F->next()) {
for(List<MethodInfo>::Element *E=node_signals.front();E;E=E->next()) {
Object::Connection&c = F->get();
if (!(c.flags&CONNECT_PERSIST))
continue;
Node *target = c.target->cast_to<Node>();
if (!target)
continue;
MethodInfo &mi =E->get();
String path = String(node->get_path_to(target))+" :: "+c.method+"()";
if (c.flags&CONNECT_DEFERRED)
path+=" (deferred)";
if (c.binds.size()) {
String signaldesc;
signaldesc=mi.name+"(";
StringArray argnames;
if (mi.arguments.size()) {
signaldesc+=" ";
for(int i=0;i<mi.arguments.size();i++) {
path+=" binds( ";
for(int i=0;i<c.binds.size();i++) {
PropertyInfo &pi = mi.arguments[i];
if (i>0)
path+=", ";
path+=c.binds[i].operator String();
signaldesc+=", ";
String tname="var";
if (pi.type!=Variant::NIL) {
tname=Variant::get_type_name(pi.type);
}
signaldesc+=tname+" "+(pi.name==""?String("arg "+itos(i)):pi.name);
argnames.push_back(pi.name);
}
path+=" )";
signaldesc+=" ";
}
TreeItem *item2=tree->create_item(item);
item2->set_text(0,path);
item2->set_metadata(0,c);
item2->set_icon(0,get_icon("Slot","EditorIcons"));
signaldesc+=")";
TreeItem *item=tree->create_item(pitem);
item->set_text(0,signaldesc);
Dictionary sinfo;
sinfo["name"]=mi.name;
sinfo["args"]=argnames;
item->set_metadata(0,sinfo);
item->set_icon(0,get_icon("Signal","EditorIcons"));
List<Object::Connection> connections;
node->get_signal_connection_list(mi.name,&connections);
for(List<Object::Connection>::Element *F=connections.front();F;F=F->next()) {
Object::Connection&c = F->get();
if (!(c.flags&CONNECT_PERSIST))
continue;
Node *target = c.target->cast_to<Node>();
if (!target)
continue;
String path = String(node->get_path_to(target))+" :: "+c.method+"()";
if (c.flags&CONNECT_DEFERRED)
path+=" (deferred)";
if (c.binds.size()) {
path+=" binds( ";
for(int i=0;i<c.binds.size();i++) {
if (i>0)
path+=", ";
path+=c.binds[i].operator String();
}
path+=" )";
}
TreeItem *item2=tree->create_item(item);
item2->set_text(0,path);
item2->set_metadata(0,c);
item2->set_icon(0,get_icon("Slot","EditorIcons"));
}
}
if (!did_script) {
did_script=true;
} else {
base=ObjectTypeDB::type_inherits_from(base);
}
}