Implement a quick script inheritance check

Optimizes, simplifies and fixes EditorResourcePicker (was not refreshing custom clases).
This commit is contained in:
Juan Linietsky 2023-01-19 16:47:01 +01:00
parent 14fdd28de9
commit 3dcf380161
5 changed files with 82 additions and 81 deletions

View File

@ -245,9 +245,12 @@ void ScriptServer::thread_exit() {
} }
HashMap<StringName, ScriptServer::GlobalScriptClass> ScriptServer::global_classes; HashMap<StringName, ScriptServer::GlobalScriptClass> ScriptServer::global_classes;
HashMap<StringName, Vector<StringName>> ScriptServer::inheriters_cache;
bool ScriptServer::inheriters_cache_dirty = true;
void ScriptServer::global_classes_clear() { void ScriptServer::global_classes_clear() {
global_classes.clear(); global_classes.clear();
inheriters_cache.clear();
} }
void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) { void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) {
@ -257,16 +260,44 @@ void ScriptServer::add_global_class(const StringName &p_class, const StringName
g.path = p_path; g.path = p_path;
g.base = p_base; g.base = p_base;
global_classes[p_class] = g; global_classes[p_class] = g;
inheriters_cache_dirty = true;
} }
void ScriptServer::remove_global_class(const StringName &p_class) { void ScriptServer::remove_global_class(const StringName &p_class) {
global_classes.erase(p_class); global_classes.erase(p_class);
inheriters_cache_dirty = true;
}
void ScriptServer::get_inheriters_list(const StringName &p_base_type, List<StringName> *r_classes) {
if (inheriters_cache_dirty) {
inheriters_cache.clear();
for (const KeyValue<StringName, GlobalScriptClass> &K : global_classes) {
if (!inheriters_cache.has(K.value.base)) {
inheriters_cache[K.value.base] = Vector<StringName>();
}
inheriters_cache[K.value.base].push_back(K.key);
}
for (KeyValue<StringName, Vector<StringName>> &K : inheriters_cache) {
K.value.sort_custom<StringName::AlphCompare>();
}
inheriters_cache_dirty = false;
}
if (!inheriters_cache.has(p_base_type)) {
return;
}
const Vector<StringName> &v = inheriters_cache[p_base_type];
for (int i = 0; i < v.size(); i++) {
r_classes->push_back(v[i]);
}
} }
void ScriptServer::remove_global_class_by_path(const String &p_path) { void ScriptServer::remove_global_class_by_path(const String &p_path) {
for (const KeyValue<StringName, GlobalScriptClass> &kv : global_classes) { for (const KeyValue<StringName, GlobalScriptClass> &kv : global_classes) {
if (kv.value.path == p_path) { if (kv.value.path == p_path) {
global_classes.erase(kv.key); global_classes.erase(kv.key);
inheriters_cache_dirty = true;
return; return;
} }
} }

View File

@ -56,10 +56,12 @@ class ScriptServer {
struct GlobalScriptClass { struct GlobalScriptClass {
StringName language; StringName language;
String path; String path;
String base; StringName base;
}; };
static HashMap<StringName, GlobalScriptClass> global_classes; static HashMap<StringName, GlobalScriptClass> global_classes;
static HashMap<StringName, Vector<StringName>> inheriters_cache;
static bool inheriters_cache_dirty;
public: public:
static ScriptEditRequestFunction edit_request_func; static ScriptEditRequestFunction edit_request_func;
@ -87,6 +89,7 @@ public:
static StringName get_global_class_base(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 StringName get_global_class_native_base(const String &p_class);
static void get_global_class_list(List<StringName> *r_global_classes); static void get_global_class_list(List<StringName> *r_global_classes);
static void get_inheriters_list(const StringName &p_base_type, List<StringName> *r_classes);
static void save_global_classes(); static void save_global_classes();
static void init_languages(); static void init_languages();

View File

@ -42,12 +42,6 @@
#include "editor/plugins/script_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h"
#include "editor/scene_tree_dock.h" #include "editor/scene_tree_dock.h"
HashMap<StringName, List<StringName>> EditorResourcePicker::allowed_types_cache;
void EditorResourcePicker::clear_caches() {
allowed_types_cache.clear();
}
void EditorResourcePicker::_update_resource() { void EditorResourcePicker::_update_resource() {
String resource_path; String resource_path;
if (edited_resource.is_valid() && edited_resource->get_path().is_resource_file()) { if (edited_resource.is_valid() && edited_resource->get_path().is_resource_file()) {
@ -464,7 +458,7 @@ void EditorResourcePicker::set_create_options(Object *p_menu_node) {
if (!base_type.is_empty()) { if (!base_type.is_empty()) {
int idx = 0; int idx = 0;
HashSet<String> allowed_types; HashSet<StringName> allowed_types;
_get_allowed_types(false, &allowed_types); _get_allowed_types(false, &allowed_types);
Vector<EditorData::CustomType> custom_resources; Vector<EditorData::CustomType> custom_resources;
@ -472,7 +466,7 @@ void EditorResourcePicker::set_create_options(Object *p_menu_node) {
custom_resources = EditorNode::get_editor_data().get_custom_types()["Resource"]; custom_resources = EditorNode::get_editor_data().get_custom_types()["Resource"];
} }
for (const String &E : allowed_types) { for (const StringName &E : allowed_types) {
const String &t = E; const String &t = E;
bool is_custom_resource = false; bool is_custom_resource = false;
@ -561,53 +555,44 @@ String EditorResourcePicker::_get_resource_type(const Ref<Resource> &p_resource)
return res_type; return res_type;
} }
void EditorResourcePicker::_get_allowed_types(bool p_with_convert, HashSet<String> *p_vector) const { static void _add_allowed_type(const StringName &p_type, HashSet<StringName> *p_vector) {
if (p_vector->has(p_type)) {
// Already added
return;
}
if (ClassDB::class_exists(p_type)) {
// Engine class,
if (!ClassDB::is_virtual(p_type)) {
p_vector->insert(p_type);
}
List<StringName> inheriters;
ClassDB::get_inheriters_from_class(p_type, &inheriters);
for (const StringName &S : inheriters) {
_add_allowed_type(S, p_vector);
}
} else {
// Script class.
p_vector->insert(p_type);
}
List<StringName> inheriters;
ScriptServer::get_inheriters_list(p_type, &inheriters);
for (const StringName &S : inheriters) {
_add_allowed_type(S, p_vector);
}
}
void EditorResourcePicker::_get_allowed_types(bool p_with_convert, HashSet<StringName> *p_vector) const {
Vector<String> allowed_types = base_type.split(","); Vector<String> allowed_types = base_type.split(",");
int size = allowed_types.size(); int size = allowed_types.size();
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
String base = allowed_types[i].strip_edges(); String base = allowed_types[i].strip_edges();
if (!ClassDB::is_virtual(base)) {
p_vector->insert(base);
}
// If we hit a familiar base type, take all the data from cache. _add_allowed_type(base, p_vector);
if (allowed_types_cache.has(base)) {
List<StringName> allowed_subtypes = allowed_types_cache[base];
for (const StringName &subtype_name : allowed_subtypes) {
if (!ClassDB::is_virtual(subtype_name)) {
p_vector->insert(subtype_name);
}
}
} else {
List<StringName> allowed_subtypes;
List<StringName> inheriters;
if (!ScriptServer::is_global_class(base)) {
ClassDB::get_inheriters_from_class(base, &inheriters);
}
for (const StringName &subtype_name : inheriters) {
if (!ClassDB::is_virtual(subtype_name)) {
p_vector->insert(subtype_name);
}
allowed_subtypes.push_back(subtype_name);
}
for (const StringName &subtype_name : global_classes) {
if (EditorNode::get_editor_data().script_class_is_parent(subtype_name, base)) {
if (!ClassDB::is_virtual(subtype_name)) {
p_vector->insert(subtype_name);
}
allowed_subtypes.push_back(subtype_name);
}
}
// Store the subtypes of the base type in the cache for future use.
allowed_types_cache[base] = allowed_subtypes;
}
if (p_with_convert) { if (p_with_convert) {
if (base == "BaseMaterial3D") { if (base == "BaseMaterial3D") {
@ -619,14 +604,6 @@ void EditorResourcePicker::_get_allowed_types(bool p_with_convert, HashSet<Strin
} }
} }
} }
if (EditorNode::get_editor_data().get_custom_types().has("Resource")) {
Vector<EditorData::CustomType> custom_resources = EditorNode::get_editor_data().get_custom_types()["Resource"];
for (int i = 0; i < custom_resources.size(); i++) {
p_vector->insert(custom_resources[i].name);
}
}
} }
bool EditorResourcePicker::_is_drop_valid(const Dictionary &p_drag_data) const { bool EditorResourcePicker::_is_drop_valid(const Dictionary &p_drag_data) const {
@ -654,7 +631,7 @@ bool EditorResourcePicker::_is_drop_valid(const Dictionary &p_drag_data) const {
} }
} }
HashSet<String> allowed_types; HashSet<StringName> allowed_types;
_get_allowed_types(true, &allowed_types); _get_allowed_types(true, &allowed_types);
if (res.is_valid()) { if (res.is_valid()) {
@ -673,9 +650,9 @@ bool EditorResourcePicker::_is_drop_valid(const Dictionary &p_drag_data) const {
return false; return false;
} }
bool EditorResourcePicker::_is_type_valid(const String p_type_name, HashSet<String> p_allowed_types) const { bool EditorResourcePicker::_is_type_valid(const String p_type_name, HashSet<StringName> p_allowed_types) const {
for (const String &E : p_allowed_types) { for (const StringName &E : p_allowed_types) {
String at = E.strip_edges(); String at = E;
if (p_type_name == at || ClassDB::is_parent_class(p_type_name, at) || EditorNode::get_editor_data().script_class_is_parent(p_type_name, at)) { if (p_type_name == at || ClassDB::is_parent_class(p_type_name, at) || EditorNode::get_editor_data().script_class_is_parent(p_type_name, at)) {
return true; return true;
} }
@ -721,15 +698,15 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
} }
if (dropped_resource.is_valid()) { if (dropped_resource.is_valid()) {
HashSet<String> allowed_types; HashSet<StringName> allowed_types;
_get_allowed_types(false, &allowed_types); _get_allowed_types(false, &allowed_types);
String res_type = _get_resource_type(dropped_resource); String res_type = _get_resource_type(dropped_resource);
// If the accepted dropped resource is from the extended list, it requires conversion. // If the accepted dropped resource is from the extended list, it requires conversion.
if (!_is_type_valid(res_type, allowed_types)) { if (!_is_type_valid(res_type, allowed_types)) {
for (const String &E : allowed_types) { for (const StringName &E : allowed_types) {
String at = E.strip_edges(); String at = E;
if (at == "BaseMaterial3D" && Ref<Texture2D>(dropped_resource).is_valid()) { if (at == "BaseMaterial3D" && Ref<Texture2D>(dropped_resource).is_valid()) {
// Use existing resource if possible and only replace its data. // Use existing resource if possible and only replace its data.
@ -832,7 +809,7 @@ void EditorResourcePicker::set_base_type(const String &p_base_type) {
// There is a possibility that the new base type is conflicting with the existing value. // There is a possibility that the new base type is conflicting with the existing value.
// Keep the value, but warn the user that there is a potential mistake. // Keep the value, but warn the user that there is a potential mistake.
if (!base_type.is_empty() && edited_resource.is_valid()) { if (!base_type.is_empty() && edited_resource.is_valid()) {
HashSet<String> allowed_types; HashSet<StringName> allowed_types;
_get_allowed_types(true, &allowed_types); _get_allowed_types(true, &allowed_types);
StringName custom_class; StringName custom_class;
@ -846,10 +823,6 @@ void EditorResourcePicker::set_base_type(const String &p_base_type) {
String class_str = (custom_class == StringName() ? edited_resource->get_class() : vformat("%s (%s)", custom_class, edited_resource->get_class())); String class_str = (custom_class == StringName() ? edited_resource->get_class() : vformat("%s (%s)", custom_class, edited_resource->get_class()));
WARN_PRINT(vformat("Value mismatch between the new base type of this EditorResourcePicker, '%s', and the type of the value it already has, '%s'.", base_type, class_str)); WARN_PRINT(vformat("Value mismatch between the new base type of this EditorResourcePicker, '%s', and the type of the value it already has, '%s'.", base_type, class_str));
} }
} else {
// Call the method to build the cache immediately.
HashSet<String> allowed_types;
_get_allowed_types(false, &allowed_types);
} }
} }
@ -858,7 +831,7 @@ String EditorResourcePicker::get_base_type() const {
} }
Vector<String> EditorResourcePicker::get_allowed_types() const { Vector<String> EditorResourcePicker::get_allowed_types() const {
HashSet<String> allowed_types; HashSet<StringName> allowed_types;
_get_allowed_types(false, &allowed_types); _get_allowed_types(false, &allowed_types);
Vector<String> types; Vector<String> types;
@ -866,7 +839,7 @@ Vector<String> EditorResourcePicker::get_allowed_types() const {
int i = 0; int i = 0;
String *w = types.ptrw(); String *w = types.ptrw();
for (const String &E : allowed_types) { for (const StringName &E : allowed_types) {
w[i] = E; w[i] = E;
i++; i++;
} }
@ -882,7 +855,7 @@ void EditorResourcePicker::set_edited_resource(Ref<Resource> p_resource) {
} }
if (!base_type.is_empty()) { if (!base_type.is_empty()) {
HashSet<String> allowed_types; HashSet<StringName> allowed_types;
_get_allowed_types(true, &allowed_types); _get_allowed_types(true, &allowed_types);
StringName custom_class; StringName custom_class;

View File

@ -42,8 +42,6 @@ class EditorQuickOpen;
class EditorResourcePicker : public HBoxContainer { class EditorResourcePicker : public HBoxContainer {
GDCLASS(EditorResourcePicker, HBoxContainer); GDCLASS(EditorResourcePicker, HBoxContainer);
static HashMap<StringName, List<StringName>> allowed_types_cache;
String base_type; String base_type;
Ref<Resource> edited_resource; Ref<Resource> edited_resource;
@ -92,9 +90,9 @@ class EditorResourcePicker : public HBoxContainer {
void _button_input(const Ref<InputEvent> &p_event); void _button_input(const Ref<InputEvent> &p_event);
String _get_resource_type(const Ref<Resource> &p_resource) const; String _get_resource_type(const Ref<Resource> &p_resource) const;
void _get_allowed_types(bool p_with_convert, HashSet<String> *p_vector) const; void _get_allowed_types(bool p_with_convert, HashSet<StringName> *p_vector) const;
bool _is_drop_valid(const Dictionary &p_drag_data) const; bool _is_drop_valid(const Dictionary &p_drag_data) const;
bool _is_type_valid(const String p_type_name, HashSet<String> p_allowed_types) const; bool _is_type_valid(const String p_type_name, HashSet<StringName> p_allowed_types) const;
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
@ -118,8 +116,6 @@ protected:
GDVIRTUAL1R(bool, _handle_menu_selected, int) GDVIRTUAL1R(bool, _handle_menu_selected, int)
public: public:
static void clear_caches();
void set_base_type(const String &p_base_type); void set_base_type(const String &p_base_type);
String get_base_type() const; String get_base_type() const;
Vector<String> get_allowed_types() const; Vector<String> get_allowed_types() const;

View File

@ -219,6 +219,4 @@ void unregister_editor_types() {
if (EditorPaths::get_singleton()) { if (EditorPaths::get_singleton()) {
EditorPaths::free(); EditorPaths::free();
} }
EditorResourcePicker::clear_caches();
} }