mirror of
https://github.com/godotengine/godot.git
synced 2024-11-27 09:16:35 +08:00
parent
199ad16bbc
commit
48f1d02da4
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -45,3 +45,4 @@ func _on_shot_body_enter( body ):
|
||||
#hit the tilemap
|
||||
_hit_something()
|
||||
pass # replace with function body
|
||||
|
||||
|
Binary file not shown.
@ -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++) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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"},
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
||||
MethodInfo &mi =E->get();
|
||||
List<MethodInfo> node_signals;
|
||||
Ref<Texture> icon;
|
||||
String name;
|
||||
|
||||
String signaldesc;
|
||||
signaldesc=mi.name+"(";
|
||||
StringArray argnames;
|
||||
if (mi.arguments.size()) {
|
||||
signaldesc+=" ";
|
||||
for(int i=0;i<mi.arguments.size();i++) {
|
||||
if (!did_script) {
|
||||
|
||||
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"));
|
||||
|
||||
List<Object::Connection> connections;
|
||||
node->get_signal_connection_list(mi.name,&connections);
|
||||
TreeItem *pitem = NULL;
|
||||
|
||||
for(List<Object::Connection>::Element *F=connections.front();F;F=F->next()) {
|
||||
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();
|
||||
}
|
||||
|
||||
Object::Connection&c = F->get();
|
||||
if (!(c.flags&CONNECT_PERSIST))
|
||||
continue;
|
||||
for(List<MethodInfo>::Element *E=node_signals.front();E;E=E->next()) {
|
||||
|
||||
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()) {
|
||||
MethodInfo &mi =E->get();
|
||||
|
||||
path+=" binds( ";
|
||||
for(int i=0;i<c.binds.size();i++) {
|
||||
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)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user