summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2015-06-24 13:29:23 -0300
committerJuan Linietsky <reduzio@gmail.com>2015-06-24 13:29:45 -0300
commit48f1d02da4795ba9d485fe5fe2bea907be2fc467 (patch)
treeae248e1e863bb675026c2cfb8399ac8d23f33ed0
parent199ad16bbc50d41aaeb76c276947156dafc81481 (diff)
added ability to define signals in script
closes #2175
-rw-r--r--core/object.cpp9
-rw-r--r--core/script_language.h4
-rw-r--r--demos/2d/space_shooter/shot.gd1
-rw-r--r--demos/2d/space_shooter/shot.scnbin3990 -> 4079 bytes
-rw-r--r--modules/gdscript/gd_compiler.cpp40
-rw-r--r--modules/gdscript/gd_parser.cpp71
-rw-r--r--modules/gdscript/gd_parser.h7
-rw-r--r--modules/gdscript/gd_script.cpp48
-rw-r--r--modules/gdscript/gd_script.h4
-rw-r--r--modules/gdscript/gd_tokenizer.cpp1
-rw-r--r--modules/gdscript/gd_tokenizer.h1
-rw-r--r--tools/editor/connections_dialog.cpp148
12 files changed, 270 insertions, 64 deletions
diff --git a/core/object.cpp b/core/object.cpp
index cdb2c6517a..6bb7973cef 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -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);
diff --git a/core/script_language.h b/core/script_language.h
index 07ad571fda..c1b906e251 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -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
diff --git a/demos/2d/space_shooter/shot.gd b/demos/2d/space_shooter/shot.gd
index 813587d670..28b67bd26d 100644
--- a/demos/2d/space_shooter/shot.gd
+++ b/demos/2d/space_shooter/shot.gd
@@ -45,3 +45,4 @@ func _on_shot_body_enter( body ):
#hit the tilemap
_hit_something()
pass # replace with function body
+
diff --git a/demos/2d/space_shooter/shot.scn b/demos/2d/space_shooter/shot.scn
index 64c8c25ebe..86a20ffa47 100644
--- a/demos/2d/space_shooter/shot.scn
+++ b/demos/2d/space_shooter/shot.scn
Binary files differ
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp
index b405555ec6..a62225f663 100644
--- a/modules/gdscript/gd_compiler.cpp
+++ b/modules/gdscript/gd_compiler.cpp
@@ -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++) {
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 6242400663..71af9ab10d 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -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;
diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h
index bf2c8c9ef2..fd5c0802ea 100644
--- a/modules/gdscript/gd_parser.h
+++ b/modules/gdscript/gd_parser.h
@@ -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;
diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp
index 70a5fd985c..b6ad7aa716 100644
--- a/modules/gdscript/gd_script.cpp
+++ b/modules/gdscript/gd_script.cpp
@@ -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};
diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h
index f505d51ba4..4672f3b8be 100644
--- a/modules/gdscript/gd_script.h
+++ b/modules/gdscript/gd_script.h
@@ -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;
diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp
index 0745baafe6..656a015c66 100644
--- a/modules/gdscript/gd_tokenizer.cpp
+++ b/modules/gdscript/gd_tokenizer.cpp
@@ -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"},
diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h
index b63687d2b4..d6bd63c5b8 100644
--- a/modules/gdscript/gd_tokenizer.h
+++ b/modules/gdscript/gd_tokenizer.h
@@ -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,
diff --git a/tools/editor/connections_dialog.cpp b/tools/editor/connections_dialog.cpp
index e927a6e441..13612c8e7a 100644
--- a/tools/editor/connections_dialog.cpp
+++ b/tools/editor/connections_dialog.cpp
@@ -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++) {
+ 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();
- PropertyInfo &pi = mi.arguments[i];
+ if (has_icon(scr->get_type(),"EditorIcons")) {
+ icon=get_icon(scr->get_type(),"EditorIcons");
+ }
+ }
- if (i>0)
- signaldesc+=", ";
- signaldesc+=Variant::get_type_name(pi.type)+" "+(pi.name==""?String("arg "+itos(i)):pi.name);
- argnames.push_back(pi.name);
+ } else {
+ ObjectTypeDB::get_signal_list(base,&node_signals,true);
+ if (has_icon(base,"EditorIcons")) {
+ icon=get_icon(base,"EditorIcons");
}
- signaldesc+=" ";
+ 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"));
- List<Object::Connection> connections;
- node->get_signal_connection_list(mi.name,&connections);
+ TreeItem *pitem = NULL;
+
+ 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);
}
}