You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-09 12:50:35 +00:00
Reworked signal connection system, added support for Callable and Signal objects and made them default.
This commit is contained in:
committed by
Juan Linietsky
parent
1a4be2cd8f
commit
69c95f4b4c
244
core/object.cpp
244
core/object.cpp
@@ -335,10 +335,8 @@ MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name, const Pr
|
||||
Object::Connection::operator Variant() const {
|
||||
|
||||
Dictionary d;
|
||||
d["source"] = source;
|
||||
d["signal"] = signal;
|
||||
d["target"] = target;
|
||||
d["method"] = method;
|
||||
d["callable"] = callable;
|
||||
d["flags"] = flags;
|
||||
d["binds"] = binds;
|
||||
return d;
|
||||
@@ -346,34 +344,19 @@ Object::Connection::operator Variant() const {
|
||||
|
||||
bool Object::Connection::operator<(const Connection &p_conn) const {
|
||||
|
||||
if (source == p_conn.source) {
|
||||
|
||||
if (signal == p_conn.signal) {
|
||||
|
||||
if (target == p_conn.target) {
|
||||
|
||||
return method < p_conn.method;
|
||||
} else {
|
||||
|
||||
return target < p_conn.target;
|
||||
}
|
||||
} else
|
||||
return signal < p_conn.signal;
|
||||
if (signal == p_conn.signal) {
|
||||
return callable < p_conn.callable;
|
||||
} else {
|
||||
return source < p_conn.source;
|
||||
return signal < p_conn.signal;
|
||||
}
|
||||
}
|
||||
Object::Connection::Connection(const Variant &p_variant) {
|
||||
|
||||
Dictionary d = p_variant;
|
||||
if (d.has("source"))
|
||||
source = d["source"];
|
||||
if (d.has("signal"))
|
||||
signal = d["signal"];
|
||||
if (d.has("target"))
|
||||
target = d["target"];
|
||||
if (d.has("method"))
|
||||
method = d["method"];
|
||||
if (d.has("callable"))
|
||||
callable = d["callable"];
|
||||
if (d.has("flags"))
|
||||
flags = d["flags"];
|
||||
if (d.has("binds"))
|
||||
@@ -655,16 +638,16 @@ void Object::get_method_list(List<MethodInfo> *p_list) const {
|
||||
}
|
||||
}
|
||||
|
||||
Variant Object::_call_bind(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
|
||||
Variant Object::_call_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
||||
|
||||
if (p_argcount < 1) {
|
||||
r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
r_error.argument = 0;
|
||||
return Variant();
|
||||
}
|
||||
|
||||
if (p_args[0]->get_type() != Variant::STRING) {
|
||||
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::STRING;
|
||||
return Variant();
|
||||
@@ -675,22 +658,22 @@ Variant Object::_call_bind(const Variant **p_args, int p_argcount, Variant::Call
|
||||
return call(method, &p_args[1], p_argcount - 1, r_error);
|
||||
}
|
||||
|
||||
Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
|
||||
Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
||||
|
||||
if (p_argcount < 1) {
|
||||
r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
r_error.argument = 0;
|
||||
return Variant();
|
||||
}
|
||||
|
||||
if (p_args[0]->get_type() != Variant::STRING) {
|
||||
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::STRING;
|
||||
return Variant();
|
||||
}
|
||||
|
||||
r_error.error = Variant::CallError::CALL_OK;
|
||||
r_error.error = Callable::CallError::CALL_OK;
|
||||
|
||||
StringName method = *p_args[0];
|
||||
|
||||
@@ -700,29 +683,29 @@ Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Vari
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
static void _test_call_error(const StringName &p_func, const Variant::CallError &error) {
|
||||
static void _test_call_error(const StringName &p_func, const Callable::CallError &error) {
|
||||
|
||||
switch (error.error) {
|
||||
|
||||
case Variant::CallError::CALL_OK:
|
||||
case Variant::CallError::CALL_ERROR_INVALID_METHOD:
|
||||
case Callable::CallError::CALL_OK:
|
||||
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
|
||||
break;
|
||||
case Variant::CallError::CALL_ERROR_INVALID_ARGUMENT: {
|
||||
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
|
||||
|
||||
ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Invalid type for argument " + itos(error.argument) + ", expected " + Variant::get_type_name(error.expected) + ".");
|
||||
ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Invalid type for argument " + itos(error.argument) + ", expected " + Variant::get_type_name(Variant::Type(error.expected)) + ".");
|
||||
break;
|
||||
}
|
||||
case Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
|
||||
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
|
||||
|
||||
ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too many arguments, expected " + itos(error.argument) + ".");
|
||||
break;
|
||||
}
|
||||
case Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: {
|
||||
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: {
|
||||
|
||||
ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too few arguments, expected " + itos(error.argument) + ".");
|
||||
break;
|
||||
}
|
||||
case Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL:
|
||||
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -749,7 +732,7 @@ void Object::call_multilevel(const StringName &p_method, const Variant **p_args,
|
||||
//Variant ret;
|
||||
OBJ_DEBUG_LOCK
|
||||
|
||||
Variant::CallError error;
|
||||
Callable::CallError error;
|
||||
|
||||
if (script_instance) {
|
||||
script_instance->call_multilevel(p_method, p_args, p_argcount);
|
||||
@@ -769,7 +752,7 @@ void Object::call_multilevel_reversed(const StringName &p_method, const Variant
|
||||
|
||||
MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
|
||||
|
||||
Variant::CallError error;
|
||||
Callable::CallError error;
|
||||
OBJ_DEBUG_LOCK
|
||||
|
||||
if (method) {
|
||||
@@ -823,9 +806,9 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) {
|
||||
}
|
||||
}
|
||||
|
||||
Variant::CallError ce;
|
||||
Callable::CallError ce;
|
||||
Variant ret = call(p_method, argptrs, p_args.size(), ce);
|
||||
if (ce.error != Variant::CallError::CALL_OK) {
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_FAIL_V_MSG(Variant(), "Error calling method from 'callv': " + Variant::get_call_error_text(this, p_method, argptrs, p_args.size(), ce) + ".");
|
||||
}
|
||||
return ret;
|
||||
@@ -842,7 +825,7 @@ Variant Object::call(const StringName &p_name, VARIANT_ARG_DECLARE) {
|
||||
argc++;
|
||||
}
|
||||
|
||||
Variant::CallError error;
|
||||
Callable::CallError error;
|
||||
|
||||
Variant ret = call(p_name, argptr, argc, error);
|
||||
return ret;
|
||||
@@ -859,38 +842,38 @@ void Object::call_multilevel(const StringName &p_name, VARIANT_ARG_DECLARE) {
|
||||
argc++;
|
||||
}
|
||||
|
||||
//Variant::CallError error;
|
||||
//Callable::CallError error;
|
||||
call_multilevel(p_name, argptr, argc);
|
||||
}
|
||||
|
||||
Variant Object::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
|
||||
Variant Object::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
||||
|
||||
r_error.error = Variant::CallError::CALL_OK;
|
||||
r_error.error = Callable::CallError::CALL_OK;
|
||||
|
||||
if (p_method == CoreStringNames::get_singleton()->_free) {
|
||||
//free must be here, before anything, always ready
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (p_argcount != 0) {
|
||||
r_error.argument = 0;
|
||||
r_error.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
return Variant();
|
||||
}
|
||||
if (Object::cast_to<Reference>(this)) {
|
||||
r_error.argument = 0;
|
||||
r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
ERR_FAIL_V_MSG(Variant(), "Can't 'free' a reference.");
|
||||
}
|
||||
|
||||
if (_lock_index.get() > 1) {
|
||||
r_error.argument = 0;
|
||||
r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
ERR_FAIL_V_MSG(Variant(), "Object is locked and can't be freed.");
|
||||
}
|
||||
|
||||
#endif
|
||||
//must be here, must be before everything,
|
||||
memdelete(this);
|
||||
r_error.error = Variant::CallError::CALL_OK;
|
||||
r_error.error = Callable::CallError::CALL_OK;
|
||||
return Variant();
|
||||
}
|
||||
|
||||
@@ -901,15 +884,15 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
|
||||
//force jumptable
|
||||
switch (r_error.error) {
|
||||
|
||||
case Variant::CallError::CALL_OK:
|
||||
case Callable::CallError::CALL_OK:
|
||||
return ret;
|
||||
case Variant::CallError::CALL_ERROR_INVALID_METHOD:
|
||||
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
|
||||
break;
|
||||
case Variant::CallError::CALL_ERROR_INVALID_ARGUMENT:
|
||||
case Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
|
||||
case Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
|
||||
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
|
||||
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
|
||||
return ret;
|
||||
case Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
|
||||
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -919,7 +902,7 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
|
||||
if (method) {
|
||||
ret = method->call(this, p_args, p_argcount, r_error);
|
||||
} else {
|
||||
r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -1102,7 +1085,7 @@ void Object::add_user_signal(const MethodInfo &p_signal) {
|
||||
ERR_FAIL_COND_MSG(p_signal.name == "", "Signal name cannot be empty.");
|
||||
ERR_FAIL_COND_MSG(ClassDB::has_signal(get_class_name(), p_signal.name), "User signal's name conflicts with a built-in signal of '" + get_class_name() + "'.");
|
||||
ERR_FAIL_COND_MSG(signal_map.has(p_signal.name), "Trying to add already existing signal '" + p_signal.name + "'.");
|
||||
Signal s;
|
||||
SignalData s;
|
||||
s.user = p_signal;
|
||||
signal_map[p_signal.name] = s;
|
||||
}
|
||||
@@ -1117,23 +1100,22 @@ bool Object::_has_user_signal(const StringName &p_name) const {
|
||||
struct _ObjectSignalDisconnectData {
|
||||
|
||||
StringName signal;
|
||||
Object *target;
|
||||
StringName method;
|
||||
Callable callable;
|
||||
};
|
||||
|
||||
Variant Object::_emit_signal(const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
|
||||
Variant Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
||||
|
||||
r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
|
||||
ERR_FAIL_COND_V(p_argcount < 1, Variant());
|
||||
if (p_args[0]->get_type() != Variant::STRING) {
|
||||
r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
r_error.argument = 0;
|
||||
r_error.expected = Variant::STRING;
|
||||
ERR_FAIL_COND_V(p_args[0]->get_type() != Variant::STRING, Variant());
|
||||
}
|
||||
|
||||
r_error.error = Variant::CallError::CALL_OK;
|
||||
r_error.error = Callable::CallError::CALL_OK;
|
||||
|
||||
StringName signal = *p_args[0];
|
||||
|
||||
@@ -1154,7 +1136,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
|
||||
if (_block_signals)
|
||||
return ERR_CANT_ACQUIRE_RESOURCE; //no emit, signals blocked
|
||||
|
||||
Signal *s = signal_map.getptr(p_name);
|
||||
SignalData *s = signal_map.getptr(p_name);
|
||||
if (!s) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_name);
|
||||
@@ -1170,7 +1152,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
|
||||
//copy on write will ensure that disconnecting the signal or even deleting the object will not affect the signal calling.
|
||||
//this happens automatically and will not change the performance of calling.
|
||||
//awesome, isn't it?
|
||||
VMap<Signal::Target, Signal::Slot> slot_map = s->slot_map;
|
||||
VMap<Callable, SignalData::Slot> slot_map = s->slot_map;
|
||||
|
||||
int ssize = slot_map.size();
|
||||
|
||||
@@ -1184,7 +1166,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
|
||||
|
||||
const Connection &c = slot_map.getv(i).conn;
|
||||
|
||||
Object *target = ObjectDB::get_instance(slot_map.getk(i)._id);
|
||||
Object *target = c.callable.get_object();
|
||||
if (!target) {
|
||||
// Target might have been deleted during signal callback, this is expected and OK.
|
||||
continue;
|
||||
@@ -1209,22 +1191,23 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
|
||||
}
|
||||
|
||||
if (c.flags & CONNECT_DEFERRED) {
|
||||
MessageQueue::get_singleton()->push_call(target->get_instance_id(), c.method, args, argc, true);
|
||||
MessageQueue::get_singleton()->push_callable(c.callable, args, argc, true);
|
||||
} else {
|
||||
Variant::CallError ce;
|
||||
Callable::CallError ce;
|
||||
_emitting = true;
|
||||
target->call(c.method, args, argc, ce);
|
||||
Variant ret;
|
||||
c.callable.call(args, argc, ret, ce);
|
||||
_emitting = false;
|
||||
|
||||
if (ce.error != Variant::CallError::CALL_OK) {
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (c.flags & CONNECT_PERSIST && Engine::get_singleton()->is_editor_hint() && (script.is_null() || !Ref<Script>(script)->is_tool()))
|
||||
continue;
|
||||
#endif
|
||||
if (ce.error == Variant::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
|
||||
if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
|
||||
//most likely object is not initialized yet, do not throw error.
|
||||
} else {
|
||||
ERR_PRINT("Error calling method from signal '" + String(p_name) + "': " + Variant::get_call_error_text(target, c.method, args, argc, ce) + ".");
|
||||
ERR_PRINT("Error calling from signal '" + String(p_name) + "': " + Variant::get_callable_error_text(c.callable, args, argc, ce) + ".");
|
||||
err = ERR_METHOD_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
@@ -1241,8 +1224,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
|
||||
|
||||
_ObjectSignalDisconnectData dd;
|
||||
dd.signal = p_name;
|
||||
dd.target = target;
|
||||
dd.method = c.method;
|
||||
dd.callable = c.callable;
|
||||
disconnect_data.push_back(dd);
|
||||
}
|
||||
}
|
||||
@@ -1250,7 +1232,8 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
|
||||
while (!disconnect_data.empty()) {
|
||||
|
||||
const _ObjectSignalDisconnectData &dd = disconnect_data.front()->get();
|
||||
disconnect(dd.signal, dd.target, dd.method);
|
||||
|
||||
_disconnect(dd.signal, dd.callable);
|
||||
disconnect_data.pop_front();
|
||||
}
|
||||
|
||||
@@ -1322,15 +1305,8 @@ Array Object::_get_signal_connection_list(const String &p_signal) const {
|
||||
for (List<Connection>::Element *E = conns.front(); E; E = E->next()) {
|
||||
|
||||
Connection &c = E->get();
|
||||
if (c.signal == p_signal) {
|
||||
Dictionary rc;
|
||||
rc["signal"] = c.signal;
|
||||
rc["method"] = c.method;
|
||||
rc["source"] = c.source;
|
||||
rc["target"] = c.target;
|
||||
rc["binds"] = c.binds;
|
||||
rc["flags"] = c.flags;
|
||||
ret.push_back(rc);
|
||||
if (c.signal.get_name() == p_signal) {
|
||||
ret.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1342,11 +1318,7 @@ Array Object::_get_incoming_connections() const {
|
||||
Array ret;
|
||||
int connections_amount = connections.size();
|
||||
for (int idx_conn = 0; idx_conn < connections_amount; idx_conn++) {
|
||||
Dictionary conn_data;
|
||||
conn_data["source"] = connections[idx_conn].source;
|
||||
conn_data["signal_name"] = connections[idx_conn].signal;
|
||||
conn_data["method_name"] = connections[idx_conn].method;
|
||||
ret.push_back(conn_data);
|
||||
ret.push_back(connections[idx_conn]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -1380,7 +1352,7 @@ void Object::get_all_signal_connections(List<Connection> *p_connections) const {
|
||||
|
||||
while ((S = signal_map.next(S))) {
|
||||
|
||||
const Signal *s = &signal_map[*S];
|
||||
const SignalData *s = &signal_map[*S];
|
||||
|
||||
for (int i = 0; i < s->slot_map.size(); i++) {
|
||||
|
||||
@@ -1391,7 +1363,7 @@ void Object::get_all_signal_connections(List<Connection> *p_connections) const {
|
||||
|
||||
void Object::get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const {
|
||||
|
||||
const Signal *s = signal_map.getptr(p_signal);
|
||||
const SignalData *s = signal_map.getptr(p_signal);
|
||||
if (!s)
|
||||
return; //nothing
|
||||
|
||||
@@ -1406,7 +1378,7 @@ int Object::get_persistent_signal_connection_count() const {
|
||||
|
||||
while ((S = signal_map.next(S))) {
|
||||
|
||||
const Signal *s = &signal_map[*S];
|
||||
const SignalData *s = &signal_map[*S];
|
||||
|
||||
for (int i = 0; i < s->slot_map.size(); i++) {
|
||||
if (s->slot_map.getv(i).conn.flags & CONNECT_PERSIST) {
|
||||
@@ -1425,11 +1397,15 @@ void Object::get_signals_connected_to_this(List<Connection> *p_connections) cons
|
||||
}
|
||||
}
|
||||
|
||||
Error Object::connect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, const Vector<Variant> &p_binds, uint32_t p_flags) {
|
||||
Error Object::connect_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, const Vector<Variant> &p_binds, uint32_t p_flags) {
|
||||
|
||||
ERR_FAIL_NULL_V(p_to_object, ERR_INVALID_PARAMETER);
|
||||
return connect(p_signal, Callable(p_to_object, p_to_method), p_binds, p_flags);
|
||||
}
|
||||
Error Object::connect(const StringName &p_signal, const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) {
|
||||
|
||||
Signal *s = signal_map.getptr(p_signal);
|
||||
ERR_FAIL_COND_V(p_callable.is_null(), ERR_INVALID_PARAMETER);
|
||||
|
||||
SignalData *s = signal_map.getptr(p_signal);
|
||||
if (!s) {
|
||||
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
|
||||
//check in script
|
||||
@@ -1448,33 +1424,32 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str
|
||||
#endif
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!signal_is_valid, ERR_INVALID_PARAMETER, "In Object of type '" + String(get_class()) + "': Attempt to connect nonexistent signal '" + p_signal + "' to method '" + p_to_object->get_class() + "." + p_to_method + "'.");
|
||||
ERR_FAIL_COND_V_MSG(!signal_is_valid, ERR_INVALID_PARAMETER, "In Object of type '" + String(get_class()) + "': Attempt to connect nonexistent signal '" + p_signal + "' to callable '" + p_callable + "'.");
|
||||
|
||||
signal_map[p_signal] = Signal();
|
||||
signal_map[p_signal] = SignalData();
|
||||
s = &signal_map[p_signal];
|
||||
}
|
||||
|
||||
Signal::Target target(p_to_object->get_instance_id(), p_to_method);
|
||||
Callable target = p_callable;
|
||||
|
||||
if (s->slot_map.has(target)) {
|
||||
if (p_flags & CONNECT_REFERENCE_COUNTED) {
|
||||
s->slot_map[target].reference_count++;
|
||||
return OK;
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Signal '" + p_signal + "' is already connected to given method '" + p_to_method + "' in that object.");
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Signal '" + p_signal + "' is already connected to given callable '" + p_callable + "' in that object.");
|
||||
}
|
||||
}
|
||||
|
||||
Signal::Slot slot;
|
||||
SignalData::Slot slot;
|
||||
|
||||
Connection conn;
|
||||
conn.source = this;
|
||||
conn.target = p_to_object;
|
||||
conn.method = p_to_method;
|
||||
conn.signal = p_signal;
|
||||
conn.callable = target;
|
||||
conn.signal = ::Signal(this, p_signal);
|
||||
conn.flags = p_flags;
|
||||
conn.binds = p_binds;
|
||||
slot.conn = conn;
|
||||
slot.cE = p_to_object->connections.push_back(conn);
|
||||
slot.cE = p_callable.get_object()->connections.push_back(conn);
|
||||
if (p_flags & CONNECT_REFERENCE_COUNTED) {
|
||||
slot.reference_count = 1;
|
||||
}
|
||||
@@ -1484,10 +1459,15 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool Object::is_connected(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) const {
|
||||
bool Object::is_connected_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) const {
|
||||
|
||||
ERR_FAIL_NULL_V(p_to_object, false);
|
||||
const Signal *s = signal_map.getptr(p_signal);
|
||||
return is_connected(p_signal, Callable(p_to_object, p_to_method));
|
||||
}
|
||||
|
||||
bool Object::is_connected(const StringName &p_signal, const Callable &p_callable) const {
|
||||
|
||||
ERR_FAIL_COND_V(p_callable.is_null(), false);
|
||||
const SignalData *s = signal_map.getptr(p_signal);
|
||||
if (!s) {
|
||||
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal);
|
||||
if (signal_is_valid)
|
||||
@@ -1499,28 +1479,31 @@ bool Object::is_connected(const StringName &p_signal, Object *p_to_object, const
|
||||
ERR_FAIL_V_MSG(false, "Nonexistent signal: " + p_signal + ".");
|
||||
}
|
||||
|
||||
Signal::Target target(p_to_object->get_instance_id(), p_to_method);
|
||||
Callable target = p_callable;
|
||||
|
||||
return s->slot_map.has(target);
|
||||
//const Map<Signal::Target,Signal::Slot>::Element *E = s->slot_map.find(target);
|
||||
//return (E!=NULL);
|
||||
}
|
||||
|
||||
void Object::disconnect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) {
|
||||
void Object::disconnect_compat(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) {
|
||||
|
||||
_disconnect(p_signal, p_to_object, p_to_method);
|
||||
_disconnect(p_signal, Callable(p_to_object, p_to_method));
|
||||
}
|
||||
void Object::_disconnect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, bool p_force) {
|
||||
|
||||
ERR_FAIL_NULL(p_to_object);
|
||||
Signal *s = signal_map.getptr(p_signal);
|
||||
void Object::disconnect(const StringName &p_signal, const Callable &p_callable) {
|
||||
_disconnect(p_signal, p_callable);
|
||||
}
|
||||
|
||||
void Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) {
|
||||
|
||||
ERR_FAIL_COND(p_callable.is_null());
|
||||
SignalData *s = signal_map.getptr(p_signal);
|
||||
ERR_FAIL_COND_MSG(!s, vformat("Nonexistent signal '%s' in %s.", p_signal, to_string()));
|
||||
|
||||
Signal::Target target(p_to_object->get_instance_id(), p_to_method);
|
||||
ERR_FAIL_COND_MSG(!s->slot_map.has(p_callable), "Disconnecting nonexistent signal '" + p_signal + "', callable: " + p_callable + ".");
|
||||
|
||||
ERR_FAIL_COND_MSG(!s->slot_map.has(target), "Disconnecting nonexistent signal '" + p_signal + "', slot: " + itos(target._id) + ":" + target.method + ".");
|
||||
|
||||
Signal::Slot *slot = &s->slot_map[target];
|
||||
SignalData::Slot *slot = &s->slot_map[p_callable];
|
||||
|
||||
if (!p_force) {
|
||||
slot->reference_count--; // by default is zero, if it was not referenced it will go below it
|
||||
@@ -1528,9 +1511,10 @@ void Object::_disconnect(const StringName &p_signal, Object *p_to_object, const
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
p_to_object->connections.erase(slot->cE);
|
||||
s->slot_map.erase(target);
|
||||
Object *object = p_callable.get_object();
|
||||
ERR_FAIL_COND(!object);
|
||||
object->connections.erase(slot->cE);
|
||||
s->slot_map.erase(p_callable);
|
||||
|
||||
if (s->slot_map.empty() && ClassDB::has_signal(get_class_name(), p_signal)) {
|
||||
//not user signal, delete
|
||||
@@ -1710,9 +1694,9 @@ void Object::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_signal_connection_list", "signal"), &Object::_get_signal_connection_list);
|
||||
ClassDB::bind_method(D_METHOD("get_incoming_connections"), &Object::_get_incoming_connections);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("connect", "signal", "target", "method", "binds", "flags"), &Object::connect, DEFVAL(Array()), DEFVAL(0));
|
||||
ClassDB::bind_method(D_METHOD("disconnect", "signal", "target", "method"), &Object::disconnect);
|
||||
ClassDB::bind_method(D_METHOD("is_connected", "signal", "target", "method"), &Object::is_connected);
|
||||
ClassDB::bind_method(D_METHOD("connect", "signal", "callable", "binds", "flags"), &Object::connect, DEFVAL(Array()), DEFVAL(0));
|
||||
ClassDB::bind_method(D_METHOD("disconnect", "signal", "callable"), &Object::disconnect);
|
||||
ClassDB::bind_method(D_METHOD("is_connected", "signal", "callable"), &Object::is_connected);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_block_signals", "enable"), &Object::set_block_signals);
|
||||
ClassDB::bind_method(D_METHOD("is_blocking_signals"), &Object::is_blocking_signals);
|
||||
@@ -1829,7 +1813,7 @@ Variant::Type Object::get_static_property_type_indexed(const Vector<StringName>
|
||||
return Variant::NIL;
|
||||
}
|
||||
|
||||
Variant::CallError ce;
|
||||
Callable::CallError ce;
|
||||
Variant check = Variant::construct(t, NULL, 0, ce);
|
||||
|
||||
for (int i = 1; i < p_path.size(); i++) {
|
||||
@@ -1956,15 +1940,15 @@ Object::~Object() {
|
||||
|
||||
while ((S = signal_map.next(NULL))) {
|
||||
|
||||
Signal *s = &signal_map[*S];
|
||||
SignalData *s = &signal_map[*S];
|
||||
|
||||
//brute force disconnect for performance
|
||||
int slot_count = s->slot_map.size();
|
||||
const VMap<Signal::Target, Signal::Slot>::Pair *slot_list = s->slot_map.get_array();
|
||||
const VMap<Callable, SignalData::Slot>::Pair *slot_list = s->slot_map.get_array();
|
||||
|
||||
for (int i = 0; i < slot_count; i++) {
|
||||
|
||||
slot_list[i].value.conn.target->connections.erase(slot_list[i].value.cE);
|
||||
slot_list[i].value.conn.callable.get_object()->connections.erase(slot_list[i].value.cE);
|
||||
}
|
||||
|
||||
signal_map.erase(*S);
|
||||
@@ -1974,7 +1958,7 @@ Object::~Object() {
|
||||
while (connections.size()) {
|
||||
|
||||
Connection c = connections.front()->get();
|
||||
c.source->_disconnect(c.signal, c.target, c.method, true);
|
||||
c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true);
|
||||
}
|
||||
|
||||
ObjectDB::remove_instance(this);
|
||||
|
||||
Reference in New Issue
Block a user