Optimize enumeration of global classes in create dialog and autocomplete

This commit is contained in:
Pedro J. Estébanez 2025-01-28 10:27:56 +01:00
parent 318af42020
commit a20934c8e4
6 changed files with 67 additions and 70 deletions

View file

@ -1446,7 +1446,7 @@ void ConnectionsDock::update_tree() {
doc_class_name = String(); doc_class_name = String();
} }
class_icon = editor_data.get_script_icon(script_base); class_icon = editor_data.get_script_icon(script_base->get_path());
if (class_icon.is_null() && has_theme_icon(native_base, EditorStringName(EditorIcons))) { if (class_icon.is_null() && has_theme_icon(native_base, EditorStringName(EditorIcons))) {
class_icon = get_editor_theme_icon(native_base); class_icon = get_editor_theme_icon(native_base);
} }

View file

@ -257,14 +257,9 @@ void CreateDialog::_add_type(const StringName &p_type, TypeCategory p_type_categ
inherits = ClassDB::get_parent_class(p_type); inherits = ClassDB::get_parent_class(p_type);
inherited_type = TypeCategory::CPP_TYPE; inherited_type = TypeCategory::CPP_TYPE;
} else { } else {
if (p_type_category == TypeCategory::PATH_TYPE || ScriptServer::is_global_class(p_type)) {
Ref<Script> scr;
if (p_type_category == TypeCategory::PATH_TYPE) { if (p_type_category == TypeCategory::PATH_TYPE) {
ERR_FAIL_COND(!ResourceLoader::exists(p_type, "Script")); ERR_FAIL_COND(!ResourceLoader::exists(p_type, "Script"));
scr = ResourceLoader::load(p_type, "Script"); Ref<Script> scr = ResourceLoader::load(p_type, "Script");
} else {
scr = EditorNode::get_editor_data().script_class_load_script(p_type);
}
ERR_FAIL_COND(scr.is_null()); ERR_FAIL_COND(scr.is_null());
Ref<Script> base = scr->get_base_script(); Ref<Script> base = scr->get_base_script();
@ -286,6 +281,10 @@ void CreateDialog::_add_type(const StringName &p_type, TypeCategory p_type_categ
inherited_type = TypeCategory::PATH_TYPE; inherited_type = TypeCategory::PATH_TYPE;
} }
} }
} else if (ScriptServer::is_global_class(p_type)) {
inherits = ScriptServer::get_global_class_base(p_type);
bool is_native_class = ClassDB::class_exists(inherits);
inherited_type = is_native_class ? TypeCategory::CPP_TYPE : TypeCategory::OTHER_TYPE;
} else { } else {
inherits = custom_type_parents[p_type]; inherits = custom_type_parents[p_type];
if (ClassDB::class_exists(inherits)) { if (ClassDB::class_exists(inherits)) {

View file

@ -983,20 +983,6 @@ bool EditorData::script_class_is_parent(const String &p_class, const String &p_i
return true; return true;
} }
StringName EditorData::script_class_get_base(const String &p_class) const {
Ref<Script> script = script_class_load_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 script->get_language()->get_global_class_name(base_script->get_path());
}
Variant EditorData::script_class_instance(const String &p_class) { Variant EditorData::script_class_instance(const String &p_class) {
if (ScriptServer::is_global_class(p_class)) { if (ScriptServer::is_global_class(p_class)) {
Ref<Script> script = script_class_load_script(p_class); Ref<Script> script = script_class_load_script(p_class);
@ -1026,22 +1012,25 @@ void EditorData::script_class_set_icon_path(const String &p_class, const String
_script_class_icon_paths[p_class] = 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 String &p_class, bool *r_valid) const {
if (!ScriptServer::is_global_class(p_class)) {
return String();
}
String current = p_class; String current = p_class;
String ret = _script_class_icon_paths[current]; while (true) {
while (ret.is_empty()) {
current = script_class_get_base(current);
if (!ScriptServer::is_global_class(current)) { if (!ScriptServer::is_global_class(current)) {
// If the classnames chain has a native class ancestor, we're done with success.
if (r_valid) {
*r_valid = ClassDB::class_exists(current);
}
return String(); return String();
} }
ret = _script_class_icon_paths.has(current) ? _script_class_icon_paths[current] : String(); HashMap<StringName, String>::ConstIterator E = _script_class_icon_paths.find(current);
if ((bool)E) {
if (r_valid) {
*r_valid = true;
}
return E->value;
}
current = ScriptServer::get_global_class_base(current);
} }
return ret;
} }
StringName EditorData::script_class_get_name(const String &p_path) const { StringName EditorData::script_class_get_name(const String &p_path) const {
@ -1126,28 +1115,42 @@ Ref<Texture2D> EditorData::_load_script_icon(const String &p_path) const {
return nullptr; return nullptr;
} }
Ref<Texture2D> EditorData::get_script_icon(const Ref<Script> &p_script) { Ref<Texture2D> EditorData::get_script_icon(const String &p_script_path) {
// Take from the local cache, if available. // Take from the local cache, if available.
if (_script_icon_cache.has(p_script)) { if (_script_icon_cache.has(p_script_path)) {
// Can be an empty value if we can't resolve any icon for this script. // Can be an empty value if we can't resolve any icon for this script.
// An empty value is still cached to avoid unnecessary attempts at resolving it again. // An empty value is still cached to avoid unnecessary attempts at resolving it again.
return _script_icon_cache[p_script]; return _script_icon_cache[p_script_path];
} }
Ref<Script> base_scr = p_script; // Fast path in case the whole hierarchy is made of global classes.
StringName class_name = script_class_get_name(p_script_path);
{
if (class_name != StringName()) {
bool icon_valid = false;
String icon_path = script_class_get_icon_path(class_name, &icon_valid);
if (icon_valid) {
Ref<Texture2D> icon = _load_script_icon(icon_path);
_script_icon_cache[p_script_path] = icon;
return icon;
}
}
}
Ref<Script> base_scr = ResourceLoader::load(p_script_path, "Script");
while (base_scr.is_valid()) { while (base_scr.is_valid()) {
// Check for scripted classes. // Check for scripted classes.
String icon_path; String icon_path;
StringName class_name = script_class_get_name(base_scr->get_path()); StringName base_class_name = script_class_get_name(base_scr->get_path());
if (base_scr->is_built_in() || class_name == StringName()) { if (base_scr->is_built_in() || base_class_name == StringName()) {
icon_path = base_scr->get_class_icon_path(); icon_path = base_scr->get_class_icon_path();
} else { } else {
icon_path = script_class_get_icon_path(class_name); icon_path = script_class_get_icon_path(base_class_name);
} }
Ref<Texture2D> icon = _load_script_icon(icon_path); Ref<Texture2D> icon = _load_script_icon(icon_path);
if (icon.is_valid()) { if (icon.is_valid()) {
_script_icon_cache[p_script] = icon; _script_icon_cache[p_script_path] = icon;
return icon; return icon;
} }
@ -1155,7 +1158,7 @@ Ref<Texture2D> EditorData::get_script_icon(const Ref<Script> &p_script) {
// TODO: Should probably be deprecated in 4.x // TODO: Should probably be deprecated in 4.x
const EditorData::CustomType *ctype = get_custom_type_by_path(base_scr->get_path()); const EditorData::CustomType *ctype = get_custom_type_by_path(base_scr->get_path());
if (ctype && ctype->icon.is_valid()) { if (ctype && ctype->icon.is_valid()) {
_script_icon_cache[p_script] = ctype->icon; _script_icon_cache[p_script_path] = ctype->icon;
return ctype->icon; return ctype->icon;
} }
@ -1163,21 +1166,16 @@ Ref<Texture2D> EditorData::get_script_icon(const Ref<Script> &p_script) {
base_scr = base_scr->get_base_script(); base_scr = base_scr->get_base_script();
} }
// No custom icon was found in the inheritance chain, so check the base
// class of the script instead.
String base_type;
p_script->get_language()->get_global_class_name(p_script->get_path(), &base_type);
// Check if the base type is an extension-defined type. // Check if the base type is an extension-defined type.
Ref<Texture2D> ext_icon = extension_class_get_icon(base_type); Ref<Texture2D> ext_icon = extension_class_get_icon(class_name);
if (ext_icon.is_valid()) { if (ext_icon.is_valid()) {
_script_icon_cache[p_script] = ext_icon; _script_icon_cache[p_script_path] = ext_icon;
return ext_icon; return ext_icon;
} }
// If no icon found, cache it as null. // If no icon found, cache it as null.
_script_icon_cache[p_script] = Ref<Texture>(); _script_icon_cache[p_script_path] = Ref<Texture2D>();
return nullptr; return Ref<Texture2D>();
} }
void EditorData::clear_script_icon_cache() { void EditorData::clear_script_icon_cache() {

View file

@ -147,7 +147,7 @@ private:
HashMap<StringName, String> _script_class_icon_paths; HashMap<StringName, String> _script_class_icon_paths;
HashMap<String, StringName> _script_class_file_to_path; HashMap<String, StringName> _script_class_file_to_path;
HashMap<Ref<Script>, Ref<Texture>> _script_icon_cache; HashMap<String, Ref<Texture>> _script_icon_cache;
Ref<Texture2D> _load_script_icon(const String &p_path) const; Ref<Texture2D> _load_script_icon(const String &p_path) const;
@ -241,7 +241,6 @@ public:
void notify_scene_saved(const String &p_path); void notify_scene_saved(const String &p_path);
bool script_class_is_parent(const String &p_class, const String &p_inherits); 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); Variant script_class_instance(const String &p_class);
Ref<Script> script_class_load_script(const String &p_class) const; Ref<Script> script_class_load_script(const String &p_class) const;
@ -249,7 +248,7 @@ public:
StringName script_class_get_name(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); void script_class_set_name(const String &p_path, const StringName &p_class);
String script_class_get_icon_path(const String &p_class) const; String script_class_get_icon_path(const String &p_class, bool *r_valid = nullptr) const;
void script_class_set_icon_path(const String &p_class, const String &p_icon_path); void script_class_set_icon_path(const String &p_class, const String &p_icon_path);
void script_class_clear_icon_paths() { _script_class_icon_paths.clear(); } void script_class_clear_icon_paths() { _script_class_icon_paths.clear(); }
void script_class_save_icon_paths(); void script_class_save_icon_paths();
@ -257,7 +256,7 @@ public:
Ref<Texture2D> extension_class_get_icon(const String &p_class) const; Ref<Texture2D> extension_class_get_icon(const String &p_class) const;
Ref<Texture2D> get_script_icon(const Ref<Script> &p_script); Ref<Texture2D> get_script_icon(const String &p_script_path);
void clear_script_icon_cache(); void clear_script_icon_cache();
EditorData(); EditorData();

View file

@ -4805,13 +4805,13 @@ void EditorNode::_pick_main_scene_custom_action(const String &p_custom_action_na
} }
} }
Ref<Texture2D> EditorNode::_get_class_or_script_icon(const String &p_class, const Ref<Script> &p_script, const String &p_fallback, bool p_fallback_script_to_theme) { Ref<Texture2D> EditorNode::_get_class_or_script_icon(const String &p_class, const String &p_script_path, const String &p_fallback, bool p_fallback_script_to_theme) {
ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty."); ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty.");
EditorData &ed = EditorNode::get_editor_data(); EditorData &ed = EditorNode::get_editor_data();
// Check for a script icon first. // Check for a script icon first.
if (p_script.is_valid()) { if (!p_script_path.is_empty()) {
Ref<Texture2D> script_icon = ed.get_script_icon(p_script); Ref<Texture2D> script_icon = ed.get_script_icon(p_script_path);
if (script_icon.is_valid()) { if (script_icon.is_valid()) {
return script_icon; return script_icon;
} }
@ -4819,14 +4819,15 @@ Ref<Texture2D> EditorNode::_get_class_or_script_icon(const String &p_class, cons
if (p_fallback_script_to_theme) { if (p_fallback_script_to_theme) {
// Look for the native base type in the editor theme. This is relevant for // Look for the native base type in the editor theme. This is relevant for
// scripts extending other scripts and for built-in classes. // scripts extending other scripts and for built-in classes.
String script_class_name = p_script->get_language()->get_global_class_name(p_script->get_path());
String base_type; String base_type;
if (script_class_name.is_empty()) { if (ScriptServer::is_global_class(p_class)) {
base_type = p_script->get_instance_base_type(); base_type = ScriptServer::get_global_class_native_base(p_class);
} else { } else {
base_type = ScriptServer::get_global_class_native_base(script_class_name); Ref<Script> scr = ResourceLoader::load(p_script_path, "Script");
if (scr.is_valid()) {
base_type = scr->get_instance_base_type();
}
} }
if (theme.is_valid() && theme->has_icon(base_type, EditorStringName(EditorIcons))) { if (theme.is_valid() && theme->has_icon(base_type, EditorStringName(EditorIcons))) {
return theme->get_icon(base_type, EditorStringName(EditorIcons)); return theme->get_icon(base_type, EditorStringName(EditorIcons));
} }
@ -4899,21 +4900,21 @@ Ref<Texture2D> EditorNode::get_object_icon(const Object *p_object, const String
if (Object::cast_to<MultiNodeEdit>(p_object)) { if (Object::cast_to<MultiNodeEdit>(p_object)) {
return get_class_icon(Object::cast_to<MultiNodeEdit>(p_object)->get_edited_class_name(), p_fallback); return get_class_icon(Object::cast_to<MultiNodeEdit>(p_object)->get_edited_class_name(), p_fallback);
} else { } else {
return _get_class_or_script_icon(p_object->get_class(), scr, p_fallback); return _get_class_or_script_icon(p_object->get_class(), scr.is_valid() ? scr->get_path() : String(), p_fallback);
} }
} }
Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) { Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p_fallback) {
ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty."); ERR_FAIL_COND_V_MSG(p_class.is_empty(), nullptr, "Class name cannot be empty.");
Ref<Script> scr; String script_path;
if (ScriptServer::is_global_class(p_class)) { if (ScriptServer::is_global_class(p_class)) {
scr = EditorNode::get_editor_data().script_class_load_script(p_class); script_path = ScriptServer::get_global_class_path(p_class);
} else if (ResourceLoader::exists(p_class)) { // If the script is not a class_name we check if the script resource exists. } else if (ResourceLoader::exists(p_class)) { // If the script is not a class_name we check if the script resource exists.
scr = ResourceLoader::load(p_class); script_path = p_class;
} }
return _get_class_or_script_icon(p_class, scr, p_fallback, true); return _get_class_or_script_icon(p_class, script_path, p_fallback, true);
} }
bool EditorNode::is_object_of_custom_type(const Object *p_object, const StringName &p_class) { bool EditorNode::is_object_of_custom_type(const Object *p_object, const StringName &p_class) {

View file

@ -648,7 +648,7 @@ private:
void _feature_profile_changed(); void _feature_profile_changed();
bool _is_class_editor_disabled_by_feature_profile(const StringName &p_class); bool _is_class_editor_disabled_by_feature_profile(const StringName &p_class);
Ref<Texture2D> _get_class_or_script_icon(const String &p_class, const Ref<Script> &p_script, const String &p_fallback = "Object", bool p_fallback_script_to_theme = false); Ref<Texture2D> _get_class_or_script_icon(const String &p_class, const String &p_script_path, const String &p_fallback = "Object", bool p_fallback_script_to_theme = false);
void _pick_main_scene_custom_action(const String &p_custom_action_name); void _pick_main_scene_custom_action(const String &p_custom_action_name);