Add ScriptServer and export all custom resources.
This commit is contained in:
parent
eae7a5384e
commit
ad10a5961f
|
@ -39,6 +39,7 @@
|
|||
#include "core/os/keyboard.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/script_language.h"
|
||||
|
||||
/**
|
||||
* Time constants borrowed from loc_time.h
|
||||
|
@ -2989,6 +2990,89 @@ _ClassDB::~_ClassDB() {
|
|||
}
|
||||
///////////////////////////////
|
||||
|
||||
bool _ScriptServer::_set(const StringName &p_name, const Variant &p_value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _ScriptServer::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
if (ScriptServer::is_global_class(p_name)) {
|
||||
r_ret = ResourceLoader::load(ScriptServer::get_global_class_path(p_name), "Script");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void _ScriptServer::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
ERR_FAIL_COND(!p_list);
|
||||
List<StringName> names;
|
||||
ScriptServer::get_global_class_list(&names);
|
||||
for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
|
||||
StringName n = E->get();
|
||||
String class_name = String(n).get_file().get_extension();
|
||||
p_list->push_back(PropertyInfo(Variant::OBJECT, class_name, PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_NETWORK, ResourceLoader::get_resource_type(ScriptServer::get_global_class_path(n))));
|
||||
}
|
||||
}
|
||||
|
||||
bool _ScriptServer::is_global_class(const StringName &p_class) const {
|
||||
return ScriptServer::is_global_class(p_class);
|
||||
}
|
||||
|
||||
String _ScriptServer::get_global_class_path(const String &p_class) const {
|
||||
return ScriptServer::get_global_class_path(p_class);
|
||||
}
|
||||
|
||||
StringName _ScriptServer::get_global_class_base(const String &p_class) const {
|
||||
return ScriptServer::get_global_class_base(p_class);
|
||||
}
|
||||
|
||||
StringName _ScriptServer::get_global_class_native_base(const String &p_class) const {
|
||||
return ScriptServer::get_global_class_native_base(p_class);
|
||||
}
|
||||
|
||||
StringName _ScriptServer::get_global_class_name(const String &p_path) const {
|
||||
return ScriptServer::get_global_class_name(p_path);
|
||||
}
|
||||
|
||||
Ref<Script> _ScriptServer::get_global_class_script(const StringName &p_class) const {
|
||||
return ScriptServer::get_global_class_script(p_class);
|
||||
}
|
||||
|
||||
Variant _ScriptServer::instantiate_global_class(const StringName &p_class) const {
|
||||
return ScriptServer::instantiate_global_class(p_class);
|
||||
}
|
||||
|
||||
Array _ScriptServer::get_global_class_list() const {
|
||||
Array ret;
|
||||
List<StringName> lst;
|
||||
ScriptServer::get_global_class_list(&lst);
|
||||
for (List<StringName>::Element *E = lst.front(); E; E = E->next()) {
|
||||
ret.push_back(E->get());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void _ScriptServer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("is_global_class", "class"), &_ScriptServer::is_global_class);
|
||||
ClassDB::bind_method(D_METHOD("get_global_class_path", "class"), &_ScriptServer::get_global_class_path);
|
||||
ClassDB::bind_method(D_METHOD("get_global_class_base", "class"), &_ScriptServer::get_global_class_base);
|
||||
ClassDB::bind_method(D_METHOD("get_global_class_native_base", "class"), &_ScriptServer::get_global_class_native_base);
|
||||
ClassDB::bind_method(D_METHOD("get_global_class_name", "path"), &_ScriptServer::get_global_class_name);
|
||||
ClassDB::bind_method(D_METHOD("get_global_class_script", "class"), &_ScriptServer::get_global_class_script);
|
||||
ClassDB::bind_method(D_METHOD("instantiate_global_class", "class"), &_ScriptServer::instantiate_global_class);
|
||||
ClassDB::bind_method(D_METHOD("get_global_class_list"), &_ScriptServer::get_global_class_list);
|
||||
}
|
||||
|
||||
_ScriptServer::_ScriptServer() {
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
_ScriptServer::~_ScriptServer() {
|
||||
}
|
||||
|
||||
_ScriptServer *_ScriptServer::singleton = nullptr;
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void _Engine::set_iterations_per_second(int p_ips) {
|
||||
Engine::get_singleton()->set_iterations_per_second(p_ips);
|
||||
}
|
||||
|
|
|
@ -754,6 +754,33 @@ public:
|
|||
~_ClassDB();
|
||||
};
|
||||
|
||||
class _ScriptServer : public Object {
|
||||
GDCLASS(_ScriptServer, Object);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
static _ScriptServer *singleton;
|
||||
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
static _ScriptServer *get_singleton() { return singleton; }
|
||||
|
||||
bool is_global_class(const StringName &p_class) const;
|
||||
String get_global_class_path(const String &p_class) const;
|
||||
StringName get_global_class_base(const String &p_class) const;
|
||||
StringName get_global_class_native_base(const String &p_class) const;
|
||||
StringName get_global_class_name(const String &p_path) const;
|
||||
Ref<Script> get_global_class_script(const StringName &p_class) const;
|
||||
Variant instantiate_global_class(const StringName &p_class) const;
|
||||
Array get_global_class_list() const;
|
||||
|
||||
_ScriptServer();
|
||||
~_ScriptServer();
|
||||
};
|
||||
|
||||
class _Engine : public Object {
|
||||
GDCLASS(_Engine, Object);
|
||||
|
||||
|
|
|
@ -101,8 +101,14 @@ void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_ty
|
|||
}
|
||||
|
||||
void ResourceLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) {
|
||||
StringName native = ScriptServer::is_global_class(p_type) ? ScriptServer::get_global_class_native_base(p_type) : StringName(p_type);
|
||||
for (int i = 0; i < loader_count; i++) {
|
||||
loader[i]->get_recognized_extensions_for_type(p_type, p_extensions);
|
||||
Ref<ResourceFormatLoader> current = loader[i];
|
||||
if (!current->get_script().is_null()) {
|
||||
current->get_recognized_extensions_for_type(p_type, p_extensions);
|
||||
} else {
|
||||
current->get_recognized_extensions_for_type(native, p_extensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
#include "core/packed_data_container.h"
|
||||
#include "core/path_remap.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/script_language.h"
|
||||
#include "core/translation.h"
|
||||
#include "core/undo_redo.h"
|
||||
|
||||
|
@ -86,6 +87,7 @@ static _Engine *_engine = nullptr;
|
|||
static _ClassDB *_classdb = nullptr;
|
||||
static _Marshalls *_marshalls = nullptr;
|
||||
static _JSON *_json = nullptr;
|
||||
static _ScriptServer *_script_server = nullptr;
|
||||
|
||||
static IP *ip = nullptr;
|
||||
|
||||
|
@ -222,6 +224,7 @@ void register_core_types() {
|
|||
_classdb = memnew(_ClassDB);
|
||||
_marshalls = memnew(_Marshalls);
|
||||
_json = memnew(_JSON);
|
||||
_script_server = memnew(_ScriptServer);
|
||||
}
|
||||
|
||||
void register_core_settings() {
|
||||
|
@ -250,6 +253,7 @@ void register_core_singletons() {
|
|||
ClassDB::register_class<InputMap>();
|
||||
ClassDB::register_class<_JSON>();
|
||||
ClassDB::register_class<Expression>();
|
||||
ClassDB::register_class<_ScriptServer>();
|
||||
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton()));
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton()));
|
||||
|
@ -264,6 +268,7 @@ void register_core_singletons() {
|
|||
Engine::get_singleton()->add_singleton(Engine::Singleton("Input", Input::get_singleton()));
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("InputMap", InputMap::get_singleton()));
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("JSON", _JSON::get_singleton()));
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("ScriptServer", _ScriptServer::get_singleton()));
|
||||
}
|
||||
|
||||
void unregister_core_types() {
|
||||
|
@ -274,6 +279,7 @@ void unregister_core_types() {
|
|||
memdelete(_classdb);
|
||||
memdelete(_marshalls);
|
||||
memdelete(_json);
|
||||
memdelete(_script_server);
|
||||
|
||||
memdelete(_geometry);
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "script_language.h"
|
||||
|
||||
#include "core/core_string_names.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/project_settings.h"
|
||||
|
||||
ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES];
|
||||
|
@ -200,20 +201,28 @@ void ScriptServer::thread_exit() {
|
|||
}
|
||||
|
||||
HashMap<StringName, ScriptServer::GlobalScriptClass> ScriptServer::global_classes;
|
||||
HashMap<String, StringName> ScriptServer::global_class_paths;
|
||||
|
||||
void ScriptServer::global_classes_clear() {
|
||||
global_classes.clear();
|
||||
global_class_paths.clear();
|
||||
}
|
||||
|
||||
void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) {
|
||||
ERR_FAIL_COND_MSG(p_class == p_base || (global_classes.has(p_base) && get_global_class_native_base(p_base) == p_class), "Cyclic inheritance in script class.");
|
||||
ERR_FAIL_COND_MSG(p_class == StringName(), vformat("Attempted to register global script class at path '%s' without a class name.", p_path));
|
||||
ERR_FAIL_COND_MSG(p_base == StringName(), vformat("Attempted to register global script class at path '%s' without a base name.", p_path));
|
||||
ERR_FAIL_COND_MSG(p_language == StringName(), vformat("Attempted to register global script class at path '%s' without a language name.", p_path));
|
||||
ERR_FAIL_COND_MSG(p_path.empty(), vformat("Attempted to register global script class named '%s' with an empty path.", p_class));
|
||||
GlobalScriptClass g;
|
||||
g.language = p_language;
|
||||
g.path = p_path;
|
||||
g.base = p_base;
|
||||
global_classes[p_class] = g;
|
||||
global_class_paths[p_path] = p_class;
|
||||
}
|
||||
void ScriptServer::remove_global_class(const StringName &p_class) {
|
||||
global_class_paths.erase(global_classes[p_class].path);
|
||||
global_classes.erase(p_class);
|
||||
}
|
||||
bool ScriptServer::is_global_class(const StringName &p_class) {
|
||||
|
@ -223,23 +232,71 @@ StringName ScriptServer::get_global_class_language(const StringName &p_class) {
|
|||
ERR_FAIL_COND_V(!global_classes.has(p_class), StringName());
|
||||
return global_classes[p_class].language;
|
||||
}
|
||||
String ScriptServer::get_global_class_path(const String &p_class) {
|
||||
String ScriptServer::get_global_class_path(const StringName &p_class) {
|
||||
ERR_FAIL_COND_V(!global_classes.has(p_class), String());
|
||||
return global_classes[p_class].path;
|
||||
}
|
||||
StringName ScriptServer::get_global_class_name(const String &p_path) {
|
||||
if (global_class_paths.has(p_path)) {
|
||||
return global_class_paths[p_path];
|
||||
}
|
||||
return StringName();
|
||||
}
|
||||
|
||||
StringName ScriptServer::get_global_class_base(const String &p_class) {
|
||||
ERR_FAIL_COND_V(!global_classes.has(p_class), String());
|
||||
StringName ScriptServer::get_global_class_base(const StringName &p_class) {
|
||||
ERR_FAIL_COND_V(!global_classes.has(p_class), StringName());
|
||||
return global_classes[p_class].base;
|
||||
}
|
||||
StringName ScriptServer::get_global_class_native_base(const String &p_class) {
|
||||
ERR_FAIL_COND_V(!global_classes.has(p_class), String());
|
||||
|
||||
StringName ScriptServer::get_global_class_native_base(const StringName &p_class) {
|
||||
ERR_FAIL_COND_V(!global_classes.has(p_class), StringName());
|
||||
String base = global_classes[p_class].base;
|
||||
while (global_classes.has(base)) {
|
||||
base = global_classes[base].base;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
Ref<Script> ScriptServer::get_global_class_script(const StringName &p_class) {
|
||||
ERR_FAIL_COND_V_MSG(!ScriptServer::is_global_class(p_class), Ref<Script>(), vformat("Class to load '%s' is not a script class.", p_class));
|
||||
if (!ScriptServer::is_global_class(p_class)) {
|
||||
return Ref<Script>();
|
||||
}
|
||||
|
||||
String path = ScriptServer::get_global_class_path(p_class);
|
||||
return ResourceLoader::load(path, "Script");
|
||||
}
|
||||
|
||||
Variant ScriptServer::instantiate_global_class(const StringName &p_class) {
|
||||
ERR_FAIL_COND_V_MSG(!global_classes.has(p_class), Variant(), vformat("Class to instantiate '%s' is not a script class.", p_class));
|
||||
String native = get_global_class_native_base(p_class);
|
||||
Object *o = ClassDB::instance(native);
|
||||
ERR_FAIL_COND_V_MSG(!o, Variant(), vformat("Could not instantiate global script class '%s'. It extends native class '%s' which is not instantiable.", p_class, native));
|
||||
|
||||
REF ref;
|
||||
Reference *r = Object::cast_to<Reference>(o);
|
||||
if (r) {
|
||||
ref = REF(r);
|
||||
}
|
||||
|
||||
Variant ret;
|
||||
if (ref.is_valid()) {
|
||||
ret = ref;
|
||||
} else {
|
||||
ret = o;
|
||||
}
|
||||
|
||||
Ref<Script> s = get_global_class_script(p_class);
|
||||
ERR_FAIL_COND_V_MSG(s.is_null(), Variant(), vformat("Failed to load global script class '%s'.", p_class));
|
||||
|
||||
o->set_script(s.get_ref_ptr());
|
||||
|
||||
ScriptInstance *si = o->get_script_instance();
|
||||
ERR_FAIL_COND_V_MSG(!si, Variant(), vformat("Failed to create script instance for global script class '%s'.", p_class));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) {
|
||||
const StringName *K = nullptr;
|
||||
List<StringName> classes;
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "core/resource.h"
|
||||
|
||||
class ScriptLanguage;
|
||||
class Script;
|
||||
|
||||
typedef void (*ScriptEditRequestFunction)(const String &p_path);
|
||||
|
||||
|
@ -59,6 +60,7 @@ class ScriptServer {
|
|||
};
|
||||
|
||||
static HashMap<StringName, GlobalScriptClass> global_classes;
|
||||
static HashMap<String, StringName> global_class_paths;
|
||||
|
||||
public:
|
||||
static ScriptEditRequestFunction edit_request_func;
|
||||
|
@ -81,9 +83,12 @@ public:
|
|||
static void remove_global_class(const StringName &p_class);
|
||||
static bool is_global_class(const StringName &p_class);
|
||||
static StringName get_global_class_language(const StringName &p_class);
|
||||
static String get_global_class_path(const String &p_class);
|
||||
static StringName get_global_class_base(const String &p_class);
|
||||
static StringName get_global_class_native_base(const String &p_class);
|
||||
static String get_global_class_path(const StringName &p_class);
|
||||
static StringName get_global_class_name(const String &p_path);
|
||||
static StringName get_global_class_base(const StringName &p_class);
|
||||
static StringName get_global_class_native_base(const StringName &p_class);
|
||||
static Ref<Script> get_global_class_script(const StringName &p_class);
|
||||
static Variant instantiate_global_class(const StringName &p_class);
|
||||
static void get_global_class_list(List<StringName> *r_global_classes);
|
||||
static void save_global_classes();
|
||||
|
||||
|
@ -283,6 +288,7 @@ public:
|
|||
virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const = 0;
|
||||
virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; }
|
||||
virtual bool overrides_external_editor() { return false; }
|
||||
virtual bool has_delayed_script_class_metadata() const { return false; }
|
||||
|
||||
virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; }
|
||||
|
||||
|
|
|
@ -77,6 +77,9 @@
|
|||
<member name="ResourceSaver" type="ResourceSaver" setter="" getter="">
|
||||
The [ResourceSaver] singleton.
|
||||
</member>
|
||||
<member name="ScriptServer" type="ScriptServer" setter="" getter="">
|
||||
The [ScriptServer] singleton.
|
||||
</member>
|
||||
<member name="TranslationServer" type="TranslationServer" setter="" getter="">
|
||||
The [TranslationServer] singleton.
|
||||
</member>
|
||||
|
|
|
@ -31,6 +31,15 @@
|
|||
[b]Warning:[/b] Removing and freeing this node will render the editor useless and may cause a crash.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_class_icon">
|
||||
<return type="Texture" />
|
||||
<argument index="0" name="class" type="String" />
|
||||
<argument index="1" name="fallback" type="String" />
|
||||
<description>
|
||||
Returns the editor icon bound to a class name or the [code]fallback[/code] class's icon if not found. The [code]fallback[/code] defaults to "Object". If still not found, returns [code]null[/code].
|
||||
[b]Node:[/b] This includes icons from custom types (see [method EditorPlugin.add_custom_type]) and global script classes from [ScriptServer].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_current_path" qualifiers="const">
|
||||
<return type="String" />
|
||||
<description>
|
||||
|
@ -78,6 +87,15 @@
|
|||
[b]Warning:[/b] Removing and freeing this node will render a part of the editor useless and may cause a crash.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_object_icon">
|
||||
<return type="Texture" />
|
||||
<argument index="0" name="object" type="Object" />
|
||||
<argument index="1" name="fallback" type="String" />
|
||||
<description>
|
||||
Returns the editor icon bound to [Object] [code]object[/code] or the class [code]fallback[/code] if a type cannot be determined. If [code]object[/code] extends [Script], then return the editor icon bound to the scripted class, not the actual script. If still not found, return [code]null[/code].
|
||||
[b]Note:[/b] if you need the editor icon for a script such as [GDScript], use [method get_class_icon].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_open_scenes" qualifiers="const">
|
||||
<return type="Array" />
|
||||
<description>
|
||||
|
|
72
doc/classes/ScriptServer.xml
Normal file
72
doc/classes/ScriptServer.xml
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="ScriptServer" inherits="Object" version="3.5">
|
||||
<brief_description>
|
||||
Global script class management singleton.
|
||||
</brief_description>
|
||||
<description>
|
||||
ScriptServer manages all information related to global script classes in Godot projects, similar to [ClassDB] for engine classes. Scripts independently opt-in to become global classes. With it, you can check if a script has a global name or icon, what its base classes are, or even instantiate them directly.
|
||||
[b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton through a global variable.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link>https://docs.godotengine.org/en/stable/getting_started/scripting/gdscript/gdscript_basics.html#classes</link>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="get_global_class_base" qualifiers="const">
|
||||
<return type="String" />
|
||||
<argument index="0" name="class" type="String" />
|
||||
<description>
|
||||
Returns the class name that the script named [code]class[/code] directly extends. This may be an engine class or another global script class.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_global_class_list" qualifiers="const">
|
||||
<return type="Array" />
|
||||
<description>
|
||||
Returns the names of all global script class names known by the ScriptServer.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_global_class_name" qualifiers="const">
|
||||
<return type="String" />
|
||||
<argument index="0" name="path" type="String" />
|
||||
<description>
|
||||
Returns the global class name bound to the [Script] at [code]path[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_global_class_native_base" qualifiers="const">
|
||||
<return type="String" />
|
||||
<argument index="0" name="class" type="String" />
|
||||
<description>
|
||||
Returns the native engine class that the script named [code]class[/code] eventually extends.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_global_class_path" qualifiers="const">
|
||||
<return type="String" />
|
||||
<argument index="0" name="class" type="String" />
|
||||
<description>
|
||||
Returns the file path to the script resource named [code]class[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_global_class_script" qualifiers="const">
|
||||
<return type="Script" />
|
||||
<argument index="0" name="class" type="String" />
|
||||
<description>
|
||||
Returns the loaded [Script] named [code]class[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="instantiate_global_class" qualifiers="const">
|
||||
<return type="Variant" />
|
||||
<argument index="0" name="class" type="String" />
|
||||
<description>
|
||||
Returns a new instance of the scripted type defined by the script named [code]class[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_global_class" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<argument index="0" name="class" type="String" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the name [code]class[/code] is a global script class.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
|
@ -147,17 +147,18 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
if (!search_loaded_scripts.has(p_type)) {
|
||||
search_loaded_scripts[p_type] = ed.script_class_load_script(p_type);
|
||||
}
|
||||
|
||||
if (!ScriptServer::is_global_class(p_type) || !ed.script_class_is_parent(p_type, base_type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!search_loaded_scripts.has(p_type)) {
|
||||
search_loaded_scripts[p_type] = ScriptServer::get_global_class_script(p_type);
|
||||
}
|
||||
|
||||
String script_path = ScriptServer::get_global_class_path(p_type);
|
||||
if (script_path.find("res://addons/", 0) != -1) {
|
||||
if (!EditorNode::get_singleton()->is_addon_plugin_enabled(script_path.get_slicec('/', 3))) {
|
||||
String cfg_path = script_path.plus_file("plugin.cfg");
|
||||
if (FileAccess::exists(cfg_path) && !EditorNode::get_singleton()->is_addon_plugin_enabled(script_path.get_slicec('/', 3))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -186,7 +187,11 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p
|
|||
item->set_text(0, p_type);
|
||||
} else {
|
||||
item->set_metadata(0, p_type);
|
||||
item->set_text(0, p_type + " (" + ScriptServer::get_global_class_path(p_type).get_file() + ")");
|
||||
String text = p_type;
|
||||
if (!EDITOR_GET("interface/editors/create_dialog_hide_script_class_filepath")) {
|
||||
text += " (" + ScriptServer::get_global_class_path(p_type).get_file() + ")";
|
||||
}
|
||||
item->set_text(0, text);
|
||||
}
|
||||
if (!can_instance) {
|
||||
item->set_custom_color(0, get_color("disabled_font_color", "Editor"));
|
||||
|
@ -348,7 +353,7 @@ void CreateDialog::_update_search() {
|
|||
bool cpp_type2 = cpp_type;
|
||||
|
||||
if (!cpp_type && !search_loaded_scripts.has(type)) {
|
||||
search_loaded_scripts[type] = ed.script_class_load_script(type);
|
||||
search_loaded_scripts[type] = ScriptServer::get_global_class_script(type);
|
||||
}
|
||||
|
||||
while (type2 != "" && (cpp_type2 ? ClassDB::is_parent_class(type2, base_type) : ed.script_class_is_parent(type2, base_type)) && type2 != base_type) {
|
||||
|
@ -361,7 +366,7 @@ void CreateDialog::_update_search() {
|
|||
cpp_type2 = cpp_type2 || ClassDB::class_exists(type2); // Built-in class can't inherit from custom type, so we can skip the check if it's already true.
|
||||
|
||||
if (!cpp_type2 && !search_loaded_scripts.has(type2)) {
|
||||
search_loaded_scripts[type2] = ed.script_class_load_script(type2);
|
||||
search_loaded_scripts[type2] = ScriptServer::get_global_class_script(type2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -865,12 +865,34 @@ void EditorData::get_plugin_window_layout(Ref<ConfigFile> p_layout) {
|
|||
}
|
||||
}
|
||||
|
||||
bool EditorData::script_class_is_parent(const String &p_class, const String &p_inherits) {
|
||||
bool EditorData::class_equals_or_inherits(const StringName &p_class, const StringName &p_inherits) const {
|
||||
if (p_class == p_inherits) {
|
||||
return true;
|
||||
}
|
||||
if (ScriptServer::is_global_class(p_class)) {
|
||||
return script_class_is_parent(p_class, p_inherits);
|
||||
}
|
||||
if (custom_types.has(p_inherits)) {
|
||||
const Vector<EditorData::CustomType> &v = custom_types[p_inherits];
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
if (v[i].name == p_class) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ClassDB::class_exists(p_class)) {
|
||||
return ClassDB::is_parent_class(p_class, p_inherits);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorData::script_class_is_parent(const StringName &p_class, const StringName &p_inherits) const {
|
||||
if (!ScriptServer::is_global_class(p_class)) {
|
||||
return false;
|
||||
}
|
||||
String base = script_class_get_base(p_class);
|
||||
Ref<Script> script = script_class_load_script(p_class);
|
||||
Ref<Script> script = ScriptServer::get_global_class_script(p_class);
|
||||
ERR_FAIL_COND_V_MSG(script.is_null(), false, vformat("Global script class '%s' failed to load."));
|
||||
Ref<Script> base_script = script->get_base_script();
|
||||
|
||||
while (p_inherits != base) {
|
||||
|
@ -887,25 +909,29 @@ bool EditorData::script_class_is_parent(const String &p_class, const String &p_i
|
|||
return true;
|
||||
}
|
||||
|
||||
StringName EditorData::script_class_get_base(const String &p_class) const {
|
||||
Ref<Script> script = script_class_load_script(p_class);
|
||||
StringName EditorData::script_class_get_base(const StringName &p_class) const {
|
||||
if (!ScriptServer::is_global_class(p_class)) {
|
||||
return StringName();
|
||||
}
|
||||
|
||||
Ref<Script> script = ScriptServer::get_global_class_script(p_class);
|
||||
if (script.is_null()) {
|
||||
return StringName();
|
||||
}
|
||||
|
||||
Ref<Script> base_script = script->get_base_script();
|
||||
if (base_script.is_null()) {
|
||||
return ScriptServer::get_global_class_base(p_class);
|
||||
return ScriptServer::get_global_class_native_base(p_class);
|
||||
}
|
||||
|
||||
return script->get_language()->get_global_class_name(base_script->get_path());
|
||||
return ScriptServer::get_global_class_name(base_script->get_path());
|
||||
}
|
||||
|
||||
Variant EditorData::script_class_instance(const String &p_class) {
|
||||
Variant EditorData::script_class_instance(const StringName &p_class) const {
|
||||
if (ScriptServer::is_global_class(p_class)) {
|
||||
Variant obj = ClassDB::instance(ScriptServer::get_global_class_native_base(p_class));
|
||||
if (obj) {
|
||||
Ref<Script> script = script_class_load_script(p_class);
|
||||
Ref<Script> script = ScriptServer::get_global_class_script(p_class);
|
||||
if (script.is_valid()) {
|
||||
((Object *)obj)->set_script(script.get_ref_ptr());
|
||||
}
|
||||
|
@ -915,25 +941,16 @@ Variant EditorData::script_class_instance(const String &p_class) {
|
|||
return Variant();
|
||||
}
|
||||
|
||||
Ref<Script> EditorData::script_class_load_script(const String &p_class) const {
|
||||
if (!ScriptServer::is_global_class(p_class)) {
|
||||
return Ref<Script>();
|
||||
}
|
||||
|
||||
String path = ScriptServer::get_global_class_path(p_class);
|
||||
return ResourceLoader::load(path, "Script");
|
||||
}
|
||||
|
||||
void EditorData::script_class_set_icon_path(const String &p_class, const String &p_icon_path) {
|
||||
void EditorData::script_class_set_icon_path(const StringName &p_class, const String &p_icon_path) {
|
||||
_script_class_icon_paths[p_class] = p_icon_path;
|
||||
}
|
||||
|
||||
String EditorData::script_class_get_icon_path(const String &p_class) const {
|
||||
String EditorData::script_class_get_icon_path(const StringName &p_class) const {
|
||||
if (!ScriptServer::is_global_class(p_class)) {
|
||||
return String();
|
||||
}
|
||||
|
||||
String current = p_class;
|
||||
StringName current = p_class;
|
||||
String ret = _script_class_icon_paths[current];
|
||||
while (ret.empty()) {
|
||||
current = script_class_get_base(current);
|
||||
|
@ -946,12 +963,25 @@ String EditorData::script_class_get_icon_path(const String &p_class) const {
|
|||
return ret;
|
||||
}
|
||||
|
||||
StringName EditorData::script_class_get_name(const String &p_path) const {
|
||||
return _script_class_file_to_path.has(p_path) ? _script_class_file_to_path[p_path] : StringName();
|
||||
}
|
||||
|
||||
void EditorData::script_class_set_name(const String &p_path, const StringName &p_class) {
|
||||
_script_class_file_to_path[p_path] = p_class;
|
||||
Ref<Script> EditorData::script_class_get_base_from_anonymous_path(const String &p_path) const {
|
||||
StringName name = ScriptServer::get_global_class_name(p_path);
|
||||
if (name != StringName()) {
|
||||
return nullptr;
|
||||
}
|
||||
Ref<Script> script = ResourceLoader::load(p_path, "Script");
|
||||
if (script.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
do {
|
||||
if (ScriptServer::get_global_class_name(script->get_path()) != StringName()) {
|
||||
return script;
|
||||
}
|
||||
if (script->get_path().find("::") != -1) {
|
||||
WARN_PRINT_ONCE("If you remove a built-in script that derives a script class, inheritance cannot be determined. The entire script is removed.");
|
||||
}
|
||||
script = script->get_base_script();
|
||||
} while (script.is_valid());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EditorData::script_class_save_icon_paths() {
|
||||
|
@ -960,8 +990,10 @@ void EditorData::script_class_save_icon_paths() {
|
|||
|
||||
Dictionary d;
|
||||
for (List<StringName>::Element *E = keys.front(); E; E = E->next()) {
|
||||
if (ScriptServer::is_global_class(E->get())) {
|
||||
d[E->get()] = _script_class_icon_paths[E->get()];
|
||||
StringName name = E->get();
|
||||
String icon_path = _script_class_icon_paths[name];
|
||||
if (ScriptServer::is_global_class(name)) {
|
||||
d[name] = icon_path;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -992,11 +1024,8 @@ void EditorData::script_class_load_icon_paths() {
|
|||
d.get_key_list(&keys);
|
||||
|
||||
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
|
||||
String name = E->get().operator String();
|
||||
StringName name = E->get().operator StringName();
|
||||
_script_class_icon_paths[name] = d[name];
|
||||
|
||||
String path = ScriptServer::get_global_class_path(name);
|
||||
script_class_set_name(path, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,7 +142,6 @@ private:
|
|||
bool _find_updated_instances(Node *p_root, Node *p_node, Set<String> &checked_paths);
|
||||
|
||||
HashMap<StringName, String> _script_class_icon_paths;
|
||||
HashMap<String, StringName> _script_class_file_to_path;
|
||||
|
||||
public:
|
||||
EditorPlugin *get_editor(Object *p_object);
|
||||
|
@ -211,17 +210,15 @@ public:
|
|||
void notify_edited_scene_changed();
|
||||
void notify_resource_saved(const Ref<Resource> &p_resource);
|
||||
|
||||
bool script_class_is_parent(const String &p_class, const String &p_inherits);
|
||||
StringName script_class_get_base(const String &p_class) const;
|
||||
Variant script_class_instance(const String &p_class);
|
||||
bool class_equals_or_inherits(const StringName &p_class, const StringName &p_inherits) const;
|
||||
bool script_class_is_parent(const StringName &p_class, const StringName &p_inherits) const;
|
||||
StringName script_class_get_base(const StringName &p_class) const;
|
||||
Variant script_class_instance(const StringName &p_class) const;
|
||||
|
||||
Ref<Script> script_class_load_script(const String &p_class) const;
|
||||
Ref<Script> script_class_get_base_from_anonymous_path(const String &p_path) const;
|
||||
|
||||
StringName script_class_get_name(const String &p_path) const;
|
||||
void script_class_set_name(const String &p_path, const StringName &p_class);
|
||||
|
||||
String script_class_get_icon_path(const String &p_class) const;
|
||||
void script_class_set_icon_path(const String &p_class, const String &p_icon_path);
|
||||
String script_class_get_icon_path(const StringName &p_class) const;
|
||||
void script_class_set_icon_path(const StringName &p_class, const String &p_icon_path);
|
||||
void script_class_clear_icon_paths() { _script_class_icon_paths.clear(); }
|
||||
void script_class_save_icon_paths();
|
||||
void script_class_load_icon_paths();
|
||||
|
|
|
@ -325,6 +325,7 @@ void EditorFileSystem::_save_filesystem_cache() {
|
|||
|
||||
void EditorFileSystem::_thread_func(void *_userdata) {
|
||||
EditorFileSystem *sd = (EditorFileSystem *)_userdata;
|
||||
sd->init_compiled_lang_script_class_file_cache();
|
||||
sd->_scan_filesystem();
|
||||
}
|
||||
|
||||
|
@ -1356,20 +1357,29 @@ Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) {
|
|||
|
||||
String EditorFileSystem::_get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const {
|
||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
if (ScriptServer::get_language(i)->handles_global_class_type(p_type)) {
|
||||
String global_name;
|
||||
String extends;
|
||||
String icon_path;
|
||||
|
||||
global_name = ScriptServer::get_language(i)->get_global_class_name(p_path, &extends, &icon_path);
|
||||
*r_extends = extends;
|
||||
*r_icon_path = icon_path;
|
||||
return global_name;
|
||||
ScriptLanguage *lang = ScriptServer::get_language(i);
|
||||
if (lang->handles_global_class_type(p_type)) {
|
||||
if (lang->has_delayed_script_class_metadata() && compiled_lang_script_class_file_cache.has(p_path)) {
|
||||
Dictionary d = compiled_lang_script_class_file_cache[p_path];
|
||||
if (r_extends) {
|
||||
*r_extends = d["base"].operator String();
|
||||
}
|
||||
if (r_icon_path) {
|
||||
*r_icon_path = d.has("icon_path") ? d["icon_path"] : "";
|
||||
}
|
||||
return d["class"].operator String();
|
||||
} else {
|
||||
return lang->get_global_class_name(p_path, r_extends, r_icon_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
*r_extends = String();
|
||||
*r_icon_path = String();
|
||||
return String();
|
||||
if (r_extends) {
|
||||
*r_extends = "";
|
||||
}
|
||||
if (r_icon_path) {
|
||||
*r_icon_path = "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) {
|
||||
|
@ -1388,7 +1398,6 @@ void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) {
|
|||
}
|
||||
ScriptServer::add_global_class(files[i]->script_class_name, files[i]->script_class_extends, lang, p_dir->get_file_path(i));
|
||||
EditorNode::get_editor_data().script_class_set_icon_path(files[i]->script_class_name, files[i]->script_class_icon_path);
|
||||
EditorNode::get_editor_data().script_class_set_name(files[i]->file, files[i]->script_class_name);
|
||||
}
|
||||
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
|
||||
_scan_script_classes(p_dir->get_subdir(i));
|
||||
|
@ -1418,6 +1427,56 @@ void EditorFileSystem::update_script_classes() {
|
|||
ResourceSaver::add_custom_savers();
|
||||
}
|
||||
|
||||
void EditorFileSystem::update_file_script_class_metadata(const String &p_path, const StringName &p_name, const StringName &p_base, const StringName &p_language, const String &p_icon_path) {
|
||||
EditorFileSystemDirectory *fs = NULL;
|
||||
int cpos = -1;
|
||||
|
||||
if (!_find_file(p_path, &fs, cpos)) {
|
||||
if (!fs)
|
||||
return;
|
||||
}
|
||||
EditorFileSystemDirectory::FileInfo *fi = fs->files[cpos];
|
||||
fi->script_class_name = p_name;
|
||||
fi->script_class_extends = p_base;
|
||||
fi->script_class_icon_path = p_icon_path;
|
||||
if (p_name != StringName() && !ScriptServer::is_global_class(p_name)) {
|
||||
ScriptServer::add_global_class(p_name, p_base, p_language, p_path);
|
||||
EditorNode::get_editor_data().script_class_set_icon_path(p_name, p_icon_path);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorFileSystem::init_compiled_lang_script_class_file_cache() {
|
||||
if (compiled_lang_script_class_file_cache.empty() && ProjectSettings::get_singleton()->has_setting("_global_script_classes")) {
|
||||
Array script_classes = ProjectSettings::get_singleton()->get_setting("_global_script_classes");
|
||||
Dictionary script_class_icons = ProjectSettings::get_singleton()->get_setting("_global_script_class_icons");
|
||||
Set<StringName> compiled_language_names;
|
||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
ScriptLanguage *lang = ScriptServer::get_language(i);
|
||||
if (lang->has_delayed_script_class_metadata()) {
|
||||
String n = lang->get_name();
|
||||
compiled_language_names.insert(n);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < script_classes.size(); i++) {
|
||||
Dictionary d = script_classes[i];
|
||||
StringName c = d["class"];
|
||||
String p = d["path"];
|
||||
StringName lg = d["language"];
|
||||
if (compiled_language_names.has(lg)) {
|
||||
String ip = script_class_icons[c];
|
||||
d["icon_path"] = ip;
|
||||
compiled_lang_script_class_file_cache[p] = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorFileSystem::remove_compiled_lang_script_class_file_cache(const String &p_file) {
|
||||
if (compiled_lang_script_class_file_cache.has(p_file)) {
|
||||
compiled_lang_script_class_file_cache.erase(p_file);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorFileSystem::_queue_update_script_classes() {
|
||||
if (update_script_classes_queued.is_set()) {
|
||||
return;
|
||||
|
|
|
@ -159,6 +159,8 @@ class EditorFileSystem : public Node {
|
|||
|
||||
void _save_late_updated_files();
|
||||
|
||||
HashMap<String, Dictionary> compiled_lang_script_class_file_cache; // keep track of script classes from compiled languages
|
||||
|
||||
EditorFileSystemDirectory *filesystem;
|
||||
|
||||
static EditorFileSystem *singleton;
|
||||
|
@ -234,8 +236,8 @@ class EditorFileSystem : public Node {
|
|||
void _scan_script_classes(EditorFileSystemDirectory *p_dir);
|
||||
SafeFlag update_script_classes_queued;
|
||||
void _queue_update_script_classes();
|
||||
|
||||
String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const;
|
||||
String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends = nullptr, String *r_icon_path = nullptr) const;
|
||||
//String _get_global_class_name(String p_path, String *p_base = nullptr, String *p_icon_path = nullptr);
|
||||
|
||||
static Error _resource_import(const String &p_path);
|
||||
|
||||
|
@ -270,6 +272,9 @@ public:
|
|||
void reimport_files(const Vector<String> &p_files);
|
||||
|
||||
void update_script_classes();
|
||||
void update_file_script_class_metadata(const String &p_path, const StringName &p_name, const StringName &p_base, const StringName &p_language, const String &p_icon_path);
|
||||
void remove_compiled_lang_script_class_file_cache(const String &p_file);
|
||||
void init_compiled_lang_script_class_file_cache();
|
||||
|
||||
bool is_group_file(const String &p_path) const;
|
||||
void move_group_file(const String &p_path, const String &p_new_path);
|
||||
|
|
|
@ -3857,9 +3857,9 @@ Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) cons
|
|||
// return name;
|
||||
|
||||
// should probably be deprecated in 4.x
|
||||
StringName base = script->get_instance_base_type();
|
||||
if (base != StringName() && EditorNode::get_editor_data().get_custom_types().has(base)) {
|
||||
const Vector<EditorData::CustomType> &types = EditorNode::get_editor_data().get_custom_types()[base];
|
||||
StringName native = script->get_instance_base_type();
|
||||
if (native != StringName() && EditorNode::get_editor_data().get_custom_types().has(native)) {
|
||||
const Vector<EditorData::CustomType> &types = EditorNode::get_editor_data().get_custom_types()[native];
|
||||
|
||||
Ref<Script> base_script = script;
|
||||
while (base_script.is_valid()) {
|
||||
|
@ -3887,7 +3887,7 @@ StringName EditorNode::get_object_custom_type_name(const Object *p_object) const
|
|||
if (script.is_valid()) {
|
||||
Ref<Script> base_script = script;
|
||||
while (base_script.is_valid()) {
|
||||
StringName name = EditorNode::get_editor_data().script_class_get_name(base_script->get_path());
|
||||
StringName name = ScriptServer::get_global_class_name(base_script->get_path());
|
||||
if (name != StringName()) {
|
||||
return name;
|
||||
}
|
||||
|
@ -3953,7 +3953,7 @@ Ref<Texture> EditorNode::get_object_icon(const Object *p_object, const String &p
|
|||
if (script.is_valid()) {
|
||||
Ref<Script> base_script = script;
|
||||
while (base_script.is_valid()) {
|
||||
StringName name = EditorNode::get_editor_data().script_class_get_name(base_script->get_path());
|
||||
StringName name = ScriptServer::get_global_class_name(base_script->get_path());
|
||||
String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name);
|
||||
Ref<ImageTexture> icon = _load_custom_class_icon(icon_path);
|
||||
if (icon.is_valid()) {
|
||||
|
@ -3993,14 +3993,15 @@ Ref<Texture> EditorNode::get_object_icon(const Object *p_object, const String &p
|
|||
Ref<Texture> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) const {
|
||||
ERR_FAIL_COND_V_MSG(p_class.empty(), nullptr, "Class name cannot be empty.");
|
||||
|
||||
EditorData &ed = get_editor_data();
|
||||
if (ScriptServer::is_global_class(p_class)) {
|
||||
Ref<ImageTexture> icon;
|
||||
Ref<Script> script = EditorNode::get_editor_data().script_class_load_script(p_class);
|
||||
Ref<Script> script = ScriptServer::get_global_class_script(p_class);
|
||||
StringName name = p_class;
|
||||
|
||||
while (script.is_valid()) {
|
||||
name = EditorNode::get_editor_data().script_class_get_name(script->get_path());
|
||||
String current_icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name);
|
||||
name = ScriptServer::get_global_class_name(script->get_path());
|
||||
String current_icon_path = ed.script_class_get_icon_path(name);
|
||||
icon = _load_custom_class_icon(current_icon_path);
|
||||
if (icon.is_valid()) {
|
||||
return icon;
|
||||
|
@ -4015,7 +4016,7 @@ Ref<Texture> EditorNode::get_class_icon(const String &p_class, const String &p_f
|
|||
return icon;
|
||||
}
|
||||
|
||||
const Map<String, Vector<EditorData::CustomType>> &p_map = EditorNode::get_editor_data().get_custom_types();
|
||||
const Map<String, Vector<EditorData::CustomType>> &p_map = ed.get_custom_types();
|
||||
for (const Map<String, Vector<EditorData::CustomType>>::Element *E = p_map.front(); E; E = E->next()) {
|
||||
const Vector<EditorData::CustomType> &ct = E->value();
|
||||
for (int i = 0; i < ct.size(); ++i) {
|
||||
|
|
|
@ -309,6 +309,14 @@ bool EditorInterface::is_distraction_free_mode_enabled() const {
|
|||
return EditorNode::get_singleton()->is_distraction_free_mode_enabled();
|
||||
}
|
||||
|
||||
Ref<Texture> EditorInterface::get_class_icon(const String &p_class, const String &p_fallback) {
|
||||
return EditorNode::get_singleton()->get_class_icon(p_class, p_fallback);
|
||||
}
|
||||
|
||||
Ref<Texture> EditorInterface::get_object_icon(const Object *p_object, const String &p_fallback) {
|
||||
return EditorNode::get_singleton()->get_object_icon(p_object, p_fallback);
|
||||
}
|
||||
|
||||
void EditorInterface::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("inspect_object", "object", "for_property", "inspector_only"), &EditorInterface::inspect_object, DEFVAL(String()), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("get_selection"), &EditorInterface::get_selection);
|
||||
|
@ -349,6 +357,9 @@ void EditorInterface::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_distraction_free_mode", "enter"), &EditorInterface::set_distraction_free_mode);
|
||||
ClassDB::bind_method(D_METHOD("is_distraction_free_mode_enabled"), &EditorInterface::is_distraction_free_mode_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_class_icon", "class", "fallback"), &EditorInterface::get_class_icon);
|
||||
ClassDB::bind_method(D_METHOD("get_object_icon", "object", "fallback"), &EditorInterface::get_object_icon);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distraction_free_mode"), "set_distraction_free_mode", "is_distraction_free_mode_enabled");
|
||||
}
|
||||
|
||||
|
|
|
@ -116,6 +116,9 @@ public:
|
|||
void set_distraction_free_mode(bool p_enter);
|
||||
bool is_distraction_free_mode_enabled() const;
|
||||
|
||||
Ref<Texture> get_class_icon(const String &p_name, const String &p_fallback);
|
||||
Ref<Texture> get_object_icon(const Object *p_object, const String &p_fallback);
|
||||
|
||||
EditorInterface();
|
||||
};
|
||||
|
||||
|
|
|
@ -2953,13 +2953,14 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ
|
|||
EditorPropertyResource *editor = memnew(EditorPropertyResource);
|
||||
editor->setup(p_object, p_path, p_hint == PROPERTY_HINT_RESOURCE_TYPE ? p_hint_text : "Resource");
|
||||
|
||||
EditorData &ed = EditorNode::get_editor_data();
|
||||
if (p_hint == PROPERTY_HINT_RESOURCE_TYPE) {
|
||||
String open_in_new = EDITOR_GET("interface/inspector/resources_to_open_in_new_inspector");
|
||||
for (int i = 0; i < open_in_new.get_slice_count(","); i++) {
|
||||
String type = open_in_new.get_slicec(',', i).strip_edges();
|
||||
for (int j = 0; j < p_hint_text.get_slice_count(","); j++) {
|
||||
String inherits = p_hint_text.get_slicec(',', j);
|
||||
if (ClassDB::is_parent_class(inherits, type)) {
|
||||
if (ed.class_equals_or_inherits(inherits, type)) {
|
||||
editor->set_use_sub_inspector(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -565,6 +565,7 @@ class EditorPropertyResource : public EditorProperty {
|
|||
bool _can_use_sub_inspector(const RES &p_resource);
|
||||
void _open_editor_pressed();
|
||||
void _fold_other_editors(Object *p_self);
|
||||
|
||||
void _update_property_bg();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -60,7 +60,15 @@ void EditorResourcePicker::_update_resource() {
|
|||
assign_button->set_text(edited_resource->get_path().get_file());
|
||||
assign_button->set_tooltip(edited_resource->get_path());
|
||||
} else {
|
||||
assign_button->set_text(edited_resource->get_class());
|
||||
String class_name = edited_resource->get_class();
|
||||
Ref<Script> res_script = edited_resource->get_script();
|
||||
if (res_script.is_valid()) {
|
||||
String script_name = ScriptServer::get_global_class_name(res_script->get_path());
|
||||
if (!script_name.empty()) {
|
||||
class_name = script_name;
|
||||
}
|
||||
}
|
||||
assign_button->set_text(class_name);
|
||||
}
|
||||
|
||||
if (edited_resource->get_path().is_resource_file()) {
|
||||
|
@ -118,9 +126,19 @@ void EditorResourcePicker::_file_selected(const String &p_path) {
|
|||
if (base_type != "") {
|
||||
bool any_type_matches = false;
|
||||
|
||||
StringName res_type = loaded_resource->get_class();
|
||||
Ref<Script> res_script = loaded_resource->get_script();
|
||||
if (res_script.is_valid()) {
|
||||
StringName script_type = ScriptServer::get_global_class_name(res_script->get_path());
|
||||
if (script_type != StringName()) {
|
||||
res_type = script_type;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < base_type.get_slice_count(","); i++) {
|
||||
String base = base_type.get_slice(",", i);
|
||||
if (loaded_resource->is_class(base)) {
|
||||
|
||||
if (EditorNode::get_editor_data().class_equals_or_inherits(res_type, base)) {
|
||||
any_type_matches = true;
|
||||
break;
|
||||
}
|
||||
|
@ -185,7 +203,9 @@ void EditorResourcePicker::_update_menu_items() {
|
|||
paste_valid = true;
|
||||
} else {
|
||||
for (int i = 0; i < base_type.get_slice_count(","); i++) {
|
||||
if (ClassDB::class_exists(cb->get_class()) && ClassDB::is_parent_class(cb->get_class(), base_type.get_slice(",", i))) {
|
||||
StringName script_name = ScriptServer::get_global_class_name(cb->get_path());
|
||||
StringName class_name = script_name != StringName() ? script_name : StringName(cb->get_class());
|
||||
if (EditorNode::get_editor_data().class_equals_or_inherits(class_name, base_type.get_slice(",", i))) {
|
||||
paste_valid = true;
|
||||
break;
|
||||
}
|
||||
|
@ -213,12 +233,7 @@ void EditorResourcePicker::_update_menu_items() {
|
|||
}
|
||||
for (int i = 0; i < conversions.size(); i++) {
|
||||
String what = conversions[i]->converts_to();
|
||||
Ref<Texture> icon;
|
||||
if (has_icon(what, "EditorIcons")) {
|
||||
icon = get_icon(what, "EditorIcons");
|
||||
} else {
|
||||
icon = get_icon(what, "Resource");
|
||||
}
|
||||
Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(what, Resource::get_class_static());
|
||||
|
||||
edit_menu->add_icon_item(icon, vformat(TTR("Convert to %s"), what), CONVERT_BASE_ID + i);
|
||||
}
|
||||
|
@ -296,10 +311,20 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
|
|||
propvalues.push_back(p);
|
||||
}
|
||||
|
||||
String orig_type = edited_resource->get_class();
|
||||
Object *inst = ClassDB::instance(orig_type);
|
||||
Ref<Resource> unique_resource = Ref<Resource>(Object::cast_to<Resource>(inst));
|
||||
ERR_FAIL_COND(unique_resource.is_null());
|
||||
Ref<Resource> inst;
|
||||
Ref<Script> res_script = edited_resource->get_script();
|
||||
if (res_script.is_valid()) {
|
||||
StringName script_name = ScriptServer::get_global_class_name(res_script->get_path());
|
||||
if (ScriptServer::is_global_class(script_name)) {
|
||||
inst = ScriptServer::instantiate_global_class(script_name);
|
||||
}
|
||||
}
|
||||
if (inst.is_null()) {
|
||||
inst = ClassDB::instance(edited_resource->get_class());
|
||||
}
|
||||
ERR_FAIL_COND_MSG(inst.is_null(), "Failed to instantiate resource during Make Unique.");
|
||||
Ref<Resource> unique_resource = Ref<Resource>(inst);
|
||||
ERR_FAIL_COND_MSG(unique_resource.is_null(), "Failed to copy resource reference during Make Unique.");
|
||||
|
||||
for (List<Pair<String, Variant>>::Element *E = propvalues.front(); E; E = E->next()) {
|
||||
Pair<String, Variant> &p = E->get();
|
||||
|
@ -360,13 +385,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
|
|||
Variant obj;
|
||||
|
||||
if (ScriptServer::is_global_class(intype)) {
|
||||
obj = ClassDB::instance(ScriptServer::get_global_class_native_base(intype));
|
||||
if (obj) {
|
||||
Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(intype));
|
||||
if (script.is_valid()) {
|
||||
((Object *)obj)->set_script(script.get_ref_ptr());
|
||||
}
|
||||
}
|
||||
obj = ScriptServer::instantiate_global_class(intype);
|
||||
} else {
|
||||
obj = ClassDB::instance(intype);
|
||||
}
|
||||
|
@ -637,14 +656,18 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
|
|||
for (Set<String>::Element *E = allowed_types.front(); E; E = E->next()) {
|
||||
String at = E->get().strip_edges();
|
||||
|
||||
if (at == "SpatialMaterial" && ClassDB::is_parent_class(dropped_resource->get_class(), "Texture")) {
|
||||
EditorData &ed = EditorNode::get_editor_data();
|
||||
StringName script_name = ScriptServer::get_global_class_name(dropped_resource->get_path());
|
||||
String class_name = script_name != StringName() ? script_name : StringName(dropped_resource->get_class());
|
||||
|
||||
if (at == "SpatialMaterial" && ed.class_equals_or_inherits(class_name, "Texture")) {
|
||||
Ref<SpatialMaterial> mat = memnew(SpatialMaterial);
|
||||
mat->set_texture(SpatialMaterial::TextureParam::TEXTURE_ALBEDO, dropped_resource);
|
||||
dropped_resource = mat;
|
||||
break;
|
||||
}
|
||||
|
||||
if (at == "ShaderMaterial" && ClassDB::is_parent_class(dropped_resource->get_class(), "Shader")) {
|
||||
if (at == "ShaderMaterial" && ed.class_equals_or_inherits(class_name, "Shader")) {
|
||||
Ref<ShaderMaterial> mat = memnew(ShaderMaterial);
|
||||
mat->set_shader(dropped_resource);
|
||||
dropped_resource = mat;
|
||||
|
|
|
@ -1256,7 +1256,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
|
|||
|
||||
Ref<Script> root_script = nullptr;
|
||||
if (ScriptServer::is_global_class(root_type)) {
|
||||
root_script = ResourceLoader::load(ScriptServer::get_global_class_path(root_type));
|
||||
root_script = ScriptServer::get_global_class_script(root_type);
|
||||
root_type = ScriptServer::get_global_class_base(root_type);
|
||||
}
|
||||
|
||||
|
|
|
@ -577,23 +577,64 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
|
|||
return;
|
||||
}
|
||||
|
||||
editor_data->get_undo_redo().create_action(TTR("Detach Script"));
|
||||
editor_data->get_undo_redo().add_do_method(editor, "push_item", (Script *)nullptr);
|
||||
Array update_array;
|
||||
EditorData &ed = EditorNode::get_editor_data();
|
||||
|
||||
for (int i = 0; i < selection.size(); i++) {
|
||||
Node *n = Object::cast_to<Node>(selection[i]);
|
||||
Ref<Script> existing = n->get_script();
|
||||
Ref<Script> empty = EditorNode::get_singleton()->get_object_custom_type_base(n);
|
||||
if (existing != empty) {
|
||||
editor_data->get_undo_redo().add_do_method(n, "set_script", empty);
|
||||
editor_data->get_undo_redo().add_undo_method(n, "set_script", existing);
|
||||
Ref<Script> base = nullptr;
|
||||
if (existing.is_valid()) {
|
||||
StringName script_class_name = ScriptServer::get_global_class_name(existing->get_path());
|
||||
if (script_class_name != StringName()) {
|
||||
update_array.clear();
|
||||
print_error("Unable to remove script class '" + script_class_name + "'. Can only remove anonymous scripts.");
|
||||
break;
|
||||
}
|
||||
base = ed.script_class_get_base_from_anonymous_path(existing->get_path());
|
||||
if (base.is_null()) {
|
||||
const Map<String, Vector<EditorData::CustomType>> &ct = EditorNode::get_editor_data().get_custom_types();
|
||||
if (ct.has(n->get_class())) {
|
||||
const Vector<EditorData::CustomType> &v = ct[n->get_class()];
|
||||
String ct_name;
|
||||
for (int j = 0; j < v.size() && !ct_name.empty(); j++) {
|
||||
if (v[j].script == existing) {
|
||||
ct_name = v[j].name;
|
||||
}
|
||||
}
|
||||
if (!ct_name.empty()) {
|
||||
update_array.clear();
|
||||
print_error("Unable to remove custom type '" + ct_name + "'. Can only remove anonymous scripts.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
base = EditorNode::get_singleton()->get_object_custom_type_base(n);
|
||||
}
|
||||
}
|
||||
if (existing != base) {
|
||||
Array an_update;
|
||||
an_update.push_back(n);
|
||||
an_update.push_back(base);
|
||||
an_update.push_back(existing);
|
||||
update_array.push_back(an_update);
|
||||
}
|
||||
}
|
||||
if (update_array.size()) {
|
||||
editor_data->get_undo_redo().create_action(TTR("Detach Script"));
|
||||
editor_data->get_undo_redo().add_do_method(editor, "push_item", (Script *)NULL);
|
||||
for (int i = 0; i < update_array.size(); i++) {
|
||||
Array an_update = update_array[i];
|
||||
Node *n = an_update[0];
|
||||
Ref<Script> do_script = an_update[1];
|
||||
Ref<Script> undo_script = an_update[2];
|
||||
editor_data->get_undo_redo().add_do_method(n, "set_script", do_script);
|
||||
editor_data->get_undo_redo().add_undo_method(n, "set_script", undo_script);
|
||||
}
|
||||
editor_data->get_undo_redo().add_do_method(this, "_update_script_button");
|
||||
editor_data->get_undo_redo().add_undo_method(this, "_update_script_button");
|
||||
|
||||
editor_data->get_undo_redo().add_do_method(this, "_update_script_button");
|
||||
editor_data->get_undo_redo().add_undo_method(this, "_update_script_button");
|
||||
|
||||
editor_data->get_undo_redo().commit_action();
|
||||
editor_data->get_undo_redo().commit_action();
|
||||
}
|
||||
} break;
|
||||
case TOOL_MOVE_UP:
|
||||
case TOOL_MOVE_DOWN: {
|
||||
|
@ -1225,6 +1266,7 @@ void SceneTreeDock::_notification(int p_what) {
|
|||
button_add->set_icon(get_icon("Add", "EditorIcons"));
|
||||
button_instance->set_icon(get_icon("Instance", "EditorIcons"));
|
||||
button_create_script->set_icon(get_icon("ScriptCreate", "EditorIcons"));
|
||||
button_extend_script->set_icon(get_icon("ScriptExtend", "EditorIcons"));
|
||||
button_detach_script->set_icon(get_icon("ScriptRemove", "EditorIcons"));
|
||||
|
||||
filter->set_right_icon(get_icon("Search", "EditorIcons"));
|
||||
|
@ -1301,6 +1343,7 @@ void SceneTreeDock::_notification(int p_what) {
|
|||
button_add->set_icon(get_icon("Add", "EditorIcons"));
|
||||
button_instance->set_icon(get_icon("Instance", "EditorIcons"));
|
||||
button_create_script->set_icon(get_icon("ScriptCreate", "EditorIcons"));
|
||||
button_extend_script->set_icon(get_icon("ScriptExtend", "EditorIcons"));
|
||||
button_detach_script->set_icon(get_icon("ScriptRemove", "EditorIcons"));
|
||||
button_2d->set_icon(get_icon("Node2D", "EditorIcons"));
|
||||
button_3d->set_icon(get_icon("Spatial", "EditorIcons"));
|
||||
|
@ -2082,29 +2125,49 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
|
|||
void SceneTreeDock::_update_script_button() {
|
||||
if (!profile_allow_script_editing) {
|
||||
button_create_script->hide();
|
||||
button_extend_script->hide();
|
||||
button_detach_script->hide();
|
||||
} else if (editor_selection->get_selection().size() == 0) {
|
||||
button_create_script->hide();
|
||||
button_extend_script->hide();
|
||||
button_detach_script->hide();
|
||||
} else if (editor_selection->get_selection().size() == 1) {
|
||||
Node *n = editor_selection->get_selected_node_list()[0];
|
||||
if (n->get_script().is_null()) {
|
||||
button_create_script->show();
|
||||
button_detach_script->hide();
|
||||
Ref<Script> s = n->get_script();
|
||||
if (s.is_valid()) {
|
||||
if (ScriptServer::get_global_class_name(s->get_path()) != StringName()) {
|
||||
button_create_script->hide();
|
||||
button_extend_script->show();
|
||||
button_detach_script->hide();
|
||||
} else {
|
||||
button_create_script->hide();
|
||||
button_extend_script->hide();
|
||||
button_detach_script->show();
|
||||
}
|
||||
} else {
|
||||
button_create_script->hide();
|
||||
button_detach_script->show();
|
||||
button_create_script->show();
|
||||
button_extend_script->hide();
|
||||
button_detach_script->hide();
|
||||
}
|
||||
} else {
|
||||
button_create_script->hide();
|
||||
Array selection = editor_selection->get_selected_nodes();
|
||||
for (int i = 0; i < selection.size(); i++) {
|
||||
Node *n = Object::cast_to<Node>(selection[i]);
|
||||
if (!n->get_script().is_null()) {
|
||||
button_detach_script->show();
|
||||
return;
|
||||
Ref<Script> s = n->get_script();
|
||||
if (s.is_valid()) {
|
||||
if (ScriptServer::get_global_class_name(s->get_path()) != StringName()) {
|
||||
button_extend_script->show();
|
||||
button_detach_script->hide();
|
||||
return;
|
||||
} else {
|
||||
button_extend_script->hide();
|
||||
button_detach_script->show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
button_extend_script->hide();
|
||||
button_detach_script->hide();
|
||||
}
|
||||
}
|
||||
|
@ -2295,6 +2358,11 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop
|
|||
List<PropertyInfo> pinfo;
|
||||
n->get_property_list(&pinfo);
|
||||
|
||||
Ref<Script> s = n->get_script();
|
||||
if (s.is_valid() && ScriptServer::get_global_class_name(s->get_path()) != StringName()) {
|
||||
n->set_script(RefPtr());
|
||||
}
|
||||
|
||||
for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
|
||||
if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
|
||||
continue;
|
||||
|
@ -2681,7 +2749,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
|
|||
menu->clear();
|
||||
|
||||
Ref<Script> existing_script;
|
||||
bool exisiting_script_removable = true;
|
||||
bool existing_script_removable = true;
|
||||
if (selection.size() == 1) {
|
||||
Node *selected = selection[0];
|
||||
|
||||
|
@ -2702,8 +2770,9 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
|
|||
|
||||
existing_script = selected->get_script();
|
||||
|
||||
if (EditorNode::get_singleton()->get_object_custom_type_base(selected) == existing_script) {
|
||||
exisiting_script_removable = false;
|
||||
if (EditorNode::get_singleton()->get_object_custom_type_base(selected) == existing_script ||
|
||||
(existing_script.is_valid() && ScriptServer::get_global_class_name(existing_script->get_path()) != StringName())) {
|
||||
existing_script_removable = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2726,7 +2795,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
|
|||
menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_EXTEND_SCRIPT);
|
||||
}
|
||||
}
|
||||
if (existing_script.is_valid() && exisiting_script_removable) {
|
||||
if (existing_script.is_valid() && existing_script_removable) {
|
||||
add_separator = true;
|
||||
menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/detach_script"), TOOL_DETACH_SCRIPT);
|
||||
} else if (full_selection.size() > 1) {
|
||||
|
@ -2905,7 +2974,7 @@ void SceneTreeDock::attach_script_to_selected(bool p_extend) {
|
|||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||
ScriptLanguage *l = ScriptServer::get_language(i);
|
||||
if (l->get_type() == existing->get_class()) {
|
||||
String name = l->get_global_class_name(existing->get_path());
|
||||
String name = ScriptServer::get_global_class_name(existing->get_path());
|
||||
if (ScriptServer::is_global_class(name) && EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) {
|
||||
inherits = name;
|
||||
} else if (l->can_inherit_from_file()) {
|
||||
|
@ -3251,6 +3320,13 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
|
|||
filter_hbc->add_child(button_create_script);
|
||||
button_create_script->hide();
|
||||
|
||||
button_extend_script = memnew(ToolButton);
|
||||
button_extend_script->connect("pressed", this, "_tool_selected", make_binds(TOOL_EXTEND_SCRIPT, false));
|
||||
button_extend_script->set_tooltip(TTR("Attach a new script extending the selected node's script."));
|
||||
button_extend_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/extend_script"));
|
||||
filter_hbc->add_child(button_extend_script);
|
||||
button_extend_script->hide();
|
||||
|
||||
button_detach_script = memnew(ToolButton);
|
||||
button_detach_script->connect("pressed", this, "_tool_selected", make_binds(TOOL_DETACH_SCRIPT, false));
|
||||
button_detach_script->set_tooltip(TTR("Detach the script from the selected node."));
|
||||
|
@ -3380,6 +3456,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
|
|||
|
||||
EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true);
|
||||
EDITOR_DEF("interface/editors/derive_script_globals_by_name", true);
|
||||
EDITOR_DEF("interface/editors/create_dialog_hide_script_class_filepath", false);
|
||||
EDITOR_DEF("_use_favorites_root_selection", false);
|
||||
}
|
||||
|
||||
|
|
|
@ -117,6 +117,7 @@ class SceneTreeDock : public VBoxContainer {
|
|||
ToolButton *button_add;
|
||||
ToolButton *button_instance;
|
||||
ToolButton *button_create_script;
|
||||
ToolButton *button_extend_script;
|
||||
ToolButton *button_detach_script;
|
||||
|
||||
Button *button_2d;
|
||||
|
|
|
@ -208,15 +208,17 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent, bool p_scroll
|
|||
Color accent = get_color("accent_color", "Editor");
|
||||
|
||||
Ref<Script> script = p_node->get_script();
|
||||
if (!script.is_null() && EditorNode::get_singleton()->get_object_custom_type_base(p_node) != script) {
|
||||
if (script.is_valid() &&
|
||||
ScriptServer::get_global_class_name(script->get_path()) == StringName() &&
|
||||
EditorNode::get_singleton()->get_object_custom_type_base(p_node) != script) {
|
||||
//has script
|
||||
item->add_button(0, get_icon("Script", "EditorIcons"), BUTTON_SCRIPT);
|
||||
} else {
|
||||
//has no script (or script is a custom type)
|
||||
//has no script (or script is a custom type / script class)
|
||||
item->set_custom_color(0, get_color("disabled_font_color", "Editor"));
|
||||
item->set_selectable(0, false);
|
||||
|
||||
if (!script.is_null()) { // make sure to mark the script if a custom type
|
||||
if (script.is_valid()) { // make sure to mark the script if a custom type or script class
|
||||
item->add_button(0, get_icon("Script", "EditorIcons"), BUTTON_SCRIPT);
|
||||
item->set_button_disabled(0, item->get_button_count(0) - 1, true);
|
||||
}
|
||||
|
@ -333,7 +335,8 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent, bool p_scroll
|
|||
Ref<Script> script = p_node->get_script();
|
||||
if (!script.is_null()) {
|
||||
item->add_button(0, get_icon("Script", "EditorIcons"), BUTTON_SCRIPT, false, TTR("Open Script:") + " " + script->get_path());
|
||||
if (EditorNode::get_singleton()->get_object_custom_type_base(p_node) == script) {
|
||||
if (EditorNode::get_singleton()->get_object_custom_type_base(p_node) == script ||
|
||||
ScriptServer::get_global_class_name(script->get_path()) != StringName()) {
|
||||
item->set_button_color(0, item->get_button_count(0) - 1, Color(1, 1, 1, 0.5));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -131,6 +131,7 @@ typedef struct {
|
|||
const char **string_delimiters; // NULL terminated array
|
||||
godot_bool has_named_classes;
|
||||
godot_bool supports_builtin_mode;
|
||||
godot_bool has_delayed_script_class_metadata;
|
||||
|
||||
godot_string (*get_template_source_code)(godot_pluginscript_language_data *p_data, const godot_string *p_class_name, const godot_string *p_base_class_name);
|
||||
godot_bool (*validate)(godot_pluginscript_language_data *p_data, const godot_string *p_script, int *r_line_error, int *r_col_error, godot_string *r_test_error, const godot_string *p_path, godot_pool_string_array *r_functions);
|
||||
|
|
|
@ -146,6 +146,10 @@ bool PluginScriptLanguage::supports_builtin_mode() const {
|
|||
return _desc.supports_builtin_mode;
|
||||
}
|
||||
|
||||
bool PluginScriptLanguage::has_delayed_script_class_metadata() const {
|
||||
return _desc.has_delayed_script_class_metadata;
|
||||
}
|
||||
|
||||
int PluginScriptLanguage::find_function(const String &p_function, const String &p_code) const {
|
||||
if (_desc.find_function) {
|
||||
return _desc.find_function(_data, (godot_string *)&p_function, (godot_string *)&p_code);
|
||||
|
|
|
@ -80,6 +80,7 @@ public:
|
|||
virtual bool has_named_classes() const;
|
||||
virtual bool supports_builtin_mode() const;
|
||||
virtual bool can_inherit_from_file() { return true; }
|
||||
virtual bool has_delayed_script_class_metadata() const;
|
||||
virtual int find_function(const String &p_function, const String &p_code) const;
|
||||
virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const;
|
||||
virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint);
|
||||
|
|
|
@ -4628,19 +4628,41 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
|
|||
Variant constant = static_cast<ConstantNode *>(subexpr)->value;
|
||||
|
||||
if (constant.get_type() == Variant::OBJECT) {
|
||||
StringName class_name;
|
||||
GDScriptNativeClass *native_class = Object::cast_to<GDScriptNativeClass>(constant);
|
||||
|
||||
if (native_class && ClassDB::is_parent_class(native_class->get_name(), "Resource")) {
|
||||
if (native_class) {
|
||||
if (ClassDB::is_parent_class(native_class->get_name(), "Resource")) {
|
||||
class_name = native_class->get_name();
|
||||
} else {
|
||||
current_export = PropertyInfo();
|
||||
_set_error("The export hint isn't a resource type.");
|
||||
}
|
||||
} else {
|
||||
Ref<Script> res_script = constant;
|
||||
StringName script_class;
|
||||
if (res_script.is_valid()) {
|
||||
script_class = res_script->get_language()->get_global_class_name(res_script->get_path());
|
||||
|
||||
if (ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(script_class), "Resource")) {
|
||||
class_name = script_class;
|
||||
} else {
|
||||
current_export = PropertyInfo();
|
||||
_set_error("The exported script does not extend Resource.");
|
||||
}
|
||||
} else {
|
||||
current_export = PropertyInfo();
|
||||
_set_error("The exported script isn't a script class.");
|
||||
}
|
||||
}
|
||||
|
||||
if (class_name != StringName()) {
|
||||
current_export.type = Variant::OBJECT;
|
||||
current_export.hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
|
||||
|
||||
current_export.hint_string = native_class->get_name();
|
||||
current_export.class_name = native_class->get_name();
|
||||
|
||||
} else {
|
||||
current_export = PropertyInfo();
|
||||
_set_error("The export hint isn't a resource type.");
|
||||
current_export.hint_string = class_name;
|
||||
current_export.class_name = class_name;
|
||||
}
|
||||
} else if (constant.get_type() == Variant::DICTIONARY) {
|
||||
// Enumeration
|
||||
|
@ -4924,12 +4946,42 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
|
|||
member._export.hint_string = member.data_type.native_type;
|
||||
member._export.class_name = member.data_type.native_type;
|
||||
} else {
|
||||
_set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line);
|
||||
_set_error(vformat("Invalid native export type. \"%s\" is not a Resource type.", member.data_type.native_type), member.line);
|
||||
return;
|
||||
}
|
||||
} else if (member.data_type.kind == DataType::SCRIPT || member.data_type.kind == DataType::GDSCRIPT) {
|
||||
if (member.data_type.script_type.is_null()) {
|
||||
_set_error("Invalid script export type. Could not load member script value.", member.line);
|
||||
return;
|
||||
}
|
||||
Ref<Script> s = member.data_type.script_type;
|
||||
StringName class_name = s->get_language()->get_global_class_name(s->get_path());
|
||||
if (class_name == StringName()) {
|
||||
_set_error("Invalid script export type. The member is a script that has no global script class name.", member.line);
|
||||
return;
|
||||
}
|
||||
if (!ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(class_name), "Resource")) {
|
||||
_set_error("Invalid script export type. The member is a script class that does not extend a Resource type.", member.line);
|
||||
return;
|
||||
}
|
||||
member._export.type = Variant::OBJECT;
|
||||
member._export.hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
|
||||
member._export.hint_string = class_name;
|
||||
member._export.class_name = class_name;
|
||||
|
||||
} else if (member.data_type.kind == DataType::UNRESOLVED && ScriptServer::is_global_class(member.data_type.native_type)) {
|
||||
StringName class_name = member.data_type.native_type;
|
||||
if (ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(class_name), "Resource")) {
|
||||
member._export.type = Variant::OBJECT;
|
||||
member._export.hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
|
||||
member._export.hint_string = class_name;
|
||||
member._export.class_name = class_name;
|
||||
}
|
||||
#undef SETUP_SCRIPT_EXPORT
|
||||
} else {
|
||||
_set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line);
|
||||
_set_error("Invalid export type. Only built-in types and native or script class Resource types can be exported.", member.line);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -6011,7 +6063,22 @@ GDScriptParser::DataType GDScriptParser::_type_from_property(const PropertyInfo
|
|||
ret.builtin_type = p_property.type;
|
||||
if (p_property.type == Variant::OBJECT) {
|
||||
ret.kind = DataType::NATIVE;
|
||||
ret.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;
|
||||
ret.native_type = "Object";
|
||||
if (p_property.class_name != StringName()) {
|
||||
if (ScriptServer::is_global_class(p_property.class_name)) {
|
||||
String p = ScriptServer::get_global_class_path(p_property.class_name);
|
||||
ret.native_type = ScriptServer::get_global_class_native_base(p_property.class_name);
|
||||
if (GDScriptLanguage::get_singleton()->get_extension() == p.get_extension()) {
|
||||
ret.kind = DataType::GDSCRIPT;
|
||||
ret.script_type = ResourceLoader::load(p, "GDScript");
|
||||
} else {
|
||||
ret.kind = DataType::SCRIPT;
|
||||
ret.script_type = ResourceLoader::load(p, "Script");
|
||||
}
|
||||
} else {
|
||||
ret.native_type = p_property.class_name;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.kind = DataType::BUILTIN;
|
||||
}
|
||||
|
@ -7914,13 +7981,25 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
|
|||
}
|
||||
|
||||
// Check export hint
|
||||
if (v.data_type.has_type && v._export.type != Variant::NIL) {
|
||||
if (v._export.type != Variant::NIL) {
|
||||
DataType export_type = _type_from_property(v._export);
|
||||
if (!_is_type_compatible(v.data_type, export_type, true)) {
|
||||
_set_error("The export hint's type (" + export_type.to_string() + ") doesn't match the variable's type (" +
|
||||
v.data_type.to_string() + ").",
|
||||
v.line);
|
||||
return;
|
||||
|
||||
if (export_type.kind == DataType::GDSCRIPT || export_type.kind == DataType::SCRIPT) {
|
||||
String class_name = v._export.class_name;
|
||||
if (ScriptServer::is_global_class(class_name)) {
|
||||
class_name = ScriptServer::get_global_class_native_base(class_name);
|
||||
}
|
||||
if (!ClassDB::is_parent_class(class_name, "Resource")) {
|
||||
_set_error(vformat("Exported script-defined type (%s) must inherit from Resource.", export_type.to_string()), v.line);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (v.data_type.has_type) {
|
||||
if (!_is_type_compatible(v.data_type, export_type, true)) {
|
||||
_set_error(vformat("The export hint's type (%s) doesn't match the variable's type (%s).", export_type.to_string(), v.data_type.to_string()), v.line);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -522,6 +522,37 @@ String CSharpLanguage::_get_indentation() const {
|
|||
return "\t";
|
||||
}
|
||||
|
||||
bool CSharpLanguage::handles_global_class_type(const String &p_type) const {
|
||||
return p_type == "CSharpScript";
|
||||
}
|
||||
|
||||
String CSharpLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
|
||||
Ref<CSharpScript> script = ResourceLoader::load(p_path, "CSharpScript");
|
||||
if (script.is_valid()) {
|
||||
String name = script->get_script_class_name();
|
||||
if (name.length()) {
|
||||
if (r_base_type) {
|
||||
StringName base_name = script->get_script_class_base();
|
||||
if (base_name != StringName()) {
|
||||
*r_base_type = base_name;
|
||||
} else {
|
||||
*r_base_type = script->get_instance_base_type();
|
||||
}
|
||||
}
|
||||
if (r_icon_path) {
|
||||
*r_icon_path = script->get_script_class_icon_path();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
if (r_base_type)
|
||||
*r_base_type = String();
|
||||
if (r_icon_path)
|
||||
*r_icon_path = String();
|
||||
return String();
|
||||
}
|
||||
|
||||
String CSharpLanguage::debug_get_error() const {
|
||||
return _debug_error;
|
||||
}
|
||||
|
@ -911,6 +942,11 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
#endif
|
||||
script->signals_invalidated = true;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorFileSystem::get_singleton()->remove_compiled_lang_script_class_file_cache(script->get_path());
|
||||
EditorNode::get_editor_data().script_class_set_icon_path(script->get_script_class_name(), script->get_script_class_icon_path());
|
||||
#endif
|
||||
|
||||
script->reload(p_soft_reload);
|
||||
script->update_exports();
|
||||
|
||||
|
@ -1010,6 +1046,11 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
to_reload_state.push_back(script);
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
ScriptServer::save_global_classes();
|
||||
EditorNode::get_editor_data().script_class_save_icon_paths();
|
||||
#endif
|
||||
|
||||
for (List<Ref<CSharpScript>>::Element *E = to_reload_state.front(); E; E = E->next()) {
|
||||
Ref<CSharpScript> script = E->get();
|
||||
|
||||
|
@ -2583,7 +2624,9 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
|
|||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
int hint_res = _try_get_member_export_hint(p_member, type, variant_type, /* allow_generics: */ true, hint, hint_string);
|
||||
PropertyHint given_hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr));
|
||||
String given_hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
|
||||
int hint_res = _try_get_member_export_hint(p_member, type, variant_type, given_hint, given_hint_string, /* allow_generics: */ true, hint, hint_string);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(hint_res == -1, false,
|
||||
"Error while trying to determine information about the exported member: '" +
|
||||
|
@ -2604,7 +2647,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
|
|||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) {
|
||||
int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, PropertyHint p_given_hint, String p_given_hint_string, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) {
|
||||
GD_MONO_ASSERT_THREAD_ATTACHED;
|
||||
|
||||
if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) {
|
||||
|
@ -2665,6 +2708,16 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
|
|||
|
||||
r_hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
r_hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class);
|
||||
|
||||
if (p_type.type_class->has_attribute(CACHED_CLASS(GlobalAttribute))) {
|
||||
MonoObject *attr = p_type.type_class->get_attribute(CACHED_CLASS(GlobalAttribute));
|
||||
StringName script_class_name = CACHED_FIELD(GlobalAttribute, name)->get_string_value(attr);
|
||||
if (script_class_name != StringName()) {
|
||||
r_hint_string = script_class_name;
|
||||
}
|
||||
} else if (!p_given_hint_string.empty()) {
|
||||
r_hint_string = p_given_hint_string;
|
||||
}
|
||||
} else if (p_allow_generics && p_variant_type == Variant::ARRAY) {
|
||||
// Nested arrays are not supported in the inspector
|
||||
|
||||
|
@ -2690,7 +2743,7 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
|
|||
}
|
||||
|
||||
if (!preset_hint) {
|
||||
int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string);
|
||||
int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, p_given_hint, p_given_hint_string, /* allow_generics: */ false, elem_hint, elem_hint_string);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(hint_res == -1, -1, "Error while trying to determine information about the array element type.");
|
||||
|
||||
|
@ -3209,6 +3262,16 @@ Error CSharpScript::reload(bool p_keep_state) {
|
|||
|
||||
load_script_signals(script_class, native);
|
||||
_update_exports();
|
||||
|
||||
_update_global_script_class_settings();
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
// Force file cache update for compiled languages to update script class metadata
|
||||
StringName n = script_class_name;
|
||||
StringName b = script_class_base;
|
||||
EditorFileSystem::get_singleton()->update_file_script_class_metadata(get_path(), n, b, get_language()->get_name(), script_class_icon_path);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
@ -3317,6 +3380,37 @@ Error CSharpScript::load_source_code(const String &p_path) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
void CSharpScript::_update_global_script_class_settings() {
|
||||
// Evaluate script's use of engine "Script Class" system.
|
||||
if (script_class->has_attribute(CACHED_CLASS(GlobalAttribute))) {
|
||||
MonoObject *attr = script_class->get_attribute(CACHED_CLASS(GlobalAttribute));
|
||||
script_class_name = CACHED_FIELD(GlobalAttribute, name)->get_string_value(attr);
|
||||
script_class_icon_path = CACHED_FIELD(GlobalAttribute, iconPath)->get_string_value(attr);
|
||||
if (script_class_name.empty()) {
|
||||
script_class_name = script_class->get_name();
|
||||
}
|
||||
} else {
|
||||
script_class_name = String();
|
||||
script_class_icon_path = String();
|
||||
}
|
||||
|
||||
GDMonoClass *parent = script_class->get_parent_class();
|
||||
while (parent) {
|
||||
if (parent->has_attribute(CACHED_CLASS(GlobalAttribute))) {
|
||||
MonoObject *attr = parent->get_attribute(CACHED_CLASS(GlobalAttribute));
|
||||
script_class_base = CACHED_FIELD(GlobalAttribute, name)->get_string_value(attr);
|
||||
if (script_class_base.empty()) {
|
||||
script_class_base = script_class->get_name();
|
||||
}
|
||||
break;
|
||||
}
|
||||
parent = parent->get_parent_class();
|
||||
}
|
||||
if (script_class_base.empty()) {
|
||||
script_class_base = get_instance_base_type();
|
||||
}
|
||||
}
|
||||
|
||||
StringName CSharpScript::get_script_name() const {
|
||||
return name;
|
||||
}
|
||||
|
|
|
@ -103,6 +103,11 @@ class CSharpScript : public Script {
|
|||
String source;
|
||||
StringName name;
|
||||
|
||||
// For engine "Script Class" support, not affiliated with `GDMonoClass *script_class` property.
|
||||
String script_class_name;
|
||||
String script_class_base;
|
||||
String script_class_icon_path;
|
||||
|
||||
SelfList<CSharpScript> script_list;
|
||||
|
||||
struct Argument {
|
||||
|
@ -140,7 +145,7 @@ class CSharpScript : public Script {
|
|||
|
||||
bool _get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported);
|
||||
#ifdef TOOLS_ENABLED
|
||||
static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
|
||||
static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, PropertyHint p_given_hint, String p_given_hint_string, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
|
||||
#endif
|
||||
|
||||
CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error);
|
||||
|
@ -202,6 +207,11 @@ public:
|
|||
|
||||
Error load_source_code(const String &p_path);
|
||||
|
||||
String get_script_class_name() const { return script_class_name; }
|
||||
String get_script_class_base() const { return script_class_base; }
|
||||
String get_script_class_icon_path() const { return script_class_icon_path; }
|
||||
void _update_global_script_class_settings();
|
||||
|
||||
StringName get_script_name() const;
|
||||
|
||||
CSharpScript();
|
||||
|
@ -419,6 +429,11 @@ public:
|
|||
virtual String _get_indentation() const;
|
||||
/* TODO? */ virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {}
|
||||
/* TODO */ virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) {}
|
||||
virtual bool has_delayed_script_class_metadata() const override { return true; }
|
||||
|
||||
/* SCRIPT CLASS FUNCTIONS */
|
||||
virtual bool handles_global_class_type(const String &p_type) const;
|
||||
virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL, String *r_icon_path = NULL) const;
|
||||
|
||||
/* DEBUGGER FUNCTIONS */
|
||||
virtual String debug_get_error() const;
|
||||
|
|
|
@ -13,5 +13,20 @@ namespace Godot
|
|||
this.hint = hint;
|
||||
this.hintString = hintString;
|
||||
}
|
||||
|
||||
public ExportAttribute(string className)
|
||||
{
|
||||
if (ClassDB.ClassExists(className) || ScriptServer.IsGlobalClass(className))
|
||||
{
|
||||
this.hint = PropertyHint.ResourceType;
|
||||
this.hintString = className;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.hint = PropertyHint.None;
|
||||
this.hintString = "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
|
||||
namespace Godot
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class GlobalAttribute : Attribute
|
||||
{
|
||||
private string name;
|
||||
private string iconPath;
|
||||
|
||||
public GlobalAttribute(string name = "", string iconPath = "")
|
||||
{
|
||||
this.name = name;
|
||||
this.iconPath = iconPath;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
<Compile Include="Core\AABB.cs" />
|
||||
<Compile Include="Core\Array.cs" />
|
||||
<Compile Include="Core\Attributes\ExportAttribute.cs" />
|
||||
<Compile Include="Core\Attributes\GlobalAttribute.cs" />
|
||||
<Compile Include="Core\Attributes\GodotMethodAttribute.cs" />
|
||||
<Compile Include="Core\Attributes\RPCAttributes.cs" />
|
||||
<Compile Include="Core\Attributes\SignalAttribute.cs" />
|
||||
|
|
|
@ -132,6 +132,9 @@ void CachedData::clear_godot_api_cache() {
|
|||
class_ExportAttribute = NULL;
|
||||
field_ExportAttribute_hint = NULL;
|
||||
field_ExportAttribute_hintString = NULL;
|
||||
class_GlobalAttribute = NULL;
|
||||
field_GlobalAttribute_name = NULL;
|
||||
field_GlobalAttribute_iconPath = NULL;
|
||||
class_SignalAttribute = NULL;
|
||||
class_ToolAttribute = NULL;
|
||||
class_RemoteAttribute = NULL;
|
||||
|
@ -245,6 +248,9 @@ void update_godot_api_cache() {
|
|||
CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
|
||||
CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
|
||||
CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString"));
|
||||
CACHE_CLASS_AND_CHECK(GlobalAttribute, GODOT_API_CLASS(GlobalAttribute));
|
||||
CACHE_FIELD_AND_CHECK(GlobalAttribute, name, CACHED_CLASS(GlobalAttribute)->get_field("name"));
|
||||
CACHE_FIELD_AND_CHECK(GlobalAttribute, iconPath, CACHED_CLASS(GlobalAttribute)->get_field("iconPath"));
|
||||
CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute));
|
||||
CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
|
||||
CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute));
|
||||
|
|
|
@ -103,6 +103,9 @@ struct CachedData {
|
|||
GDMonoClass *class_ExportAttribute;
|
||||
GDMonoField *field_ExportAttribute_hint;
|
||||
GDMonoField *field_ExportAttribute_hintString;
|
||||
GDMonoClass *class_GlobalAttribute;
|
||||
GDMonoField *field_GlobalAttribute_name;
|
||||
GDMonoField *field_GlobalAttribute_iconPath;
|
||||
GDMonoClass *class_SignalAttribute;
|
||||
GDMonoClass *class_ToolAttribute;
|
||||
GDMonoClass *class_RemoteAttribute;
|
||||
|
|
|
@ -165,6 +165,18 @@
|
|||
Returns a node's position in pixels.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_script_class_icon_path">
|
||||
<return type="String" />
|
||||
<description>
|
||||
Returns this [VisualScript]'s global class icon path provided to the [ScriptServer].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_script_class_name">
|
||||
<return type="String" />
|
||||
<description>
|
||||
Returns this [VisualScript]'s global class name provided to the [ScriptServer].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_variable_default_value" qualifiers="const">
|
||||
<return type="Variant" />
|
||||
<argument index="0" name="name" type="String" />
|
||||
|
@ -334,6 +346,20 @@
|
|||
Position a node on the screen.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_script_class_icon_path">
|
||||
<return type="void" />
|
||||
<argument index="0" name="script_class_icon_path" type="String" />
|
||||
<description>
|
||||
Assigns the global class icon path that this [VisualScript] provides to the [ScriptServer].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_script_class_name">
|
||||
<return type="void" />
|
||||
<argument index="0" name="script_class_name" type="String" />
|
||||
<description>
|
||||
Assigns the global class name that this [VisualScript] provides to the [ScriptServer].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_variable_default_value">
|
||||
<return type="void" />
|
||||
<argument index="0" name="name" type="String" />
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="VisualScriptScriptClass" inherits="VisualScriptNode" version="3.5">
|
||||
<brief_description>
|
||||
Gets a global script class resource.
|
||||
</brief_description>
|
||||
<description>
|
||||
This node returns a global script class with a given name. See the [ScriptServer] class for more information.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="script_class" type="Script" setter="set_script_class" getter="get_script_class">
|
||||
The global script class resource.
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
</constants>
|
||||
</class>
|
|
@ -67,6 +67,7 @@ void register_visual_script_types() {
|
|||
ClassDB::register_class<VisualScriptClassConstant>();
|
||||
ClassDB::register_class<VisualScriptMathConstant>();
|
||||
ClassDB::register_class<VisualScriptBasicTypeConstant>();
|
||||
ClassDB::register_class<VisualScriptScriptClass>();
|
||||
ClassDB::register_class<VisualScriptEngineSingleton>();
|
||||
ClassDB::register_class<VisualScriptSceneNode>();
|
||||
ClassDB::register_class<VisualScriptSceneTree>();
|
||||
|
|
|
@ -1112,6 +1112,13 @@ void VisualScript::_set_data(const Dictionary &p_data) {
|
|||
}
|
||||
}
|
||||
|
||||
if (d.has("script_class_name")) {
|
||||
script_class_name = d["script_class_name"];
|
||||
}
|
||||
if (d.has("script_class_icon_path")) {
|
||||
script_class_icon_path = d["script_class_icon_path"];
|
||||
}
|
||||
|
||||
if (d.has("is_tool_script")) {
|
||||
is_tool_script = d["is_tool_script"];
|
||||
} else {
|
||||
|
@ -1194,9 +1201,35 @@ Dictionary VisualScript::_get_data() const {
|
|||
d["is_tool_script"] = is_tool_script;
|
||||
d["vs_unify"] = true;
|
||||
|
||||
if (ScriptServer::is_global_class(script_class_name)) {
|
||||
d["script_class_name"] = script_class_name;
|
||||
d["script_class_icon_path"] = script_class_icon_path;
|
||||
} else {
|
||||
d["script_class_name"] = "";
|
||||
d["script_class_icon_path"] = "";
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
String VisualScript::get_script_class_name() {
|
||||
return script_class_name;
|
||||
}
|
||||
|
||||
String VisualScript::get_script_class_icon_path() {
|
||||
return script_class_icon_path;
|
||||
}
|
||||
|
||||
void VisualScript::set_script_class_name(const String &p_name) {
|
||||
if (p_name.is_valid_identifier()) {
|
||||
script_class_name = p_name;
|
||||
}
|
||||
}
|
||||
|
||||
void VisualScript::set_script_class_icon_path(const String &p_path) {
|
||||
script_class_icon_path = p_path;
|
||||
}
|
||||
|
||||
void VisualScript::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_node_ports_changed"), &VisualScript::_node_ports_changed);
|
||||
|
||||
|
@ -1256,6 +1289,11 @@ void VisualScript::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("_set_data", "data"), &VisualScript::_set_data);
|
||||
ClassDB::bind_method(D_METHOD("_get_data"), &VisualScript::_get_data);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_script_class_name", "script_class_name"), &VisualScript::set_script_class_name);
|
||||
ClassDB::bind_method(D_METHOD("get_script_class_name"), &VisualScript::get_script_class_name);
|
||||
ClassDB::bind_method(D_METHOD("set_script_class_icon_path", "script_class_icon_path"), &VisualScript::set_script_class_icon_path);
|
||||
ClassDB::bind_method(D_METHOD("get_script_class_icon_path"), &VisualScript::get_script_class_icon_path);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("node_ports_changed", PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::INT, "id")));
|
||||
|
@ -2306,6 +2344,27 @@ Error VisualScriptLanguage::execute_file(const String &p_path) {
|
|||
void VisualScriptLanguage::finish() {
|
||||
}
|
||||
|
||||
bool VisualScriptLanguage::handles_global_class_type(const String &p_type) const {
|
||||
return p_type == "VisualScript";
|
||||
}
|
||||
|
||||
String VisualScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
|
||||
Ref<VisualScript> script = ResourceLoader::load(p_path, "VisualScript");
|
||||
if (script.is_null()) {
|
||||
if (r_base_type)
|
||||
*r_base_type = String();
|
||||
if (r_icon_path)
|
||||
*r_icon_path = String();
|
||||
return String();
|
||||
}
|
||||
|
||||
if (r_base_type)
|
||||
*r_base_type = script->get_instance_base_type();
|
||||
if (r_icon_path)
|
||||
*r_icon_path = script->get_script_class_icon_path();
|
||||
return script->get_script_class_name().is_valid_identifier() ? script->get_script_class_name() : "";
|
||||
}
|
||||
|
||||
/* EDITOR FUNCTIONS */
|
||||
void VisualScriptLanguage::get_reserved_words(List<String> *p_words) const {
|
||||
}
|
||||
|
|
|
@ -241,6 +241,8 @@ private:
|
|||
Map<Object *, VisualScriptInstance *> instances;
|
||||
|
||||
bool is_tool_script;
|
||||
String script_class_name;
|
||||
String script_class_icon_path;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Set<PlaceHolderScriptInstance *> placeholders;
|
||||
|
@ -360,6 +362,11 @@ public:
|
|||
virtual bool are_subnodes_edited() const;
|
||||
#endif
|
||||
|
||||
String get_script_class_name();
|
||||
String get_script_class_icon_path();
|
||||
void set_script_class_name(const String &p_name);
|
||||
void set_script_class_icon_path(const String &p_path);
|
||||
|
||||
VisualScript();
|
||||
~VisualScript();
|
||||
};
|
||||
|
@ -556,6 +563,9 @@ public:
|
|||
virtual Error execute_file(const String &p_path);
|
||||
virtual void finish();
|
||||
|
||||
virtual bool handles_global_class_type(const String &p_type) const;
|
||||
virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL, String *r_icon_path = NULL) const;
|
||||
|
||||
/* EDITOR FUNCTIONS */
|
||||
virtual void get_reserved_words(List<String> *p_words) const;
|
||||
virtual bool is_control_flow_keyword(String p_keyword) const;
|
||||
|
|
|
@ -1277,6 +1277,18 @@ void VisualScriptEditor::_member_edited() {
|
|||
}
|
||||
}
|
||||
|
||||
void VisualScriptEditor::_script_class_icon_btn_gui_input(Ref<InputEvent> p_event) {
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
if (mb.is_valid() && !mb->is_pressed()) {
|
||||
int index = mb->get_button_index();
|
||||
if (index == BUTTON_LEFT) {
|
||||
_script_class_icon_path_dialog->popup_centered_ratio();
|
||||
} else if (index == BUTTON_RIGHT) {
|
||||
_script_class_icon_btn_set_icon("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VisualScriptEditor::_create_function_dialog() {
|
||||
function_create_dialog->popup_centered();
|
||||
func_name_box->set_text("");
|
||||
|
@ -2464,6 +2476,12 @@ void VisualScriptEditor::set_edited_resource(const RES &p_res) {
|
|||
script->set_edited(true); //so that if a function was added it's saved
|
||||
}
|
||||
|
||||
_script_class_name_edit->set_text(script->get_script_class_name());
|
||||
String icon_path = script->get_script_class_icon_path();
|
||||
Control *gui_base = EditorNode::get_singleton()->get_gui_base();
|
||||
RES icon = FileAccess::exists(icon_path) ? ResourceLoader::load(icon_path, "Texture") : (RES)gui_base->get_icon("Nil", "EditorIcons");
|
||||
_script_class_icon_btn->set_icon(icon);
|
||||
|
||||
_update_graph();
|
||||
call_deferred("_update_members");
|
||||
}
|
||||
|
@ -4664,6 +4682,42 @@ void VisualScriptEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter
|
|||
void VisualScriptEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
|
||||
}
|
||||
|
||||
void VisualScriptEditor::_script_class_icon_path_dialog_confirmed() {
|
||||
const String &path = _script_class_icon_path_dialog->get_current_path();
|
||||
_script_class_icon_btn_set_icon(path);
|
||||
}
|
||||
|
||||
void VisualScriptEditor::_script_class_icon_path_dialog_file_selected(const String &p_path) {
|
||||
_script_class_icon_btn_set_icon(p_path);
|
||||
}
|
||||
|
||||
void VisualScriptEditor::_script_class_icon_btn_set_icon(const String &p_path) {
|
||||
ERR_FAIL_COND(!_script_class_icon_btn);
|
||||
ERR_FAIL_COND_MSG(p_path.length() && !FileAccess::exists(p_path), "The given script class icon path does not exist.");
|
||||
RES icon = FileAccess::exists(p_path) ? ResourceLoader::load(p_path, "Texture") : (RES)EditorNode::get_singleton()->get_gui_base()->get_icon("Nil", "EditorIcons");
|
||||
undo_redo->create_action("Set script class icon");
|
||||
undo_redo->add_do_method(_script_class_icon_btn, "set_button_icon", icon);
|
||||
undo_redo->add_undo_method(_script_class_icon_btn, "set_button_icon", _script_class_icon_btn->get_icon());
|
||||
if (script.is_valid()) {
|
||||
undo_redo->add_do_method(script.operator->(), "set_script_class_icon_path", p_path);
|
||||
undo_redo->add_undo_method(script.operator->(), "set_script_class_icon_path", script->get_script_class_icon_path());
|
||||
}
|
||||
undo_redo->commit_action();
|
||||
set_edited(true);
|
||||
}
|
||||
|
||||
void VisualScriptEditor::_script_class_name_text_changed(const String &p_text) {
|
||||
if (script.is_valid())
|
||||
script->set_script_class_name(p_text);
|
||||
set_edited(true);
|
||||
}
|
||||
|
||||
void VisualScriptEditor::_script_class_icon_path_text_changed(const String &p_text) {
|
||||
if (script.is_valid())
|
||||
script->set_script_class_icon_path(p_text);
|
||||
set_edited(true);
|
||||
}
|
||||
|
||||
void VisualScriptEditor::_bind_methods() {
|
||||
ClassDB::bind_method("_member_button", &VisualScriptEditor::_member_button);
|
||||
ClassDB::bind_method("_member_edited", &VisualScriptEditor::_member_edited);
|
||||
|
@ -4737,6 +4791,12 @@ void VisualScriptEditor::_bind_methods() {
|
|||
ClassDB::bind_method("_draw_color_over_button", &VisualScriptEditor::_draw_color_over_button);
|
||||
|
||||
ClassDB::bind_method("_generic_search", &VisualScriptEditor::_generic_search);
|
||||
|
||||
ClassDB::bind_method("_script_class_icon_path_dialog_confirmed", &VisualScriptEditor::_script_class_icon_path_dialog_confirmed);
|
||||
ClassDB::bind_method("_script_class_icon_path_dialog_file_selected", &VisualScriptEditor::_script_class_icon_path_dialog_file_selected);
|
||||
ClassDB::bind_method("_script_class_name_text_changed", &VisualScriptEditor::_script_class_name_text_changed);
|
||||
ClassDB::bind_method("_script_class_icon_path_text_changed", &VisualScriptEditor::_script_class_icon_path_text_changed);
|
||||
ClassDB::bind_method("_script_class_icon_btn_gui_input", &VisualScriptEditor::_script_class_icon_btn_gui_input);
|
||||
}
|
||||
|
||||
VisualScriptEditor::VisualScriptEditor() {
|
||||
|
@ -4772,6 +4832,37 @@ VisualScriptEditor::VisualScriptEditor() {
|
|||
members_section->add_child(tool_script_check);
|
||||
tool_script_check->connect("pressed", this, "_toggle_tool_script");
|
||||
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
hb->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
Label *lbl = memnew(Label);
|
||||
lbl->set_text(TTR("Class Name:"));
|
||||
hb->add_child(lbl);
|
||||
LineEdit *le = memnew(LineEdit);
|
||||
le->set_tooltip(TTR("Script Class names must be valid identifiers. Alphanumeric characters and underscores not starting with a number."));
|
||||
le->connect("text_changed", this, "_script_class_name_text_changed");
|
||||
le->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
hb->add_child(le);
|
||||
_script_class_name_edit = le;
|
||||
_script_class_icon_btn = memnew(Button);
|
||||
_script_class_icon_btn->set_tooltip(TTR("Choose a 16x16 icon for the class. Right-click to clear."));
|
||||
FileDialog *fd = memnew(FileDialog);
|
||||
fd->set_mode(FileDialog::MODE_OPEN_FILE);
|
||||
List<String> texture_type_exts;
|
||||
ResourceLoader::get_recognized_extensions_for_type("Texture", &texture_type_exts);
|
||||
for (List<String>::Element *E = texture_type_exts.front(); E; E = E->next()) {
|
||||
fd->add_filter(vformat("*.%s", E->get()));
|
||||
}
|
||||
fd->set_enable_multiple_selection(false);
|
||||
fd->set_show_hidden_files(false);
|
||||
fd->set_title("Choose an image.");
|
||||
fd->connect("confirmed", this, "_script_class_icon_path_dialog_confirmed");
|
||||
fd->connect("file_selected", this, "_script_class_icon_path_dialog_file_selected");
|
||||
_script_class_icon_btn->add_child(fd);
|
||||
_script_class_icon_path_dialog = fd;
|
||||
_script_class_icon_btn->connect("gui_input", this, "_script_class_icon_btn_gui_input");
|
||||
hb->add_child(_script_class_icon_btn);
|
||||
members_section->add_child(hb);
|
||||
|
||||
/// Members ///
|
||||
|
||||
members = memnew(Tree);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "editor/create_dialog.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/property_editor.h"
|
||||
#include "scene/gui/file_dialog.h"
|
||||
#include "scene/gui/graph_edit.h"
|
||||
#include "visual_script.h"
|
||||
#include "visual_script_property_selector.h"
|
||||
|
@ -172,6 +173,10 @@ class VisualScriptEditor : public ScriptEditorBase {
|
|||
Vector2 port_action_pos;
|
||||
int port_action_new_node;
|
||||
|
||||
LineEdit *_script_class_name_edit;
|
||||
Button *_script_class_icon_btn;
|
||||
FileDialog *_script_class_icon_path_dialog;
|
||||
|
||||
bool saved_pos_dirty;
|
||||
Vector2 saved_position;
|
||||
|
||||
|
@ -202,6 +207,7 @@ class VisualScriptEditor : public ScriptEditorBase {
|
|||
void _toggle_tool_script();
|
||||
void _member_selected();
|
||||
void _member_edited();
|
||||
void _script_class_icon_btn_gui_input(Ref<InputEvent> p_event);
|
||||
|
||||
void _begin_node_move();
|
||||
void _end_node_move();
|
||||
|
@ -288,6 +294,12 @@ class VisualScriptEditor : public ScriptEditorBase {
|
|||
void _member_rmb_selected(const Vector2 &p_pos);
|
||||
void _member_option(int p_option);
|
||||
|
||||
void _script_class_icon_path_dialog_confirmed();
|
||||
void _script_class_icon_path_dialog_file_selected(const String &p_path);
|
||||
void _script_class_icon_btn_set_icon(const String &p_path);
|
||||
void _script_class_name_text_changed(const String &p_text);
|
||||
void _script_class_icon_path_text_changed(const String &p_text);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
|
|
@ -2151,6 +2151,118 @@ VisualScriptMathConstant::VisualScriptMathConstant() {
|
|||
constant = MATH_CONSTANT_ONE;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
////////////////SCRIPTCLASS///////////////
|
||||
//////////////////////////////////////////
|
||||
|
||||
int VisualScriptScriptClass::get_output_sequence_port_count() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool VisualScriptScriptClass::has_input_sequence_port() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
int VisualScriptScriptClass::get_input_value_port_count() const {
|
||||
return 0;
|
||||
}
|
||||
int VisualScriptScriptClass::get_output_value_port_count() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
String VisualScriptScriptClass::get_output_sequence_port_text(int p_port) const {
|
||||
return String();
|
||||
}
|
||||
|
||||
PropertyInfo VisualScriptScriptClass::get_input_value_port_info(int p_idx) const {
|
||||
return PropertyInfo();
|
||||
}
|
||||
|
||||
PropertyInfo VisualScriptScriptClass::get_output_value_port_info(int p_idx) const {
|
||||
if (ScriptServer::is_global_class(script_class)) {
|
||||
Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(script_class), "Script");
|
||||
return PropertyInfo(Variant::OBJECT, script_class, PROPERTY_HINT_RESOURCE_TYPE, script->get_class(), PROPERTY_USAGE_DEFAULT, script->get_class());
|
||||
}
|
||||
return PropertyInfo(Variant::OBJECT, script_class, PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT, "Script");
|
||||
}
|
||||
|
||||
String VisualScriptScriptClass::get_caption() const {
|
||||
return "Script Class";
|
||||
}
|
||||
|
||||
void VisualScriptScriptClass::set_script_class(const String &p_name) {
|
||||
ERR_FAIL_COND(!ScriptServer::is_global_class(p_name));
|
||||
script_class = p_name;
|
||||
_change_notify();
|
||||
ports_changed_notify();
|
||||
}
|
||||
|
||||
const String &VisualScriptScriptClass::get_script_class() const {
|
||||
return script_class;
|
||||
}
|
||||
|
||||
class VisualScriptNodeInstanceScriptClass : public VisualScriptNodeInstance {
|
||||
public:
|
||||
Ref<Script> script;
|
||||
|
||||
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Variant::CallError &r_error, String &r_error_str) {
|
||||
*p_outputs[0] = script;
|
||||
return OK;
|
||||
}
|
||||
};
|
||||
|
||||
VisualScriptNodeInstance *VisualScriptScriptClass::instance(VisualScriptInstance *p_instance) {
|
||||
VisualScriptNodeInstanceScriptClass *instance = memnew(VisualScriptNodeInstanceScriptClass);
|
||||
instance->script = script_class != StringName() ? ResourceLoader::load(ScriptServer::get_global_class_path(script_class), "Script") : RES();
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool VisualScriptScriptClass::_set(const StringName &p_name, const Variant &p_value) {
|
||||
if (p_name == "script_class") {
|
||||
set_script_class(p_value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VisualScriptScriptClass::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
if (p_name == "script_class") {
|
||||
r_ret = get_script_class();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VisualScriptScriptClass::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
String cc;
|
||||
|
||||
List<StringName> lst;
|
||||
ScriptServer::get_global_class_list(&lst);
|
||||
int i = 0;
|
||||
for (List<StringName>::Element *E = lst.front(); E; E = E->next()) {
|
||||
if (i > 0)
|
||||
cc += ",";
|
||||
i++;
|
||||
cc += E->get();
|
||||
}
|
||||
p_list->push_back(PropertyInfo(Variant::STRING, "script_class", PROPERTY_HINT_ENUM, cc));
|
||||
}
|
||||
|
||||
void VisualScriptScriptClass::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_script_class", "name"), &VisualScriptScriptClass::set_script_class);
|
||||
ClassDB::bind_method(D_METHOD("get_script_class"), &VisualScriptScriptClass::get_script_class);
|
||||
}
|
||||
|
||||
VisualScriptScriptClass::VisualScriptScriptClass() {
|
||||
List<StringName> lst;
|
||||
ScriptServer::get_global_class_list(&lst);
|
||||
script_class = "";
|
||||
if (lst.size()) {
|
||||
StringName name = lst.front()->get();
|
||||
script_class = name;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
////////////////ENGINESINGLETON///////////
|
||||
//////////////////////////////////////////
|
||||
|
@ -3830,6 +3942,7 @@ void register_visual_script_nodes() {
|
|||
VisualScriptLanguage::singleton->add_register_func("constants/class_constant", create_node_generic<VisualScriptClassConstant>);
|
||||
VisualScriptLanguage::singleton->add_register_func("constants/global_constant", create_node_generic<VisualScriptGlobalConstant>);
|
||||
VisualScriptLanguage::singleton->add_register_func("constants/basic_type_constant", create_node_generic<VisualScriptBasicTypeConstant>);
|
||||
VisualScriptLanguage::singleton->add_register_func("constants/script_class", create_node_generic<VisualScriptScriptClass>);
|
||||
|
||||
VisualScriptLanguage::singleton->add_register_func("custom/custom_node", create_node_generic<VisualScriptCustomNode>);
|
||||
VisualScriptLanguage::singleton->add_register_func("custom/sub_call", create_node_generic<VisualScriptSubCall>);
|
||||
|
|
|
@ -593,6 +593,41 @@ public:
|
|||
|
||||
VARIANT_ENUM_CAST(VisualScriptMathConstant::MathConstant)
|
||||
|
||||
class VisualScriptScriptClass : public VisualScriptNode {
|
||||
GDCLASS(VisualScriptScriptClass, VisualScriptNode);
|
||||
|
||||
String script_class;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
virtual int get_output_sequence_port_count() const;
|
||||
virtual bool has_input_sequence_port() const;
|
||||
|
||||
virtual String get_output_sequence_port_text(int p_port) const;
|
||||
|
||||
virtual int get_input_value_port_count() const;
|
||||
virtual int get_output_value_port_count() const;
|
||||
|
||||
virtual PropertyInfo get_input_value_port_info(int p_idx) const;
|
||||
virtual PropertyInfo get_output_value_port_info(int p_idx) const;
|
||||
|
||||
virtual String get_caption() const;
|
||||
virtual String get_category() const { return "constants"; }
|
||||
|
||||
void set_script_class(const String &p_name);
|
||||
const String &get_script_class() const;
|
||||
|
||||
virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance);
|
||||
|
||||
VisualScriptScriptClass();
|
||||
};
|
||||
|
||||
class VisualScriptEngineSingleton : public VisualScriptNode {
|
||||
GDCLASS(VisualScriptEngineSingleton, VisualScriptNode);
|
||||
|
||||
|
|
Loading…
Reference in a new issue