Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement hidden class in ScriptServer to replace EditorX class being hidden. #91020

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions core/config/project_settings.cpp
Original file line number Diff line number Diff line change
@@ -1245,10 +1245,10 @@ void ProjectSettings::refresh_global_class_list() {
Array script_classes = get_global_class_list();
for (int i = 0; i < script_classes.size(); i++) {
Dictionary c = script_classes[i];
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool")) {
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool") || !c.has("is_hidden")) {
continue;
}
ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"]);
ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"], c["is_hidden"]);
}
}

20 changes: 14 additions & 6 deletions core/object/script_language.cpp
Original file line number Diff line number Diff line change
@@ -269,10 +269,10 @@ void ScriptServer::init_languages() {

for (const Variant &script_class : script_classes) {
Dictionary c = script_class;
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool")) {
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool") || !c.has("is_hidden")) {
continue;
}
add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"]);
add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"], c["is_hidden"]);
}
ProjectSettings::get_singleton()->clear("_global_script_classes");
}
@@ -281,10 +281,10 @@ void ScriptServer::init_languages() {
Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
for (const Variant &script_class : script_classes) {
Dictionary c = script_class;
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool")) {
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool") || !c.has("is_hidden")) {
continue;
}
add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"]);
add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"], c["is_hidden"]);
}
}

@@ -390,17 +390,18 @@ void ScriptServer::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, bool p_is_abstract, bool p_is_tool) {
void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path, bool p_is_abstract, bool p_is_tool, bool p_is_hidden) {
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.");
GlobalScriptClass *existing = global_classes.getptr(p_class);
if (existing) {
// Update an existing class (only set dirty if something changed).
if (existing->base != p_base || existing->path != p_path || existing->language != p_language) {
if (existing->base != p_base || existing->path != p_path || existing->language != p_language || existing->language != p_language) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new condition seems to be a duplicate.

existing->base = p_base;
existing->path = p_path;
existing->language = p_language;
existing->is_abstract = p_is_abstract;
existing->is_tool = p_is_tool;
existing->is_hidden = p_is_hidden;
inheriters_cache_dirty = true;
}
} else {
@@ -411,6 +412,7 @@ void ScriptServer::add_global_class(const StringName &p_class, const StringName
g.base = p_base;
g.is_abstract = p_is_abstract;
g.is_tool = p_is_tool;
g.is_hidden = p_is_hidden;
global_classes[p_class] = g;
inheriters_cache_dirty = true;
}
@@ -494,6 +496,11 @@ bool ScriptServer::is_global_class_tool(const String &p_class) {
return global_classes[p_class].is_tool;
}

bool ScriptServer::is_global_class_hidden(const StringName &p_class) {
ERR_FAIL_COND_V(!global_classes.has(p_class), false);
return global_classes[p_class].is_hidden;
}

void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) {
List<StringName> classes;
for (const KeyValue<StringName, GlobalScriptClass> &E : global_classes) {
@@ -530,6 +537,7 @@ void ScriptServer::save_global_classes() {
d["icon"] = class_icons.get(E, "");
d["is_abstract"] = global_class.is_abstract;
d["is_tool"] = global_class.is_tool;
d["is_hidden"] = global_class.is_hidden;
gcarr.push_back(d);
}
ProjectSettings::get_singleton()->store_global_class_list(gcarr);
6 changes: 4 additions & 2 deletions core/object/script_language.h
Original file line number Diff line number Diff line change
@@ -63,6 +63,7 @@ class ScriptServer {
StringName base;
bool is_abstract = false;
bool is_tool = false;
bool is_hidden = false;
};

static HashMap<StringName, GlobalScriptClass> global_classes;
@@ -87,7 +88,7 @@ class ScriptServer {
static void thread_exit();

static void global_classes_clear();
static void add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path, bool p_is_abstract, bool p_is_tool);
static void add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path, bool p_is_abstract, bool p_is_tool, bool p_is_hidden);
static void remove_global_class(const StringName &p_class);
static void remove_global_class_by_path(const String &p_path);
static bool is_global_class(const StringName &p_class);
@@ -97,6 +98,7 @@ class ScriptServer {
static StringName get_global_class_native_base(const String &p_class);
static bool is_global_class_abstract(const String &p_class);
static bool is_global_class_tool(const String &p_class);
static bool is_global_class_hidden(const StringName &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();
@@ -446,7 +448,7 @@ class ScriptLanguage : public Object {
virtual void frame();

virtual bool handles_global_class_type(const String &p_type) const { return false; }
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr, bool *r_is_abstract = nullptr, bool *r_is_tool = nullptr) const { return String(); }
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr, bool *r_is_abstract = nullptr, bool *r_is_tool = nullptr, bool *r_is_hidden = nullptr) const { return String(); }

virtual ~ScriptLanguage() {}
};
5 changes: 4 additions & 1 deletion core/object/script_language_extension.h
Original file line number Diff line number Diff line change
@@ -669,7 +669,7 @@ class ScriptLanguageExtension : public ScriptLanguage {

GDVIRTUAL1RC_REQUIRED(Dictionary, _get_global_class_name, const String &)

virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr, bool *r_is_abstract = nullptr, bool *r_is_tool = nullptr) const override {
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr, bool *r_is_abstract = nullptr, bool *r_is_tool = nullptr, bool *r_is_hidden = nullptr) const override {
Dictionary ret;
GDVIRTUAL_CALL(_get_global_class_name, p_path, ret);
if (!ret.has("name")) {
@@ -687,6 +687,9 @@ class ScriptLanguageExtension : public ScriptLanguage {
if (r_is_tool != nullptr && ret.has("is_tool")) {
*r_is_tool = ret["is_tool"];
}
if (r_is_hidden != nullptr && ret.has("is_hidden")) {
*r_is_hidden = ret["is_hidden"];
}
return ret["name"];
}
};
3 changes: 3 additions & 0 deletions editor/create_dialog.cpp
Original file line number Diff line number Diff line change
@@ -174,6 +174,9 @@ bool CreateDialog::_should_hide_type(const StringName &p_type) const {
if (!EditorNode::get_editor_data().script_class_is_parent(p_type, base_type)) {
return true; // Wrong inheritance.
}
if (ScriptServer::is_global_class_hidden(p_type)) {
return true;
}

StringName native_type = ScriptServer::get_global_class_native_base(p_type);
if (ClassDB::class_exists(native_type)) {
1 change: 1 addition & 0 deletions editor/editor_data.cpp
Original file line number Diff line number Diff line change
@@ -1056,6 +1056,7 @@ void EditorData::script_class_save_global_classes() {
d["icon"] = icon ? *icon : String();
d["is_abstract"] = ScriptServer::is_global_class_abstract(class_name);
d["is_tool"] = ScriptServer::is_global_class_tool(class_name);
d["is_hidden"] = ScriptServer::is_global_class_hidden(class_name);
array_classes.push_back(d);
}
ProjectSettings::get_singleton()->store_global_class_list(array_classes);
8 changes: 6 additions & 2 deletions editor/editor_file_system.cpp
Original file line number Diff line number Diff line change
@@ -172,6 +172,10 @@ String EditorFileSystemDirectory::get_file_script_class_name(int p_idx) const {
return files[p_idx]->class_info.name;
}

bool EditorFileSystemDirectory::get_file_script_class_hidden(int p_idx) const {
return files[p_idx]->class_info.is_hidden;
}

String EditorFileSystemDirectory::get_file_script_class_extends(int p_idx) const {
return files[p_idx]->class_info.extends;
}
@@ -2045,7 +2049,7 @@ EditorFileSystem::ScriptClassInfo EditorFileSystem::_get_global_script_class(con
ScriptClassInfo info;
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
if (ScriptServer::get_language(i)->handles_global_class_type(p_type)) {
info.name = ScriptServer::get_language(i)->get_global_class_name(p_path, &info.extends, &info.icon_path, &info.is_abstract, &info.is_tool);
info.name = ScriptServer::get_language(i)->get_global_class_name(p_path, &info.extends, &info.icon_path, &info.is_abstract, &info.is_tool, &info.is_hidden);
break;
}
}
@@ -2503,7 +2507,7 @@ void EditorFileSystem::_register_global_class_script(const String &p_search_path
return; // No lang found that can handle this global class
}

ScriptServer::add_global_class(p_script_update.name, p_script_update.extends, lang, p_target_path, p_script_update.is_abstract, p_script_update.is_tool);
ScriptServer::add_global_class(p_script_update.name, p_script_update.extends, lang, p_target_path, p_script_update.is_abstract, p_script_update.is_tool, p_script_update.is_hidden);
EditorNode::get_editor_data().script_class_set_icon_path(p_script_update.name, p_script_update.icon_path);
EditorNode::get_editor_data().script_class_set_name(p_target_path, p_script_update.name);
}
3 changes: 3 additions & 0 deletions editor/editor_file_system.h
Original file line number Diff line number Diff line change
@@ -72,6 +72,7 @@ class EditorFileSystemDirectory : public Object {
String icon_path;
bool is_abstract = false;
bool is_tool = false;
bool is_hidden = false;
};
ScriptClassInfo class_info;
};
@@ -98,6 +99,7 @@ class EditorFileSystemDirectory : public Object {
uint64_t get_file_modified_time(int p_idx) const;
uint64_t get_file_import_modified_time(int p_idx) const;
String get_file_script_class_name(int p_idx) const; //used for scripts
bool get_file_script_class_hidden(int p_idx) const; //used for scripts
String get_file_script_class_extends(int p_idx) const; //used for scripts
String get_file_script_class_icon_path(int p_idx) const; //used for scripts
String get_file_icon_path(int p_idx) const; //used for FileSystemDock
@@ -308,6 +310,7 @@ class EditorFileSystem : public Node {
update.icon_path = p_fi->class_info.icon_path;
update.is_abstract = p_fi->class_info.is_abstract;
update.is_tool = p_fi->class_info.is_tool;
update.is_hidden = p_fi->class_info.is_hidden;
return update;
}
};
10 changes: 10 additions & 0 deletions modules/gdscript/doc_classes/@GDScript.xml
Original file line number Diff line number Diff line change
@@ -717,6 +717,16 @@
[b]Note:[/b] Avoid storing lambda callables in member variables of [RefCounted]-based classes (e.g. resources), as this can lead to memory leaks. Use only method callables and optionally [method Callable.bind] or [method Callable.unbind].
</description>
</annotation>
<annotation name="@hide">
<return type="void" />
<description>
Prevent the current class from appearing in the Create Node dialog.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also applies to Create Resource etc.

[codeblock]
@hide class_name MyClass
[/codeblock]
[b]Note:[/b] As annotations describe their subject, the [annotation @hide] annotation must be placed before the class definition and inheritance.
</description>
</annotation>
<annotation name="@icon">
<return type="void" />
<param index="0" name="icon_path" type="String" />
5 changes: 4 additions & 1 deletion modules/gdscript/gdscript.cpp
Original file line number Diff line number Diff line change
@@ -2811,7 +2811,7 @@ bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
return p_type == "GDScript";
}

String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path, bool *r_is_abstract, bool *r_is_tool) const {
String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path, bool *r_is_abstract, bool *r_is_tool, bool *r_is_hidden) const {
Error err;
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
if (err) {
@@ -2918,6 +2918,9 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
if (r_is_tool) {
*r_is_tool = parser.is_tool();
}
if (r_is_hidden) {
*r_is_hidden = parser.is_hidden();
}
return c->identifier != nullptr ? String(c->identifier->name) : String();
}

2 changes: 1 addition & 1 deletion modules/gdscript/gdscript.h
Original file line number Diff line number Diff line change
@@ -636,7 +636,7 @@ class GDScriptLanguage : public ScriptLanguage {
/* GLOBAL CLASSES */

virtual bool handles_global_class_type(const String &p_type) const override;
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr, bool *r_is_abstract = nullptr, bool *r_is_tool = nullptr) const override;
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr, bool *r_is_abstract = nullptr, bool *r_is_tool = nullptr, bool *r_hidden = nullptr) const override;

void add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass);
Ref<GDScript> get_orphan_subclass(const String &p_qualified_name);
15 changes: 14 additions & 1 deletion modules/gdscript/gdscript_parser.cpp
Original file line number Diff line number Diff line change
@@ -94,6 +94,7 @@ GDScriptParser::GDScriptParser() {
if (unlikely(valid_annotations.is_empty())) {
// Script annotations.
register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
register_annotation(MethodInfo("@hide"), AnnotationInfo::SCRIPT, &GDScriptParser::hide_annotation);
register_annotation(MethodInfo("@icon", PropertyInfo(Variant::STRING, "icon_path")), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
register_annotation(MethodInfo("@static_unload"), AnnotationInfo::SCRIPT, &GDScriptParser::static_unload_annotation);
// Onready annotation.
@@ -631,7 +632,7 @@ void GDScriptParser::parse_program() {
annotation_stack.push_back(annotation);
} else if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
PUSH_PENDING_ANNOTATIONS_TO_HEAD;
if (annotation->name == SNAME("@tool") || annotation->name == SNAME("@icon")) {
if (annotation->name == SNAME("@tool") || annotation->name == SNAME("@icon") || annotation->name == SNAME("@hide")) {
// Some annotations need to be resolved and applied in the parser.
annotation->apply(this, head, nullptr); // `head->outer == nullptr`.
} else {
@@ -4220,6 +4221,18 @@ bool GDScriptParser::tool_annotation(AnnotationNode *p_annotation, Node *p_targe
return true;
}

bool GDScriptParser::hide_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
#ifdef DEBUG_ENABLED
if (_is_hidden) {
push_error(R"("@hide" annotation can only be used once.)", p_annotation);
return false;
}
#endif // DEBUG_ENABLED

_is_hidden = true;
return true;
}

bool GDScriptParser::icon_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
ERR_FAIL_COND_V_MSG(p_target->type != Node::CLASS, false, R"("@icon" annotation can only be applied to classes.)");
ERR_FAIL_COND_V(p_annotation->resolved_arguments.is_empty(), false);
4 changes: 4 additions & 0 deletions modules/gdscript/gdscript_parser.h
Original file line number Diff line number Diff line change
@@ -742,6 +742,7 @@ class GDScriptParser {

IdentifierNode *identifier = nullptr;
String icon_path;
bool hidden = false;
String simplified_icon_path;
Vector<Member> members;
HashMap<StringName, int> members_indices;
@@ -1333,6 +1334,7 @@ class GDScriptParser {
friend class GDScriptParserRef;

bool _is_tool = false;
bool _is_hidden = false;
String script_path;
bool for_completion = false;
bool parse_body = true;
@@ -1506,6 +1508,7 @@ class GDScriptParser {
bool validate_annotation_arguments(AnnotationNode *p_annotation);
void clear_unused_annotations();
bool tool_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
bool hide_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
bool icon_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
bool static_unload_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
bool onready_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
@@ -1579,6 +1582,7 @@ class GDScriptParser {
Error parse_binary(const Vector<uint8_t> &p_binary, const String &p_script_path);
ClassNode *get_tree() const { return head; }
bool is_tool() const { return _is_tool; }
bool is_hidden() const { return _is_hidden; }
Ref<GDScriptParserRef> get_depended_parser_for(const String &p_path);
const HashMap<String, Ref<GDScriptParserRef>> &get_depended_parsers();
ClassNode *find_class(const String &p_qualified_name) const;
5 changes: 3 additions & 2 deletions modules/gdscript/tests/gdscript_test_runner.cpp
Original file line number Diff line number Diff line change
@@ -370,15 +370,16 @@ static bool generate_class_index_recursive(const String &p_dir) {
String source_file = current_dir.path_join(next);
bool is_abstract = false;
bool is_tool = false;
String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(source_file, &base_type, nullptr, &is_abstract, &is_tool);
bool is_hidden = false;
String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(source_file, &base_type, nullptr, &is_abstract, &is_tool, &is_hidden);
if (class_name.is_empty()) {
next = dir->get_next();
continue;
}
ERR_FAIL_COND_V_MSG(ScriptServer::is_global_class(class_name), false,
"Class name '" + class_name + "' from " + source_file + " is already used in " + ScriptServer::get_global_class_path(class_name));

ScriptServer::add_global_class(class_name, base_type, gdscript_name, source_file, is_abstract, is_tool);
ScriptServer::add_global_class(class_name, base_type, gdscript_name, source_file, is_abstract, is_tool, is_hidden);
}

next = dir->get_next();
4 changes: 2 additions & 2 deletions modules/gdscript/tests/test_gdscript.cpp
Original file line number Diff line number Diff line change
@@ -297,10 +297,10 @@ void test(TestType p_type) {
TypedArray<Dictionary> script_classes = ProjectSettings::get_singleton()->get_global_class_list();
for (int i = 0; i < script_classes.size(); i++) {
Dictionary c = script_classes[i];
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool")) {
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool") || !c.has("hidden")) {
continue;
}
ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"]);
ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"], c["hidden"]);
}

Vector<uint8_t> buf;
4 changes: 2 additions & 2 deletions modules/mono/csharp_script.cpp
Original file line number Diff line number Diff line change
@@ -445,9 +445,9 @@ bool CSharpLanguage::handles_global_class_type(const String &p_type) const {
return p_type == get_type();
}

String CSharpLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path, bool *r_is_abstract, bool *r_is_tool) const {
String CSharpLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path, bool *r_is_abstract, bool *r_is_tool, bool *r_is_hidden) const {
String class_name;
GDMonoCache::managed_callbacks.ScriptManagerBridge_GetGlobalClassName(&p_path, r_base_type, r_icon_path, r_is_abstract, r_is_tool, &class_name);
GDMonoCache::managed_callbacks.ScriptManagerBridge_GetGlobalClassName(&p_path, r_base_type, r_icon_path, r_is_abstract, r_is_tool, r_is_hidden, &class_name);
return class_name;
}

Loading