From 91e5f494de98936fb36bbad14a3c0a97dc230f76 Mon Sep 17 00:00:00 2001 From: ajreckof Date: Sat, 15 Mar 2025 19:58:23 +0100 Subject: [PATCH 1/3] =?UTF-8?q?Implement=20hidden=20class=20in=20ScriptSer?= =?UTF-8?q?ver=C2=A0to=C2=A0replace=C2=A0EditorX=C2=A0class=C2=A0being?= =?UTF-8?q?=C2=A0hidden.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/config/project_settings.cpp | 4 ++-- core/object/script_language.cpp | 20 +++++++++++++------ core/object/script_language.h | 6 ++++-- core/object/script_language_extension.h | 5 ++++- editor/create_dialog.cpp | 3 +++ editor/editor_data.cpp | 1 + editor/editor_file_system.cpp | 8 ++++++-- editor/editor_file_system.h | 3 +++ modules/gdscript/doc_classes/@GDScript.xml | 10 ++++++++++ modules/gdscript/gdscript.cpp | 5 ++++- modules/gdscript/gdscript.h | 2 +- .../gdscript/tests/gdscript_test_runner.cpp | 5 +++-- modules/gdscript/tests/test_gdscript.cpp | 2 +- modules/mono/csharp_script.cpp | 2 +- modules/mono/csharp_script.h | 2 +- 15 files changed, 58 insertions(+), 20 deletions(-) diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 48f89ca0ad9e..ef4e556d8bcd 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -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"]); } } diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 33bf7ab48a0f..090e42005e1a 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -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) { 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 *r_global_classes) { List classes; for (const KeyValue &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); diff --git a/core/object/script_language.h b/core/object/script_language.h index 7d439da6caca..82514bba755b 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -63,6 +63,7 @@ class ScriptServer { StringName base; bool is_abstract = false; bool is_tool = false; + bool is_hidden = false; }; static HashMap 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 *r_global_classes); static void get_inheriters_list(const StringName &p_base_type, List *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() {} }; diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index 40e0b1b42e5a..2cbaabfea5f4 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -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"]; } }; diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 5eebb1c492e5..a1d34eec7f4d 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -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)) { diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 69ad7fe9923f..6daf1394899e 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -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); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index f823900744f8..8ad6f1a58f29 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -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); } diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index a92a03addbc3..670b9b8a3a46 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -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; } }; diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index caed2a808d45..43d54fe9f184 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -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]. + + + + Prevent the current class from appearing in the Create Node dialog. + [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. + + diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index d1ac826b6ab8..665e6515fab2 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -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 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 = false; + } return c->identifier != nullptr ? String(c->identifier->name) : String(); } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 11e00b4c3c32..a4155d37783c 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -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 get_orphan_subclass(const String &p_qualified_name); diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index c7f92daa646a..7e8804c0f719 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -370,7 +370,8 @@ 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; @@ -378,7 +379,7 @@ static bool generate_class_index_recursive(const String &p_dir) { 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(); diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp index ec20ed19fb9b..6c46b6c38f81 100644 --- a/modules/gdscript/tests/test_gdscript.cpp +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -300,7 +300,7 @@ void test(TestType p_type) { if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base") || !c.has("is_abstract") || !c.has("is_tool")) { 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"], false); } Vector buf; diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 55c1a0990258..9064265db44a 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -445,7 +445,7 @@ 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); return class_name; diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 3253a8679a3c..19e9aee5d5c4 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -529,7 +529,7 @@ class CSharpLanguage : public ScriptLanguage { /* SCRIPT GLOBAL CLASS FUNCTIONS */ 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_is_hidden = nullptr) const override; /* DEBUGGER FUNCTIONS */ String debug_get_error() const override; From f7ce55f9850a66562f83f1de9ed7fefee148ea2e Mon Sep 17 00:00:00 2001 From: ajreckof Date: Sat, 15 Mar 2025 19:59:39 +0100 Subject: [PATCH 2/3] Implement @hide in GdScript. --- modules/gdscript/gdscript.cpp | 2 +- modules/gdscript/gdscript_parser.cpp | 15 ++++++++++++++- modules/gdscript/gdscript_parser.h | 4 ++++ modules/gdscript/tests/test_gdscript.cpp | 4 ++-- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 665e6515fab2..e5fe85ffaa37 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2919,7 +2919,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b *r_is_tool = parser.is_tool(); } if (r_is_hidden) { - *r_is_hidden = false; + *r_is_hidden = parser.is_hidden(); } return c->identifier != nullptr ? String(c->identifier->name) : String(); } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index a7a1c076be4b..c9f11f1070b6 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -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); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 6b10f0b69676..eb2ad326c4c2 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -742,6 +742,7 @@ class GDScriptParser { IdentifierNode *identifier = nullptr; String icon_path; + bool hidden = false; String simplified_icon_path; Vector members; HashMap 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 &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 get_depended_parser_for(const String &p_path); const HashMap> &get_depended_parsers(); ClassNode *find_class(const String &p_qualified_name) const; diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp index 6c46b6c38f81..a7b5fe0bf3a2 100644 --- a/modules/gdscript/tests/test_gdscript.cpp +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -297,10 +297,10 @@ void test(TestType p_type) { TypedArray 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"], false); + ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"], c["is_abstract"], c["is_tool"], c["hidden"]); } Vector buf; From 6f1f1fc558a0c6484c2e186864451909b18bda48 Mon Sep 17 00:00:00 2001 From: ajreckof Date: Sat, 15 Mar 2025 19:59:55 +0100 Subject: [PATCH 3/3] Implement Hidden in C#. --- modules/mono/csharp_script.cpp | 2 +- .../Core/Attributes/GlobalClassAttribute.cs | 8 +++++++- .../GodotSharp/Core/Bridge/ManagedCallbacks.cs | 2 +- .../GodotSharp/Core/Bridge/ScriptManagerBridge.cs | 11 ++++++++++- modules/mono/mono_gd/gd_mono_cache.h | 2 +- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 9064265db44a..640dbcac6a7a 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -447,7 +447,7 @@ bool CSharpLanguage::handles_global_class_type(const String &p_type) 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; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GlobalClassAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GlobalClassAttribute.cs index 8f3297839d6a..c5bfaf9fcf66 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GlobalClassAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GlobalClassAttribute.cs @@ -8,5 +8,11 @@ namespace Godot /// Exposes the target class as a global script class to Godot Engine. /// [AttributeUsage(AttributeTargets.Class)] - public sealed class GlobalClassAttribute : Attribute { } + public sealed class GlobalClassAttribute : Attribute + { + /// + /// Prevent the current class from appearing in the Create Node dialog. + /// + public bool Hidden { get; init; } + } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs index b0ff041fa49a..dd4f0b6d1806 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -19,7 +19,7 @@ public unsafe struct ManagedCallbacks public delegate* unmanaged ScriptManagerBridge_CreateManagedForGodotObjectBinding; public delegate* unmanaged ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance; public delegate* unmanaged ScriptManagerBridge_GetScriptNativeName; - public delegate* unmanaged ScriptManagerBridge_GetGlobalClassName; + public delegate* unmanaged ScriptManagerBridge_GetGlobalClassName; public delegate* unmanaged ScriptManagerBridge_SetGodotObjectPtr; public delegate* unmanaged ScriptManagerBridge_RaiseEventSignal; public delegate* unmanaged ScriptManagerBridge_ScriptIsOrInherits; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index a327b9cd354b..be028a7dd982 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -199,7 +199,7 @@ internal static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_n } [UnmanagedCallersOnly] - internal static unsafe void GetGlobalClassName(godot_string* scriptPath, godot_string* outBaseType, godot_string* outIconPath, godot_bool* outIsAbstract, godot_bool* outIsTool, godot_string* outClassName) + internal static unsafe void GetGlobalClassName(godot_string* scriptPath, godot_string* outBaseType, godot_string* outIconPath, godot_bool* outIsAbstract, godot_bool* outIsTool, godot_bool* outHidden, godot_string* outClassName) { // This method must always return the outBaseType for every script, even if the script is // not a global class. But if the script is not a global class it must return an empty @@ -230,6 +230,15 @@ internal static unsafe void GetGlobalClassName(godot_string* scriptPath, godot_s } } + if (outHidden != null) + { + var globalClassAttr = scriptType.GetCustomAttributes(inherit: false) + .OfType() + .FirstOrDefault(); + + *outHidden = (globalClassAttr?.Hidden == true).ToGodotBool(); + } + if (outBaseType != null) { bool foundGlobalBaseScript = false; diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 6caec0b7379b..8b224e186eca 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -84,7 +84,7 @@ struct ManagedCallbacks { using FuncScriptManagerBridge_CreateManagedForGodotObjectBinding = GCHandleIntPtr(GD_CLR_STDCALL *)(const StringName *, Object *); using FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = bool(GD_CLR_STDCALL *)(const CSharpScript *, Object *, const Variant **, int32_t); using FuncScriptManagerBridge_GetScriptNativeName = void(GD_CLR_STDCALL *)(const CSharpScript *, StringName *); - using FuncScriptManagerBridge_GetGlobalClassName = void(GD_CLR_STDCALL *)(const String *, String *, String *, bool *, bool *, String *); + using FuncScriptManagerBridge_GetGlobalClassName = void(GD_CLR_STDCALL *)(const String *, String *, String *, bool *, bool *, bool *, String *); using FuncScriptManagerBridge_SetGodotObjectPtr = void(GD_CLR_STDCALL *)(GCHandleIntPtr, Object *); using FuncScriptManagerBridge_RaiseEventSignal = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int32_t, bool *); using FuncScriptManagerBridge_ScriptIsOrInherits = bool(GD_CLR_STDCALL *)(const CSharpScript *, const CSharpScript *);