Browse Source

Optimize enumeration of global classes in create dialog and autocomplete

Pedro J. Estébanez 6 months ago
parent
commit
a20934c8e4

+ 1 - 1
editor/connections_dialog.cpp

@@ -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);
 			}
 			}

+ 7 - 8
editor/create_dialog.cpp

@@ -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) {
-				ERR_FAIL_COND(!ResourceLoader::exists(p_type, "Script"));
-				scr = ResourceLoader::load(p_type, "Script");
-			} else {
-				scr = EditorNode::get_editor_data().script_class_load_script(p_type);
-			}
+		if (p_type_category == TypeCategory::PATH_TYPE) {
+			ERR_FAIL_COND(!ResourceLoader::exists(p_type, "Script"));
+			Ref<Script> scr = ResourceLoader::load(p_type, "Script");
 			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)) {

+ 41 - 43
editor/editor_data.cpp

@@ -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 {
-	if (!ScriptServer::is_global_class(p_class)) {
-		return String();
-	}
-
+String EditorData::script_class_get_icon_path(const String &p_class, bool *r_valid) const {
 	String current = p_class;
 	String current = p_class;
-	String ret = _script_class_icon_paths[current];
-	while (ret.is_empty()) {
-		current = script_class_get_base(current);
+	while (true) {
 		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];
+	}
+
+	// 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 = p_script;
+	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());
-		if (base_scr->is_built_in() || class_name == StringName()) {
+		StringName base_class_name = script_class_get_name(base_scr->get_path());
+		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>();
-	return nullptr;
+	_script_icon_cache[p_script_path] = Ref<Texture2D>();
+	return Ref<Texture2D>();
 }
 }
 
 
 void EditorData::clear_script_icon_cache() {
 void EditorData::clear_script_icon_cache() {

+ 3 - 4
editor/editor_data.h

@@ -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();

+ 14 - 13
editor/editor_node.cpp

@@ -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()) {
-		Ref<Texture2D> script_icon = ed.get_script_icon(p_script);
+	if (!p_script_path.is_empty()) {
+		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()) {
-				base_type = p_script->get_instance_base_type();
+			if (ScriptServer::is_global_class(p_class)) {
+				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) {

+ 1 - 1
editor/editor_node.h

@@ -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);