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, Vector<StringName>> ScriptServer::inheriters_cache;
bool ScriptServer::inheriters_cache_dirty = true;
void ScriptServer::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) {
@ -257,16 +260,44 @@ void ScriptServer::add_global_class(const StringName &p_class, const StringName
g.path = p_path;
g.base = p_base;
global_classes[p_class] = g;
inheriters_cache_dirty = true;
}
void ScriptServer::remove_global_class(const StringName &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) {
for (const KeyValue<StringName, GlobalScriptClass> &kv : global_classes) {
if (kv.value.path == p_path) {
global_classes.erase(kv.key);
inheriters_cache_dirty = true;
return;
}
}

View File

@ -56,10 +56,12 @@ class ScriptServer {
struct GlobalScriptClass {
StringName language;
String path;
String base;
StringName base;
};
static HashMap<StringName, GlobalScriptClass> global_classes;
static HashMap<StringName, Vector<StringName>> inheriters_cache;
static bool inheriters_cache_dirty;
public:
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_native_base(const String &p_class);
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 init_languages();

View File

@ -42,12 +42,6 @@
#include "editor/plugins/script_editor_plugin.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() {
String resource_path;
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()) {
int idx = 0;
HashSet<String> allowed_types;
HashSet<StringName> allowed_types;
_get_allowed_types(false, &allowed_types);
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"];
}
for (const String &E : allowed_types) {
for (const StringName &E : allowed_types) {
const String &t = E;
bool is_custom_resource = false;
@ -561,53 +555,44 @@ String EditorResourcePicker::_get_resource_type(const Ref<Resource> &p_resource)
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(",");
int size = allowed_types.size();
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
for (int i = 0; i < size; i++) {
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.
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;
}
_add_allowed_type(base, p_vector);
if (p_with_convert) {
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 {
@ -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);
if (res.is_valid()) {
@ -673,9 +650,9 @@ bool EditorResourcePicker::_is_drop_valid(const Dictionary &p_drag_data) const {
return false;
}
bool EditorResourcePicker::_is_type_valid(const String p_type_name, HashSet<String> p_allowed_types) const {
for (const String &E : p_allowed_types) {
String at = E.strip_edges();
bool EditorResourcePicker::_is_type_valid(const String p_type_name, HashSet<StringName> p_allowed_types) const {
for (const StringName &E : p_allowed_types) {
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)) {
return true;
}
@ -721,15 +698,15 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
}
if (dropped_resource.is_valid()) {
HashSet<String> allowed_types;
HashSet<StringName> allowed_types;
_get_allowed_types(false, &allowed_types);
String res_type = _get_resource_type(dropped_resource);
// If the accepted dropped resource is from the extended list, it requires conversion.
if (!_is_type_valid(res_type, allowed_types)) {
for (const String &E : allowed_types) {
String at = E.strip_edges();
for (const StringName &E : allowed_types) {
String at = E;
if (at == "BaseMaterial3D" && Ref<Texture2D>(dropped_resource).is_valid()) {
// 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.
// Keep the value, but warn the user that there is a potential mistake.
if (!base_type.is_empty() && edited_resource.is_valid()) {
HashSet<String> allowed_types;
HashSet<StringName> allowed_types;
_get_allowed_types(true, &allowed_types);
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()));
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 {
HashSet<String> allowed_types;
HashSet<StringName> allowed_types;
_get_allowed_types(false, &allowed_types);
Vector<String> types;
@ -866,7 +839,7 @@ Vector<String> EditorResourcePicker::get_allowed_types() const {
int i = 0;
String *w = types.ptrw();
for (const String &E : allowed_types) {
for (const StringName &E : allowed_types) {
w[i] = E;
i++;
}
@ -882,7 +855,7 @@ void EditorResourcePicker::set_edited_resource(Ref<Resource> p_resource) {
}
if (!base_type.is_empty()) {
HashSet<String> allowed_types;
HashSet<StringName> allowed_types;
_get_allowed_types(true, &allowed_types);
StringName custom_class;

View File

@ -42,8 +42,6 @@ class EditorQuickOpen;
class EditorResourcePicker : public HBoxContainer {
GDCLASS(EditorResourcePicker, HBoxContainer);
static HashMap<StringName, List<StringName>> allowed_types_cache;
String base_type;
Ref<Resource> edited_resource;
@ -92,9 +90,9 @@ class EditorResourcePicker : public HBoxContainer {
void _button_input(const Ref<InputEvent> &p_event);
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_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);
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)
public:
static void clear_caches();
void set_base_type(const String &p_base_type);
String get_base_type() const;
Vector<String> get_allowed_types() const;

View File

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