Allow overriding how scripted objects are converted to strings

solves #26796

- ADD `String to_string()` method to Object which can be overriden by `String _to_string()` in scripts
- ADD `String to_string(r_valid)` method to ScriptInstance to allow langauges to control how scripted objects are converted to strings
- IMPLEMENT to_string for GDScriptInstance, VisualScriptInstance, and NativeScriptInstance
- ADD Documentation about `Object.to_string` and `Object._to_string`
- Changed `Variant::operator String` to use `obj->to_string()`
This commit is contained in:
Leonard Meagher 2019-04-09 22:07:40 -07:00
parent 5772f60f96
commit f7eb426e2e
14 changed files with 107 additions and 1 deletions

View file

@ -44,6 +44,7 @@ CoreStringNames::CoreStringNames() :
_iter_next(StaticCString::create("_iter_next")),
_iter_get(StaticCString::create("_iter_get")),
get_rid(StaticCString::create("get_rid")),
_to_string(StaticCString::create("_to_string")),
#ifdef TOOLS_ENABLED
_sections_unfolded(StaticCString::create("_sections_unfolded")),
#endif

View file

@ -62,6 +62,7 @@ public:
StringName _iter_next;
StringName _iter_get;
StringName get_rid;
StringName _to_string;
#ifdef TOOLS_ENABLED
StringName _sections_unfolded;
#endif

View file

@ -956,6 +956,16 @@ void Object::notification(int p_notification, bool p_reversed) {
}
}
String Object::to_string() {
if (script_instance) {
bool valid;
String ret = script_instance->to_string(&valid);
if (valid)
return ret;
}
return "[" + get_class() + ":" + itos(get_instance_id()) + "]";
}
void Object::_changed_callback(Object *p_changed, const char *p_prop) {
}
@ -1682,6 +1692,7 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_property_list"), &Object::_get_property_list_bind);
ClassDB::bind_method(D_METHOD("get_method_list"), &Object::_get_method_list_bind);
ClassDB::bind_method(D_METHOD("notification", "what", "reversed"), &Object::notification, DEFVAL(false));
ClassDB::bind_method(D_METHOD("to_string"), &Object::to_string);
ClassDB::bind_method(D_METHOD("get_instance_id"), &Object::get_instance_id);
ClassDB::bind_method(D_METHOD("set_script", "script"), &Object::set_script);
@ -1768,6 +1779,7 @@ void Object::_bind_methods() {
#endif
BIND_VMETHOD(MethodInfo("_init"));
BIND_VMETHOD(MethodInfo(Variant::STRING, "_to_string"));
BIND_CONSTANT(NOTIFICATION_POSTINITIALIZE);
BIND_CONSTANT(NOTIFICATION_PREDELETE);

View file

@ -658,6 +658,7 @@ public:
void call_multilevel(const StringName &p_name, VARIANT_ARG_LIST); // C++ helper
void notification(int p_notification, bool p_reversed = false);
String to_string();
//used mainly by script, get and set all INCLUDING string
virtual Variant getvar(const Variant &p_key, bool *r_valid = NULL) const;

View file

@ -30,6 +30,7 @@
#include "script_language.h"
#include "core/core_string_names.h"
#include "core/project_settings.h"
ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES];

View file

@ -173,6 +173,11 @@ public:
virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
virtual void notification(int p_notification) = 0;
virtual String to_string(bool *r_valid) {
if (r_valid)
*r_valid = false;
return String();
}
//this is used by script languages that keep a reference counter of their own
//you can make make Ref<> not die when it reaches zero, so deleting the reference

View file

@ -1582,7 +1582,7 @@ Variant::operator String() const {
};
};
#endif
return "[" + _get_obj().obj->get_class() + ":" + itos(_get_obj().obj->get_instance_id()) + "]";
return _get_obj().obj->to_string();
} else
return "[Object:null]";

View file

@ -59,6 +59,22 @@
Sets a property. Returns [code]true[/code] if the [code]property[/code] exists.
</description>
</method>
<method name="_to_string" qualifiers="virtual">
<return type="String">
</return>
<description>
Returns a [String] representing the object. Default is [code]"[ClassName:RID]"[/code].
Override this method to customize the [String] representation of the object when it's being converted to a string, for example: [code]print(obj)[/code].
</description>
</method>
<method name="to_string">
<return type="String">
</return>
<description>
Returns a [String] representing the object. Default is [code]"[ClassName:RID]"[/code].
Override the method [method _to_string] to customize the [String] representation.
</description>
</method>
<method name="add_user_signal">
<return type="void">
</return>

View file

@ -32,6 +32,7 @@
#include "gdnative/gdnative.h"
#include "core/core_string_names.h"
#include "core/global_constants.h"
#include "core/io/file_access_encrypted.h"
#include "core/os/file_access.h"
@ -771,6 +772,27 @@ void NativeScriptInstance::notification(int p_notification) {
call_multilevel("_notification", args, 1);
}
String NativeScriptInstance::to_string(bool *r_valid) {
if (has_method(CoreStringNames::get_singleton()->_to_string)) {
Variant::CallError ce;
Variant ret = call(CoreStringNames::get_singleton()->_to_string, NULL, 0, ce);
if (ce.error == Variant::CallError::CALL_OK) {
if (ret.get_type() != Variant::STRING) {
if (r_valid)
*r_valid = false;
ERR_EXPLAIN("Wrong type for " + CoreStringNames::get_singleton()->_to_string + ", must be a String.");
ERR_FAIL_V(String());
}
if (r_valid)
*r_valid = true;
return ret.operator String();
}
}
if (r_valid)
*r_valid = false;
return String();
}
void NativeScriptInstance::refcount_incremented() {
Variant::CallError err;
call("_refcount_incremented", NULL, 0, err);

View file

@ -208,6 +208,7 @@ public:
virtual bool has_method(const StringName &p_method) const;
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
virtual void notification(int p_notification);
String to_string(bool *r_valid);
virtual Ref<Script> get_script() const;
virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;

View file

@ -30,6 +30,7 @@
#include "gdscript.h"
#include "core/core_string_names.h"
#include "core/engine.h"
#include "core/global_constants.h"
#include "core/io/file_access_encrypted.h"
@ -1234,6 +1235,27 @@ void GDScriptInstance::notification(int p_notification) {
}
}
String GDScriptInstance::to_string(bool *r_valid) {
if (has_method(CoreStringNames::get_singleton()->_to_string)) {
Variant::CallError ce;
Variant ret = call(CoreStringNames::get_singleton()->_to_string, NULL, 0, ce);
if (ce.error == Variant::CallError::CALL_OK) {
if (ret.get_type() != Variant::STRING) {
if (r_valid)
*r_valid = false;
ERR_EXPLAIN("Wrong type for " + CoreStringNames::get_singleton()->_to_string + ", must be a String.");
ERR_FAIL_V(String());
}
if (r_valid)
*r_valid = true;
return ret.operator String();
}
}
if (r_valid)
*r_valid = false;
return String();
}
Ref<Script> GDScriptInstance::get_script() const {
return script;

View file

@ -251,6 +251,7 @@ public:
Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; }
virtual void notification(int p_notification);
String to_string(bool *r_valid);
virtual Ref<Script> get_script() const;

View file

@ -30,6 +30,7 @@
#include "visual_script.h"
#include "core/core_string_names.h"
#include "core/os/os.h"
#include "core/project_settings.h"
#include "scene/main/node.h"
@ -1976,6 +1977,27 @@ void VisualScriptInstance::notification(int p_notification) {
call(VisualScriptLanguage::singleton->notification, &whatp, 1, ce); //do as call
}
String VisualScriptInstance::to_string(bool *r_valid) {
if (has_method(CoreStringNames::get_singleton()->_to_string)) {
Variant::CallError ce;
Variant ret = call(CoreStringNames::get_singleton()->_to_string, NULL, 0, ce);
if (ce.error == Variant::CallError::CALL_OK) {
if (ret.get_type() != Variant::STRING) {
if (r_valid)
*r_valid = false;
ERR_EXPLAIN("Wrong type for " + CoreStringNames::get_singleton()->_to_string + ", must be a String.");
ERR_FAIL_V(String());
}
if (r_valid)
*r_valid = true;
return ret.operator String();
}
}
if (r_valid)
*r_valid = false;
return String();
}
Ref<Script> VisualScriptInstance::get_script() const {
return script;

View file

@ -405,6 +405,7 @@ public:
virtual bool has_method(const StringName &p_method) const;
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
virtual void notification(int p_notification);
String to_string(bool *r_valid);
bool set_variable(const StringName &p_variable, const Variant &p_value) {