diff options
227 files changed, 8804 insertions, 1718 deletions
diff --git a/SConstruct b/SConstruct index da2f89fbeb..c7b9d5bc86 100644 --- a/SConstruct +++ b/SConstruct @@ -308,18 +308,14 @@ else: if selected_platform != "": print("Automatically detected platform: " + selected_platform) -if selected_platform in ["macos", "osx"]: - if selected_platform == "osx": - # Deprecated alias kept for compatibility. - print('Platform "osx" has been renamed to "macos" in Godot 4. Building for platform "macos".') - # Alias for convenience. +if selected_platform == "osx": + # Deprecated alias kept for compatibility. + print('Platform "osx" has been renamed to "macos" in Godot 4. Building for platform "macos".') selected_platform = "macos" -if selected_platform in ["ios", "iphone"]: - if selected_platform == "iphone": - # Deprecated alias kept for compatibility. - print('Platform "iphone" has been renamed to "ios" in Godot 4. Building for platform "ios".') - # Alias for convenience. +if selected_platform == "iphone": + # Deprecated alias kept for compatibility. + print('Platform "iphone" has been renamed to "ios" in Godot 4. Building for platform "ios".') selected_platform = "ios" if selected_platform in ["linux", "bsd", "x11"]: @@ -329,6 +325,11 @@ if selected_platform in ["linux", "bsd", "x11"]: # Alias for convenience. selected_platform = "linuxbsd" +if selected_platform == "javascript": + # Deprecated alias kept for compatibility. + print('Platform "javascript" has been renamed to "web" in Godot 4. Building for platform "web".') + selected_platform = "web" + # Make sure to update this to the found, valid platform as it's used through the buildsystem as the reference. # It should always be re-set after calling `opts.Update()` otherwise it uses the original input value. env_base["platform"] = selected_platform diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index bf1595b41b..8096407430 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -281,6 +281,11 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { if (autoloads.has(node_name)) { remove_autoload(node_name); } + } else if (p_name.operator String().begins_with("global_group/")) { + String group_name = p_name.operator String().get_slice("/", 1); + if (global_groups.has(group_name)) { + remove_global_group(group_name); + } } } else { if (p_name == CoreStringNames::get_singleton()->_custom_features) { @@ -327,6 +332,9 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { autoload.path = path; } add_autoload(autoload); + } else if (p_name.operator String().begins_with("global_group/")) { + String group_name = p_name.operator String().get_slice("/", 1); + add_global_group(group_name, p_value); } } @@ -674,6 +682,8 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo Compression::gzip_level = GLOBAL_GET("compression/formats/gzip/compression_level"); + load_scene_groups_cache(); + project_loaded = err == OK; return err; } @@ -1241,6 +1251,73 @@ ProjectSettings::AutoloadInfo ProjectSettings::get_autoload(const StringName &p_ return autoloads[p_name]; } +const HashMap<StringName, String> &ProjectSettings::get_global_groups_list() const { + return global_groups; +} + +void ProjectSettings::add_global_group(const StringName &p_name, const String &p_description) { + ERR_FAIL_COND_MSG(p_name == StringName(), "Trying to add global group with no name."); + global_groups[p_name] = p_description; +} + +void ProjectSettings::remove_global_group(const StringName &p_name) { + ERR_FAIL_COND_MSG(!global_groups.has(p_name), "Trying to remove non-existent global group."); + global_groups.erase(p_name); +} + +bool ProjectSettings::has_global_group(const StringName &p_name) const { + return global_groups.has(p_name); +} + +void ProjectSettings::remove_scene_groups_cache(const StringName &p_path) { + scene_groups_cache.erase(p_path); +} + +void ProjectSettings::add_scene_groups_cache(const StringName &p_path, const HashSet<StringName> &p_cache) { + scene_groups_cache[p_path] = p_cache; +} + +void ProjectSettings::save_scene_groups_cache() { + Ref<ConfigFile> cf; + cf.instantiate(); + for (const KeyValue<StringName, HashSet<StringName>> &E : scene_groups_cache) { + if (E.value.is_empty()) { + continue; + } + Array list; + for (const StringName &group : E.value) { + list.push_back(group); + } + cf->set_value(E.key, "groups", list); + } + cf->save(get_scene_groups_cache_path()); +} + +String ProjectSettings::get_scene_groups_cache_path() const { + return get_project_data_path().path_join("scene_groups_cache.cfg"); +} + +void ProjectSettings::load_scene_groups_cache() { + Ref<ConfigFile> cf; + cf.instantiate(); + if (cf->load(get_scene_groups_cache_path()) == OK) { + List<String> scene_paths; + cf->get_sections(&scene_paths); + for (const String &E : scene_paths) { + Array scene_groups = cf->get_value(E, "groups", Array()); + HashSet<StringName> cache; + for (int i = 0; i < scene_groups.size(); ++i) { + cache.insert(scene_groups[i]); + } + add_scene_groups_cache(E, cache); + } + } +} + +const HashMap<StringName, HashSet<StringName>> &ProjectSettings::get_scene_groups_cache() const { + return scene_groups_cache; +} + void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting); ClassDB::bind_method(D_METHOD("set_setting", "name", "value"), &ProjectSettings::set_setting); @@ -1387,7 +1464,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/occlusion_culling/bvh_build_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), 2); GLOBAL_DEF(PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1"), 60); // No negative and limit to 500 due to crashes. GLOBAL_DEF_RST("internationalization/rendering/force_right_to_left_layout_direction", false); - GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Locale,Left-to-Right,Right-to-Left"), 0); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale"), 0); GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 2000); diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 302df7e8d0..55d5957ad1 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -106,6 +106,8 @@ protected: LocalVector<String> hidden_prefixes; HashMap<StringName, AutoloadInfo> autoloads; + HashMap<StringName, String> global_groups; + HashMap<StringName, HashSet<StringName>> scene_groups_cache; Array global_class_list; bool is_global_class_list_loaded = false; @@ -208,6 +210,18 @@ public: bool has_autoload(const StringName &p_autoload) const; AutoloadInfo get_autoload(const StringName &p_name) const; + const HashMap<StringName, String> &get_global_groups_list() const; + void add_global_group(const StringName &p_name, const String &p_description); + void remove_global_group(const StringName &p_name); + bool has_global_group(const StringName &p_name) const; + + const HashMap<StringName, HashSet<StringName>> &get_scene_groups_cache() const; + void add_scene_groups_cache(const StringName &p_path, const HashSet<StringName> &p_cache); + void remove_scene_groups_cache(const StringName &p_path); + void save_scene_groups_cache(); + String get_scene_groups_cache_path() const; + void load_scene_groups_cache(); + ProjectSettings(); ~ProjectSettings(); }; diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 74c5c1c191..265d9ef56c 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -327,7 +327,7 @@ uint64_t FileAccessPack::get_buffer(uint8_t *p_dst, uint64_t p_length) const { to_read = (int64_t)pf.size - (int64_t)pos; } - pos += p_length; + pos += to_read; if (to_read <= 0) { return 0; diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 3d384d9345..bc2493d360 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -638,6 +638,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (str.is_empty()) { r_variant = (Object *)nullptr; } else { + ERR_FAIL_COND_V(!ClassDB::can_instantiate(str), ERR_INVALID_DATA); + Object *obj = ClassDB::instantiate(str); ERR_FAIL_NULL_V(obj, ERR_UNAVAILABLE); @@ -1492,6 +1494,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; } else { + ERR_FAIL_COND_V(!ClassDB::can_instantiate(obj->get_class()), ERR_INVALID_PARAMETER); + _encode_string(obj->get_class(), buf, r_len); List<PropertyInfo> props; diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 64fa597a67..04ecabaf27 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -489,12 +489,14 @@ RWLock ResourceCache::path_cache_lock; #endif void ResourceCache::clear() { - if (resources.size()) { - ERR_PRINT("Resources still in use at exit (run with --verbose for details)."); + if (!resources.is_empty()) { if (OS::get_singleton()->is_stdout_verbose()) { + ERR_PRINT(vformat("%d resources still in use at exit.", resources.size())); for (const KeyValue<String, Resource *> &E : resources) { print_line(vformat("Resource still in use: %s (%s)", E.key, E.value->get_class())); } + } else { + ERR_PRINT(vformat("%d resources still in use at exit (run with --verbose for details).", resources.size())); } } diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h index dbc1cb31de..9b49fcc3c8 100644 --- a/core/math/dynamic_bvh.h +++ b/core/math/dynamic_bvh.h @@ -328,7 +328,8 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) { volume.min = p_box.position; volume.max = p_box.position + p_box.size; - const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **stack = alloca_stack; stack[0] = bvh_root; int32_t depth = 1; int32_t threshold = ALLOCA_STACK_SIZE - 2; @@ -343,7 +344,8 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) { if (depth > threshold) { if (aux_stack.is_empty()) { aux_stack.resize(ALLOCA_STACK_SIZE * 2); - memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + alloca_stack = nullptr; } else { aux_stack.resize(aux_stack.size() * 2); } @@ -384,7 +386,8 @@ void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Ve } } - const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **stack = alloca_stack; stack[0] = bvh_root; int32_t depth = 1; int32_t threshold = ALLOCA_STACK_SIZE - 2; @@ -399,7 +402,8 @@ void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Ve if (depth > threshold) { if (aux_stack.is_empty()) { aux_stack.resize(ALLOCA_STACK_SIZE * 2); - memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + alloca_stack = nullptr; } else { aux_stack.resize(aux_stack.size() * 2); } @@ -436,7 +440,8 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu Vector3 bounds[2]; - const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **stack = alloca_stack; stack[0] = bvh_root; int32_t depth = 1; int32_t threshold = ALLOCA_STACK_SIZE - 2; @@ -456,7 +461,8 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu if (depth > threshold) { if (aux_stack.is_empty()) { aux_stack.resize(ALLOCA_STACK_SIZE * 2); - memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + alloca_stack = nullptr; } else { aux_stack.resize(aux_stack.size() * 2); } diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 3b274ecc2f..0b2d5e41cf 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -138,6 +138,8 @@ void Script::_bind_methods() { ClassDB::bind_method(D_METHOD("get_base_script"), &Script::get_base_script); ClassDB::bind_method(D_METHOD("get_instance_base_type"), &Script::get_instance_base_type); + ClassDB::bind_method(D_METHOD("get_global_name"), &Script::get_global_name); + ClassDB::bind_method(D_METHOD("has_script_signal", "signal_name"), &Script::has_script_signal); ClassDB::bind_method(D_METHOD("get_script_property_list"), &Script::_get_script_property_list); diff --git a/core/object/script_language.h b/core/object/script_language.h index 85e64c8d62..69da50f074 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -384,10 +384,12 @@ public: uint64_t call_count; uint64_t total_time; uint64_t self_time; + uint64_t internal_time; }; virtual void profiling_start() = 0; virtual void profiling_stop() = 0; + virtual void profiling_set_save_native_calls(bool p_enable) = 0; virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) = 0; virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) = 0; diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp index e326baf7eb..79f39cb626 100644 --- a/core/object/script_language_extension.cpp +++ b/core/object/script_language_extension.cpp @@ -145,6 +145,7 @@ void ScriptLanguageExtension::_bind_methods() { GDVIRTUAL_BIND(_profiling_start); GDVIRTUAL_BIND(_profiling_stop); + GDVIRTUAL_BIND(_profiling_set_save_native_calls, "enable"); GDVIRTUAL_BIND(_profiling_get_accumulated_data, "info_array", "info_max"); GDVIRTUAL_BIND(_profiling_get_frame_data, "info_array", "info_max"); diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index 00ab1cd6c0..852b2aebd8 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -607,6 +607,7 @@ public: EXBIND0(profiling_start) EXBIND0(profiling_stop) + EXBIND1(profiling_set_save_native_calls, bool) GDVIRTUAL2R(int, _profiling_get_accumulated_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int) diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 116210e8de..171074188f 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -1302,192 +1302,192 @@ struct VariantInitializer<Object *> { }; template <class T> -struct VariantZeroAssigner { +struct VariantDefaultInitializer { }; template <> -struct VariantZeroAssigner<bool> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_bool(v) = false; } +struct VariantDefaultInitializer<bool> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_bool(v) = false; } }; template <> -struct VariantZeroAssigner<int64_t> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int(v) = 0; } +struct VariantDefaultInitializer<int64_t> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_int(v) = 0; } }; template <> -struct VariantZeroAssigner<double> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float(v) = 0.0; } +struct VariantDefaultInitializer<double> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float(v) = 0.0; } }; template <> -struct VariantZeroAssigner<float> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float(v) = 0.0; } +struct VariantDefaultInitializer<float> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float(v) = 0.0; } }; template <> -struct VariantZeroAssigner<String> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string(v) = String(); } +struct VariantDefaultInitializer<String> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_string(v) = String(); } }; template <> -struct VariantZeroAssigner<Vector2> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2(v) = Vector2(); } +struct VariantDefaultInitializer<Vector2> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector2(v) = Vector2(); } }; template <> -struct VariantZeroAssigner<Vector2i> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2i(v) = Vector2i(); } +struct VariantDefaultInitializer<Vector2i> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector2i(v) = Vector2i(); } }; template <> -struct VariantZeroAssigner<Rect2> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rect2(v) = Rect2(); } +struct VariantDefaultInitializer<Rect2> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_rect2(v) = Rect2(); } }; template <> -struct VariantZeroAssigner<Rect2i> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rect2i(v) = Rect2i(); } +struct VariantDefaultInitializer<Rect2i> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_rect2i(v) = Rect2i(); } }; template <> -struct VariantZeroAssigner<Vector3> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3(v) = Vector3(); } +struct VariantDefaultInitializer<Vector3> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector3(v) = Vector3(); } }; template <> -struct VariantZeroAssigner<Vector3i> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3i(v) = Vector3i(); } +struct VariantDefaultInitializer<Vector3i> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector3i(v) = Vector3i(); } }; template <> -struct VariantZeroAssigner<Vector4> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector4(v) = Vector4(); } +struct VariantDefaultInitializer<Vector4> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector4(v) = Vector4(); } }; template <> -struct VariantZeroAssigner<Vector4i> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector4i(v) = Vector4i(); } +struct VariantDefaultInitializer<Vector4i> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector4i(v) = Vector4i(); } }; template <> -struct VariantZeroAssigner<Transform2D> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform2d(v) = Transform2D(); } +struct VariantDefaultInitializer<Transform2D> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_transform2d(v) = Transform2D(); } }; template <> -struct VariantZeroAssigner<Plane> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_plane(v) = Plane(); } +struct VariantDefaultInitializer<Plane> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_plane(v) = Plane(); } }; template <> -struct VariantZeroAssigner<Quaternion> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_quaternion(v) = Quaternion(); } +struct VariantDefaultInitializer<Quaternion> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_quaternion(v) = Quaternion(); } }; template <> -struct VariantZeroAssigner<AABB> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_aabb(v) = AABB(); } +struct VariantDefaultInitializer<AABB> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_aabb(v) = AABB(); } }; template <> -struct VariantZeroAssigner<Basis> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_basis(v) = Basis(); } +struct VariantDefaultInitializer<Basis> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_basis(v) = Basis(); } }; template <> -struct VariantZeroAssigner<Transform3D> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform(v) = Transform3D(); } +struct VariantDefaultInitializer<Transform3D> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_transform(v) = Transform3D(); } }; template <> -struct VariantZeroAssigner<Projection> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_projection(v) = Projection(); } +struct VariantDefaultInitializer<Projection> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_projection(v) = Projection(); } }; template <> -struct VariantZeroAssigner<Color> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_color(v) = Color(); } +struct VariantDefaultInitializer<Color> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_color(v) = Color(); } }; template <> -struct VariantZeroAssigner<StringName> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string_name(v) = StringName(); } +struct VariantDefaultInitializer<StringName> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_string_name(v) = StringName(); } }; template <> -struct VariantZeroAssigner<NodePath> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_node_path(v) = NodePath(); } +struct VariantDefaultInitializer<NodePath> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_node_path(v) = NodePath(); } }; template <> -struct VariantZeroAssigner<::RID> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rid(v) = RID(); } +struct VariantDefaultInitializer<::RID> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_rid(v) = RID(); } }; template <> -struct VariantZeroAssigner<Callable> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_callable(v) = Callable(); } +struct VariantDefaultInitializer<Callable> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_callable(v) = Callable(); } }; template <> -struct VariantZeroAssigner<Signal> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_signal(v) = Signal(); } +struct VariantDefaultInitializer<Signal> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_signal(v) = Signal(); } }; template <> -struct VariantZeroAssigner<Dictionary> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_dictionary(v) = Dictionary(); } +struct VariantDefaultInitializer<Dictionary> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_dictionary(v) = Dictionary(); } }; template <> -struct VariantZeroAssigner<Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_array(v) = Array(); } +struct VariantDefaultInitializer<Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_array(v) = Array(); } }; template <> -struct VariantZeroAssigner<PackedByteArray> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_byte_array(v) = PackedByteArray(); } +struct VariantDefaultInitializer<PackedByteArray> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_byte_array(v) = PackedByteArray(); } }; template <> -struct VariantZeroAssigner<PackedInt32Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int32_array(v) = PackedInt32Array(); } +struct VariantDefaultInitializer<PackedInt32Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_int32_array(v) = PackedInt32Array(); } }; template <> -struct VariantZeroAssigner<PackedInt64Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int64_array(v) = PackedInt64Array(); } +struct VariantDefaultInitializer<PackedInt64Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_int64_array(v) = PackedInt64Array(); } }; template <> -struct VariantZeroAssigner<PackedFloat32Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float32_array(v) = PackedFloat32Array(); } +struct VariantDefaultInitializer<PackedFloat32Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float32_array(v) = PackedFloat32Array(); } }; template <> -struct VariantZeroAssigner<PackedFloat64Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float64_array(v) = PackedFloat64Array(); } +struct VariantDefaultInitializer<PackedFloat64Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float64_array(v) = PackedFloat64Array(); } }; template <> -struct VariantZeroAssigner<PackedStringArray> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string_array(v) = PackedStringArray(); } +struct VariantDefaultInitializer<PackedStringArray> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_string_array(v) = PackedStringArray(); } }; template <> -struct VariantZeroAssigner<PackedVector2Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2_array(v) = PackedVector2Array(); } +struct VariantDefaultInitializer<PackedVector2Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector2_array(v) = PackedVector2Array(); } }; template <> -struct VariantZeroAssigner<PackedVector3Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3_array(v) = PackedVector3Array(); } +struct VariantDefaultInitializer<PackedVector3Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector3_array(v) = PackedVector3Array(); } }; template <> -struct VariantZeroAssigner<PackedColorArray> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_color_array(v) = PackedColorArray(); } +struct VariantDefaultInitializer<PackedColorArray> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_color_array(v) = PackedColorArray(); } }; template <class T> @@ -1504,7 +1504,7 @@ struct VariantTypeChanger { VariantInitializer<T>::init(v); } - VariantZeroAssigner<T>::zero(v); + VariantDefaultInitializer<T>::init(v); } }; diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 193284896a..77c5a168f3 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1393,7 +1393,7 @@ <description> Converts the given [param variant] to the given [param type], using the [enum Variant.Type] values. This method is generous with how it handles types, it can automatically convert between array types, convert numeric [String]s to [int], and converting most things to [String]. If the type conversion cannot be done, this method will return the default value for that type, for example converting [Rect2] to [Vector2] will always return [constant Vector2.ZERO]. This method will never show error messages as long as [param type] is a valid Variant type. - The returned value is a [Variant], but the data inside and the [enum Variant.Type] will be the same as the requested type. + The returned value is a [Variant], but the data inside and its type will be the same as the requested type. [codeblock] type_convert("Hi!", TYPE_INT) # Returns 0 type_convert("123", TYPE_INT) # Returns 123 diff --git a/doc/classes/AnimationNodeStateMachine.xml b/doc/classes/AnimationNodeStateMachine.xml index 0a1a7df2f4..1c96110540 100644 --- a/doc/classes/AnimationNodeStateMachine.xml +++ b/doc/classes/AnimationNodeStateMachine.xml @@ -143,6 +143,7 @@ <param index="0" name="name" type="StringName" /> <param index="1" name="node" type="AnimationNode" /> <description> + Replaces the given animation node with a new animation node. </description> </method> <method name="set_graph_offset"> diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml index 6fa8e4ae9f..98110748ef 100644 --- a/doc/classes/Area2D.xml +++ b/doc/classes/Area2D.xml @@ -6,6 +6,7 @@ <description> [Area2D] is a region of 2D space defined by one or multiple [CollisionShape2D] or [CollisionPolygon2D] child nodes. It detects when other [CollisionObject2D]s enter or exit it, and it also keeps track of which collision objects haven't exited it yet (i.e. which one are overlapping it). This node can also locally alter or override physics parameters (gravity, damping) and route audio to custom audio buses. + [b]Note:[/b] Areas and bodies created with [PhysicsServer2D] might not interact as expected with [Area2D]s, and might not emit signals or track objects correctly. </description> <tutorials> <link title="Using Area2D">$DOCS_URL/tutorials/physics/using_area_2d.html</link> diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml index 4f89e9b015..918869b83d 100644 --- a/doc/classes/Area3D.xml +++ b/doc/classes/Area3D.xml @@ -6,6 +6,7 @@ <description> [Area3D] is a region of 3D space defined by one or multiple [CollisionShape3D] or [CollisionPolygon3D] child nodes. It detects when other [CollisionObject3D]s enter or exit it, and it also keeps track of which collision objects haven't exited it yet (i.e. which one are overlapping it). This node can also locally alter or override physics parameters (gravity, damping) and route audio to custom audio buses. + [b]Note:[/b] Areas and bodies created with [PhysicsServer3D] might not interact as expected with [Area3D]s, and might not emit signals or track objects correctly. [b]Warning:[/b] Using a [ConcavePolygonShape3D] inside a [CollisionShape3D] child of this node (created e.g. by using the [b]Create Trimesh Collision Sibling[/b] option in the [b]Mesh[/b] menu that appears when selecting a [MeshInstance3D] node) may give unexpected results, since this collision shape is hollow. If this is not desired, it has to be split into multiple [ConvexPolygonShape3D]s or primitive shapes like [BoxShape3D], or in some cases it may be replaceable by a [CollisionPolygon3D]. </description> <tutorials> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 7f7dc1d288..8436cbf6ee 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -235,6 +235,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -258,6 +261,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -281,6 +287,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -305,6 +314,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -327,6 +339,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -353,6 +368,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -376,6 +394,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -391,6 +412,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -408,6 +432,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -421,6 +448,9 @@ [codeblock] "_main" - Main menu (macOS). "_dock" - Dock popup menu (macOS). + "_apple" - Apple menu (macOS, custom items added before "Services"). + "_window" - Window menu (macOS, custom items added after "Bring All to Front"). + "_help" - Help menu (macOS). [/codeblock] </description> </method> @@ -549,6 +579,13 @@ [b]Note:[/b] This method is implemented only on macOS. </description> </method> + <method name="global_menu_get_system_menu_roots" qualifiers="const"> + <return type="Dictionary" /> + <description> + Returns Dictionary of supported system menu IDs and names. + [b]Note:[/b] This method is implemented only on macOS. + </description> + </method> <method name="global_menu_is_item_checkable" qualifiers="const"> <return type="bool" /> <param index="0" name="menu_root" type="String" /> @@ -1085,6 +1122,10 @@ <param index="0" name="name" type="String" /> <description> Set active tablet driver name. + Supported drivers: + - [code]winink[/code]: Windows Ink API, default (Windows 8.1+ required). + - [code]wintab[/code]: Wacom Wintab API (compatible device driver required). + - [code]dummy[/code]: Dummy driver, tablet input is disabled. [b]Note:[/b] This method is implemented only on Windows. </description> </method> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index ec051c0545..93d2bb8548 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -185,6 +185,9 @@ <member name="debugger/auto_switch_to_remote_scene_tree" type="bool" setter="" getter=""> If [code]true[/code], automatically switches to the [b]Remote[/b] scene tree when running the project from the editor. If [code]false[/code], stays on the [b]Local[/b] scene tree when running the project from the editor. </member> + <member name="debugger/profile_native_calls" type="bool" setter="" getter=""> + If [code]true[/code], enables collection of profiling data from non-GDScript Godot functions, such as engine class methods. Enabling this slows execution while profiling further. + </member> <member name="debugger/profiler_frame_history_size" type="int" setter="" getter=""> The size of the profiler's frame history. The default value (3600) allows seeing up to 60 seconds of profiling if the project renders at a constant 60 FPS. Higher values allow viewing longer periods of profiling in the graphs, especially when the project is running at high framerates. </member> @@ -599,6 +602,9 @@ <member name="interface/editor/single_window_mode" type="bool" setter="" getter=""> If [code]true[/code], embed modal windows such as docks inside the main editor window. When single-window mode is enabled, tooltips will also be embedded inside the main editor window, which means they can't be displayed outside of the editor window. </member> + <member name="interface/editor/ui_layout_direction" type="int" setter="" getter=""> + Editor UI default layout direction. + </member> <member name="interface/editor/unfocused_low_processor_mode_sleep_usec" type="float" setter="" getter=""> When the editor window is unfocused, the amount of sleeping between frames when the low-processor usage mode is enabled (in microseconds). Higher values will result in lower CPU/GPU usage, which can improve battery life on laptops (in addition to improving the running project's performance if the editor has to redraw continuously). However, higher values will result in a less responsive editor. The default value is set to limit the editor to 20 FPS when the editor window is unfocused. See also [member interface/editor/low_processor_mode_sleep_usec]. </member> diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index 9765b10d22..95e760be9f 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -237,6 +237,9 @@ The thickness of the lines between the nodes. </member> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="2" /> + <member name="grid_pattern" type="int" setter="set_grid_pattern" getter="get_grid_pattern" enum="GraphEdit.GridPattern" default="0"> + The pattern used for drawing the grid. + </member> <member name="minimap_enabled" type="bool" setter="set_minimap_enabled" getter="is_minimap_enabled" default="true"> If [code]true[/code], the minimap is visible. </member> @@ -405,16 +408,22 @@ <constant name="SCROLL_PANS" value="1" enum="PanningScheme"> [kbd]Mouse Wheel[/kbd] will move the view, [kbd]Ctrl + Mouse Wheel[/kbd] will zoom. </constant> + <constant name="GRID_PATTERN_LINES" value="0" enum="GridPattern"> + Draw the grid using solid lines. + </constant> + <constant name="GRID_PATTERN_DOTS" value="1" enum="GridPattern"> + Draw the grid using dots. + </constant> </constants> <theme_items> <theme_item name="activity" data_type="color" type="Color" default="Color(1, 1, 1, 1)"> Color of the connection's activity (see [method set_connection_activity]). </theme_item> <theme_item name="grid_major" data_type="color" type="Color" default="Color(1, 1, 1, 0.2)"> - Color of major grid lines. + Color of major grid lines/dots. </theme_item> <theme_item name="grid_minor" data_type="color" type="Color" default="Color(1, 1, 1, 0.05)"> - Color of minor grid lines. + Color of minor grid lines/dots. </theme_item> <theme_item name="selection_fill" data_type="color" type="Color" default="Color(1, 1, 1, 0.3)"> The fill color of the selection rectangle. diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml index fda45a42c8..94b1815dce 100644 --- a/doc/classes/Label.xml +++ b/doc/classes/Label.xml @@ -10,6 +10,13 @@ <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> </tutorials> <methods> + <method name="get_character_bounds" qualifiers="const"> + <return type="Rect2" /> + <param index="0" name="pos" type="int" /> + <description> + Returns the bounding rectangle of the character at position [param pos]. If the character is a non-visual character or [param pos] is outside the valid range, an empty [Rect2] is returned. If the character is a part of a composite grapheme, the bounding rectangle of the whole grapheme is returned. + </description> + </method> <method name="get_line_count" qualifiers="const"> <return type="int" /> <description> diff --git a/doc/classes/MultiplayerPeer.xml b/doc/classes/MultiplayerPeer.xml index 350fffed32..39980a05e1 100644 --- a/doc/classes/MultiplayerPeer.xml +++ b/doc/classes/MultiplayerPeer.xml @@ -48,7 +48,7 @@ <method name="get_packet_mode" qualifiers="const"> <return type="int" enum="MultiplayerPeer.TransferMode" /> <description> - Returns the [enum MultiplayerPeer.TransferMode] the remote peer used to send the next available packet. See [method PacketPeer.get_available_packet_count]. + Returns the transfer mode the remote peer used to send the next available packet. See [method PacketPeer.get_available_packet_count]. </description> </method> <method name="get_packet_peer" qualifiers="const"> diff --git a/doc/classes/MultiplayerPeerExtension.xml b/doc/classes/MultiplayerPeerExtension.xml index 9b2b5dbc94..8fd6755a7b 100644 --- a/doc/classes/MultiplayerPeerExtension.xml +++ b/doc/classes/MultiplayerPeerExtension.xml @@ -58,7 +58,7 @@ <method name="_get_packet_mode" qualifiers="virtual const"> <return type="int" enum="MultiplayerPeer.TransferMode" /> <description> - Called to get the [enum MultiplayerPeer.TransferMode] the remote peer used to send the next available packet. See [method MultiplayerPeer.get_packet_mode]. + Called to get the transfer mode the remote peer used to send the next available packet. See [method MultiplayerPeer.get_packet_mode]. </description> </method> <method name="_get_packet_peer" qualifiers="virtual const"> diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index e1c79ad8a8..02631841bf 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -31,6 +31,27 @@ Return [code]true[/code] if the specified [param agent] uses avoidance. </description> </method> + <method name="agent_get_avoidance_layers" qualifiers="const"> + <return type="int" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the [code]avoidance_layers[/code] bitmask of the specified [param agent]. + </description> + </method> + <method name="agent_get_avoidance_mask" qualifiers="const"> + <return type="int" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the [code]avoidance_mask[/code] bitmask of the specified [param agent]. + </description> + </method> + <method name="agent_get_avoidance_priority" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the [code]avoidance_priority[/code] of the specified [param agent]. + </description> + </method> <method name="agent_get_map" qualifiers="const"> <return type="RID" /> <param index="0" name="agent" type="RID" /> @@ -38,6 +59,27 @@ Returns the navigation map [RID] the requested [param agent] is currently assigned to. </description> </method> + <method name="agent_get_max_neighbors" qualifiers="const"> + <return type="int" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the maximum number of other agents the specified [param agent] takes into account in the navigation. + </description> + </method> + <method name="agent_get_max_speed" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the maximum speed of the specified [param agent]. + </description> + </method> + <method name="agent_get_neighbor_distance" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the maximum distance to other agents the specified [param agent] takes into account in the navigation. + </description> + </method> <method name="agent_get_paused" qualifiers="const"> <return type="bool" /> <param index="0" name="agent" type="RID" /> @@ -45,6 +87,48 @@ Returns [code]true[/code] if the specified [param agent] is paused. </description> </method> + <method name="agent_get_position" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the position of the specified [param agent] in world space. + </description> + </method> + <method name="agent_get_radius" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the radius of the specified [param agent]. + </description> + </method> + <method name="agent_get_time_horizon_agents" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the minimal amount of time for which the specified [param agent]'s velocities that are computed by the simulation are safe with respect to other agents. + </description> + </method> + <method name="agent_get_time_horizon_obstacles" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the minimal amount of time for which the specified [param agent]'s velocities that are computed by the simulation are safe with respect to static avoidance obstacles. + </description> + </method> + <method name="agent_get_velocity" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the velocity of the specified [param agent]. + </description> + </method> + <method name="agent_has_avoidance_callback" qualifiers="const"> + <return type="bool" /> + <param index="0" name="agent" type="RID" /> + <description> + Return [code]true[/code] if the specified [param agent] has an avoidance callback. + </description> + </method> <method name="agent_is_map_changed" qualifiers="const"> <return type="bool" /> <param index="0" name="agent" type="RID" /> @@ -530,6 +614,13 @@ Returns [code]true[/code] if the provided [param obstacle] has avoidance enabled. </description> </method> + <method name="obstacle_get_avoidance_layers" qualifiers="const"> + <return type="int" /> + <param index="0" name="obstacle" type="RID" /> + <description> + Returns the [code]avoidance_layers[/code] bitmask of the specified [param obstacle]. + </description> + </method> <method name="obstacle_get_map" qualifiers="const"> <return type="RID" /> <param index="0" name="obstacle" type="RID" /> @@ -544,6 +635,34 @@ Returns [code]true[/code] if the specified [param obstacle] is paused. </description> </method> + <method name="obstacle_get_position" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="obstacle" type="RID" /> + <description> + Returns the position of the specified [param obstacle] in world space. + </description> + </method> + <method name="obstacle_get_radius" qualifiers="const"> + <return type="float" /> + <param index="0" name="obstacle" type="RID" /> + <description> + Returns the radius of the specified dynamic [param obstacle]. + </description> + </method> + <method name="obstacle_get_velocity" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="obstacle" type="RID" /> + <description> + Returns the velocity of the specified dynamic [param obstacle]. + </description> + </method> + <method name="obstacle_get_vertices" qualifiers="const"> + <return type="PackedVector2Array" /> + <param index="0" name="obstacle" type="RID" /> + <description> + Returns the outline vertices for the specified [param obstacle]. + </description> + </method> <method name="obstacle_set_avoidance_enabled"> <return type="void" /> <param index="0" name="obstacle" type="RID" /> @@ -703,6 +822,13 @@ If [param uniformly] is [code]false[/code], just a random polygon and face is picked (faster). </description> </method> + <method name="region_get_transform" qualifiers="const"> + <return type="Transform2D" /> + <param index="0" name="region" type="RID" /> + <description> + Returns the global transformation of this [param region]. + </description> + </method> <method name="region_get_travel_cost" qualifiers="const"> <return type="float" /> <param index="0" name="region" type="RID" /> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index 46b6697947..20af517397 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -31,6 +31,34 @@ Returns [code]true[/code] if the provided [param agent] has avoidance enabled. </description> </method> + <method name="agent_get_avoidance_layers" qualifiers="const"> + <return type="int" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the [code]avoidance_layers[/code] bitmask of the specified [param agent]. + </description> + </method> + <method name="agent_get_avoidance_mask" qualifiers="const"> + <return type="int" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the [code]avoidance_mask[/code] bitmask of the specified [param agent]. + </description> + </method> + <method name="agent_get_avoidance_priority" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the [code]avoidance_priority[/code] of the specified [param agent]. + </description> + </method> + <method name="agent_get_height" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the [code]height[/code] of the specified [param agent]. + </description> + </method> <method name="agent_get_map" qualifiers="const"> <return type="RID" /> <param index="0" name="agent" type="RID" /> @@ -38,6 +66,27 @@ Returns the navigation map [RID] the requested [param agent] is currently assigned to. </description> </method> + <method name="agent_get_max_neighbors" qualifiers="const"> + <return type="int" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the maximum number of other agents the specified [param agent] takes into account in the navigation. + </description> + </method> + <method name="agent_get_max_speed" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the maximum speed of the specified [param agent]. + </description> + </method> + <method name="agent_get_neighbor_distance" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the maximum distance to other agents the specified [param agent] takes into account in the navigation. + </description> + </method> <method name="agent_get_paused" qualifiers="const"> <return type="bool" /> <param index="0" name="agent" type="RID" /> @@ -45,6 +94,34 @@ Returns [code]true[/code] if the specified [param agent] is paused. </description> </method> + <method name="agent_get_position" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the position of the specified [param agent] in world space. + </description> + </method> + <method name="agent_get_radius" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the radius of the specified [param agent]. + </description> + </method> + <method name="agent_get_time_horizon_agents" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the minimal amount of time for which the specified [param agent]'s velocities that are computed by the simulation are safe with respect to other agents. + </description> + </method> + <method name="agent_get_time_horizon_obstacles" qualifiers="const"> + <return type="float" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the minimal amount of time for which the specified [param agent]'s velocities that are computed by the simulation are safe with respect to static avoidance obstacles. + </description> + </method> <method name="agent_get_use_3d_avoidance" qualifiers="const"> <return type="bool" /> <param index="0" name="agent" type="RID" /> @@ -52,6 +129,20 @@ Returns [code]true[/code] if the provided [param agent] uses avoidance in 3D space Vector3(x,y,z) instead of horizontal 2D Vector2(x,y) / Vector3(x,0.0,z). </description> </method> + <method name="agent_get_velocity" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="agent" type="RID" /> + <description> + Returns the velocity of the specified [param agent]. + </description> + </method> + <method name="agent_has_avoidance_callback" qualifiers="const"> + <return type="bool" /> + <param index="0" name="agent" type="RID" /> + <description> + Return [code]true[/code] if the specified [param agent] has an avoidance callback. + </description> + </method> <method name="agent_is_map_changed" qualifiers="const"> <return type="bool" /> <param index="0" name="agent" type="RID" /> @@ -610,6 +701,20 @@ Returns [code]true[/code] if the provided [param obstacle] has avoidance enabled. </description> </method> + <method name="obstacle_get_avoidance_layers" qualifiers="const"> + <return type="int" /> + <param index="0" name="obstacle" type="RID" /> + <description> + Returns the [code]avoidance_layers[/code] bitmask of the specified [param obstacle]. + </description> + </method> + <method name="obstacle_get_height" qualifiers="const"> + <return type="float" /> + <param index="0" name="obstacle" type="RID" /> + <description> + Returns the [code]height[/code] of the specified [param obstacle]. + </description> + </method> <method name="obstacle_get_map" qualifiers="const"> <return type="RID" /> <param index="0" name="obstacle" type="RID" /> @@ -624,6 +729,20 @@ Returns [code]true[/code] if the specified [param obstacle] is paused. </description> </method> + <method name="obstacle_get_position" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="obstacle" type="RID" /> + <description> + Returns the position of the specified [param obstacle] in world space. + </description> + </method> + <method name="obstacle_get_radius" qualifiers="const"> + <return type="float" /> + <param index="0" name="obstacle" type="RID" /> + <description> + Returns the radius of the specified dynamic [param obstacle]. + </description> + </method> <method name="obstacle_get_use_3d_avoidance" qualifiers="const"> <return type="bool" /> <param index="0" name="obstacle" type="RID" /> @@ -631,6 +750,20 @@ Returns [code]true[/code] if the provided [param obstacle] uses avoidance in 3D space Vector3(x,y,z) instead of horizontal 2D Vector2(x,y) / Vector3(x,0.0,z). </description> </method> + <method name="obstacle_get_velocity" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="obstacle" type="RID" /> + <description> + Returns the velocity of the specified dynamic [param obstacle]. + </description> + </method> + <method name="obstacle_get_vertices" qualifiers="const"> + <return type="PackedVector3Array" /> + <param index="0" name="obstacle" type="RID" /> + <description> + Returns the outline vertices for the specified [param obstacle]. + </description> + </method> <method name="obstacle_set_avoidance_enabled"> <return type="void" /> <param index="0" name="obstacle" type="RID" /> @@ -815,6 +948,13 @@ If [param uniformly] is [code]false[/code], just a random polygon and face is picked (faster). </description> </method> + <method name="region_get_transform" qualifiers="const"> + <return type="Transform3D" /> + <param index="0" name="region" type="RID" /> + <description> + Returns the global transformation of this [param region]. + </description> + </method> <method name="region_get_travel_cost" qualifiers="const"> <return type="float" /> <param index="0" name="region" type="RID" /> diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml index aa20260849..3e2d939e10 100644 --- a/doc/classes/ParticleProcessMaterial.xml +++ b/doc/classes/ParticleProcessMaterial.xml @@ -9,6 +9,14 @@ <tutorials> </tutorials> <methods> + <method name="get_param" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="param" type="int" enum="ParticleProcessMaterial.Parameter" /> + <description> + Returns the minimum and maximum values of the given [param param] as a vector. + The [code]x[/code] component of the returned vector corresponds to minimum and the [code]y[/code] component corresponds to maximum. + </description> + </method> <method name="get_param_max" qualifiers="const"> <return type="float" /> <param index="0" name="param" type="int" enum="ParticleProcessMaterial.Parameter" /> @@ -37,6 +45,15 @@ Returns [code]true[/code] if the specified particle flag is enabled. See [enum ParticleFlags] for options. </description> </method> + <method name="set_param"> + <return type="void" /> + <param index="0" name="param" type="int" enum="ParticleProcessMaterial.Parameter" /> + <param index="1" name="value" type="Vector2" /> + <description> + Sets the minimum and maximum values of the given [param param]. + The [code]x[/code] component of the argument vector corresponds to minimum and the [code]y[/code] component corresponds to maximum. + </description> + </method> <method name="set_param_max"> <return type="void" /> <param index="0" name="param" type="int" enum="ParticleProcessMaterial.Parameter" /> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index 3f4ec1b677..40d5a9f4a2 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -346,6 +346,12 @@ Returns [code]true[/code] if the specified item's shortcut is disabled. </description> </method> + <method name="is_system_menu" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the menu is bound to the special system menu. + </description> + </method> <method name="remove_item"> <return type="void" /> <param index="0" name="index" type="int" /> @@ -566,6 +572,9 @@ <member name="submenu_popup_delay" type="float" setter="set_submenu_popup_delay" getter="get_submenu_popup_delay" default="0.3"> Sets the delay time in seconds for the submenu item to popup on mouse hovering. If the popup menu is added as a child of another (acting as a submenu), it will inherit the delay time of the parent menu item. </member> + <member name="system_menu_root" type="String" setter="set_system_menu_root" getter="get_system_menu_root" default=""""> + If set to one of the values returned by [method DisplayServer.global_menu_get_system_menu_roots], this [PopupMenu] is bound to the special system menu. Only one [PopupMenu] can be bound to each special menu at a time. + </member> </members> <signals> <signal name="id_focused"> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 6bbef47763..a3ec4d25f1 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -3337,7 +3337,7 @@ <return type="int" enum="Image.Format" /> <param index="0" name="texture" type="RID" /> <description> - Returns the [enum Image.Format] for the texture. + Returns the format for the texture. </description> </method> <method name="texture_get_native_handle" qualifiers="const"> diff --git a/doc/classes/RichTextEffect.xml b/doc/classes/RichTextEffect.xml index ca95557f1b..774fa2bf9c 100644 --- a/doc/classes/RichTextEffect.xml +++ b/doc/classes/RichTextEffect.xml @@ -4,7 +4,7 @@ A custom effect for a [RichTextLabel]. </brief_description> <description> - A custom effect for a [RichTextLabel]. + A custom effect for a [RichTextLabel], which can be loaded in the [RichTextLabel] inspector or using [method RichTextLabel.install_effect]. [b]Note:[/b] For a [RichTextEffect] to be usable, a BBCode tag must be defined as a member variable called [code]bbcode[/code] in the script. [codeblocks] [gdscript skip-lint] diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index c9a48e46b2..85dea1485a 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -225,7 +225,28 @@ <return type="void" /> <param index="0" name="effect" type="Variant" /> <description> - Installs a custom effect. [param effect] should be a valid [RichTextEffect]. + Installs a custom effect. This can also be done in the RichTextLabel inspector using the [member custom_effects] property. [param effect] should be a valid [RichTextEffect]. + Example RichTextEffect: + [codeblock] + # effect.gd + class_name MyCustomEffect + extends RichTextEffect + + var bbcode = "my_custom_effect" + + # ... + [/codeblock] + Registering the above effect in RichTextLabel from script: + [codeblock] + # rich_text_label.gd + extends RichTextLabel + + func _ready(): + install_effect(MyCustomEffect.new()) + + # Alternatively, if not using `class_name` in the script that extends RichTextEffect: + install_effect(preload("res://effect.gd").new()) + [/codeblock] </description> </method> <method name="is_menu_visible" qualifiers="const"> diff --git a/doc/classes/Script.xml b/doc/classes/Script.xml index 28d763585d..fa8e4ef5f2 100644 --- a/doc/classes/Script.xml +++ b/doc/classes/Script.xml @@ -24,6 +24,27 @@ Returns the script directly inherited by this script. </description> </method> + <method name="get_global_name" qualifiers="const"> + <return type="StringName" /> + <description> + Returns the class name associated with the script, if there is one. Returns an empty string otherwise. + To give the script a global name, you can use the [code]class_name[/code] keyword in GDScript and the [code][GlobalClass][/code] attribute in C#. + [codeblocks] + [gdscript] + class_name MyNode + extends Node + [/gdscript] + [csharp] + using Godot; + + [GlobalClass] + public partial class MyNode : Node + { + } + [/csharp] + [/codeblocks] + </description> + </method> <method name="get_instance_base_type" qualifiers="const"> <return type="StringName" /> <description> diff --git a/doc/classes/ScriptLanguageExtension.xml b/doc/classes/ScriptLanguageExtension.xml index 1a61618b53..b4e2f40612 100644 --- a/doc/classes/ScriptLanguageExtension.xml +++ b/doc/classes/ScriptLanguageExtension.xml @@ -276,6 +276,12 @@ <description> </description> </method> + <method name="_profiling_set_save_native_calls" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="enable" type="bool" /> + <description> + </description> + </method> <method name="_profiling_start" qualifiers="virtual"> <return type="void" /> <description> diff --git a/doc/classes/TextureProgressBar.xml b/doc/classes/TextureProgressBar.xml index 0c1e9adf71..6ab1b86e5c 100644 --- a/doc/classes/TextureProgressBar.xml +++ b/doc/classes/TextureProgressBar.xml @@ -34,28 +34,28 @@ If [code]true[/code], Godot treats the bar's textures like in [NinePatchRect]. Use the [code]stretch_margin_*[/code] properties like [member stretch_margin_bottom] to set up the nine patch's 3×3 grid. When using a radial [member fill_mode], this setting will enable stretching. </member> <member name="radial_center_offset" type="Vector2" setter="set_radial_center_offset" getter="get_radial_center_offset" default="Vector2(0, 0)"> - Offsets [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE] or [constant FILL_COUNTER_CLOCKWISE]. + Offsets [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE], [constant FILL_COUNTER_CLOCKWISE], or [constant FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE]. </member> <member name="radial_fill_degrees" type="float" setter="set_fill_degrees" getter="get_fill_degrees" default="360.0"> - Upper limit for the fill of [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE] or [constant FILL_COUNTER_CLOCKWISE]. When the node's [code]value[/code] is equal to its [code]max_value[/code], the texture fills up to this angle. + Upper limit for the fill of [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE], [constant FILL_COUNTER_CLOCKWISE], or [constant FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE]. When the node's [code]value[/code] is equal to its [code]max_value[/code], the texture fills up to this angle. See [member Range.value], [member Range.max_value]. </member> <member name="radial_initial_angle" type="float" setter="set_radial_initial_angle" getter="get_radial_initial_angle" default="0.0"> - Starting angle for the fill of [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE] or [constant FILL_COUNTER_CLOCKWISE]. When the node's [code]value[/code] is equal to its [code]min_value[/code], the texture doesn't show up at all. When the [code]value[/code] increases, the texture fills and tends towards [member radial_fill_degrees]. + Starting angle for the fill of [member texture_progress] if [member fill_mode] is [constant FILL_CLOCKWISE], [constant FILL_COUNTER_CLOCKWISE], or [constant FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE]. When the node's [code]value[/code] is equal to its [code]min_value[/code], the texture doesn't show up at all. When the [code]value[/code] increases, the texture fills and tends towards [member radial_fill_degrees]. </member> <member name="size_flags_vertical" type="int" setter="set_v_size_flags" getter="get_v_size_flags" overrides="Control" enum="Control.SizeFlags" is_bitfield="true" default="1" /> <member name="step" type="float" setter="set_step" getter="get_step" overrides="Range" default="1.0" /> <member name="stretch_margin_bottom" type="int" setter="set_stretch_margin" getter="get_stretch_margin" default="0"> - The height of the 9-patch's bottom row. A margin of 16 means the 9-slice's bottom corners and side will have a height of 16 pixels. You can set all 4 margin values individually to create panels with non-uniform borders. + The height of the 9-patch's bottom row. A margin of 16 means the 9-slice's bottom corners and side will have a height of 16 pixels. You can set all 4 margin values individually to create panels with non-uniform borders. Only effective if [member nine_patch_stretch] is [code]true[/code]. </member> <member name="stretch_margin_left" type="int" setter="set_stretch_margin" getter="get_stretch_margin" default="0"> - The width of the 9-patch's left column. + The width of the 9-patch's left column. Only effective if [member nine_patch_stretch] is [code]true[/code]. </member> <member name="stretch_margin_right" type="int" setter="set_stretch_margin" getter="get_stretch_margin" default="0"> - The width of the 9-patch's right column. + The width of the 9-patch's right column. Only effective if [member nine_patch_stretch] is [code]true[/code]. </member> <member name="stretch_margin_top" type="int" setter="set_stretch_margin" getter="get_stretch_margin" default="0"> - The height of the 9-patch's top row. + The height of the 9-patch's top row. Only effective if [member nine_patch_stretch] is [code]true[/code]. </member> <member name="texture_over" type="Texture2D" setter="set_over_texture" getter="get_over_texture"> [Texture2D] that draws over the progress bar. Use it to add highlights or an upper-frame that hides part of [member texture_progress]. diff --git a/doc/classes/TileSetAtlasSource.xml b/doc/classes/TileSetAtlasSource.xml index 755c266cd9..6f212274f8 100644 --- a/doc/classes/TileSetAtlasSource.xml +++ b/doc/classes/TileSetAtlasSource.xml @@ -90,7 +90,7 @@ <return type="int" enum="TileSetAtlasSource.TileAnimationMode" /> <param index="0" name="atlas_coords" type="Vector2i" /> <description> - Returns the [enum TileAnimationMode] of the tile at [param atlas_coords]. See also [method set_tile_animation_mode]. + Returns the tile animation mode of the tile at [param atlas_coords]. See also [method set_tile_animation_mode]. </description> </method> <method name="get_tile_animation_separation" qualifiers="const"> @@ -239,7 +239,7 @@ <param index="0" name="atlas_coords" type="Vector2i" /> <param index="1" name="mode" type="int" enum="TileSetAtlasSource.TileAnimationMode" /> <description> - Sets the [enum TileAnimationMode] of the tile at [param atlas_coords] to [param mode]. See also [method get_tile_animation_mode]. + Sets the tile animation mode of the tile at [param atlas_coords] to [param mode]. See also [method get_tile_animation_mode]. </description> </method> <method name="set_tile_animation_separation"> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index e322e3adc0..bf5a504aba 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -506,6 +506,9 @@ <theme_item name="font_color" data_type="color" type="Color" default="Color(0.7, 0.7, 0.7, 1)"> Default text [Color] of the item. </theme_item> + <theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)"> + Text [Color] for a [constant TreeItem.CELL_MODE_CHECK] mode cell when it's non-editable (see [method TreeItem.set_editable]). + </theme_item> <theme_item name="font_outline_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)"> The tint of text outline of the item. </theme_item> @@ -619,16 +622,25 @@ The arrow icon used when a foldable item is collapsed (for right-to-left layouts). </theme_item> <theme_item name="checked" data_type="icon" type="Texture2D"> - The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is checked. + The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is checked and editable (see [method TreeItem.set_editable]). + </theme_item> + <theme_item name="checked_disabled" data_type="icon" type="Texture2D"> + The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is checked and non-editable (see [method TreeItem.set_editable]). </theme_item> <theme_item name="indeterminate" data_type="icon" type="Texture2D"> - The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is indeterminate. + The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is indeterminate and editable (see [method TreeItem.set_editable]). + </theme_item> + <theme_item name="indeterminate_disabled" data_type="icon" type="Texture2D"> + The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is indeterminate and non-editable (see [method TreeItem.set_editable]). </theme_item> <theme_item name="select_arrow" data_type="icon" type="Texture2D"> The arrow icon to display for the [constant TreeItem.CELL_MODE_RANGE] mode cell. </theme_item> <theme_item name="unchecked" data_type="icon" type="Texture2D"> - The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is unchecked. + The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is unchecked and editable (see [method TreeItem.set_editable]). + </theme_item> + <theme_item name="unchecked_disabled" data_type="icon" type="Texture2D"> + The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is unchecked and non-editable (see [method TreeItem.set_editable]). </theme_item> <theme_item name="updown" data_type="icon" type="Texture2D"> The updown arrow icon to display for the [constant TreeItem.CELL_MODE_RANGE] mode cell. diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index b6a407e042..6e521c9e14 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -75,7 +75,7 @@ <return type="int" enum="Viewport.PositionalShadowAtlasQuadrantSubdiv" /> <param index="0" name="quadrant" type="int" /> <description> - Returns the [enum PositionalShadowAtlasQuadrantSubdiv] of the specified quadrant. + Returns the positional shadow atlas quadrant subdivision of the specified quadrant. </description> </method> <method name="get_render_info"> @@ -128,6 +128,13 @@ Returns the [Control] having the focus within this viewport. If no [Control] has the focus, returns null. </description> </method> + <method name="gui_get_hovered_control" qualifiers="const"> + <return type="Control" /> + <description> + Returns the [Control] that the mouse is currently hovering over in this viewport. If no [Control] has the cursor, returns null. + Typically the leaf [Control] node or deepest level of the subtree which claims hover. This is very useful when used together with [method Node.is_ancestor_of] to find if the mouse is within a control tree. + </description> + </method> <method name="gui_is_drag_successful" qualifiers="const"> <return type="bool" /> <description> diff --git a/doc/classes/XRInterface.xml b/doc/classes/XRInterface.xml index 99d6e67e51..a7878378dd 100644 --- a/doc/classes/XRInterface.xml +++ b/doc/classes/XRInterface.xml @@ -119,7 +119,7 @@ <param index="0" name="mode" type="int" enum="XRInterface.EnvironmentBlendMode" /> <description> Sets the active environment blend mode. - [param mode] is the [enum XRInterface.EnvironmentBlendMode] starting with the next frame. + [param mode] is the environment blend mode starting with the next frame. [b]Note:[/b] Not all runtimes support all environment blend modes, so it is important to check this at startup. For example: [codeblock] func _ready(): diff --git a/doc/classes/XRInterfaceExtension.xml b/doc/classes/XRInterfaceExtension.xml index ea2bbf4cfb..b2c27bedf3 100644 --- a/doc/classes/XRInterfaceExtension.xml +++ b/doc/classes/XRInterfaceExtension.xml @@ -67,7 +67,7 @@ <method name="_get_play_area_mode" qualifiers="virtual const"> <return type="int" enum="XRInterface.PlayAreaMode" /> <description> - Returns the [enum XRInterface.PlayAreaMode] that sets up our play area. + Returns the play area mode that sets up our play area. </description> </method> <method name="_get_projection_for_view" qualifiers="virtual"> diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index d47b315c40..ab923f99fe 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -168,7 +168,7 @@ void ActionMapEditor::_tree_button_pressed(Object *p_item, int p_column, int p_i current_action_name = item->get_meta("__name"); current_action_event_index = -1; - event_config_dialog->popup_and_configure(); + event_config_dialog->popup_and_configure(Ref<InputEvent>(), current_action_name); } break; case ActionMapEditor::BUTTON_EDIT_EVENT: { // Action and Action name is located on the parent of the event. @@ -179,7 +179,7 @@ void ActionMapEditor::_tree_button_pressed(Object *p_item, int p_column, int p_i Ref<InputEvent> ie = item->get_meta("__event"); if (ie.is_valid()) { - event_config_dialog->popup_and_configure(ie); + event_config_dialog->popup_and_configure(ie, current_action_name); } } break; case ActionMapEditor::BUTTON_REMOVE_ACTION: { diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index f72538e3de..d54fd62e8c 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -97,6 +97,7 @@ void EditorProfiler::clear() { plot_sigs.clear(); plot_sigs.insert("physics_frame_time"); plot_sigs.insert("category_frame_time"); + display_internal_profiles->set_visible(EDITOR_GET("debugger/profile_native_calls")); updating_frame = true; cursor_metric_edit->set_min(0); @@ -352,6 +353,9 @@ void EditorProfiler::_update_frame() { for (int j = m.categories[i].items.size() - 1; j >= 0; j--) { const Metric::Category::Item &it = m.categories[i].items[j]; + if (it.internal == it.total && !display_internal_profiles->is_pressed() && m.categories[i].name == "Script Functions") { + continue; + } TreeItem *item = variables->create_item(category); item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); item->set_editable(0, true); @@ -363,6 +367,9 @@ void EditorProfiler::_update_frame() { item->set_tooltip_text(0, it.name + "\n" + it.script + ":" + itos(it.line)); float time = dtime == DISPLAY_SELF_TIME ? it.self : it.total; + if (dtime == DISPLAY_SELF_TIME && !display_internal_profiles->is_pressed()) { + time += it.internal; + } item->set_text(1, _get_time_as_text(m, time, it.calls)); @@ -404,6 +411,10 @@ void EditorProfiler::_clear_pressed() { _update_plot(); } +void EditorProfiler::_internal_profiles_pressed() { + _combo_changed(0); +} + void EditorProfiler::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: @@ -635,6 +646,11 @@ EditorProfiler::EditorProfiler() { hb->add_child(display_time); + display_internal_profiles = memnew(CheckButton(TTR("Display internal functions"))); + display_internal_profiles->set_pressed(false); + display_internal_profiles->connect("pressed", callable_mp(this, &EditorProfiler::_internal_profiles_pressed)); + hb->add_child(display_internal_profiles); + hb->add_spacer(); hb->add_child(memnew(Label(TTR("Frame #:")))); diff --git a/editor/debugger/editor_profiler.h b/editor/debugger/editor_profiler.h index 3f7a0cade5..620d21fe98 100644 --- a/editor/debugger/editor_profiler.h +++ b/editor/debugger/editor_profiler.h @@ -33,6 +33,7 @@ #include "scene/gui/box_container.h" #include "scene/gui/button.h" +#include "scene/gui/check_button.h" #include "scene/gui/label.h" #include "scene/gui/option_button.h" #include "scene/gui/spin_box.h" @@ -67,6 +68,7 @@ public: int line = 0; float self = 0; float total = 0; + float internal = 0; int calls = 0; }; @@ -105,6 +107,8 @@ private: OptionButton *display_mode = nullptr; OptionButton *display_time = nullptr; + CheckButton *display_internal_profiles = nullptr; + SpinBox *cursor_metric_edit = nullptr; Vector<Metric> frame_metrics; @@ -130,6 +134,8 @@ private: void _activate_pressed(); void _clear_pressed(); + void _internal_profiles_pressed(); + String _get_time_as_text(const Metric &m, float p_time, int p_calls); void _make_metric_ptrs(Metric &m); diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index ab8df824e8..7077bce9f7 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -758,6 +758,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread int calls = frame.script_functions[i].call_count; float total = frame.script_functions[i].total_time; float self = frame.script_functions[i].self_time; + float internal = frame.script_functions[i].internal_time; EditorProfiler::Metric::Category::Item item; if (profiler_signature.has(signature)) { @@ -782,6 +783,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread item.calls = calls; item.self = self; item.total = total; + item.internal = internal; funcs.items.write[i] = item; } @@ -1097,7 +1099,9 @@ void ScriptEditorDebugger::_profiler_activate(bool p_enable, int p_type) { // Add max funcs options to request. Array opts; int max_funcs = EDITOR_GET("debugger/profiler_frame_max_functions"); + bool include_native = EDITOR_GET("debugger/profile_native_calls"); opts.push_back(CLAMP(max_funcs, 16, 512)); + opts.push_back(include_native); msg_data.push_back(opts); } _put_msg("profiler:servers", msg_data); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 6d1e1eaaa0..807f0c8eb7 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -44,6 +44,7 @@ #include "editor/editor_paths.h" #include "editor/editor_resource_preview.h" #include "editor/editor_settings.h" +#include "scene/resources/packed_scene.h" EditorFileSystem *EditorFileSystem::singleton = nullptr; //the name is the version, to keep compatibility with different versions of Godot @@ -615,6 +616,9 @@ bool EditorFileSystem::_update_scan_actions() { if (ClassDB::is_parent_class(ia.new_file->type, SNAME("Script"))) { _queue_update_script_class(ia.dir->get_file_path(idx)); } + if (ia.new_file->type == SNAME("PackedScene")) { + _queue_update_scene_groups(ia.dir->get_file_path(idx)); + } } break; case ItemAction::ACTION_FILE_REMOVE: { @@ -624,6 +628,9 @@ bool EditorFileSystem::_update_scan_actions() { if (ClassDB::is_parent_class(ia.dir->files[idx]->type, SNAME("Script"))) { _queue_update_script_class(ia.dir->get_file_path(idx)); } + if (ia.dir->files[idx]->type == SNAME("PackedScene")) { + _queue_update_scene_groups(ia.dir->get_file_path(idx)); + } _delete_internal_files(ia.dir->files[idx]->file); memdelete(ia.dir->files[idx]); @@ -662,6 +669,9 @@ bool EditorFileSystem::_update_scan_actions() { if (ClassDB::is_parent_class(ia.dir->files[idx]->type, SNAME("Script"))) { _queue_update_script_class(full_path); } + if (ia.dir->files[idx]->type == SNAME("PackedScene")) { + _queue_update_scene_groups(full_path); + } reloads.push_back(full_path); @@ -732,6 +742,7 @@ void EditorFileSystem::scan() { _update_scan_actions(); scanning = false; _update_pending_script_classes(); + _update_pending_scene_groups(); emit_signal(SNAME("filesystem_changed")); emit_signal(SNAME("sources_changed"), sources_changed.size() > 0); first_scan = false; @@ -942,6 +953,9 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, Ref<DirAc if (ClassDB::is_parent_class(fi->type, SNAME("Script"))) { _queue_update_script_class(path); } + if (fi->type == SNAME("PackedScene")) { + _queue_update_scene_groups(path); + } } } @@ -1196,6 +1210,7 @@ void EditorFileSystem::scan_changes() { _scan_fs_changes(filesystem, sp); bool changed = _update_scan_actions(); _update_pending_script_classes(); + _update_pending_scene_groups(); if (changed) { emit_signal(SNAME("filesystem_changed")); } @@ -1262,6 +1277,7 @@ void EditorFileSystem::_notification(int p_what) { } bool changed = _update_scan_actions(); _update_pending_script_classes(); + _update_pending_scene_groups(); if (changed) { emit_signal(SNAME("filesystem_changed")); } @@ -1281,6 +1297,7 @@ void EditorFileSystem::_notification(int p_what) { thread.wait_to_finish(); _update_scan_actions(); _update_pending_script_classes(); + _update_pending_scene_groups(); emit_signal(SNAME("filesystem_changed")); emit_signal(SNAME("sources_changed"), sources_changed.size() > 0); first_scan = false; @@ -1635,6 +1652,65 @@ void EditorFileSystem::_queue_update_script_class(const String &p_path) { update_script_mutex.unlock(); } +void EditorFileSystem::_update_scene_groups() { + update_scene_mutex.lock(); + + for (const String &path : update_scene_paths) { + ProjectSettings::get_singleton()->remove_scene_groups_cache(path); + + int index = -1; + EditorFileSystemDirectory *efd = find_file(path, &index); + + if (!efd || index < 0) { + // The file was removed. + continue; + } + + const HashSet<StringName> scene_groups = _get_scene_groups(path); + if (!scene_groups.is_empty()) { + ProjectSettings::get_singleton()->add_scene_groups_cache(path, scene_groups); + } + } + + update_scene_paths.clear(); + update_scene_mutex.unlock(); + + ProjectSettings::get_singleton()->save_scene_groups_cache(); +} + +void EditorFileSystem::_update_pending_scene_groups() { + if (!FileAccess::exists(ProjectSettings::get_singleton()->get_scene_groups_cache_path())) { + _get_all_scenes(get_filesystem(), update_scene_paths); + _update_scene_groups(); + } else if (!update_scene_paths.is_empty()) { + _update_scene_groups(); + } +} + +void EditorFileSystem::_queue_update_scene_groups(const String &p_path) { + update_scene_mutex.lock(); + update_scene_paths.insert(p_path); + update_scene_mutex.unlock(); +} + +void EditorFileSystem::_get_all_scenes(EditorFileSystemDirectory *p_dir, HashSet<String> &r_list) { + for (int i = 0; i < p_dir->get_file_count(); i++) { + if (p_dir->get_file_type(i) == SNAME("PackedScene")) { + r_list.insert(p_dir->get_file_path(i)); + } + } + + for (int i = 0; i < p_dir->get_subdir_count(); i++) { + _get_all_scenes(p_dir->get_subdir(i), r_list); + } +} + +HashSet<StringName> EditorFileSystem::_get_scene_groups(const String &p_path) { + Ref<PackedScene> packed_scene = ResourceLoader::load(p_path); + ERR_FAIL_COND_V(packed_scene.is_null(), HashSet<StringName>()); + return packed_scene->get_state()->get_all_groups(); +} + void EditorFileSystem::update_file(const String &p_file) { ERR_FAIL_COND(p_file.is_empty()); EditorFileSystemDirectory *fs = nullptr; @@ -1658,12 +1734,16 @@ void EditorFileSystem::update_file(const String &p_file) { if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) { _queue_update_script_class(p_file); } + if (fs->files[cpos]->type == SNAME("PackedScene")) { + _queue_update_scene_groups(p_file); + } memdelete(fs->files[cpos]); fs->files.remove_at(cpos); } _update_pending_script_classes(); + _update_pending_scene_groups(); call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later return; } @@ -1730,8 +1810,12 @@ void EditorFileSystem::update_file(const String &p_file) { if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) { _queue_update_script_class(p_file); } + if (fs->files[cpos]->type == SNAME("PackedScene")) { + _queue_update_scene_groups(p_file); + } _update_pending_script_classes(); + _update_pending_scene_groups(); call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later } @@ -2341,6 +2425,7 @@ void EditorFileSystem::reimport_files(const Vector<String> &p_files) { _save_filesystem_cache(); _update_pending_script_classes(); + _update_pending_scene_groups(); importing = false; if (!is_scanning()) { emit_signal(SNAME("filesystem_changed")); diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 0d558c84c5..2a34c06b0d 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -267,6 +267,14 @@ class EditorFileSystem : public Node { void _update_script_classes(); void _update_pending_script_classes(); + Mutex update_scene_mutex; + HashSet<String> update_scene_paths; + void _queue_update_scene_groups(const String &p_path); + void _update_scene_groups(); + void _update_pending_scene_groups(); + HashSet<StringName> _get_scene_groups(const String &p_path); + void _get_all_scenes(EditorFileSystemDirectory *p_dir, HashSet<String> &r_list); + String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const; static Error _resource_import(const String &p_path); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 3fa90b3d81..59816a0eca 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -149,6 +149,9 @@ void EditorProperty::_notification(int p_what) { if (c->is_set_as_top_level()) { continue; } + if (!c->is_visible()) { + continue; + } if (c == bottom_editor) { continue; } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 52f432135e..a5a5c97426 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -146,6 +146,7 @@ #include "editor/plugins/mesh_library_editor_plugin.h" #include "editor/plugins/node_3d_editor_plugin.h" #include "editor/plugins/packed_scene_translation_parser_plugin.h" +#include "editor/plugins/particle_process_material_editor_plugin.h" #include "editor/plugins/root_motion_editor_plugin.h" #include "editor/plugins/script_text_editor.h" #include "editor/plugins/text_editor.h" @@ -3692,7 +3693,7 @@ void EditorNode::_set_current_scene_nocheck(int p_idx) { _edit_current(true); _update_title(); - scene_tabs->update_scene_tabs(); + callable_mp(scene_tabs, &EditorSceneTabs::update_scene_tabs).call_deferred(); if (tabs_to_close.is_empty()) { call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); // Do after everything else is done setting up. @@ -6864,6 +6865,10 @@ EditorNode::EditorNode() { AcceptDialog::set_swap_cancel_ok(swap_cancel_ok == 2); } + int ed_root_dir = EDITOR_GET("interface/editor/ui_layout_direction"); + Control::set_root_layout_direction(ed_root_dir); + Window::set_root_layout_direction(ed_root_dir); + ResourceLoader::set_abort_on_missing_resources(false); ResourceLoader::set_error_notify_func(&EditorNode::add_io_error); ResourceLoader::set_dependency_error_notify_func(&EditorNode::_dependency_error_report); @@ -6970,6 +6975,10 @@ EditorNode::EditorNode() { Ref<EditorInspectorVisualShaderModePlugin> smp; smp.instantiate(); EditorInspector::add_inspector_plugin(smp); + + Ref<EditorInspectorParticleProcessMaterialPlugin> ppm; + ppm.instantiate(); + EditorInspector::add_inspector_plugin(ppm); } editor_selection = memnew(EditorSelection); @@ -7357,6 +7366,20 @@ EditorNode::EditorNode() { file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/file_quit", TTR("Quit"), KeyModifierMask::CMD_OR_CTRL + Key::Q), FILE_QUIT, true); } + ED_SHORTCUT_AND_COMMAND("editor/editor_settings", TTR("Editor Settings...")); + ED_SHORTCUT_OVERRIDE("editor/editor_settings", "macos", KeyModifierMask::META + Key::COMMA); +#ifdef MACOS_ENABLED + if (global_menu) { + apple_menu = memnew(PopupMenu); + apple_menu->set_system_menu_root("_apple"); + main_menu->add_child(apple_menu); + + apple_menu->add_shortcut(ED_GET_SHORTCUT("editor/editor_settings"), SETTINGS_PREFERENCES); + apple_menu->add_separator(); + apple_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); + } +#endif + project_menu = memnew(PopupMenu); project_menu->set_name(TTR("Project")); main_menu->add_child(project_menu); @@ -7422,9 +7445,13 @@ EditorNode::EditorNode() { settings_menu->set_name(TTR("Editor")); main_menu->add_child(settings_menu); - ED_SHORTCUT_AND_COMMAND("editor/editor_settings", TTR("Editor Settings...")); - ED_SHORTCUT_OVERRIDE("editor/editor_settings", "macos", KeyModifierMask::META + Key::COMMA); +#ifdef MACOS_ENABLED + if (!global_menu) { + settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/editor_settings"), SETTINGS_PREFERENCES); + } +#else settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/editor_settings"), SETTINGS_PREFERENCES); +#endif settings_menu->add_shortcut(ED_SHORTCUT("editor/command_palette", TTR("Command Palette..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::P), HELP_COMMAND_PALETTE); settings_menu->add_separator(); @@ -7471,6 +7498,7 @@ EditorNode::EditorNode() { help_menu = memnew(PopupMenu); help_menu->set_name(TTR("Help")); + help_menu->set_system_menu_root("_help"); main_menu->add_child(help_menu); help_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); diff --git a/editor/editor_node.h b/editor/editor_node.h index 8b440b798c..c72a8f9324 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -346,6 +346,7 @@ private: EditorRunBar *project_run_bar = nullptr; VBoxContainer *main_screen_vbox = nullptr; MenuBar *main_menu = nullptr; + PopupMenu *apple_menu = nullptr; PopupMenu *file_menu = nullptr; PopupMenu *project_menu = nullptr; PopupMenu *debug_menu = nullptr; diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index f891bfbff7..27530dc641 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1312,17 +1312,12 @@ void EditorPropertyInteger::_set_read_only(bool p_read_only) { } void EditorPropertyInteger::_value_changed(int64_t val) { - if (setting) { - return; - } emit_changed(get_edited_property(), val); } void EditorPropertyInteger::update_property() { int64_t val = get_edited_property_value(); - setting = true; - spin->set_value(val); - setting = false; + spin->set_value_no_signal(val); #ifdef DEBUG_ENABLED // If spin (currently EditorSplinSlider : Range) is changed so that it can use int64_t, then the below warning wouldn't be a problem. if (val != (int64_t)(double)(val)) { @@ -1452,10 +1447,6 @@ void EditorPropertyFloat::_set_read_only(bool p_read_only) { } void EditorPropertyFloat::_value_changed(double val) { - if (setting) { - return; - } - if (radians_as_degrees) { val = Math::deg_to_rad(val); } @@ -1467,9 +1458,7 @@ void EditorPropertyFloat::update_property() { if (radians_as_degrees) { val = Math::rad_to_deg(val); } - setting = true; - spin->set_value(val); - setting = false; + spin->set_value_no_signal(val); } void EditorPropertyFloat::_bind_methods() { @@ -1627,18 +1616,12 @@ void EditorPropertyEasing::_set_preset(int p_preset) { } void EditorPropertyEasing::_setup_spin() { - setting = true; spin->setup_and_show(); spin->get_line_edit()->set_text(TS->format_number(rtos(get_edited_property_value()))); - setting = false; spin->show(); } void EditorPropertyEasing::_spin_value_changed(double p_value) { - if (setting) { - return; - } - // 0 is a singularity, but both positive and negative values // are otherwise allowed. Enforce 0+ as workaround. if (Math::is_zero_approx(p_value)) { @@ -1725,10 +1708,6 @@ void EditorPropertyRect2::_set_read_only(bool p_read_only) { } void EditorPropertyRect2::_value_changed(double val, const String &p_name) { - if (setting) { - return; - } - Rect2 r2; r2.position.x = spin[0]->get_value(); r2.position.y = spin[1]->get_value(); @@ -1739,12 +1718,10 @@ void EditorPropertyRect2::_value_changed(double val, const String &p_name) { void EditorPropertyRect2::update_property() { Rect2 val = get_edited_property_value(); - setting = true; - spin[0]->set_value(val.position.x); - spin[1]->set_value(val.position.y); - spin[2]->set_value(val.size.x); - spin[3]->set_value(val.size.y); - setting = false; + spin[0]->set_value_no_signal(val.position.x); + spin[1]->set_value_no_signal(val.position.y); + spin[2]->set_value_no_signal(val.size.x); + spin[3]->set_value_no_signal(val.size.y); } void EditorPropertyRect2::_notification(int p_what) { @@ -1828,10 +1805,6 @@ void EditorPropertyRect2i::_set_read_only(bool p_read_only) { } void EditorPropertyRect2i::_value_changed(double val, const String &p_name) { - if (setting) { - return; - } - Rect2i r2; r2.position.x = spin[0]->get_value(); r2.position.y = spin[1]->get_value(); @@ -1842,12 +1815,10 @@ void EditorPropertyRect2i::_value_changed(double val, const String &p_name) { void EditorPropertyRect2i::update_property() { Rect2i val = get_edited_property_value(); - setting = true; - spin[0]->set_value(val.position.x); - spin[1]->set_value(val.position.y); - spin[2]->set_value(val.size.x); - spin[3]->set_value(val.size.y); - setting = false; + spin[0]->set_value_no_signal(val.position.x); + spin[1]->set_value_no_signal(val.position.y); + spin[2]->set_value_no_signal(val.size.x); + spin[3]->set_value_no_signal(val.size.y); } void EditorPropertyRect2i::_notification(int p_what) { @@ -1930,10 +1901,6 @@ void EditorPropertyPlane::_set_read_only(bool p_read_only) { } void EditorPropertyPlane::_value_changed(double val, const String &p_name) { - if (setting) { - return; - } - Plane p; p.normal.x = spin[0]->get_value(); p.normal.y = spin[1]->get_value(); @@ -1944,12 +1911,10 @@ void EditorPropertyPlane::_value_changed(double val, const String &p_name) { void EditorPropertyPlane::update_property() { Plane val = get_edited_property_value(); - setting = true; - spin[0]->set_value(val.normal.x); - spin[1]->set_value(val.normal.y); - spin[2]->set_value(val.normal.z); - spin[3]->set_value(val.d); - setting = false; + spin[0]->set_value_no_signal(val.normal.x); + spin[1]->set_value_no_signal(val.normal.y); + spin[2]->set_value_no_signal(val.normal.z); + spin[3]->set_value_no_signal(val.d); } void EditorPropertyPlane::_notification(int p_what) { @@ -2041,10 +2006,6 @@ void EditorPropertyQuaternion::_edit_custom_value() { } void EditorPropertyQuaternion::_custom_value_changed(double val) { - if (setting) { - return; - } - edit_euler.x = euler[0]->get_value(); edit_euler.y = euler[1]->get_value(); edit_euler.z = euler[2]->get_value(); @@ -2055,17 +2016,13 @@ void EditorPropertyQuaternion::_custom_value_changed(double val) { v.z = Math::deg_to_rad(edit_euler.z); Quaternion temp_q = Quaternion::from_euler(v); - spin[0]->set_value(temp_q.x); - spin[1]->set_value(temp_q.y); - spin[2]->set_value(temp_q.z); - spin[3]->set_value(temp_q.w); + spin[0]->set_value_no_signal(temp_q.x); + spin[1]->set_value_no_signal(temp_q.y); + spin[2]->set_value_no_signal(temp_q.z); + spin[3]->set_value_no_signal(temp_q.w); } void EditorPropertyQuaternion::_value_changed(double val, const String &p_name) { - if (setting) { - return; - } - Quaternion p; p.x = spin[0]->get_value(); p.y = spin[1]->get_value(); @@ -2085,21 +2042,19 @@ bool EditorPropertyQuaternion::is_grabbing_euler() { void EditorPropertyQuaternion::update_property() { Quaternion val = get_edited_property_value(); - setting = true; - spin[0]->set_value(val.x); - spin[1]->set_value(val.y); - spin[2]->set_value(val.z); - spin[3]->set_value(val.w); + spin[0]->set_value_no_signal(val.x); + spin[1]->set_value_no_signal(val.y); + spin[2]->set_value_no_signal(val.z); + spin[3]->set_value_no_signal(val.w); if (!is_grabbing_euler()) { Vector3 v = val.normalized().get_euler(); edit_euler.x = Math::rad_to_deg(v.x); edit_euler.y = Math::rad_to_deg(v.y); edit_euler.z = Math::rad_to_deg(v.z); - euler[0]->set_value(edit_euler.x); - euler[1]->set_value(edit_euler.y); - euler[2]->set_value(edit_euler.z); + euler[0]->set_value_no_signal(edit_euler.x); + euler[1]->set_value_no_signal(edit_euler.y); + euler[2]->set_value_no_signal(edit_euler.z); } - setting = false; } void EditorPropertyQuaternion::_warning_pressed() { @@ -2240,10 +2195,6 @@ void EditorPropertyAABB::_set_read_only(bool p_read_only) { } void EditorPropertyAABB::_value_changed(double val, const String &p_name) { - if (setting) { - return; - } - AABB p; p.position.x = spin[0]->get_value(); p.position.y = spin[1]->get_value(); @@ -2251,21 +2202,17 @@ void EditorPropertyAABB::_value_changed(double val, const String &p_name) { p.size.x = spin[3]->get_value(); p.size.y = spin[4]->get_value(); p.size.z = spin[5]->get_value(); - emit_changed(get_edited_property(), p, p_name); } void EditorPropertyAABB::update_property() { AABB val = get_edited_property_value(); - setting = true; - spin[0]->set_value(val.position.x); - spin[1]->set_value(val.position.y); - spin[2]->set_value(val.position.z); - spin[3]->set_value(val.size.x); - spin[4]->set_value(val.size.y); - spin[5]->set_value(val.size.z); - - setting = false; + spin[0]->set_value_no_signal(val.position.x); + spin[1]->set_value_no_signal(val.position.y); + spin[2]->set_value_no_signal(val.position.z); + spin[3]->set_value_no_signal(val.size.x); + spin[4]->set_value_no_signal(val.size.y); + spin[5]->set_value_no_signal(val.size.z); } void EditorPropertyAABB::_notification(int p_what) { @@ -2323,10 +2270,6 @@ void EditorPropertyTransform2D::_set_read_only(bool p_read_only) { } void EditorPropertyTransform2D::_value_changed(double val, const String &p_name) { - if (setting) { - return; - } - Transform2D p; p[0][0] = spin[0]->get_value(); p[1][0] = spin[1]->get_value(); @@ -2340,15 +2283,12 @@ void EditorPropertyTransform2D::_value_changed(double val, const String &p_name) void EditorPropertyTransform2D::update_property() { Transform2D val = get_edited_property_value(); - setting = true; - spin[0]->set_value(val[0][0]); - spin[1]->set_value(val[1][0]); - spin[2]->set_value(val[2][0]); - spin[3]->set_value(val[0][1]); - spin[4]->set_value(val[1][1]); - spin[5]->set_value(val[2][1]); - - setting = false; + spin[0]->set_value_no_signal(val[0][0]); + spin[1]->set_value_no_signal(val[1][0]); + spin[2]->set_value_no_signal(val[2][0]); + spin[3]->set_value_no_signal(val[0][1]); + spin[4]->set_value_no_signal(val[1][1]); + spin[5]->set_value_no_signal(val[2][1]); } void EditorPropertyTransform2D::_notification(int p_what) { @@ -2414,10 +2354,6 @@ void EditorPropertyBasis::_set_read_only(bool p_read_only) { } void EditorPropertyBasis::_value_changed(double val, const String &p_name) { - if (setting) { - return; - } - Basis p; p[0][0] = spin[0]->get_value(); p[0][1] = spin[1]->get_value(); @@ -2434,18 +2370,15 @@ void EditorPropertyBasis::_value_changed(double val, const String &p_name) { void EditorPropertyBasis::update_property() { Basis val = get_edited_property_value(); - setting = true; - spin[0]->set_value(val[0][0]); - spin[1]->set_value(val[0][1]); - spin[2]->set_value(val[0][2]); - spin[3]->set_value(val[1][0]); - spin[4]->set_value(val[1][1]); - spin[5]->set_value(val[1][2]); - spin[6]->set_value(val[2][0]); - spin[7]->set_value(val[2][1]); - spin[8]->set_value(val[2][2]); - - setting = false; + spin[0]->set_value_no_signal(val[0][0]); + spin[1]->set_value_no_signal(val[0][1]); + spin[2]->set_value_no_signal(val[0][2]); + spin[3]->set_value_no_signal(val[1][0]); + spin[4]->set_value_no_signal(val[1][1]); + spin[5]->set_value_no_signal(val[1][2]); + spin[6]->set_value_no_signal(val[2][0]); + spin[7]->set_value_no_signal(val[2][1]); + spin[8]->set_value_no_signal(val[2][2]); } void EditorPropertyBasis::_notification(int p_what) { @@ -2504,10 +2437,6 @@ void EditorPropertyTransform3D::_set_read_only(bool p_read_only) { } void EditorPropertyTransform3D::_value_changed(double val, const String &p_name) { - if (setting) { - return; - } - Transform3D p; p.basis[0][0] = spin[0]->get_value(); p.basis[0][1] = spin[1]->get_value(); @@ -2530,20 +2459,18 @@ void EditorPropertyTransform3D::update_property() { } void EditorPropertyTransform3D::update_using_transform(Transform3D p_transform) { - setting = true; - spin[0]->set_value(p_transform.basis[0][0]); - spin[1]->set_value(p_transform.basis[0][1]); - spin[2]->set_value(p_transform.basis[0][2]); - spin[3]->set_value(p_transform.origin[0]); - spin[4]->set_value(p_transform.basis[1][0]); - spin[5]->set_value(p_transform.basis[1][1]); - spin[6]->set_value(p_transform.basis[1][2]); - spin[7]->set_value(p_transform.origin[1]); - spin[8]->set_value(p_transform.basis[2][0]); - spin[9]->set_value(p_transform.basis[2][1]); - spin[10]->set_value(p_transform.basis[2][2]); - spin[11]->set_value(p_transform.origin[2]); - setting = false; + spin[0]->set_value_no_signal(p_transform.basis[0][0]); + spin[1]->set_value_no_signal(p_transform.basis[0][1]); + spin[2]->set_value_no_signal(p_transform.basis[0][2]); + spin[3]->set_value_no_signal(p_transform.origin[0]); + spin[4]->set_value_no_signal(p_transform.basis[1][0]); + spin[5]->set_value_no_signal(p_transform.basis[1][1]); + spin[6]->set_value_no_signal(p_transform.basis[1][2]); + spin[7]->set_value_no_signal(p_transform.origin[1]); + spin[8]->set_value_no_signal(p_transform.basis[2][0]); + spin[9]->set_value_no_signal(p_transform.basis[2][1]); + spin[10]->set_value_no_signal(p_transform.basis[2][2]); + spin[11]->set_value_no_signal(p_transform.origin[2]); } void EditorPropertyTransform3D::_notification(int p_what) { @@ -2602,10 +2529,6 @@ void EditorPropertyProjection::_set_read_only(bool p_read_only) { } void EditorPropertyProjection::_value_changed(double val, const String &p_name) { - if (setting) { - return; - } - Projection p; p.columns[0][0] = spin[0]->get_value(); p.columns[0][1] = spin[1]->get_value(); @@ -2632,24 +2555,22 @@ void EditorPropertyProjection::update_property() { } void EditorPropertyProjection::update_using_transform(Projection p_transform) { - setting = true; - spin[0]->set_value(p_transform.columns[0][0]); - spin[1]->set_value(p_transform.columns[0][1]); - spin[2]->set_value(p_transform.columns[0][2]); - spin[3]->set_value(p_transform.columns[0][3]); - spin[4]->set_value(p_transform.columns[1][0]); - spin[5]->set_value(p_transform.columns[1][1]); - spin[6]->set_value(p_transform.columns[1][2]); - spin[7]->set_value(p_transform.columns[1][3]); - spin[8]->set_value(p_transform.columns[2][0]); - spin[9]->set_value(p_transform.columns[2][1]); - spin[10]->set_value(p_transform.columns[2][2]); - spin[11]->set_value(p_transform.columns[2][3]); - spin[12]->set_value(p_transform.columns[3][0]); - spin[13]->set_value(p_transform.columns[3][1]); - spin[14]->set_value(p_transform.columns[3][2]); - spin[15]->set_value(p_transform.columns[3][3]); - setting = false; + spin[0]->set_value_no_signal(p_transform.columns[0][0]); + spin[1]->set_value_no_signal(p_transform.columns[0][1]); + spin[2]->set_value_no_signal(p_transform.columns[0][2]); + spin[3]->set_value_no_signal(p_transform.columns[0][3]); + spin[4]->set_value_no_signal(p_transform.columns[1][0]); + spin[5]->set_value_no_signal(p_transform.columns[1][1]); + spin[6]->set_value_no_signal(p_transform.columns[1][2]); + spin[7]->set_value_no_signal(p_transform.columns[1][3]); + spin[8]->set_value_no_signal(p_transform.columns[2][0]); + spin[9]->set_value_no_signal(p_transform.columns[2][1]); + spin[10]->set_value_no_signal(p_transform.columns[2][2]); + spin[11]->set_value_no_signal(p_transform.columns[2][3]); + spin[12]->set_value_no_signal(p_transform.columns[3][0]); + spin[13]->set_value_no_signal(p_transform.columns[3][1]); + spin[14]->set_value_no_signal(p_transform.columns[3][2]); + spin[15]->set_value_no_signal(p_transform.columns[3][3]); } void EditorPropertyProjection::_notification(int p_what) { diff --git a/editor/editor_properties.h b/editor/editor_properties.h index ff9d47627a..b7ae4bd1ca 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -340,7 +340,6 @@ public: class EditorPropertyInteger : public EditorProperty { GDCLASS(EditorPropertyInteger, EditorProperty); EditorSpinSlider *spin = nullptr; - bool setting = false; void _value_changed(int64_t p_val); protected: @@ -399,7 +398,6 @@ public: class EditorPropertyFloat : public EditorProperty { GDCLASS(EditorPropertyFloat, EditorProperty); EditorSpinSlider *spin = nullptr; - bool setting = false; bool radians_as_degrees = false; void _value_changed(double p_val); @@ -418,7 +416,6 @@ class EditorPropertyEasing : public EditorProperty { Control *easing_draw = nullptr; PopupMenu *preset = nullptr; EditorSpinSlider *spin = nullptr; - bool setting = false; bool dragging = false; bool full = false; @@ -459,7 +456,6 @@ public: class EditorPropertyRect2 : public EditorProperty { GDCLASS(EditorPropertyRect2, EditorProperty); EditorSpinSlider *spin[4]; - bool setting = false; void _value_changed(double p_val, const String &p_name); protected: @@ -476,7 +472,6 @@ public: class EditorPropertyRect2i : public EditorProperty { GDCLASS(EditorPropertyRect2i, EditorProperty); EditorSpinSlider *spin[4]; - bool setting = false; void _value_changed(double p_val, const String &p_name); protected: @@ -493,7 +488,6 @@ public: class EditorPropertyPlane : public EditorProperty { GDCLASS(EditorPropertyPlane, EditorProperty); EditorSpinSlider *spin[4]; - bool setting = false; void _value_changed(double p_val, const String &p_name); protected: @@ -511,7 +505,6 @@ class EditorPropertyQuaternion : public EditorProperty { GDCLASS(EditorPropertyQuaternion, EditorProperty); BoxContainer *default_layout = nullptr; EditorSpinSlider *spin[4]; - bool setting = false; Button *warning = nullptr; AcceptDialog *warning_dialog = nullptr; @@ -544,7 +537,6 @@ public: class EditorPropertyAABB : public EditorProperty { GDCLASS(EditorPropertyAABB, EditorProperty); EditorSpinSlider *spin[6]; - bool setting = false; void _value_changed(double p_val, const String &p_name); protected: @@ -561,7 +553,6 @@ public: class EditorPropertyTransform2D : public EditorProperty { GDCLASS(EditorPropertyTransform2D, EditorProperty); EditorSpinSlider *spin[6]; - bool setting = false; void _value_changed(double p_val, const String &p_name); protected: @@ -578,7 +569,6 @@ public: class EditorPropertyBasis : public EditorProperty { GDCLASS(EditorPropertyBasis, EditorProperty); EditorSpinSlider *spin[9]; - bool setting = false; void _value_changed(double p_val, const String &p_name); protected: @@ -595,7 +585,6 @@ public: class EditorPropertyTransform3D : public EditorProperty { GDCLASS(EditorPropertyTransform3D, EditorProperty); EditorSpinSlider *spin[12]; - bool setting = false; void _value_changed(double p_val, const String &p_name); protected: @@ -613,7 +602,6 @@ public: class EditorPropertyProjection : public EditorProperty { GDCLASS(EditorPropertyProjection, EditorProperty); EditorSpinSlider *spin[16]; - bool setting = false; void _value_changed(double p_val, const String &p_name); protected: diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 1eaeee97a5..98706a10f2 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -400,6 +400,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { const String display_scale_hint_string = vformat("Auto (%d%%),75%%,100%%,125%%,150%%,175%%,200%%,Custom", Math::round(get_auto_display_scale() * 100)); EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/display_scale", 0, display_scale_hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/ui_layout_direction", 0, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + String ed_screen_hints = "Screen With Mouse Pointer:-4,Screen With Keyboard Focus:-3,Primary Screen:-2"; // Note: Main Window Screen:-1 is not used for the main window. for (int i = 0; i < DisplayServer::get_singleton()->get_screen_count(); i++) { ed_screen_hints += ",Screen " + itos(i + 1) + ":" + itos(i); @@ -632,9 +634,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // Use a similar color to the 2D editor selection. EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d/selection_box_color", Color(1.0, 0.5, 0), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) - _initial_set("editors/3d_gizmos/gizmo_colors/instantiated", Color(0.7, 0.7, 0.7, 0.6)); - _initial_set("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1)); - _initial_set("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/instantiated", Color(0.7, 0.7, 0.7, 0.6), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::COLOR, PROPERTY_HINT_NONE, "editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1), "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) // If a line is a multiple of this, it uses the primary grid color. // Use a power of 2 value by default as it's more common to use powers of 2 in level design. @@ -774,6 +776,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "debugger/profiler_frame_max_functions", 64, "16,512,1") EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "debugger/remote_scene_tree_refresh_interval", 1.0, "0.1,10,0.01,or_greater") EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "debugger/remote_inspect_refresh_interval", 0.2, "0.02,10,0.01,or_greater") + EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "debugger/profile_native_calls", false, "") // HTTP Proxy _initial_set("network/http_proxy/host", ""); @@ -884,6 +887,10 @@ bool EditorSettings::_is_default_text_editor_theme(String p_theme_name) { return p_theme_name == "default" || p_theme_name == "godot 2" || p_theme_name == "custom"; } +const String EditorSettings::_get_project_metadata_path() const { + return EditorPaths::get_singleton()->get_project_settings_dir().path_join("project_metadata.cfg"); +} + // PUBLIC METHODS EditorSettings *EditorSettings::get_singleton() { @@ -1126,8 +1133,8 @@ Variant _EDITOR_DEF(const String &p_setting, const Variant &p_default, bool p_re ret = EDITOR_GET(p_setting); } else { EditorSettings::get_singleton()->set_manually(p_setting, p_default); - EditorSettings::get_singleton()->set_restart_if_changed(p_setting, p_restart_if_changed); } + EditorSettings::get_singleton()->set_restart_if_changed(p_setting, p_restart_if_changed); if (!EditorSettings::get_singleton()->has_default_value(p_setting)) { EditorSettings::get_singleton()->set_initial_value(p_setting, p_default); @@ -1171,24 +1178,31 @@ void EditorSettings::add_property_hint(const PropertyInfo &p_hint) { // Metadata void EditorSettings::set_project_metadata(const String &p_section, const String &p_key, Variant p_data) { - Ref<ConfigFile> cf = memnew(ConfigFile); - String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join("project_metadata.cfg"); - Error err; - err = cf->load(path); - ERR_FAIL_COND_MSG(err != OK && err != ERR_FILE_NOT_FOUND, "Cannot load editor settings from file '" + path + "'."); - cf->set_value(p_section, p_key, p_data); - err = cf->save(path); - ERR_FAIL_COND_MSG(err != OK, "Cannot save editor settings to file '" + path + "'."); + const String path = _get_project_metadata_path(); + + if (project_metadata.is_null()) { + project_metadata.instantiate(); + + Error err = project_metadata->load(path); + if (err != OK && err != ERR_FILE_NOT_FOUND) { + ERR_PRINT("Cannot load project metadata from file '" + path + "'."); + } + } + project_metadata->set_value(p_section, p_key, p_data); + + Error err = project_metadata->save(path); + ERR_FAIL_COND_MSG(err != OK, "Cannot save project metadata to file '" + path + "'."); } Variant EditorSettings::get_project_metadata(const String &p_section, const String &p_key, Variant p_default) const { - Ref<ConfigFile> cf = memnew(ConfigFile); - String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join("project_metadata.cfg"); - Error err = cf->load(path); - if (err != OK) { - return p_default; + if (project_metadata.is_null()) { + project_metadata.instantiate(); + + const String path = _get_project_metadata_path(); + Error err = project_metadata->load(path); + ERR_FAIL_COND_V_MSG(err != OK && err != ERR_FILE_NOT_FOUND, p_default, "Cannot load project metadata from file '" + path + "'."); } - return cf->get_value(p_section, p_key, p_default); + return project_metadata->get_value(p_section, p_key, p_default); } void EditorSettings::set_favorites(const Vector<String> &p_favorites) { diff --git a/editor/editor_settings.h b/editor/editor_settings.h index 660a9501a2..c3ce790e0e 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -79,6 +79,7 @@ private: HashSet<String> changed_settings; + mutable Ref<ConfigFile> project_metadata; HashMap<String, PropertyInfo> hints; HashMap<String, VariantContainer> props; int last_order; @@ -106,6 +107,7 @@ private: void _load_godot2_text_editor_theme(); bool _save_text_editor_theme(String p_file); bool _is_default_text_editor_theme(String p_theme_name); + const String _get_project_metadata_path() const; protected: static void _bind_methods(); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 96d2abf202..ef68554a3d 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1423,8 +1423,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Tree theme->set_icon("checked", "Tree", theme->get_icon(SNAME("GuiChecked"), EditorStringName(EditorIcons))); + theme->set_icon("checked_disabled", "Tree", theme->get_icon(SNAME("GuiCheckedDisabled"), EditorStringName(EditorIcons))); theme->set_icon("indeterminate", "Tree", theme->get_icon(SNAME("GuiIndeterminate"), EditorStringName(EditorIcons))); + theme->set_icon("indeterminate_disabled", "Tree", theme->get_icon(SNAME("GuiIndeterminateDisabled"), EditorStringName(EditorIcons))); theme->set_icon("unchecked", "Tree", theme->get_icon(SNAME("GuiUnchecked"), EditorStringName(EditorIcons))); + theme->set_icon("unchecked_disabled", "Tree", theme->get_icon(SNAME("GuiUncheckedDisabled"), EditorStringName(EditorIcons))); theme->set_icon("arrow", "Tree", theme->get_icon(SNAME("GuiTreeArrowDown"), EditorStringName(EditorIcons))); theme->set_icon("arrow_collapsed", "Tree", theme->get_icon(SNAME("GuiTreeArrowRight"), EditorStringName(EditorIcons))); theme->set_icon("arrow_collapsed_mirrored", "Tree", theme->get_icon(SNAME("GuiTreeArrowLeft"), EditorStringName(EditorIcons))); @@ -1437,6 +1440,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("custom_button_font_highlight", "Tree", font_hover_color); theme->set_color("font_color", "Tree", font_color); theme->set_color("font_selected_color", "Tree", mono_color); + theme->set_color("font_disabled_color", "Tree", font_disabled_color); theme->set_color("font_outline_color", "Tree", font_outline_color); theme->set_color("title_button_color", "Tree", font_color); theme->set_color("drop_position_color", "Tree", accent_color); diff --git a/editor/group_settings_editor.cpp b/editor/group_settings_editor.cpp new file mode 100644 index 0000000000..a91154ae36 --- /dev/null +++ b/editor/group_settings_editor.cpp @@ -0,0 +1,537 @@ +/**************************************************************************/ +/* group_settings_editor.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "group_settings_editor.h" + +#include "core/config/project_settings.h" +#include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" +#include "editor/filesystem_dock.h" +#include "editor/gui/editor_validation_panel.h" +#include "editor/scene_tree_dock.h" +#include "editor_file_system.h" +#include "editor_node.h" +#include "scene/resources/packed_scene.h" + +void GroupSettingsEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + update_groups(); + } break; + } +} + +void GroupSettingsEditor::_item_edited() { + if (updating_groups) { + return; + } + + TreeItem *ti = tree->get_edited(); + int column = tree->get_edited_column(); + + if (!ti) { + return; + } + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + if (column == 1) { + // Description Edited. + String name = ti->get_text(0); + String new_description = ti->get_text(1); + String old_description = ti->get_meta("__description"); + + if (new_description == old_description) { + return; + } + + name = GLOBAL_GROUP_PREFIX + name; + + undo_redo->create_action(TTR("Set Group Description")); + + undo_redo->add_do_property(ProjectSettings::get_singleton(), name, new_description); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, old_description); + + undo_redo->add_do_method(this, "call_deferred", "update_groups"); + undo_redo->add_undo_method(this, "call_deferred", "update_groups"); + + undo_redo->add_do_method(this, "emit_signal", group_changed); + undo_redo->add_undo_method(this, "emit_signal", group_changed); + + undo_redo->commit_action(); + } +} + +void GroupSettingsEditor::_item_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) { + if (p_button != MouseButton::LEFT) { + return; + } + + TreeItem *ti = Object::cast_to<TreeItem>(p_item); + + if (!ti) { + return; + } + ti->select(0); + _show_remove_dialog(); +} + +String GroupSettingsEditor::_check_new_group_name(const String &p_name) { + if (p_name.is_empty()) { + return TTR("Invalid group name. It cannot be empty."); + } + + if (ProjectSettings::get_singleton()->has_global_group(p_name)) { + return vformat(TTR("A group with the name '%s' already exists."), p_name); + } + + return ""; +} + +void GroupSettingsEditor::_check_rename() { + String new_name = rename_group->get_text().strip_edges(); + String old_name = rename_group_dialog->get_meta("__name"); + + if (new_name == old_name) { + return; + } + + if (new_name.is_empty()) { + rename_validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group can't be empty."), EditorValidationPanel::MSG_ERROR); + } else if (ProjectSettings::get_singleton()->has_global_group(new_name)) { + rename_validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group already exists."), EditorValidationPanel::MSG_ERROR); + } +} + +void GroupSettingsEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("remove_references"), &GroupSettingsEditor::remove_references); + ClassDB::bind_method(D_METHOD("rename_references"), &GroupSettingsEditor::rename_references); + + ClassDB::bind_method(D_METHOD("update_groups"), &GroupSettingsEditor::update_groups); + + ADD_SIGNAL(MethodInfo("group_changed")); +} + +void GroupSettingsEditor::_add_group(const String &p_name, const String &p_description) { + String name = p_name.strip_edges(); + + String error = _check_new_group_name(name); + if (!error.is_empty()) { + show_message(error); + return; + } + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Add Group")); + + name = GLOBAL_GROUP_PREFIX + name; + + undo_redo->add_do_property(ProjectSettings::get_singleton(), name, p_description); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, Variant()); + + undo_redo->add_do_method(this, "call_deferred", "update_groups"); + undo_redo->add_undo_method(this, "call_deferred", "update_groups"); + + undo_redo->add_do_method(this, "emit_signal", group_changed); + undo_redo->add_undo_method(this, "emit_signal", group_changed); + + undo_redo->commit_action(); + + group_name->clear(); + group_description->clear(); +} + +void GroupSettingsEditor::_add_group() { + _add_group(group_name->get_text(), group_description->get_text()); +} + +void GroupSettingsEditor::_text_submitted(const String &p_text) { + if (!add_button->is_disabled()) { + _add_group(); + } +} + +void GroupSettingsEditor::_group_name_text_changed(const String &p_name) { + String error = _check_new_group_name(p_name.strip_edges()); + add_button->set_tooltip_text(error); + add_button->set_disabled(!error.is_empty()); +} + +void GroupSettingsEditor::_modify_references(const StringName &p_name, const StringName &p_new_name, bool p_is_rename) { + HashSet<String> scenes; + + HashMap<StringName, HashSet<StringName>> scene_groups_cache = ProjectSettings::get_singleton()->get_scene_groups_cache(); + for (const KeyValue<StringName, HashSet<StringName>> &E : scene_groups_cache) { + if (E.value.has(p_name)) { + scenes.insert(E.key); + } + } + + int steps = scenes.size(); + Vector<EditorData::EditedScene> edited_scenes = EditorNode::get_editor_data().get_edited_scenes(); + for (const EditorData::EditedScene &es : edited_scenes) { + if (!es.root) { + continue; + } + if (es.path.is_empty()) { + ++steps; + } else if (!scenes.has(es.path)) { + ++steps; + } + } + + String progress_task = p_is_rename ? "rename_reference" : "remove_references"; + String progress_label = p_is_rename ? TTR("Renaming Group References") : TTR("Removing Group References"); + EditorProgress progress(progress_task, progress_label, steps); + + int step = 0; + // Update opened scenes. + HashSet<String> edited_scenes_path; + for (const EditorData::EditedScene &es : edited_scenes) { + if (!es.root) { + continue; + } + progress.step(es.path, step++); + bool edited = p_is_rename ? rename_node_references(es.root, p_name, p_new_name) : remove_node_references(es.root, p_name); + if (!es.path.is_empty()) { + scenes.erase(es.path); + if (edited) { + edited_scenes_path.insert(es.path); + } + } + } + if (!edited_scenes_path.is_empty()) { + EditorNode::get_singleton()->save_scene_list(edited_scenes_path); + SceneTreeDock::get_singleton()->get_tree_editor()->update_tree(); + } + + for (const String &E : scenes) { + Ref<PackedScene> packed_scene = ResourceLoader::load(E); + progress.step(E, step++); + ERR_CONTINUE(packed_scene.is_null()); + if (p_is_rename) { + if (packed_scene->get_state()->rename_group_references(p_name, p_new_name)) { + ResourceSaver::save(packed_scene, E); + } + } else { + if (packed_scene->get_state()->remove_group_references(p_name)) { + ResourceSaver::save(packed_scene, E); + } + } + } +} + +void GroupSettingsEditor::remove_references(const StringName &p_name) { + _modify_references(p_name, StringName(), false); +} + +void GroupSettingsEditor::rename_references(const StringName &p_old_name, const StringName &p_new_name) { + _modify_references(p_old_name, p_new_name, true); +} + +bool GroupSettingsEditor::remove_node_references(Node *p_node, const StringName &p_name) { + bool edited = false; + if (p_node->is_in_group(p_name)) { + p_node->remove_from_group(p_name); + edited = true; + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + edited |= remove_node_references(p_node->get_child(i), p_name); + } + return edited; +} + +bool GroupSettingsEditor::rename_node_references(Node *p_node, const StringName &p_old_name, const StringName &p_new_name) { + bool edited = false; + if (p_node->is_in_group(p_old_name)) { + p_node->remove_from_group(p_old_name); + p_node->add_to_group(p_new_name, true); + edited = true; + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + edited |= rename_node_references(p_node->get_child(i), p_old_name, p_new_name); + } + return edited; +} + +void GroupSettingsEditor::update_groups() { + if (updating_groups) { + return; + } + updating_groups = true; + groups_cache = ProjectSettings::get_singleton()->get_global_groups_list(); + + tree->clear(); + TreeItem *root = tree->create_item(); + + List<StringName> keys; + for (const KeyValue<StringName, String> &E : groups_cache) { + keys.push_back(E.key); + } + keys.sort_custom<NoCaseComparator>(); + + for (const StringName &E : keys) { + TreeItem *item = tree->create_item(root); + item->set_meta("__name", E); + item->set_meta("__description", groups_cache[E]); + + item->set_text(0, E); + item->set_editable(0, false); + + item->set_text(1, groups_cache[E]); + item->set_editable(1, true); + item->add_button(2, get_editor_theme_icon(SNAME("Remove"))); + item->set_selectable(2, false); + } + + updating_groups = false; +} + +void GroupSettingsEditor::connect_filesystem_dock_signals(FileSystemDock *p_fs_dock) { + p_fs_dock->connect("files_moved", callable_mp(ProjectSettings::get_singleton(), &ProjectSettings::remove_scene_groups_cache).unbind(1)); + p_fs_dock->connect("file_removed", callable_mp(ProjectSettings::get_singleton(), &ProjectSettings::remove_scene_groups_cache)); +} + +void GroupSettingsEditor::_confirm_rename() { + TreeItem *ti = tree->get_selected(); + if (!ti) { + return; + } + + String old_name = ti->get_meta("__name"); + String new_name = rename_group->get_text().strip_edges(); + + if (old_name == new_name) { + return; + } + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Rename Group")); + + String property_new_name = GLOBAL_GROUP_PREFIX + new_name; + String property_old_name = GLOBAL_GROUP_PREFIX + old_name; + + String description = ti->get_meta("__description"); + + undo_redo->add_do_property(ProjectSettings::get_singleton(), property_new_name, description); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_new_name, Variant()); + + undo_redo->add_do_property(ProjectSettings::get_singleton(), property_old_name, Variant()); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_old_name, description); + + if (rename_check_box->is_pressed()) { + undo_redo->add_do_method(this, "rename_references", old_name, new_name); + undo_redo->add_undo_method(this, "rename_references", new_name, old_name); + } + + undo_redo->add_do_method(this, "call_deferred", "update_groups"); + undo_redo->add_undo_method(this, "call_deferred", "update_groups"); + + undo_redo->add_do_method(this, "emit_signal", group_changed); + undo_redo->add_undo_method(this, "emit_signal", group_changed); + + undo_redo->commit_action(); +} + +void GroupSettingsEditor::_confirm_delete() { + TreeItem *ti = tree->get_selected(); + if (!ti) { + return; + } + + String name = ti->get_text(0); + String description = groups_cache[name]; + String property_name = GLOBAL_GROUP_PREFIX + name; + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Remove Group")); + + undo_redo->add_do_property(ProjectSettings::get_singleton(), property_name, Variant()); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_name, description); + + if (remove_check_box->is_pressed()) { + undo_redo->add_do_method(this, "remove_references", name); + } + + undo_redo->add_do_method(this, "call_deferred", "update_groups"); + undo_redo->add_undo_method(this, "call_deferred", "update_groups"); + + undo_redo->add_do_method(this, "emit_signal", group_changed); + undo_redo->add_undo_method(this, "emit_signal", group_changed); + + undo_redo->commit_action(); +} + +void GroupSettingsEditor::show_message(const String &p_message) { + message->set_text(p_message); + message->popup_centered(); +} + +void GroupSettingsEditor::_show_remove_dialog() { + if (!remove_dialog) { + remove_dialog = memnew(ConfirmationDialog); + remove_dialog->connect("confirmed", callable_mp(this, &GroupSettingsEditor::_confirm_delete)); + + VBoxContainer *vbox = memnew(VBoxContainer); + remove_label = memnew(Label); + vbox->add_child(remove_label); + + remove_check_box = memnew(CheckBox); + remove_check_box->set_text(TTR("Delete references from all scenes")); + vbox->add_child(remove_check_box); + + remove_dialog->add_child(vbox); + + add_child(remove_dialog); + } + + TreeItem *ti = tree->get_selected(); + if (!ti) { + return; + } + + remove_check_box->set_pressed(false); + remove_label->set_text(vformat(TTR("Delete group \"%s\"?"), ti->get_text(0))); + + remove_dialog->reset_size(); + remove_dialog->popup_centered(); +} + +void GroupSettingsEditor::_show_rename_dialog() { + if (!rename_group_dialog) { + rename_group_dialog = memnew(ConfirmationDialog); + rename_group_dialog->set_title(TTR("Rename Group")); + rename_group_dialog->connect("confirmed", callable_mp(this, &GroupSettingsEditor::_confirm_rename)); + + VBoxContainer *vbc = memnew(VBoxContainer); + rename_group_dialog->add_child(vbc); + + HBoxContainer *hbc = memnew(HBoxContainer); + hbc->add_child(memnew(Label(TTR("Name:")))); + + rename_group = memnew(LineEdit); + rename_group->set_custom_minimum_size(Size2(300 * EDSCALE, 1)); + hbc->add_child(rename_group); + vbc->add_child(hbc); + + rename_group_dialog->register_text_enter(rename_group); + + rename_validation_panel = memnew(EditorValidationPanel); + rename_validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group name is valid.")); + rename_validation_panel->set_update_callback(callable_mp(this, &GroupSettingsEditor::_check_rename)); + rename_validation_panel->set_accept_button(rename_group_dialog->get_ok_button()); + + rename_group->connect("text_changed", callable_mp(rename_validation_panel, &EditorValidationPanel::update).unbind(1)); + + vbc->add_child(rename_validation_panel); + + rename_check_box = memnew(CheckBox); + rename_check_box->set_text(TTR("Rename references in all scenes")); + vbc->add_child(rename_check_box); + + add_child(rename_group_dialog); + } + + TreeItem *ti = tree->get_selected(); + if (!ti) { + return; + } + + rename_check_box->set_pressed(false); + + String name = ti->get_meta("__name"); + + rename_group->set_text(name); + rename_group_dialog->set_meta("__name", name); + + rename_validation_panel->update(); + + rename_group_dialog->reset_size(); + rename_group_dialog->popup_centered(); + rename_group->select_all(); + rename_group->grab_focus(); +} + +GroupSettingsEditor::GroupSettingsEditor() { + ProjectSettings::get_singleton()->add_hidden_prefix("global_group/"); + + HBoxContainer *hbc = memnew(HBoxContainer); + add_child(hbc); + + Label *l = memnew(Label); + l->set_text(TTR("Name:")); + hbc->add_child(l); + + group_name = memnew(LineEdit); + group_name->set_h_size_flags(SIZE_EXPAND_FILL); + group_name->set_clear_button_enabled(true); + group_name->connect("text_changed", callable_mp(this, &GroupSettingsEditor::_group_name_text_changed)); + group_name->connect("text_submitted", callable_mp(this, &GroupSettingsEditor::_text_submitted)); + hbc->add_child(group_name); + + l = memnew(Label); + l->set_text(TTR("Description:")); + hbc->add_child(l); + + group_description = memnew(LineEdit); + group_description->set_clear_button_enabled(true); + group_description->set_h_size_flags(SIZE_EXPAND_FILL); + group_description->connect("text_submitted", callable_mp(this, &GroupSettingsEditor::_text_submitted)); + hbc->add_child(group_description); + + add_button = memnew(Button); + add_button->set_text(TTR("Add")); + add_button->set_disabled(true); + add_button->connect("pressed", callable_mp(this, &GroupSettingsEditor::_add_group)); + hbc->add_child(add_button); + + tree = memnew(Tree); + tree->set_hide_root(true); + tree->set_select_mode(Tree::SELECT_SINGLE); + tree->set_allow_reselect(true); + + tree->set_columns(3); + tree->set_column_titles_visible(true); + + tree->set_column_title(0, TTR("Name")); + tree->set_column_title(1, TTR("Description")); + tree->set_column_expand(2, false); + + tree->connect("item_edited", callable_mp(this, &GroupSettingsEditor::_item_edited)); + tree->connect("item_activated", callable_mp(this, &GroupSettingsEditor::_show_rename_dialog)); + tree->connect("button_clicked", callable_mp(this, &GroupSettingsEditor::_item_button_pressed)); + tree->set_v_size_flags(SIZE_EXPAND_FILL); + + add_child(tree, true); + + message = memnew(AcceptDialog); + add_child(message); +} diff --git a/editor/group_settings_editor.h b/editor/group_settings_editor.h new file mode 100644 index 0000000000..660c15865e --- /dev/null +++ b/editor/group_settings_editor.h @@ -0,0 +1,107 @@ +/**************************************************************************/ +/* group_settings_editor.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef GROUP_SETTINGS_EDITOR_H +#define GROUP_SETTINGS_EDITOR_H + +#include "scene/gui/dialogs.h" + +class CheckBox; +class EditorFileSystemDirectory; +class EditorValidationPanel; +class FileSystemDock; +class Label; +class Tree; + +class GroupSettingsEditor : public VBoxContainer { + GDCLASS(GroupSettingsEditor, VBoxContainer); + + const String GLOBAL_GROUP_PREFIX = "global_group/"; + const StringName group_changed = "group_changed"; + + HashMap<StringName, String> groups_cache; + + bool updating_groups = false; + + AcceptDialog *message = nullptr; + Tree *tree = nullptr; + LineEdit *group_name = nullptr; + LineEdit *group_description = nullptr; + Button *add_button = nullptr; + + ConfirmationDialog *remove_dialog = nullptr; + CheckBox *remove_check_box = nullptr; + Label *remove_label = nullptr; + + ConfirmationDialog *rename_group_dialog = nullptr; + LineEdit *rename_group = nullptr; + CheckBox *rename_check_box = nullptr; + EditorValidationPanel *rename_validation_panel = nullptr; + + void _show_remove_dialog(); + void _show_rename_dialog(); + + String _check_new_group_name(const String &p_name); + void _check_rename(); + + void _add_group(); + void _add_group(const String &p_name, const String &p_description); + + void _modify_references(const StringName &p_name, const StringName &p_new_name, bool p_is_rename); + + void _confirm_rename(); + void _confirm_delete(); + + void _text_submitted(const String &p_text); + void _group_name_text_changed(const String &p_name); + + void _item_edited(); + void _item_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void show_message(const String &p_message); + + void remove_references(const StringName &p_name); + void rename_references(const StringName &p_old_name, const StringName &p_new_name); + + bool remove_node_references(Node *p_node, const StringName &p_name); + bool rename_node_references(Node *p_node, const StringName &p_old_name, const StringName &p_new_name); + + void update_groups(); + void connect_filesystem_dock_signals(FileSystemDock *p_fs_dock); + + GroupSettingsEditor(); +}; + +#endif // GROUP_SETTINGS_EDITOR_H diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index d083bc294a..9fa9fe5a9d 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -32,17 +32,18 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" -#include "editor/editor_string_names.h" +#include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" -#include "editor/gui/scene_tree_editor.h" +#include "editor/gui/editor_validation_panel.h" +#include "editor/project_settings_editor.h" #include "editor/scene_tree_dock.h" -#include "scene/gui/button.h" +#include "scene/gui/box_container.h" +#include "scene/gui/check_button.h" +#include "scene/gui/grid_container.h" #include "scene/gui/label.h" -#include "scene/gui/line_edit.h" -#include "scene/gui/tree.h" #include "scene/resources/packed_scene.h" -static bool can_edit(Node *p_node, String p_group) { +static bool can_edit(Node *p_node, const String &p_group) { Node *n = p_node; bool can_edit = true; while (n) { @@ -52,6 +53,7 @@ static bool can_edit(Node *p_node, String p_group) { if (path != -1) { if (ss->is_node_in_group(path, p_group)) { can_edit = false; + break; } } } @@ -60,775 +62,795 @@ static bool can_edit(Node *p_node, String p_group) { return can_edit; } -void GroupDialog::_group_selected() { - nodes_to_add->clear(); - add_node_root = nodes_to_add->create_item(); - - nodes_to_remove->clear(); - remove_node_root = nodes_to_remove->create_item(); - - if (!groups->is_anything_selected()) { - group_empty->hide(); - return; +struct _GroupInfoComparator { + bool operator()(const Node::GroupInfo &p_a, const Node::GroupInfo &p_b) const { + return p_a.name.operator String() < p_b.name.operator String(); } +}; - selected_group = groups->get_selected()->get_text(0); - _load_nodes(scene_tree->get_edited_scene_root()); - - group_empty->set_visible(!remove_node_root->get_first_child()); +void GroupsEditor::_add_scene_group(const String &p_name) { + scene_groups[p_name] = true; } -void GroupDialog::_load_nodes(Node *p_current) { - String item_name = p_current->get_name(); - if (p_current != scene_tree->get_edited_scene_root()) { - item_name = String(p_current->get_parent()->get_name()) + "/" + item_name; - } +void GroupsEditor::_remove_scene_group(const String &p_name) { + scene_groups.erase(p_name); + ProjectSettingsEditor::get_singleton()->get_group_settings()->remove_node_references(scene_root_node, p_name); +} - bool keep = true; - Node *root = scene_tree->get_edited_scene_root(); - Node *owner = p_current->get_owner(); - if (owner != root && p_current != root && !owner && !root->is_editable_instance(owner)) { - keep = false; - } +void GroupsEditor::_rename_scene_group(const String &p_old_name, const String &p_new_name) { + scene_groups[p_new_name] = scene_groups[p_old_name]; + scene_groups.erase(p_old_name); + ProjectSettingsEditor::get_singleton()->get_group_settings()->rename_node_references(scene_root_node, p_old_name, p_new_name); +} - TreeItem *node = nullptr; - NodePath path = scene_tree->get_edited_scene_root()->get_path_to(p_current); - if (keep && p_current->is_in_group(selected_group)) { - if (remove_filter->get_text().is_subsequence_ofn(String(p_current->get_name()))) { - node = nodes_to_remove->create_item(remove_node_root); - keep = true; - } else { - keep = false; - } - } else if (keep && add_filter->get_text().is_subsequence_ofn(String(p_current->get_name()))) { - node = nodes_to_add->create_item(add_node_root); - keep = true; - } else { - keep = false; +void GroupsEditor::_set_group_checked(const String &p_name, bool p_checked) { + TreeItem *ti = tree->get_item_with_text(p_name); + if (!ti) { + return; } - if (keep) { - node->set_text(0, item_name); - node->set_metadata(0, path); - node->set_tooltip_text(0, path); + ti->set_checked(0, p_checked); +} - Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(p_current, "Node"); - node->set_icon(0, icon); +bool GroupsEditor::_has_group(const String &p_name) { + return global_groups.has(p_name) || scene_groups.has(p_name); +} - if (!can_edit(p_current, selected_group)) { - node->set_selectable(0, false); - node->set_custom_color(0, get_theme_color(SNAME("disabled_font_color"), EditorStringName(Editor))); - } +void GroupsEditor::_modify_group(Object *p_item, int p_column, int p_id, MouseButton p_mouse_button) { + if (p_mouse_button != MouseButton::LEFT) { + return; } - for (int i = 0; i < p_current->get_child_count(); i++) { - _load_nodes(p_current->get_child(i)); + if (!node) { + return; } -} -void GroupDialog::_add_pressed() { - TreeItem *selected = nodes_to_add->get_next_selected(nullptr); - - if (!selected) { + TreeItem *ti = Object::cast_to<TreeItem>(p_item); + if (!ti) { return; } - EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Add to Group")); + if (p_id == COPY_GROUP) { + DisplayServer::get_singleton()->clipboard_set(ti->get_text(p_column)); + } +} - while (selected) { - Node *node = scene_tree->get_edited_scene_root()->get_node(selected->get_metadata(0)); - undo_redo->add_do_method(node, "add_to_group", selected_group, true); - undo_redo->add_undo_method(node, "remove_from_group", selected_group); +void GroupsEditor::_load_scene_groups(Node *p_node) { + List<Node::GroupInfo> groups; + p_node->get_groups(&groups); - selected = nodes_to_add->get_next_selected(selected); - } + for (const GroupInfo &gi : groups) { + if (!gi.persistent) { + continue; + } - undo_redo->add_do_method(this, "_group_selected"); - undo_redo->add_undo_method(this, "_group_selected"); - undo_redo->add_do_method(this, "emit_signal", "group_edited"); - undo_redo->add_undo_method(this, "emit_signal", "group_edited"); + if (global_groups.has(gi.name)) { + continue; + } - // To force redraw of scene tree. - undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); - undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); + bool is_editable = can_edit(p_node, gi.name); + if (scene_groups.has(gi.name)) { + scene_groups[gi.name] = scene_groups[gi.name] && is_editable; + } else { + scene_groups[gi.name] = is_editable; + } + } - undo_redo->commit_action(); + for (int i = 0; i < p_node->get_child_count(); i++) { + _load_scene_groups(p_node->get_child(i)); + } } -void GroupDialog::_removed_pressed() { - TreeItem *selected = nodes_to_remove->get_next_selected(nullptr); - - if (!selected) { +void GroupsEditor::_update_groups() { + if (!is_visible_in_tree()) { + groups_dirty = true; return; } - EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Remove from Group")); - - while (selected) { - Node *node = scene_tree->get_edited_scene_root()->get_node(selected->get_metadata(0)); - undo_redo->add_do_method(node, "remove_from_group", selected_group); - undo_redo->add_undo_method(node, "add_to_group", selected_group, true); - - selected = nodes_to_add->get_next_selected(selected); + if (updating_groups) { + return; } - undo_redo->add_do_method(this, "_group_selected"); - undo_redo->add_undo_method(this, "_group_selected"); - undo_redo->add_do_method(this, "emit_signal", "group_edited"); - undo_redo->add_undo_method(this, "emit_signal", "group_edited"); + updating_groups = true; - // To force redraw of scene tree. - undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); - undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); + global_groups = ProjectSettings::get_singleton()->get_global_groups_list(); - undo_redo->commit_action(); -} + _load_scene_groups(scene_root_node); -void GroupDialog::_remove_filter_changed(const String &p_filter) { - _group_selected(); -} + for (const KeyValue<StringName, bool> &E : scene_groups) { + if (global_groups.has(E.key)) { + scene_groups.erase(E.key); + } + } -void GroupDialog::_add_filter_changed(const String &p_filter) { - _group_selected(); + updating_groups = false; } -void GroupDialog::_add_group_pressed(const String &p_name) { - _add_group(add_group_text->get_text()); - add_group_text->clear(); -} +void GroupsEditor::_update_tree() { + if (!is_visible_in_tree()) { + groups_dirty = true; + return; + } -void GroupDialog::_add_group(String p_name) { - if (!is_visible()) { - return; // No need to edit the dialog if it's not being used. + if (!node) { + return; } - String name = p_name.strip_edges(); - if (name.is_empty() || groups->get_item_with_text(name)) { + if (updating_tree) { return; } - TreeItem *new_group = groups->create_item(groups_root); - new_group->set_text(0, name); - new_group->add_button(0, get_editor_theme_icon(SNAME("Remove")), DELETE_GROUP); - new_group->add_button(0, get_editor_theme_icon(SNAME("ActionCopy")), COPY_GROUP); - new_group->set_editable(0, true); - new_group->select(0); - groups->ensure_cursor_is_visible(); -} + updating_tree = true; -void GroupDialog::_add_group_text_changed(const String &p_new_text) { - add_group_button->set_disabled(p_new_text.strip_edges().is_empty()); -} + tree->clear(); -void GroupDialog::_group_renamed() { - TreeItem *renamed_group = groups->get_selected(); - if (!renamed_group) { - return; - } + List<Node::GroupInfo> groups; + node->get_groups(&groups); + groups.sort_custom<_GroupInfoComparator>(); - const String name = renamed_group->get_text(0).strip_edges(); - if (name == selected_group) { - return; + List<StringName> current_groups; + for (const Node::GroupInfo &gi : groups) { + current_groups.push_back(gi.name); } - if (name.is_empty()) { - renamed_group->set_text(0, selected_group); - error->set_text(TTR("Invalid group name.")); - error->popup_centered(); - return; + TreeItem *root = tree->create_item(); + + TreeItem *local_root = tree->create_item(root); + local_root->set_text(0, "Scene Groups"); + local_root->set_icon(0, get_editor_theme_icon(SNAME("PackedScene"))); + local_root->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), SNAME("Editor"))); + local_root->set_selectable(0, false); + + List<StringName> scene_keys; + for (const KeyValue<StringName, bool> &E : scene_groups) { + scene_keys.push_back(E.key); } + scene_keys.sort_custom<NoCaseComparator>(); - for (TreeItem *E = groups_root->get_first_child(); E; E = E->get_next()) { - if (E != renamed_group && E->get_text(0) == name) { - renamed_group->set_text(0, selected_group); - error->set_text(TTR("Group name already exists.")); - error->popup_centered(); - return; + for (const StringName &E : scene_keys) { + if (!filter->get_text().is_subsequence_ofn(E)) { + continue; + } + + TreeItem *item = tree->create_item(local_root); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_editable(0, can_edit(node, E)); + item->set_checked(0, current_groups.find(E) != nullptr); + item->set_text(0, E); + item->set_meta("__local", true); + item->set_meta("__name", E); + item->set_meta("__description", ""); + if (!scene_groups[E]) { + item->add_button(0, get_editor_theme_icon(SNAME("Lock")), -1, true, TTR("This group belongs to another scene and can't be edited.")); } + item->add_button(0, get_editor_theme_icon(SNAME("ActionCopy")), COPY_GROUP, false, TTR("Copy group name to clipboard.")); } - renamed_group->set_text(0, name); // Spaces trimmed. + List<StringName> keys; + for (const KeyValue<StringName, String> &E : global_groups) { + keys.push_back(E.key); + } + keys.sort_custom<NoCaseComparator>(); - EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Rename Group")); + TreeItem *global_root = tree->create_item(root); + global_root->set_text(0, "Global Groups"); + global_root->set_icon(0, get_editor_theme_icon(SNAME("Environment"))); + global_root->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), SNAME("Editor"))); + global_root->set_selectable(0, false); - List<Node *> nodes; - scene_tree->get_nodes_in_group(selected_group, &nodes); - bool removed_all = true; - for (Node *node : nodes) { - if (can_edit(node, selected_group)) { - undo_redo->add_do_method(node, "remove_from_group", selected_group); - undo_redo->add_undo_method(node, "remove_from_group", name); - undo_redo->add_do_method(node, "add_to_group", name, true); - undo_redo->add_undo_method(node, "add_to_group", selected_group, true); - } else { - removed_all = false; + for (const StringName &E : keys) { + if (!filter->get_text().is_subsequence_ofn(E)) { + continue; } - } - if (!removed_all) { - undo_redo->add_do_method(this, "_add_group", selected_group); - undo_redo->add_undo_method(this, "_delete_group_item", selected_group); + TreeItem *item = tree->create_item(global_root); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_editable(0, can_edit(node, E)); + item->set_checked(0, current_groups.find(E) != nullptr); + item->set_text(0, E); + item->set_meta("__local", false); + item->set_meta("__name", E); + item->set_meta("__description", global_groups[E]); + if (!global_groups[E].is_empty()) { + item->set_tooltip_text(0, vformat("%s\n\n%s", E, global_groups[E])); + } + item->add_button(0, get_editor_theme_icon(SNAME("ActionCopy")), COPY_GROUP, false, TTR("Copy group name to clipboard.")); } - undo_redo->add_do_method(this, "_rename_group_item", selected_group, name); - undo_redo->add_undo_method(this, "_rename_group_item", name, selected_group); - undo_redo->add_do_method(this, "_group_selected"); - undo_redo->add_undo_method(this, "_group_selected"); - undo_redo->add_do_method(this, "emit_signal", "group_edited"); - undo_redo->add_undo_method(this, "emit_signal", "group_edited"); - - undo_redo->commit_action(); + updating_tree = false; } -void GroupDialog::_rename_group_item(const String &p_old_name, const String &p_new_name) { - if (!is_visible()) { - return; // No need to edit the dialog if it's not being used. +void GroupsEditor::_queue_update_groups_and_tree() { + if (update_groups_and_tree_queued) { + return; } + update_groups_and_tree_queued = true; + callable_mp(this, &GroupsEditor::_update_groups_and_tree).call_deferred(); +} - selected_group = p_new_name; +void GroupsEditor::_update_groups_and_tree() { + update_groups_and_tree_queued = false; + _update_groups(); + _update_tree(); +} - for (TreeItem *E = groups_root->get_first_child(); E; E = E->get_next()) { - if (E->get_text(0) == p_old_name) { - E->set_text(0, p_new_name); - return; - } +void GroupsEditor::_update_scene_groups(Node *p_node) { + if (scene_groups_cache.has(p_node)) { + scene_groups = scene_groups_cache[p_node]; + scene_groups_cache.erase(p_node); + } else { + scene_groups = HashMap<StringName, bool>(); } } -void GroupDialog::_load_groups(Node *p_current) { - List<Node::GroupInfo> gi; - p_current->get_groups(&gi); - - for (const Node::GroupInfo &E : gi) { - if (!E.persistent) { - continue; +void GroupsEditor::_cache_scene_groups(Node *p_node) { + const int edited_scene_count = EditorNode::get_editor_data().get_edited_scene_count(); + for (int i = 0; i < edited_scene_count; i++) { + if (p_node == EditorNode::get_editor_data().get_edited_scene_root(i)) { + scene_groups_cache[p_node] = scene_groups_for_caching; + break; } - _add_group(E.name); } +} - for (int i = 0; i < p_current->get_child_count(); i++) { - _load_groups(p_current->get_child(i)); +void GroupsEditor::set_current(Node *p_node) { + if (node == p_node) { + return; } -} + node = p_node; -void GroupDialog::_modify_group_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) { - if (p_button != MouseButton::LEFT) { + if (!node) { return; } - TreeItem *ti = Object::cast_to<TreeItem>(p_item); + if (scene_tree->get_edited_scene_root() != scene_root_node) { + scene_root_node = scene_tree->get_edited_scene_root(); + _update_scene_groups(scene_root_node); + _update_groups(); + } + + _update_tree(); +} + +void GroupsEditor::_item_edited() { + TreeItem *ti = tree->get_edited(); if (!ti) { return; } - switch (p_id) { - case DELETE_GROUP: { - String name = ti->get_text(0); + String name = ti->get_text(0); - EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Delete Group")); - - List<Node *> nodes; - scene_tree->get_nodes_in_group(name, &nodes); - bool removed_all = true; - for (Node *E : nodes) { - if (can_edit(E, name)) { - undo_redo->add_do_method(E, "remove_from_group", name); - undo_redo->add_undo_method(E, "add_to_group", name, true); - } else { - removed_all = false; - } - } + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + if (ti->is_checked(0)) { + undo_redo->create_action(TTR("Add to Group")); - if (removed_all) { - undo_redo->add_do_method(this, "_delete_group_item", name); - undo_redo->add_undo_method(this, "_add_group", name); - } + undo_redo->add_do_method(node, "add_to_group", name, true); + undo_redo->add_undo_method(node, "remove_from_group", name); - undo_redo->add_do_method(this, "_group_selected"); - undo_redo->add_undo_method(this, "_group_selected"); - undo_redo->add_do_method(this, "emit_signal", "group_edited"); - undo_redo->add_undo_method(this, "emit_signal", "group_edited"); + undo_redo->add_do_method(this, "_set_group_checked", name, true); + undo_redo->add_undo_method(this, "_set_group_checked", name, false); - // To force redraw of scene tree. - undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); - undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); + // To force redraw of scene tree. + undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); + undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); - undo_redo->commit_action(); - } break; - case COPY_GROUP: { - DisplayServer::get_singleton()->clipboard_set(ti->get_text(p_column)); - } break; - } -} + undo_redo->commit_action(); -void GroupDialog::_delete_group_item(const String &p_name) { - if (!is_visible()) { - return; // No need to edit the dialog if it's not being used. - } + } else { + undo_redo->create_action(TTR("Remove from Group")); - if (selected_group == p_name) { - add_filter->clear(); - remove_filter->clear(); - nodes_to_remove->clear(); - nodes_to_add->clear(); - groups->deselect_all(); - selected_group = ""; - } + undo_redo->add_do_method(node, "remove_from_group", name); + undo_redo->add_undo_method(node, "add_to_group", name, true); - for (TreeItem *E = groups_root->get_first_child(); E; E = E->get_next()) { - if (E->get_text(0) == p_name) { - groups_root->remove_child(E); - return; - } + undo_redo->add_do_method(this, "_set_group_checked", name, false); + undo_redo->add_undo_method(this, "_set_group_checked", name, true); + + // To force redraw of scene tree. + undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); + undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); + + undo_redo->commit_action(); } } -void GroupDialog::_notification(int p_what) { +void GroupsEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_TRANSLATION_CHANGED: - case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_READY: { + get_tree()->connect("node_added", callable_mp(this, &GroupsEditor::_load_scene_groups)); + get_tree()->connect("node_removed", callable_mp(this, &GroupsEditor::_node_removed)); + } break; case NOTIFICATION_THEME_CHANGED: { - if (is_layout_rtl()) { - add_button->set_icon(get_editor_theme_icon(SNAME("Back"))); - remove_button->set_icon(get_editor_theme_icon(SNAME("Forward"))); - } else { - add_button->set_icon(get_editor_theme_icon(SNAME("Forward"))); - remove_button->set_icon(get_editor_theme_icon(SNAME("Back"))); + filter->set_right_icon(get_editor_theme_icon("Search")); + add->set_icon(get_editor_theme_icon("Add")); + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (groups_dirty && is_visible_in_tree()) { + groups_dirty = false; + _update_groups_and_tree(); } - - add_filter->set_right_icon(get_editor_theme_icon(SNAME("Search"))); - remove_filter->set_right_icon(get_editor_theme_icon(SNAME("Search"))); } break; } } -void GroupDialog::edit() { - popup_centered(); +void GroupsEditor::_menu_id_pressed(int p_id) { + TreeItem *ti = tree->get_selected(); + if (!ti) { + return; + } - groups->clear(); - groups_root = groups->create_item(); + bool is_local = ti->get_meta("__local"); + String group_name = ti->get_meta("__name"); - nodes_to_add->clear(); - nodes_to_remove->clear(); + switch (p_id) { + case DELETE_GROUP: { + if (!is_local || scene_groups[group_name]) { + _show_remove_group_dialog(); + } + } break; + case RENAME_GROUP: { + if (!is_local || scene_groups[group_name]) { + _show_rename_group_dialog(); + } + } break; + case CONVERT_GROUP: { + String description = ti->get_meta("__description"); + String property_name = GLOBAL_GROUP_PREFIX + group_name; - add_group_text->clear(); - add_filter->clear(); - remove_filter->clear(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + if (is_local) { + undo_redo->create_action(TTR("Convert to Global Group")); - _load_groups(scene_tree->get_edited_scene_root()); -} + undo_redo->add_do_property(ProjectSettings::get_singleton(), property_name, ""); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_name, Variant()); -void GroupDialog::_bind_methods() { - ClassDB::bind_method("_delete_group_item", &GroupDialog::_delete_group_item); + undo_redo->add_do_method(ProjectSettings::get_singleton(), "save"); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "save"); - ClassDB::bind_method("_add_group", &GroupDialog::_add_group); + undo_redo->add_undo_method(this, "_add_scene_group", group_name); - ClassDB::bind_method("_rename_group_item", &GroupDialog::_rename_group_item); + undo_redo->add_do_method(this, "_update_groups_and_tree"); + undo_redo->add_undo_method(this, "_update_groups_and_tree"); - ClassDB::bind_method("_group_selected", &GroupDialog::_group_selected); + undo_redo->commit_action(); + } else { + undo_redo->create_action(TTR("Convert to Scene Group")); - ADD_SIGNAL(MethodInfo("group_edited")); -} + undo_redo->add_do_property(ProjectSettings::get_singleton(), property_name, Variant()); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_name, description); -GroupDialog::GroupDialog() { - set_min_size(Size2(600, 400) * EDSCALE); + undo_redo->add_do_method(ProjectSettings::get_singleton(), "save"); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "save"); - scene_tree = SceneTree::get_singleton(); + undo_redo->add_do_method(this, "_add_scene_group", group_name); - VBoxContainer *vbc = memnew(VBoxContainer); - add_child(vbc); - vbc->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_KEEP_SIZE, 8 * EDSCALE); + undo_redo->add_do_method(this, "_update_groups_and_tree"); + undo_redo->add_undo_method(this, "_update_groups_and_tree"); - HBoxContainer *hbc = memnew(HBoxContainer); - vbc->add_child(hbc); - hbc->set_v_size_flags(Control::SIZE_EXPAND_FILL); - - VBoxContainer *vbc_left = memnew(VBoxContainer); - hbc->add_child(vbc_left); - vbc_left->set_h_size_flags(Control::SIZE_EXPAND_FILL); - - Label *group_title = memnew(Label); - group_title->set_theme_type_variation("HeaderSmall"); - - group_title->set_text(TTR("Groups")); - vbc_left->add_child(group_title); - - groups = memnew(Tree); - vbc_left->add_child(groups); - groups->set_hide_root(true); - groups->set_select_mode(Tree::SELECT_SINGLE); - groups->set_allow_reselect(true); - groups->set_allow_rmb_select(true); - groups->set_v_size_flags(Control::SIZE_EXPAND_FILL); - groups->add_theme_constant_override("draw_guides", 1); - groups->connect("item_selected", callable_mp(this, &GroupDialog::_group_selected)); - groups->connect("button_clicked", callable_mp(this, &GroupDialog::_modify_group_pressed)); - groups->connect("item_edited", callable_mp(this, &GroupDialog::_group_renamed)); - - HBoxContainer *chbc = memnew(HBoxContainer); - vbc_left->add_child(chbc); - chbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); - - add_group_text = memnew(LineEdit); - chbc->add_child(add_group_text); - add_group_text->set_h_size_flags(Control::SIZE_EXPAND_FILL); - add_group_text->connect("text_submitted", callable_mp(this, &GroupDialog::_add_group_pressed)); - add_group_text->connect("text_changed", callable_mp(this, &GroupDialog::_add_group_text_changed)); - - add_group_button = memnew(Button); - add_group_button->set_text(TTR("Add")); - chbc->add_child(add_group_button); - add_group_button->connect("pressed", callable_mp(this, &GroupDialog::_add_group_pressed).bind(String())); - - VBoxContainer *vbc_add = memnew(VBoxContainer); - hbc->add_child(vbc_add); - vbc_add->set_h_size_flags(Control::SIZE_EXPAND_FILL); - - Label *out_of_group_title = memnew(Label); - out_of_group_title->set_theme_type_variation("HeaderSmall"); - - out_of_group_title->set_text(TTR("Nodes Not in Group")); - vbc_add->add_child(out_of_group_title); - - nodes_to_add = memnew(Tree); - vbc_add->add_child(nodes_to_add); - nodes_to_add->set_hide_root(true); - nodes_to_add->set_hide_folding(true); - nodes_to_add->set_select_mode(Tree::SELECT_MULTI); - nodes_to_add->set_v_size_flags(Control::SIZE_EXPAND_FILL); - nodes_to_add->add_theme_constant_override("draw_guides", 1); - - HBoxContainer *add_filter_hbc = memnew(HBoxContainer); - add_filter_hbc->add_theme_constant_override("separate", 0); - vbc_add->add_child(add_filter_hbc); - - add_filter = memnew(LineEdit); - add_filter->set_h_size_flags(Control::SIZE_EXPAND_FILL); - add_filter->set_placeholder(TTR("Filter Nodes")); - add_filter->set_clear_button_enabled(true); - add_filter_hbc->add_child(add_filter); - add_filter->connect("text_changed", callable_mp(this, &GroupDialog::_add_filter_changed)); - - VBoxContainer *vbc_buttons = memnew(VBoxContainer); - hbc->add_child(vbc_buttons); - vbc_buttons->set_h_size_flags(Control::SIZE_SHRINK_CENTER); - vbc_buttons->set_v_size_flags(Control::SIZE_SHRINK_CENTER); - - add_button = memnew(Button); - add_button->set_flat(true); - add_button->set_text(TTR("Add")); - add_button->connect("pressed", callable_mp(this, &GroupDialog::_add_pressed)); - - vbc_buttons->add_child(add_button); - vbc_buttons->add_spacer(); - vbc_buttons->add_spacer(); - vbc_buttons->add_spacer(); - - remove_button = memnew(Button); - remove_button->set_flat(true); - remove_button->set_text(TTR("Remove")); - remove_button->connect("pressed", callable_mp(this, &GroupDialog::_removed_pressed)); - - vbc_buttons->add_child(remove_button); - - VBoxContainer *vbc_remove = memnew(VBoxContainer); - hbc->add_child(vbc_remove); - vbc_remove->set_h_size_flags(Control::SIZE_EXPAND_FILL); - - Label *in_group_title = memnew(Label); - in_group_title->set_theme_type_variation("HeaderSmall"); - - in_group_title->set_text(TTR("Nodes in Group")); - vbc_remove->add_child(in_group_title); - - nodes_to_remove = memnew(Tree); - vbc_remove->add_child(nodes_to_remove); - nodes_to_remove->set_v_size_flags(Control::SIZE_EXPAND_FILL); - nodes_to_remove->set_hide_root(true); - nodes_to_remove->set_hide_folding(true); - nodes_to_remove->set_select_mode(Tree::SELECT_MULTI); - nodes_to_remove->add_theme_constant_override("draw_guides", 1); - - HBoxContainer *remove_filter_hbc = memnew(HBoxContainer); - remove_filter_hbc->add_theme_constant_override("separate", 0); - vbc_remove->add_child(remove_filter_hbc); - - remove_filter = memnew(LineEdit); - remove_filter->set_h_size_flags(Control::SIZE_EXPAND_FILL); - remove_filter->set_placeholder(TTR("Filter Nodes")); - remove_filter->set_clear_button_enabled(true); - remove_filter_hbc->add_child(remove_filter); - remove_filter->connect("text_changed", callable_mp(this, &GroupDialog::_remove_filter_changed)); - - group_empty = memnew(Label()); - group_empty->set_theme_type_variation("HeaderSmall"); - - group_empty->set_text(TTR("Empty groups will be automatically removed.")); - group_empty->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); - group_empty->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); - group_empty->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); - group_empty->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); - nodes_to_remove->add_child(group_empty); - group_empty->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_KEEP_SIZE, 8 * EDSCALE); - - set_title(TTR("Group Editor")); - - error = memnew(AcceptDialog); - add_child(error); - error->set_ok_button_text(TTR("Close")); - - _add_group_text_changed(""); -} - -//////////////////////////////////////////////////////////////////////////////// - -void GroupsEditor::_add_group(const String &p_group) { - if (!node) { - return; + undo_redo->commit_action(); + } + } break; } - const String name = group_name->get_text().strip_edges(); +} - group_name->clear(); - if (node->is_in_group(name)) { - error->set_text(TTR("Group name already exists.")); - error->popup_centered(); +void GroupsEditor::_item_mouse_selected(const Vector2 &p_pos, MouseButton p_mouse_button) { + TreeItem *ti = tree->get_selected(); + if (!ti) { return; } + if (p_mouse_button == MouseButton::LEFT) { + callable_mp(this, &GroupsEditor::_item_edited).call_deferred(); + } else if (p_mouse_button == MouseButton::RIGHT) { + // Restore the previous state after clicking RMB. + if (ti->is_editable(0)) { + ti->set_checked(0, !ti->is_checked(0)); + } + + menu->clear(); + if (ti->get_meta("__local")) { + menu->add_icon_item(get_editor_theme_icon(SNAME("Environment")), TTR("Convert to Global Group"), CONVERT_GROUP); + } else { + menu->add_icon_item(get_editor_theme_icon(SNAME("PackedScene")), TTR("Convert to Scene Group"), CONVERT_GROUP); + } + + String group_name = ti->get_meta("__name"); + if (global_groups.has(group_name) || scene_groups[group_name]) { + menu->add_separator(); + menu->add_icon_shortcut(get_editor_theme_icon(SNAME("Rename")), ED_GET_SHORTCUT("groups_editor/rename"), RENAME_GROUP); + menu->add_icon_shortcut(get_editor_theme_icon(SNAME("Remove")), ED_GET_SHORTCUT("groups_editor/delete"), DELETE_GROUP); + } + + menu->set_position(tree->get_screen_position() + p_pos); + menu->reset_size(); + menu->popup(); + } +} + +void GroupsEditor::_confirm_add() { + String name = add_group_name->get_text().strip_edges(); + + String description = add_group_description->get_text(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Add to Group")); undo_redo->add_do_method(node, "add_to_group", name, true); undo_redo->add_undo_method(node, "remove_from_group", name); - undo_redo->add_do_method(this, "update_tree"); - undo_redo->add_undo_method(this, "update_tree"); + + bool is_local = !global_group_button->is_pressed(); + if (is_local) { + undo_redo->add_do_method(this, "_add_scene_group", name); + undo_redo->add_undo_method(this, "_remove_scene_group", name); + } else { + String property_name = GLOBAL_GROUP_PREFIX + name; + + undo_redo->add_do_property(ProjectSettings::get_singleton(), property_name, description); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_name, Variant()); + + undo_redo->add_do_method(ProjectSettings::get_singleton(), "save"); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "save"); + + undo_redo->add_do_method(this, "_update_groups"); + undo_redo->add_undo_method(this, "_update_groups"); + } + + undo_redo->add_do_method(this, "_update_tree"); + undo_redo->add_undo_method(this, "_update_tree"); // To force redraw of scene tree. undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); undo_redo->commit_action(); + tree->grab_focus(); } -void GroupsEditor::_group_selected() { - if (!tree->is_anything_selected()) { +void GroupsEditor::_confirm_rename() { + TreeItem *ti = tree->get_selected(); + if (!ti) { return; } - selected_group = tree->get_selected()->get_text(0); -} -void GroupsEditor::_group_renamed() { - if (!node || !can_edit(node, selected_group)) { + String old_name = ti->get_meta("__name"); + String new_name = rename_group->get_text().strip_edges(); + + if (old_name == new_name) { return; } + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Rename Group")); + + if (!global_groups.has(old_name)) { + undo_redo->add_do_method(this, "_rename_scene_group", old_name, new_name); + undo_redo->add_undo_method(this, "_rename_scene_group", new_name, old_name); + } else { + String property_new_name = GLOBAL_GROUP_PREFIX + new_name; + String property_old_name = GLOBAL_GROUP_PREFIX + old_name; + + String description = ti->get_meta("__description"); + + undo_redo->add_do_property(ProjectSettings::get_singleton(), property_new_name, description); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_new_name, Variant()); + + undo_redo->add_do_property(ProjectSettings::get_singleton(), property_old_name, Variant()); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_old_name, description); + + if (rename_check_box->is_pressed()) { + undo_redo->add_do_method(ProjectSettingsEditor::get_singleton()->get_group_settings(), "rename_references", old_name, new_name); + undo_redo->add_undo_method(ProjectSettingsEditor::get_singleton()->get_group_settings(), "rename_references", new_name, old_name); + } + + undo_redo->add_do_method(ProjectSettings::get_singleton(), "save"); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "save"); + + undo_redo->add_do_method(this, "_update_groups"); + undo_redo->add_undo_method(this, "_update_groups"); + } + + undo_redo->add_do_method(this, "_update_tree"); + undo_redo->add_undo_method(this, "_update_tree"); + + undo_redo->commit_action(); + + tree->grab_focus(); +} + +void GroupsEditor::_confirm_delete() { TreeItem *ti = tree->get_selected(); if (!ti) { return; } - const String name = ti->get_text(0).strip_edges(); - if (name == selected_group) { - return; - } + String name = ti->get_meta("__name"); + bool is_local = ti->get_meta("__local"); - if (name.is_empty()) { - ti->set_text(0, selected_group); - error->set_text(TTR("Invalid group name.")); - error->popup_centered(); - return; - } + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Remove Group")); - for (TreeItem *E = groups_root->get_first_child(); E; E = E->get_next()) { - if (E != ti && E->get_text(0) == name) { - ti->set_text(0, selected_group); - error->set_text(TTR("Group name already exists.")); - error->popup_centered(); - return; - } - } + if (is_local) { + undo_redo->add_do_method(this, "_remove_scene_group", name); + undo_redo->add_undo_method(this, "_add_scene_group", name); + } else { + String property_name = GLOBAL_GROUP_PREFIX + name; + String description = ti->get_meta("__description"); - ti->set_text(0, name); // Spaces trimmed. + undo_redo->add_do_property(ProjectSettings::get_singleton(), property_name, Variant()); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_name, description); - EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Rename Group")); + if (remove_check_box->is_pressed()) { + undo_redo->add_do_method(ProjectSettingsEditor::get_singleton()->get_group_settings(), "remove_references", name); + } - undo_redo->add_do_method(node, "remove_from_group", selected_group); - undo_redo->add_undo_method(node, "remove_from_group", name); - undo_redo->add_do_method(node, "add_to_group", name, true); - undo_redo->add_undo_method(node, "add_to_group", selected_group, true); + undo_redo->add_do_method(ProjectSettings::get_singleton(), "save"); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "save"); - undo_redo->add_do_method(this, "_group_selected"); - undo_redo->add_undo_method(this, "_group_selected"); - undo_redo->add_do_method(this, "update_tree"); - undo_redo->add_undo_method(this, "update_tree"); + undo_redo->add_do_method(this, "_update_groups"); + undo_redo->add_undo_method(this, "_update_groups"); + } - // To force redraw of scene tree. - undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); - undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); + undo_redo->add_do_method(this, "_update_tree"); + undo_redo->add_undo_method(this, "_update_tree"); undo_redo->commit_action(); + tree->grab_focus(); } -void GroupsEditor::_modify_group(Object *p_item, int p_column, int p_id, MouseButton p_button) { - if (p_button != MouseButton::LEFT) { - return; +void GroupsEditor::_show_add_group_dialog() { + if (!add_group_dialog) { + add_group_dialog = memnew(ConfirmationDialog); + add_group_dialog->set_title(TTR("Create New Group")); + add_group_dialog->connect("confirmed", callable_mp(this, &GroupsEditor::_confirm_add)); + + VBoxContainer *vbc = memnew(VBoxContainer); + add_group_dialog->add_child(vbc); + + GridContainer *gc = memnew(GridContainer); + gc->set_columns(2); + vbc->add_child(gc); + + Label *label_name = memnew(Label(TTR("Name:"))); + label_name->set_h_size_flags(SIZE_SHRINK_BEGIN); + gc->add_child(label_name); + + HBoxContainer *hbc = memnew(HBoxContainer); + hbc->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(hbc); + + add_group_name = memnew(LineEdit); + add_group_name->set_custom_minimum_size(Size2(200 * EDSCALE, 0)); + add_group_name->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(add_group_name); + + global_group_button = memnew(CheckButton); + global_group_button->set_text(TTR("Global")); + hbc->add_child(global_group_button); + + Label *label_description = memnew(Label(TTR("Description:"))); + label_name->set_h_size_flags(SIZE_SHRINK_BEGIN); + gc->add_child(label_description); + + add_group_description = memnew(LineEdit); + add_group_description->set_h_size_flags(SIZE_EXPAND_FILL); + add_group_description->set_editable(false); + gc->add_child(add_group_description); + + global_group_button->connect("toggled", callable_mp(add_group_description, &LineEdit::set_editable)); + + add_group_dialog->register_text_enter(add_group_name); + add_group_dialog->register_text_enter(add_group_description); + + add_validation_panel = memnew(EditorValidationPanel); + add_validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group name is valid.")); + add_validation_panel->set_update_callback(callable_mp(this, &GroupsEditor::_check_add)); + add_validation_panel->set_accept_button(add_group_dialog->get_ok_button()); + + add_group_name->connect("text_changed", callable_mp(add_validation_panel, &EditorValidationPanel::update).unbind(1)); + + vbc->add_child(add_validation_panel); + + add_child(add_group_dialog); } + add_group_name->clear(); + add_group_description->clear(); - if (!node) { - return; + global_group_button->set_pressed(false); + + add_validation_panel->update(); + + add_group_dialog->popup_centered(); + add_group_name->grab_focus(); +} + +void GroupsEditor::_show_rename_group_dialog() { + if (!rename_group_dialog) { + rename_group_dialog = memnew(ConfirmationDialog); + rename_group_dialog->set_title(TTR("Rename Group")); + rename_group_dialog->connect("confirmed", callable_mp(this, &GroupsEditor::_confirm_rename)); + + VBoxContainer *vbc = memnew(VBoxContainer); + rename_group_dialog->add_child(vbc); + + HBoxContainer *hbc = memnew(HBoxContainer); + hbc->add_child(memnew(Label(TTR("Name:")))); + + rename_group = memnew(LineEdit); + rename_group->set_custom_minimum_size(Size2(300 * EDSCALE, 1)); + hbc->add_child(rename_group); + vbc->add_child(hbc); + + rename_group_dialog->register_text_enter(rename_group); + + rename_validation_panel = memnew(EditorValidationPanel); + rename_validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group name is valid.")); + rename_validation_panel->set_update_callback(callable_mp(this, &GroupsEditor::_check_rename)); + rename_validation_panel->set_accept_button(rename_group_dialog->get_ok_button()); + + rename_group->connect("text_changed", callable_mp(rename_validation_panel, &EditorValidationPanel::update).unbind(1)); + + vbc->add_child(rename_validation_panel); + + rename_check_box = memnew(CheckBox); + rename_check_box->set_text(TTR("Rename references in all scenes")); + vbc->add_child(rename_check_box); + + add_child(rename_group_dialog); } - TreeItem *ti = Object::cast_to<TreeItem>(p_item); + TreeItem *ti = tree->get_selected(); if (!ti) { return; } - switch (p_id) { - case DELETE_GROUP: { - const String name = ti->get_text(0); - EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Remove from Group")); - undo_redo->add_do_method(node, "remove_from_group", name); - undo_redo->add_undo_method(node, "add_to_group", name, true); - undo_redo->add_do_method(this, "update_tree"); - undo_redo->add_undo_method(this, "update_tree"); + bool is_global = !ti->get_meta("__local"); + rename_check_box->set_visible(is_global); + rename_check_box->set_pressed(false); - // To force redraw of scene tree. - undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); - undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); + String name = ti->get_meta("__name"); - undo_redo->commit_action(); - } break; - case COPY_GROUP: { - DisplayServer::get_singleton()->clipboard_set(ti->get_text(p_column)); - } break; - } -} + rename_group->set_text(name); + rename_group_dialog->set_meta("__name", name); -void GroupsEditor::_group_name_changed(const String &p_new_text) { - add->set_disabled(p_new_text.strip_edges().is_empty()); + rename_validation_panel->update(); + + rename_group_dialog->reset_size(); + rename_group_dialog->popup_centered(); + rename_group->select_all(); + rename_group->grab_focus(); } -struct _GroupInfoComparator { - bool operator()(const Node::GroupInfo &p_a, const Node::GroupInfo &p_b) const { - return p_a.name.operator String() < p_b.name.operator String(); - } -}; +void GroupsEditor::_show_remove_group_dialog() { + if (!remove_group_dialog) { + remove_group_dialog = memnew(ConfirmationDialog); + remove_group_dialog->connect("confirmed", callable_mp(this, &GroupsEditor::_confirm_delete)); -void GroupsEditor::update_tree() { - tree->clear(); + VBoxContainer *vbox = memnew(VBoxContainer); + remove_label = memnew(Label); + vbox->add_child(remove_label); - if (!node) { - return; + remove_check_box = memnew(CheckBox); + remove_check_box->set_text(TTR("Delete references from all scenes")); + vbox->add_child(remove_check_box); + + remove_group_dialog->add_child(vbox); + + add_child(remove_group_dialog); } - List<Node::GroupInfo> groups; - node->get_groups(&groups); - groups.sort_custom<_GroupInfoComparator>(); + TreeItem *ti = tree->get_selected(); + if (!ti) { + return; + } - TreeItem *root = tree->create_item(); - groups_root = root; + bool is_global = !ti->get_meta("__local"); + remove_check_box->set_visible(is_global); + remove_check_box->set_pressed(false); + remove_label->set_text(vformat(TTR("Delete group \"%s\" and all its references?"), ti->get_text(0))); - for (const GroupInfo &gi : groups) { - if (!gi.persistent) { - continue; - } + remove_group_dialog->reset_size(); + remove_group_dialog->popup_centered(); +} - Node *n = node; - bool can_be_deleted = true; +void GroupsEditor::_check_add() { + String group_name = add_group_name->get_text().strip_edges(); + _validate_name(group_name, add_validation_panel); +} - while (n) { - Ref<SceneState> ss = (n == EditorNode::get_singleton()->get_edited_scene()) ? n->get_scene_inherited_state() : n->get_scene_instance_state(); +void GroupsEditor::_check_rename() { + String group_name = rename_group->get_text().strip_edges(); + String old_name = rename_group_dialog->get_meta("__name"); - if (ss.is_valid()) { - int path = ss->find_node_by_path(n->get_path_to(node)); - if (path != -1) { - if (ss->is_node_in_group(path, gi.name)) { - can_be_deleted = false; - } - } - } + if (group_name == old_name) { + return; + } + _validate_name(group_name, rename_validation_panel); +} - n = n->get_owner(); - } +void GroupsEditor::_validate_name(const String &p_name, EditorValidationPanel *p_validation_panel) { + if (p_name.is_empty()) { + p_validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group can't be empty."), EditorValidationPanel::MSG_ERROR); + } else if (_has_group(p_name)) { + p_validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group already exists."), EditorValidationPanel::MSG_ERROR); + } +} - TreeItem *item = tree->create_item(root); - item->set_text(0, gi.name); - item->set_editable(0, true); - if (can_be_deleted) { - item->add_button(0, get_editor_theme_icon(SNAME("Remove")), DELETE_GROUP); - item->add_button(0, get_editor_theme_icon(SNAME("ActionCopy")), COPY_GROUP); +void GroupsEditor::_groups_gui_input(Ref<InputEvent> p_event) { + Ref<InputEventKey> key = p_event; + if (key.is_valid() && key->is_pressed() && !key->is_echo()) { + if (ED_IS_SHORTCUT("groups_editor/delete", p_event)) { + _menu_id_pressed(DELETE_GROUP); + } else if (ED_IS_SHORTCUT("groups_editor/rename", p_event)) { + _menu_id_pressed(RENAME_GROUP); } else { - item->set_selectable(0, false); + return; } + + accept_event(); } } -void GroupsEditor::set_current(Node *p_node) { - node = p_node; - update_tree(); +void GroupsEditor::_bind_methods() { + ClassDB::bind_method("_update_tree", &GroupsEditor::_update_tree); + ClassDB::bind_method("_update_groups", &GroupsEditor::_update_groups); + ClassDB::bind_method("_update_groups_and_tree", &GroupsEditor::_update_groups_and_tree); + + ClassDB::bind_method("_add_scene_group", &GroupsEditor::_add_scene_group); + ClassDB::bind_method("_rename_scene_group", &GroupsEditor::_rename_scene_group); + ClassDB::bind_method("_remove_scene_group", &GroupsEditor::_remove_scene_group); + ClassDB::bind_method("_set_group_checked", &GroupsEditor::_set_group_checked); } -void GroupsEditor::_show_group_dialog() { - group_dialog->edit(); -} +void GroupsEditor::_node_removed(Node *p_node) { + if (scene_root_node == p_node) { + scene_groups_for_caching = scene_groups; + callable_mp(this, &GroupsEditor::_cache_scene_groups).call_deferred(p_node); + scene_root_node = nullptr; + } -void GroupsEditor::_bind_methods() { - ClassDB::bind_method("update_tree", &GroupsEditor::update_tree); - ClassDB::bind_method("_group_selected", &GroupsEditor::_group_selected); + if (scene_root_node && scene_root_node == p_node->get_owner()) { + _queue_update_groups_and_tree(); + } } GroupsEditor::GroupsEditor() { node = nullptr; + scene_tree = SceneTree::get_singleton(); - VBoxContainer *vbc = this; - - group_dialog = memnew(GroupDialog); - - add_child(group_dialog); - group_dialog->connect("group_edited", callable_mp(this, &GroupsEditor::update_tree)); - - Button *group_dialog_button = memnew(Button); - group_dialog_button->set_text(TTR("Manage Groups")); - vbc->add_child(group_dialog_button); - group_dialog_button->connect("pressed", callable_mp(this, &GroupsEditor::_show_group_dialog)); + ED_SHORTCUT("groups_editor/delete", TTR("Delete"), Key::KEY_DELETE); + ED_SHORTCUT("groups_editor/rename", TTR("Rename"), Key::F2); + ED_SHORTCUT_OVERRIDE("groups_editor/rename", "macos", Key::ENTER); HBoxContainer *hbc = memnew(HBoxContainer); - vbc->add_child(hbc); - - group_name = memnew(LineEdit); - group_name->set_h_size_flags(Control::SIZE_EXPAND_FILL); - hbc->add_child(group_name); - group_name->connect("text_submitted", callable_mp(this, &GroupsEditor::_add_group)); - group_name->connect("text_changed", callable_mp(this, &GroupsEditor::_group_name_changed)); + add_child(hbc); add = memnew(Button); - add->set_text(TTR("Add")); + add->set_flat(true); + add->set_tooltip_text(TTR("Add a new group.")); + add->connect("pressed", callable_mp(this, &GroupsEditor::_show_add_group_dialog)); hbc->add_child(add); - add->connect("pressed", callable_mp(this, &GroupsEditor::_add_group).bind(String())); + + filter = memnew(LineEdit); + filter->set_clear_button_enabled(true); + filter->set_placeholder(TTR("Filter Groups")); + filter->set_h_size_flags(SIZE_EXPAND_FILL); + filter->connect("text_changed", callable_mp(this, &GroupsEditor::_update_tree).unbind(1)); + hbc->add_child(filter); tree = memnew(Tree); - vbc->add_child(tree); tree->set_hide_root(true); - tree->set_allow_reselect(true); + tree->set_v_size_flags(SIZE_EXPAND_FILL); tree->set_allow_rmb_select(true); - tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); - tree->connect("item_selected", callable_mp(this, &GroupsEditor::_group_selected)); + tree->set_select_mode(Tree::SelectMode::SELECT_SINGLE); tree->connect("button_clicked", callable_mp(this, &GroupsEditor::_modify_group)); - tree->connect("item_edited", callable_mp(this, &GroupsEditor::_group_renamed)); - tree->add_theme_constant_override("draw_guides", 1); - add_theme_constant_override("separation", 3 * EDSCALE); + tree->connect("item_mouse_selected", callable_mp(this, &GroupsEditor::_item_mouse_selected)); + tree->connect("gui_input", callable_mp(this, &GroupsEditor::_groups_gui_input)); + add_child(tree); - error = memnew(AcceptDialog); - add_child(error); - error->get_ok_button()->set_text(TTR("Close")); + menu = memnew(PopupMenu); + menu->connect("id_pressed", callable_mp(this, &GroupsEditor::_menu_id_pressed)); + tree->add_child(menu); - _group_name_changed(""); + ProjectSettingsEditor::get_singleton()->get_group_settings()->connect("group_changed", callable_mp(this, &GroupsEditor::_update_groups_and_tree)); } GroupsEditor::~GroupsEditor() { diff --git a/editor/groups_editor.h b/editor/groups_editor.h index 27322b63da..cf74470b1b 100644 --- a/editor/groups_editor.h +++ b/editor/groups_editor.h @@ -34,105 +34,105 @@ #include "scene/gui/dialogs.h" class Button; +class CheckBox; +class CheckButton; +class EditorValidationPanel; +class Label; class LineEdit; +class PopupMenu; class Tree; class TreeItem; -class GroupDialog : public AcceptDialog { - GDCLASS(GroupDialog, AcceptDialog); +class GroupsEditor : public VBoxContainer { + GDCLASS(GroupsEditor, VBoxContainer); - AcceptDialog *error = nullptr; + const String GLOBAL_GROUP_PREFIX = "global_group/"; + bool updating_tree = false; + bool updating_groups = false; + bool groups_dirty = false; + bool update_groups_and_tree_queued = false; + + Node *node = nullptr; + Node *scene_root_node = nullptr; SceneTree *scene_tree = nullptr; - TreeItem *groups_root = nullptr; - LineEdit *add_group_text = nullptr; - Button *add_group_button = nullptr; + ConfirmationDialog *add_group_dialog = nullptr; + LineEdit *add_group_name = nullptr; + LineEdit *add_group_description = nullptr; + CheckButton *global_group_button = nullptr; + EditorValidationPanel *add_validation_panel = nullptr; - Tree *groups = nullptr; + ConfirmationDialog *rename_group_dialog = nullptr; + LineEdit *rename_group = nullptr; + CheckBox *rename_check_box = nullptr; + EditorValidationPanel *rename_validation_panel = nullptr; - Tree *nodes_to_add = nullptr; - TreeItem *add_node_root = nullptr; - LineEdit *add_filter = nullptr; + ConfirmationDialog *remove_group_dialog = nullptr; + CheckBox *remove_check_box = nullptr; + Label *remove_label = nullptr; - Tree *nodes_to_remove = nullptr; - TreeItem *remove_node_root = nullptr; - LineEdit *remove_filter = nullptr; + PopupMenu *menu = nullptr; - Label *group_empty = nullptr; + LineEdit *filter = nullptr; + Button *add = nullptr; + Tree *tree = nullptr; - Button *add_button = nullptr; - Button *remove_button = nullptr; + HashMap<Node *, HashMap<StringName, bool>> scene_groups_cache; + HashMap<StringName, bool> scene_groups_for_caching; - String selected_group; + HashMap<StringName, bool> scene_groups; + HashMap<StringName, String> global_groups; - void _group_selected(); + void _update_scene_groups(Node *p_node); + void _cache_scene_groups(Node *p_node); - void _remove_filter_changed(const String &p_filter); - void _add_filter_changed(const String &p_filter); + void _show_add_group_dialog(); + void _show_rename_group_dialog(); + void _show_remove_group_dialog(); - void _add_pressed(); - void _removed_pressed(); - void _add_group_pressed(const String &p_name); - void _add_group_text_changed(const String &p_new_text); + void _check_add(); + void _check_rename(); + void _validate_name(const String &p_name, EditorValidationPanel *p_validation_panel); - void _group_renamed(); - void _rename_group_item(const String &p_old_name, const String &p_new_name); + void _update_tree(); - void _add_group(String p_name); - void _modify_group_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); - void _delete_group_item(const String &p_name); + void _update_groups(); + void _load_scene_groups(Node *p_node); - void _load_groups(Node *p_current); - void _load_nodes(Node *p_current); + void _add_scene_group(const String &p_name); + void _rename_scene_group(const String &p_old_name, const String &p_new_name); + void _remove_scene_group(const String &p_name); -protected: - void _notification(int p_what); - static void _bind_methods(); + bool _has_group(const String &p_name); + void _set_group_checked(const String &p_name, bool p_checked); -public: - enum ModifyButton { - DELETE_GROUP, - COPY_GROUP, - }; + void _confirm_add(); + void _confirm_rename(); + void _confirm_delete(); - void edit(); + void _item_edited(); + void _item_mouse_selected(const Vector2 &p_pos, MouseButton p_mouse_button); + void _modify_group(Object *p_item, int p_column, int p_id, MouseButton p_mouse_button); + void _menu_id_pressed(int p_id); - GroupDialog(); -}; + void _update_groups_and_tree(); + void _queue_update_groups_and_tree(); -class GroupsEditor : public VBoxContainer { - GDCLASS(GroupsEditor, VBoxContainer); + void _groups_gui_input(Ref<InputEvent> p_event); - Node *node = nullptr; - TreeItem *groups_root = nullptr; - - GroupDialog *group_dialog = nullptr; - AcceptDialog *error = nullptr; - - LineEdit *group_name = nullptr; - Button *add = nullptr; - Tree *tree = nullptr; - - String selected_group; - - void update_tree(); - void _add_group(const String &p_group = ""); - void _modify_group(Object *p_item, int p_column, int p_id, MouseButton p_button); - void _group_name_changed(const String &p_new_text); - - void _group_selected(); - void _group_renamed(); - - void _show_group_dialog(); + void _node_removed(Node *p_node); protected: + void _notification(int p_what); static void _bind_methods(); public: enum ModifyButton { DELETE_GROUP, COPY_GROUP, + RENAME_GROUP, + CONVERT_GROUP, }; void set_current(Node *p_node); diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp index 7c74432ee9..7f8f2fd8a3 100644 --- a/editor/gui/editor_scene_tabs.cpp +++ b/editor/gui/editor_scene_tabs.cpp @@ -191,12 +191,14 @@ void EditorSceneTabs::_disable_menu_option_if(int p_option, bool p_condition) { } void EditorSceneTabs::update_scene_tabs() { + static bool menu_initialized = false; tab_preview_panel->hide(); - if (scene_tabs->get_tab_count() == EditorNode::get_editor_data().get_edited_scene_count()) { + if (menu_initialized && scene_tabs->get_tab_count() == EditorNode::get_editor_data().get_edited_scene_count()) { _update_tab_titles(); return; } + menu_initialized = true; if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { DisplayServer::get_singleton()->global_menu_clear("_dock"); diff --git a/editor/icons/GuiIndeterminateDisabled.svg b/editor/icons/GuiIndeterminateDisabled.svg new file mode 100644 index 0000000000..edaf69f7a1 --- /dev/null +++ b/editor/icons/GuiIndeterminateDisabled.svg @@ -0,0 +1 @@ +<svg height="16" width="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" rx="2.33" height="14" width="14" fill="#808080"/><path d="M3 7h10v2H3z" fill="#b3b3b3"/></svg> diff --git a/editor/icons/RangeSliderLeft.svg b/editor/icons/RangeSliderLeft.svg new file mode 100644 index 0000000000..7647db64ff --- /dev/null +++ b/editor/icons/RangeSliderLeft.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 12 16" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m10 2-9.99983819 6 9.99983819 6z" fill="#fff" stroke-width=".866025"/></svg> diff --git a/editor/icons/RangeSliderRight.svg b/editor/icons/RangeSliderRight.svg new file mode 100644 index 0000000000..3b46592c14 --- /dev/null +++ b/editor/icons/RangeSliderRight.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 12 16" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 2 10 6-10 6z" fill="#fff" stroke-width=".866025"/></svg> diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index a5813cf192..80dc3f194c 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -1933,7 +1933,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI),Static Lightmaps (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Static,Static Lightmaps,Dynamic", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/force_disable_compression"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true)); diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp index a0d2662045..db7922233e 100644 --- a/editor/input_event_configuration_dialog.cpp +++ b/editor/input_event_configuration_dialog.cpp @@ -572,7 +572,7 @@ void InputEventConfigurationDialog::_notification(int p_what) { } } -void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p_event) { +void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p_event, const String &p_current_action_name) { if (p_event.is_valid()) { _set_event(p_event->duplicate(), p_event); } else { @@ -596,6 +596,12 @@ void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p device_id_option->select(0); } + if (!p_current_action_name.is_empty()) { + set_title(vformat(TTR("Event Configuration for \"%s\""), p_current_action_name)); + } else { + set_title(TTR("Event Configuration")); + } + popup_centered(Size2(0, 400) * EDSCALE); } @@ -611,7 +617,6 @@ void InputEventConfigurationDialog::set_allowed_input_types(int p_type_masks) { InputEventConfigurationDialog::InputEventConfigurationDialog() { allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION; - set_title(TTR("Event Configuration")); set_min_size(Size2i(550, 0) * EDSCALE); VBoxContainer *main_vbox = memnew(VBoxContainer); diff --git a/editor/input_event_configuration_dialog.h b/editor/input_event_configuration_dialog.h index e7ab0da4d6..bde0b73ade 100644 --- a/editor/input_event_configuration_dialog.h +++ b/editor/input_event_configuration_dialog.h @@ -120,7 +120,8 @@ protected: public: // Pass an existing event to configure it. Alternatively, pass no event to start with a blank configuration. - void popup_and_configure(const Ref<InputEvent> &p_event = Ref<InputEvent>()); + // An action name can be passed for descriptive purposes. + void popup_and_configure(const Ref<InputEvent> &p_event = Ref<InputEvent>(), const String &p_current_action_name = ""); Ref<InputEvent> get_event() const; void set_allowed_input_types(int p_type_masks); diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index 98b7ab6cc1..041a121a3a 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -146,7 +146,7 @@ void PluginConfigDialog::_notification(int p_what) { } void PluginConfigDialog::config(const String &p_config_path) { - if (p_config_path.length()) { + if (!p_config_path.is_empty()) { Ref<ConfigFile> cf = memnew(ConfigFile); Error err = cf->load(p_config_path); ERR_FAIL_COND_MSG(err != OK, "Cannot load config file from path '" + p_config_path + "'."); @@ -159,20 +159,17 @@ void PluginConfigDialog::config(const String &p_config_path) { script_edit->set_text(cf->get_value("plugin", "script", "")); _edit_mode = true; - active_edit->hide(); - Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 1))->hide(); - subfolder_edit->hide(); - Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 1))->hide(); set_title(TTR("Edit a Plugin")); } else { _clear_fields(); _edit_mode = false; - active_edit->show(); - Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 1))->show(); - subfolder_edit->show(); - Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 1))->show(); set_title(TTR("Create a Plugin")); } + + for (Control *control : plugin_edit_hidden_controls) { + control->set_visible(!_edit_mode); + } + validation_panel->update(); get_ok_button()->set_disabled(!_edit_mode); @@ -214,12 +211,14 @@ PluginConfigDialog::PluginConfigDialog() { subfolder_lb->set_text(TTR("Subfolder:")); subfolder_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(subfolder_lb); + plugin_edit_hidden_controls.push_back(subfolder_lb); subfolder_edit = memnew(LineEdit); subfolder_edit->set_placeholder("\"my_plugin\" -> res://addons/my_plugin"); subfolder_edit->set_tooltip_text(TTR("Optional. The folder name should generally use `snake_case` naming (avoid spaces and special characters).\nIf left empty, the folder will be named after the plugin name converted to `snake_case`.")); subfolder_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(subfolder_edit); + plugin_edit_hidden_controls.push_back(subfolder_edit); // Description Label *desc_lb = memnew(Label); @@ -296,10 +295,12 @@ PluginConfigDialog::PluginConfigDialog() { active_lb->set_text(TTR("Activate now?")); active_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); grid->add_child(active_lb); + plugin_edit_hidden_controls.push_back(active_lb); active_edit = memnew(CheckBox); active_edit->set_pressed(true); grid->add_child(active_edit); + plugin_edit_hidden_controls.push_back(active_edit); Control *spacing = memnew(Control); vbox->add_child(spacing); diff --git a/editor/plugin_config_dialog.h b/editor/plugin_config_dialog.h index 1221d347a7..c6befbab85 100644 --- a/editor/plugin_config_dialog.h +++ b/editor/plugin_config_dialog.h @@ -59,6 +59,8 @@ class PluginConfigDialog : public ConfirmationDialog { LineEdit *script_edit = nullptr; CheckBox *active_edit = nullptr; + LocalVector<Control *> plugin_edit_hidden_controls; + EditorValidationPanel *validation_panel = nullptr; bool _edit_mode = false; diff --git a/editor/plugins/gizmos/audio_stream_player_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/audio_stream_player_3d_gizmo_plugin.cpp index cda00b9543..9d4b5e9d70 100644 --- a/editor/plugins/gizmos/audio_stream_player_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/audio_stream_player_3d_gizmo_plugin.cpp @@ -38,7 +38,7 @@ #include "scene/3d/audio_stream_player_3d.h" AudioStreamPlayer3DGizmoPlugin::AudioStreamPlayer3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1)); + Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1)); create_icon_material("stream_player_3d_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Gizmo3DSamplePlayer"), EditorStringName(EditorIcons))); create_material("stream_player_3d_material_primary", gizmo_color); diff --git a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp index af7874e4da..19dd45a3ea 100644 --- a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp @@ -39,7 +39,7 @@ #include "scene/3d/camera_3d.h" Camera3DGizmoPlugin::Camera3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8)); + Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8)); create_material("camera_material", gizmo_color); create_icon_material("camera_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoCamera3D"), EditorStringName(EditorIcons))); diff --git a/editor/plugins/gizmos/decal_gizmo_plugin.cpp b/editor/plugins/gizmos/decal_gizmo_plugin.cpp index 7572e1dcd5..f2b44790ee 100644 --- a/editor/plugins/gizmos/decal_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/decal_gizmo_plugin.cpp @@ -40,7 +40,7 @@ DecalGizmoPlugin::DecalGizmoPlugin() { helper.instantiate(); - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/decal", Color(0.6, 0.5, 1.0)); + Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/decal", Color(0.6, 0.5, 1.0)); create_icon_material("decal_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoDecal"), EditorStringName(EditorIcons))); diff --git a/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp b/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp index 931a738aa2..b7bba8f8d7 100644 --- a/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/fog_volume_gizmo_plugin.cpp @@ -38,7 +38,7 @@ #include "scene/3d/fog_volume.h" FogVolumeGizmoPlugin::FogVolumeGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/fog_volume", Color(0.5, 0.7, 1)); + Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/fog_volume", Color(0.5, 0.7, 1)); create_material("shape_material", gizmo_color); gizmo_color.a = 0.15; create_material("shape_material_internal", gizmo_color); diff --git a/editor/plugins/gizmos/gpu_particles_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/gpu_particles_3d_gizmo_plugin.cpp index 2b673207ab..bef6971eba 100644 --- a/editor/plugins/gizmos/gpu_particles_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/gpu_particles_3d_gizmo_plugin.cpp @@ -38,7 +38,7 @@ #include "scene/3d/gpu_particles_3d.h" GPUParticles3DGizmoPlugin::GPUParticles3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4)); + Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4)); create_material("particles_material", gizmo_color); gizmo_color.a = MAX((gizmo_color.a - 0.2) * 0.02, 0.0); create_material("particles_solid_material", gizmo_color); diff --git a/editor/plugins/gizmos/gpu_particles_collision_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/gpu_particles_collision_3d_gizmo_plugin.cpp index 2cbfccc05e..6f20a53459 100644 --- a/editor/plugins/gizmos/gpu_particles_collision_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/gpu_particles_collision_3d_gizmo_plugin.cpp @@ -39,12 +39,12 @@ GPUParticlesCollision3DGizmoPlugin::GPUParticlesCollision3DGizmoPlugin() { helper.instantiate(); - Color gizmo_color_attractor = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particle_attractor", Color(1, 0.7, 0.5)); + Color gizmo_color_attractor = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/particle_attractor", Color(1, 0.7, 0.5)); create_material("shape_material_attractor", gizmo_color_attractor); gizmo_color_attractor.a = 0.15; create_material("shape_material_attractor_internal", gizmo_color_attractor); - Color gizmo_color_collision = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particle_collision", Color(0.5, 0.7, 1)); + Color gizmo_color_collision = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/particle_collision", Color(0.5, 0.7, 1)); create_material("shape_material_collision", gizmo_color_collision); gizmo_color_collision.a = 0.15; create_material("shape_material_collision_internal", gizmo_color_collision); diff --git a/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp index c4cb44c8fa..1fce7f5efb 100644 --- a/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp @@ -276,8 +276,8 @@ void JointGizmosDrawer::draw_cone(const Transform3D &p_offset, const Basis &p_ba Joint3DGizmoPlugin::Joint3DGizmoPlugin() { create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint")); - create_material("joint_body_a_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_a", Color(0.6, 0.8, 1))); - create_material("joint_body_b_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_b", Color(0.6, 0.9, 1))); + create_material("joint_body_a_material", EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/joint_body_a", Color(0.6, 0.8, 1))); + create_material("joint_body_b_material", EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/joint_body_b", Color(0.6, 0.9, 1))); update_timer = memnew(Timer); update_timer->set_name("JointGizmoUpdateTimer"); diff --git a/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp b/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp index 74e6e818c6..64913dc779 100644 --- a/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/lightmap_gi_gizmo_plugin.cpp @@ -37,7 +37,7 @@ #include "scene/3d/lightmap_gi.h" LightmapGIGizmoPlugin::LightmapGIGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/lightmap_lines", Color(0.5, 0.6, 1)); + Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/lightmap_lines", Color(0.5, 0.6, 1)); gizmo_color.a = 0.1; create_material("lightmap_lines", gizmo_color); diff --git a/editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp b/editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp index 420829515f..5fd8ad2235 100644 --- a/editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/lightmap_probe_gizmo_plugin.cpp @@ -39,7 +39,7 @@ LightmapProbeGizmoPlugin::LightmapProbeGizmoPlugin() { create_icon_material("lightmap_probe_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoLightmapProbe"), EditorStringName(EditorIcons))); - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/lightprobe_lines", Color(0.5, 0.6, 1)); + Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/lightprobe_lines", Color(0.5, 0.6, 1)); gizmo_color.a = 0.3; create_material("lightprobe_lines", gizmo_color); diff --git a/editor/plugins/gizmos/occluder_instance_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/occluder_instance_3d_gizmo_plugin.cpp index f9a210b0e7..37c3a05b50 100644 --- a/editor/plugins/gizmos/occluder_instance_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/occluder_instance_3d_gizmo_plugin.cpp @@ -36,7 +36,7 @@ #include "scene/3d/occluder_instance_3d.h" OccluderInstance3DGizmoPlugin::OccluderInstance3DGizmoPlugin() { - create_material("line_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/occluder", Color(0.8, 0.5, 1))); + create_material("line_material", EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/occluder", Color(0.8, 0.5, 1))); create_handle_material("handles"); } diff --git a/editor/plugins/gizmos/reflection_probe_gizmo_plugin.cpp b/editor/plugins/gizmos/reflection_probe_gizmo_plugin.cpp index d25311f1be..eb7e668f41 100644 --- a/editor/plugins/gizmos/reflection_probe_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/reflection_probe_gizmo_plugin.cpp @@ -40,7 +40,7 @@ ReflectionProbeGizmoPlugin::ReflectionProbeGizmoPlugin() { helper.instantiate(); - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5)); + Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5)); create_material("reflection_probe_material", gizmo_color); diff --git a/editor/plugins/gizmos/visible_on_screen_notifier_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/visible_on_screen_notifier_3d_gizmo_plugin.cpp index 315f8aed53..d3ae823fdc 100644 --- a/editor/plugins/gizmos/visible_on_screen_notifier_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/visible_on_screen_notifier_3d_gizmo_plugin.cpp @@ -36,7 +36,7 @@ #include "scene/3d/visible_on_screen_notifier_3d.h" VisibleOnScreenNotifier3DGizmoPlugin::VisibleOnScreenNotifier3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/visibility_notifier", Color(0.8, 0.5, 0.7)); + Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/visibility_notifier", Color(0.8, 0.5, 0.7)); create_material("visibility_notifier_material", gizmo_color); gizmo_color.a = 0.1; create_material("visibility_notifier_solid_material", gizmo_color); diff --git a/editor/plugins/gizmos/voxel_gi_gizmo_plugin.cpp b/editor/plugins/gizmos/voxel_gi_gizmo_plugin.cpp index b916e99ab6..8d09400f78 100644 --- a/editor/plugins/gizmos/voxel_gi_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/voxel_gi_gizmo_plugin.cpp @@ -41,7 +41,7 @@ VoxelGIGizmoPlugin::VoxelGIGizmoPlugin() { helper.instantiate(); - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/voxel_gi", Color(0.5, 1, 0.6)); + Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/voxel_gi", Color(0.5, 1, 0.6)); create_material("voxel_gi_material", gizmo_color); diff --git a/editor/plugins/particle_process_material_editor_plugin.cpp b/editor/plugins/particle_process_material_editor_plugin.cpp new file mode 100644 index 0000000000..e696da3f5e --- /dev/null +++ b/editor/plugins/particle_process_material_editor_plugin.cpp @@ -0,0 +1,471 @@ +/**************************************************************************/ +/* particle_process_material_editor_plugin.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "particle_process_material_editor_plugin.h" + +#include "editor/editor_property_name_processor.h" +#include "editor/editor_settings.h" +#include "editor/editor_string_names.h" +#include "editor/gui/editor_spin_slider.h" +#include "scene/gui/box_container.h" +#include "scene/gui/button.h" +#include "scene/gui/label.h" +#include "scene/resources/particle_process_material.h" + +void ParticleProcessMaterialMinMaxPropertyEditor::_update_sizing() { + edit_size = range_edit_widget->get_size(); + margin = Vector2(range_slider_left_icon->get_width(), (edit_size.y - range_slider_left_icon->get_height()) * 0.5); + usable_area = edit_size - margin * 2; +} + +void ParticleProcessMaterialMinMaxPropertyEditor::_range_edit_draw() { + ERR_FAIL_COND(range_slider_left_icon.is_null()); + ERR_FAIL_COND(range_slider_right_icon.is_null()); + _update_sizing(); + + bool widget_active = mouse_inside || drag != Drag::NONE; + + // FIXME: Need to offset by 1 due to some outline bug. + range_edit_widget->draw_rect(Rect2(margin + Vector2(1, 1), usable_area - Vector2(1, 1)), widget_active ? background_color.lerp(normal_color, 0.3) : background_color, false, 1.0); + + Color draw_color; + + if (widget_active) { + float icon_offset = _get_left_offset() - range_slider_left_icon->get_width() - 1; + + if (drag == Drag::LEFT || drag == Drag::SCALE) { + draw_color = drag_color; + } else if (hover == Hover::LEFT) { + draw_color = hovered_color; + } else { + draw_color = normal_color; + } + range_edit_widget->draw_texture(range_slider_left_icon, Vector2(icon_offset, margin.y), draw_color); + + icon_offset = _get_right_offset(); + + if (drag == Drag::RIGHT || drag == Drag::SCALE) { + draw_color = drag_color; + } else if (hover == Hover::RIGHT) { + draw_color = hovered_color; + } else { + draw_color = normal_color; + } + range_edit_widget->draw_texture(range_slider_right_icon, Vector2(icon_offset, margin.y), draw_color); + } + + if (drag == Drag::MIDDLE || drag == Drag::SCALE) { + draw_color = drag_color; + } else if (hover == Hover::MIDDLE) { + draw_color = hovered_color; + } else { + draw_color = normal_color; + } + range_edit_widget->draw_rect(_get_middle_rect(), draw_color); + + Rect2 midpoint_rect(Vector2(margin.x + usable_area.x * (_get_min_ratio() + _get_max_ratio()) * 0.5 - 1, margin.y + 2), + Vector2(2, usable_area.y - 4)); + + range_edit_widget->draw_rect(midpoint_rect, midpoint_color); +} + +void ParticleProcessMaterialMinMaxPropertyEditor::_range_edit_gui_input(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> mb = p_event; + Ref<InputEventMouseMotion> mm = p_event; + + // Prevent unnecessary computations. + if ((mb.is_null() || mb->get_button_index() != MouseButton::LEFT) && (mm.is_null())) { + return; + } + + ERR_FAIL_COND(range_slider_left_icon.is_null()); + ERR_FAIL_COND(range_slider_right_icon.is_null()); + _update_sizing(); + + if (mb.is_valid()) { + const Drag prev_drag = drag; + + if (mb->is_pressed()) { + if (mb->is_shift_pressed()) { + drag = Drag::SCALE; + drag_from_value = (max_range->get_value() - min_range->get_value()) * 0.5; + drag_midpoint = (max_range->get_value() + min_range->get_value()) * 0.5; + } else if (hover == Hover::LEFT) { + drag = Drag::LEFT; + drag_from_value = min_range->get_value(); + } else if (hover == Hover::RIGHT) { + drag = Drag::RIGHT; + drag_from_value = max_range->get_value(); + } else { + drag = Drag::MIDDLE; + drag_from_value = min_range->get_value(); + } + drag_origin = mb->get_position().x; + } else { + drag = Drag::NONE; + } + + if (drag != prev_drag) { + range_edit_widget->queue_redraw(); + } + } + + float property_length = property_range.y - property_range.x; + if (mm.is_valid()) { + switch (drag) { + case Drag::NONE: { + const Hover prev_hover = hover; + float left_icon_offset = _get_left_offset() - range_slider_left_icon->get_width() - 1; + + if (Rect2(Vector2(left_icon_offset, 0), range_slider_left_icon->get_size()).has_point(mm->get_position())) { + hover = Hover::LEFT; + } else if (Rect2(Vector2(_get_right_offset(), 0), range_slider_right_icon->get_size()).has_point(mm->get_position())) { + hover = Hover::RIGHT; + } else if (_get_middle_rect().has_point(mm->get_position())) { + hover = Hover::MIDDLE; + } else { + hover = Hover::NONE; + } + + if (hover != prev_hover) { + range_edit_widget->queue_redraw(); + } + } break; + + case Drag::LEFT: + case Drag::RIGHT: { + float new_value = drag_from_value + (mm->get_position().x - drag_origin) / usable_area.x * property_length; + if (drag == Drag::LEFT) { + new_value = MIN(new_value, max_range->get_value()); + _set_clamped_values(new_value, max_range->get_value()); + } else { + new_value = MAX(new_value, min_range->get_value()); + _set_clamped_values(min_range->get_value(), new_value); + } + } break; + + case Drag::MIDDLE: { + float delta = (mm->get_position().x - drag_origin) / usable_area.x * property_length; + float diff = max_range->get_value() - min_range->get_value(); + delta = CLAMP(drag_from_value + delta, property_range.x, property_range.y - diff) - drag_from_value; + _set_clamped_values(drag_from_value + delta, drag_from_value + delta + diff); + } break; + + case Drag::SCALE: { + float delta = (mm->get_position().x - drag_origin) / usable_area.x * property_length + drag_from_value; + _set_clamped_values(MIN(drag_midpoint, drag_midpoint - delta), MAX(drag_midpoint, drag_midpoint + delta)); + } break; + } + } +} + +void ParticleProcessMaterialMinMaxPropertyEditor::_set_mouse_inside(bool p_inside) { + mouse_inside = p_inside; + if (!p_inside) { + hover = Hover::NONE; + } + range_edit_widget->queue_redraw(); +} + +float ParticleProcessMaterialMinMaxPropertyEditor::_get_min_ratio() const { + return (min_range->get_value() - property_range.x) / (property_range.y - property_range.x); +} + +float ParticleProcessMaterialMinMaxPropertyEditor::_get_max_ratio() const { + return (max_range->get_value() - property_range.x) / (property_range.y - property_range.x); +} + +float ParticleProcessMaterialMinMaxPropertyEditor::_get_left_offset() const { + return margin.x + usable_area.x * _get_min_ratio(); +} + +float ParticleProcessMaterialMinMaxPropertyEditor::_get_right_offset() const { + return margin.x + usable_area.x * _get_max_ratio(); +} + +Rect2 ParticleProcessMaterialMinMaxPropertyEditor::_get_middle_rect() const { + if (Math::is_equal_approx(min_range->get_value(), max_range->get_value())) { + return Rect2(); + } + + return Rect2( + Vector2(_get_left_offset() - 1, margin.y), + Vector2(usable_area.x * (_get_max_ratio() - _get_min_ratio()) + 1, usable_area.y)); +} + +void ParticleProcessMaterialMinMaxPropertyEditor::_set_clamped_values(float p_min, float p_max) { + // This is required for editing widget in case the properties have or_less or or_greater hint. + min_range->set_value(MAX(p_min, property_range.x)); + max_range->set_value(MIN(p_max, property_range.y)); + _update_slider_values(); + _sync_property(); +} + +void ParticleProcessMaterialMinMaxPropertyEditor::_sync_property() { + const Vector2 value = Vector2(min_range->get_value(), max_range->get_value()); + emit_changed(get_edited_property(), value, "", true); + range_edit_widget->queue_redraw(); +} + +void ParticleProcessMaterialMinMaxPropertyEditor::_update_mode() { + max_edit->set_read_only(false); + + switch (slider_mode) { + case Mode::RANGE: { + min_edit->set_label("min"); + max_edit->set_label("max"); + max_edit->set_block_signals(true); + max_edit->set_min(max_range->get_min()); + max_edit->set_max(max_range->get_max()); + max_edit->set_block_signals(false); + + min_edit->set_allow_lesser(min_range->is_lesser_allowed()); + min_edit->set_allow_greater(min_range->is_greater_allowed()); + max_edit->set_allow_lesser(max_range->is_lesser_allowed()); + max_edit->set_allow_greater(max_range->is_greater_allowed()); + } break; + + case Mode::MIDPOINT: { + min_edit->set_label("val"); + max_edit->set_label(U"±"); + max_edit->set_block_signals(true); + max_edit->set_min(0); + max_edit->set_block_signals(false); + + min_edit->set_allow_lesser(min_range->is_lesser_allowed()); + min_edit->set_allow_greater(max_range->is_greater_allowed()); + max_edit->set_allow_lesser(false); + max_edit->set_allow_greater(min_range->is_lesser_allowed() && max_range->is_greater_allowed()); + } break; + } + _update_slider_values(); +} + +void ParticleProcessMaterialMinMaxPropertyEditor::_toggle_mode(bool p_edit_mode) { + slider_mode = p_edit_mode ? Mode::MIDPOINT : Mode::RANGE; + EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "particle_spin_mode", int(slider_mode)); + _update_mode(); +} + +void ParticleProcessMaterialMinMaxPropertyEditor::_update_slider_values() { + switch (slider_mode) { + case Mode::RANGE: { + min_edit->set_value_no_signal(min_range->get_value()); + max_edit->set_value_no_signal(max_range->get_value()); + } break; + + case Mode::MIDPOINT: { + min_edit->set_value_no_signal((min_range->get_value() + max_range->get_value()) * 0.5); + max_edit->set_value_no_signal((max_range->get_value() - min_range->get_value()) * 0.5); + + max_edit->set_block_signals(true); + max_edit->set_max(_get_max_spread()); + max_edit->set_read_only(max_edit->get_max() == 0); + max_edit->set_block_signals(false); + } break; + } +} + +void ParticleProcessMaterialMinMaxPropertyEditor::_sync_sliders(float, const EditorSpinSlider *p_changed_slider) { + switch (slider_mode) { + case Mode::RANGE: { + if (p_changed_slider == max_edit) { + min_edit->set_value_no_signal(MIN(min_edit->get_value(), max_edit->get_value())); + } + min_range->set_value(min_edit->get_value()); + if (p_changed_slider == min_edit) { + max_edit->set_value_no_signal(MAX(min_edit->get_value(), max_edit->get_value())); + } + max_range->set_value(max_edit->get_value()); + _sync_property(); + } break; + + case Mode::MIDPOINT: { + if (p_changed_slider == min_edit) { + max_edit->set_block_signals(true); // If max changes, value may change. + max_edit->set_max(_get_max_spread()); + max_edit->set_read_only(max_edit->get_max() == 0); + max_edit->set_block_signals(false); + } + min_range->set_value(min_edit->get_value() - max_edit->get_value()); + max_range->set_value(min_edit->get_value() + max_edit->get_value()); + _sync_property(); + } break; + } + + property_range.x = MIN(min_range->get_value(), min_range->get_min()); + property_range.y = MAX(max_range->get_value(), max_range->get_max()); +} + +float ParticleProcessMaterialMinMaxPropertyEditor::_get_max_spread() const { + float max_spread = max_range->get_max() - min_range->get_min(); + + if (max_edit->is_greater_allowed()) { + return max_spread; + } + + if (!min_edit->is_lesser_allowed()) { + max_spread = MIN(max_spread, min_edit->get_value() - min_edit->get_min()); + } + + if (!min_edit->is_greater_allowed()) { + max_spread = MIN(max_spread, min_edit->get_max() - min_edit->get_value()); + } + + return max_spread; +} + +void ParticleProcessMaterialMinMaxPropertyEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + toggle_mode_button->set_icon(get_editor_theme_icon(SNAME("Anchor"))); + range_slider_left_icon = get_editor_theme_icon(SNAME("RangeSliderLeft")); + range_slider_right_icon = get_editor_theme_icon(SNAME("RangeSliderRight")); + + min_edit->add_theme_color_override(SNAME("label_color"), get_theme_color(SNAME("property_color_x"), EditorStringName(Editor))); + max_edit->add_theme_color_override(SNAME("label_color"), get_theme_color(SNAME("property_color_y"), EditorStringName(Editor))); + + const bool dark_theme = EditorSettings::get_singleton()->is_dark_theme(); + const Color accent_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor)); + background_color = dark_theme ? Color(0.3, 0.3, 0.3) : Color(0.7, 0.7, 0.7); + normal_color = dark_theme ? Color(0.5, 0.5, 0.5) : Color(0.8, 0.8, 0.8); + hovered_color = dark_theme ? Color(0.8, 0.8, 0.8) : Color(0.6, 0.6, 0.6); + drag_color = hovered_color.lerp(accent_color, 0.8); + midpoint_color = dark_theme ? Color(1, 1, 1) : Color(0, 0, 0); + + range_edit_widget->set_custom_minimum_size(Vector2(0, range_slider_left_icon->get_height() + 8)); + } break; + } +} + +void ParticleProcessMaterialMinMaxPropertyEditor::setup(float p_min, float p_max, float p_step, bool p_allow_less, bool p_allow_greater, bool p_degrees) { + property_range = Vector2(p_min, p_max); + + // Initially all Ranges share properties. + for (Range *range : Vector<Range *>{ min_range, min_edit, max_range, max_edit }) { + range->set_min(p_min); + range->set_max(p_max); + range->set_step(p_step); + range->set_allow_lesser(p_allow_less); + range->set_allow_greater(p_allow_greater); + } + + if (p_degrees) { + min_edit->set_suffix(U" \u00B0"); + max_edit->set_suffix(U" \u00B0"); + } + _update_mode(); +} + +void ParticleProcessMaterialMinMaxPropertyEditor::update_property() { + const Vector2i value = get_edited_property_value(); + min_range->set_value(value.x); + max_range->set_value(value.y); + _update_slider_values(); + range_edit_widget->queue_redraw(); +} + +ParticleProcessMaterialMinMaxPropertyEditor::ParticleProcessMaterialMinMaxPropertyEditor() { + VBoxContainer *content_vb = memnew(VBoxContainer); + content_vb->add_theme_constant_override(SNAME("separation"), 0); + add_child(content_vb); + + // Helper Range objects to keep absolute min and max values. + min_range = memnew(Range); + min_range->hide(); + add_child(min_range); + + max_range = memnew(Range); + max_range->hide(); + add_child(max_range); + + // Range edit widget. + HBoxContainer *hb = memnew(HBoxContainer); + content_vb->add_child(hb); + + range_edit_widget = memnew(Control); + range_edit_widget->set_h_size_flags(SIZE_EXPAND_FILL); + range_edit_widget->set_tooltip_text(TTR("Hold Shift to scale around midpoint instead of moving.")); + hb->add_child(range_edit_widget); + range_edit_widget->connect(SNAME("draw"), callable_mp(this, &ParticleProcessMaterialMinMaxPropertyEditor::_range_edit_draw)); + range_edit_widget->connect(SNAME("gui_input"), callable_mp(this, &ParticleProcessMaterialMinMaxPropertyEditor::_range_edit_gui_input)); + range_edit_widget->connect(SNAME("mouse_entered"), callable_mp(this, &ParticleProcessMaterialMinMaxPropertyEditor::_set_mouse_inside).bind(true)); + range_edit_widget->connect(SNAME("mouse_exited"), callable_mp(this, &ParticleProcessMaterialMinMaxPropertyEditor::_set_mouse_inside).bind(false)); + + // Range controls for actual editing. Their min/max may depend on editing mode. + hb = memnew(HBoxContainer); + content_vb->add_child(hb); + + min_edit = memnew(EditorSpinSlider); + min_edit->set_h_size_flags(SIZE_EXPAND_FILL); + hb->add_child(min_edit); + min_edit->connect(SNAME("value_changed"), callable_mp(this, &ParticleProcessMaterialMinMaxPropertyEditor::_sync_sliders).bind(min_edit)); + + max_edit = memnew(EditorSpinSlider); + max_edit->set_h_size_flags(SIZE_EXPAND_FILL); + hb->add_child(max_edit); + max_edit->connect(SNAME("value_changed"), callable_mp(this, &ParticleProcessMaterialMinMaxPropertyEditor::_sync_sliders).bind(max_edit)); + + toggle_mode_button = memnew(Button); + toggle_mode_button->set_toggle_mode(true); + toggle_mode_button->set_tooltip_text(TTR("Toggle between minimum/maximum and base value/spread modes.")); + hb->add_child(toggle_mode_button); + toggle_mode_button->connect(SNAME("toggled"), callable_mp(this, &ParticleProcessMaterialMinMaxPropertyEditor::_toggle_mode)); + + set_bottom_editor(content_vb); +} + +bool EditorInspectorParticleProcessMaterialPlugin::can_handle(Object *p_object) { + return Object::cast_to<ParticleProcessMaterial>(p_object); +} + +bool EditorInspectorParticleProcessMaterialPlugin::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) { + if (!ParticleProcessMaterial::has_min_max_property(p_path)) { + return false; + } + ERR_FAIL_COND_V(p_hint != PROPERTY_HINT_RANGE, false); + + Ref<ParticleProcessMaterial> mat = Ref<ParticleProcessMaterial>(p_object); + ERR_FAIL_COND_V(mat.is_null(), false); + + PackedStringArray range_hint = p_hint_text.split(","); + float min = range_hint[0].to_float(); + float max = range_hint[1].to_float(); + float step = range_hint[2].to_float(); + bool allow_less = range_hint.find("or_less", 3) > -1; + bool allow_greater = range_hint.find("or_greater", 3) > -1; + bool degrees = range_hint.find("degrees", 3) > -1; + + ParticleProcessMaterialMinMaxPropertyEditor *ed = memnew(ParticleProcessMaterialMinMaxPropertyEditor); + ed->setup(min, max, step, allow_less, allow_greater, degrees); + add_property_editor(p_path, ed); + + return true; +} diff --git a/editor/plugins/particle_process_material_editor_plugin.h b/editor/plugins/particle_process_material_editor_plugin.h new file mode 100644 index 0000000000..0d9725397e --- /dev/null +++ b/editor/plugins/particle_process_material_editor_plugin.h @@ -0,0 +1,138 @@ +/**************************************************************************/ +/* particle_process_material_editor_plugin.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef PARTICLE_PROCESS_MATERIAL_EDITOR_PLUGIN_H +#define PARTICLE_PROCESS_MATERIAL_EDITOR_PLUGIN_H + +#include "editor/editor_plugin.h" +#include "editor/editor_properties.h" + +class Button; +class EditorSpinSlider; +class Label; +class ParticleProcessMaterial; +class Range; +class VBoxContainer; + +class ParticleProcessMaterialMinMaxPropertyEditor : public EditorProperty { + GDCLASS(ParticleProcessMaterialMinMaxPropertyEditor, EditorProperty); + + enum class Hover { + NONE, + LEFT, + RIGHT, + MIDDLE, + }; + + enum class Drag { + NONE, + LEFT, + RIGHT, + MIDDLE, + SCALE, + }; + + enum class Mode { + RANGE, + MIDPOINT, + }; + + Ref<Texture2D> range_slider_left_icon; + Ref<Texture2D> range_slider_right_icon; + + Color background_color; + Color normal_color; + Color hovered_color; + Color drag_color; + Color midpoint_color; + + Control *range_edit_widget = nullptr; + Button *toggle_mode_button = nullptr; + Range *min_range = nullptr; + Range *max_range = nullptr; + + EditorSpinSlider *min_edit = nullptr; + EditorSpinSlider *max_edit = nullptr; + + Vector2 edit_size; + Vector2 margin; + Vector2 usable_area; + + Vector2 property_range; + + bool mouse_inside = false; + Hover hover = Hover::NONE; + + Drag drag = Drag::NONE; + float drag_from_value = 0.0; + float drag_midpoint = 0.0; + float drag_origin = 0.0; + + Mode slider_mode = Mode::RANGE; + + void _update_sizing(); + void _range_edit_draw(); + void _range_edit_gui_input(const Ref<InputEvent> &p_event); + void _set_mouse_inside(bool p_inside); + + float _get_min_ratio() const; + float _get_max_ratio() const; + float _get_left_offset() const; + float _get_right_offset() const; + Rect2 _get_middle_rect() const; + + void _set_clamped_values(float p_min, float p_max); + void _sync_property(); + + void _update_mode(); + void _toggle_mode(bool p_edit_mode); + void _update_slider_values(); + void _sync_sliders(float, const EditorSpinSlider *p_changed_slider); + float _get_max_spread() const; + +protected: + void _notification(int p_what); + +public: + void setup(float p_min, float p_max, float p_step, bool p_allow_less, bool p_allow_greater, bool p_degrees); + virtual void update_property() override; + + ParticleProcessMaterialMinMaxPropertyEditor(); +}; + +class EditorInspectorParticleProcessMaterialPlugin : public EditorInspectorPlugin { + GDCLASS(EditorInspectorParticleProcessMaterialPlugin, EditorInspectorPlugin); + +public: + virtual bool can_handle(Object *p_object) override; + virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override; +}; + +#endif // PARTICLE_PROCESS_MATERIAL_EDITOR_PLUGIN_H diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index e917aab6ad..c1a65b7bb7 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -548,7 +548,7 @@ void ScriptEditor::_set_breakpoint(Ref<RefCounted> p_script, int p_line, bool p_ } state["breakpoints"] = breakpoints; script_editor_cache->set_value(scr->get_path(), "state", state); - EditorDebuggerNode::get_singleton()->set_breakpoint(scr->get_path(), p_line + 1, false); + EditorDebuggerNode::get_singleton()->set_breakpoint(scr->get_path(), p_line + 1, p_enabled); } } diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 04eda502d2..eb934da4dd 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -2482,7 +2482,10 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT_OVERRIDE("script_text_editor/contextual_help", "macos", KeyModifierMask::ALT | KeyModifierMask::SHIFT | Key::SPACE); ED_SHORTCUT("script_text_editor/toggle_bookmark", TTR("Toggle Bookmark"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::B); + ED_SHORTCUT("script_text_editor/goto_next_bookmark", TTR("Go to Next Bookmark"), KeyModifierMask::CMD_OR_CTRL | Key::B); + ED_SHORTCUT_OVERRIDE("script_text_editor/goto_next_bookmark", "macos", KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | KeyModifierMask::ALT | Key::B); + ED_SHORTCUT("script_text_editor/goto_previous_bookmark", TTR("Go to Previous Bookmark"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::B); ED_SHORTCUT("script_text_editor/remove_all_bookmarks", TTR("Remove All Bookmarks"), Key::NONE); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index eeefd1c90d..9a709e8dda 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -1219,8 +1219,8 @@ void fragment() { selected_mat->set_shader(selected_sh); // Register properties in editor settings. - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/selected_bone", Color(0.8, 0.3, 0.0)); + EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4)); + EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/selected_bone", Color(0.8, 0.3, 0.0)); EDITOR_DEF("editors/3d_gizmos/gizmo_settings/bone_axis_length", (float)0.1); EDITOR_DEF("editors/3d_gizmos/gizmo_settings/bone_shape", 1); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d_gizmos/gizmo_settings/bone_shape", PROPERTY_HINT_ENUM, "Wire,Octahedron")); diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 9ea0a03e68..6c153f6113 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -2287,8 +2287,11 @@ VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) { item_add_edit->connect("text_submitted", callable_mp(this, &ThemeTypeEditor::_item_add_lineedit_cbk).bind(p_data_type, item_add_edit)); Button *item_add_button = memnew(Button); item_add_button->set_text(TTR("Add")); + item_add_button->set_disabled(true); item_add_hb->add_child(item_add_button); item_add_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_add_cbk).bind(p_data_type, item_add_edit)); + item_add_edit->set_meta("button", item_add_button); + item_add_edit->connect("text_changed", callable_mp(this, &ThemeTypeEditor::_update_add_button).bind(item_add_edit)); return items_list; } @@ -2850,6 +2853,11 @@ void ThemeTypeEditor::_add_default_type_items() { ur->commit_action(); } +void ThemeTypeEditor::_update_add_button(const String &p_text, LineEdit *p_for_edit) { + Button *button = Object::cast_to<Button>(p_for_edit->get_meta("button")); + button->set_disabled(p_text.strip_edges().is_empty()); +} + void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) { LineEdit *le = Object::cast_to<LineEdit>(p_control); if (le->get_text().strip_edges().is_empty()) { @@ -2895,6 +2903,7 @@ void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) { ur->commit_action(); le->set_text(""); + _update_add_button("", le); } void ThemeTypeEditor::_item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control) { diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 33accf587a..cf8c5ceb28 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -382,6 +382,7 @@ class ThemeTypeEditor : public MarginContainer { void _add_type_button_cbk(); void _add_default_type_items(); + void _update_add_button(const String &p_text, LineEdit *p_for_edit); void _item_add_cbk(int p_data_type, Control *p_control); void _item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control); void _item_override_cbk(int p_data_type, String p_item_name); diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 7b3847e548..661af16ce8 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -3439,7 +3439,7 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() { terrains_tile_list->set_item_metadata(item_index, list_metadata_dict); item_index = terrains_tile_list->add_icon_item(main_vbox_container->get_editor_theme_icon(SNAME("TerrainPath"))); - terrains_tile_list->set_item_tooltip(item_index, TTR("Path mode: paints a terrain, thens connects it to the previous tile painted within the same stroke.")); + terrains_tile_list->set_item_tooltip(item_index, TTR("Path mode: paints a terrain, then connects it to the previous tile painted within the same stroke.")); list_metadata_dict = Dictionary(); list_metadata_dict["type"] = SELECTED_TYPE_PATH; terrains_tile_list->set_item_metadata(item_index, list_metadata_dict); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 291484600c..2073b1f374 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -2845,6 +2845,10 @@ ProjectManager::ProjectManager() { AcceptDialog::set_swap_cancel_ok(swap_cancel_ok == 2); } + int pm_root_dir = EDITOR_GET("interface/editor/ui_layout_direction"); + Control::set_root_layout_direction(pm_root_dir); + Window::set_root_layout_direction(pm_root_dir); + EditorColorMap::create(); EditorTheme::initialize(); Ref<Theme> theme = create_custom_theme(); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 09de9cda49..d587737ed4 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -45,6 +45,7 @@ ProjectSettingsEditor *ProjectSettingsEditor::singleton = nullptr; void ProjectSettingsEditor::connect_filesystem_dock_signals(FileSystemDock *p_fs_dock) { localization_editor->connect_filesystem_dock_signals(p_fs_dock); + group_settings->connect_filesystem_dock_signals(p_fs_dock); } void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) { @@ -62,6 +63,7 @@ void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) { localization_editor->update_translations(); autoload_settings->update_autoload(); + group_settings->update_groups(); plugin_settings->update_plugins(); import_defaults_editor->clear(); @@ -709,6 +711,11 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { shaders_global_shader_uniforms_editor->connect("globals_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); tab_container->add_child(shaders_global_shader_uniforms_editor); + group_settings = memnew(GroupSettingsEditor); + group_settings->set_name(TTR("Global Groups")); + group_settings->connect("group_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); + tab_container->add_child(group_settings); + plugin_settings = memnew(EditorPluginSettings); plugin_settings->set_name(TTR("Plugins")); tab_container->add_child(plugin_settings); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index eaac7e8c5a..1f18c68acc 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -37,6 +37,7 @@ #include "editor/editor_data.h" #include "editor/editor_plugin_settings.h" #include "editor/editor_sectioned_inspector.h" +#include "editor/group_settings_editor.h" #include "editor/import_defaults_editor.h" #include "editor/localization_editor.h" #include "editor/shader_globals_editor.h" @@ -58,6 +59,7 @@ class ProjectSettingsEditor : public AcceptDialog { LocalizationEditor *localization_editor = nullptr; EditorAutoloadSettings *autoload_settings = nullptr; ShaderGlobalsEditor *shaders_global_shader_uniforms_editor = nullptr; + GroupSettingsEditor *group_settings = nullptr; EditorPluginSettings *plugin_settings = nullptr; LineEdit *search_box = nullptr; @@ -122,6 +124,7 @@ public: void update_plugins(); EditorAutoloadSettings *get_autoload_settings() { return autoload_settings; } + GroupSettingsEditor *get_group_settings() { return group_settings; } TabContainer *get_tabs() { return tab_container; } void queue_save(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index c12e53fc88..6553a592d0 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -3330,7 +3330,7 @@ void SceneTreeDock::_filter_changed(const String &p_filter) { filter->set_tooltip_text(warning); } else { filter->remove_theme_icon_override(SNAME("clear")); - filter->set_tooltip_text(""); + filter->set_tooltip_text(TTR("Filter nodes by entering a part of their name, type (if prefixed with \"type:\" or \"t:\")\nor group (if prefixed with \"group:\" or \"g:\"). Filtering is case-insensitive.")); } } diff --git a/main/main.cpp b/main/main.cpp index 350b8606b9..0570af3566 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2502,7 +2502,7 @@ Error Main::setup2() { OS::get_singleton()->benchmark_begin_measure("Servers", "Tablet Driver"); GLOBAL_DEF_RST_NOVAL("input_devices/pen_tablet/driver", ""); - GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "input_devices/pen_tablet/driver.windows", PROPERTY_HINT_ENUM, "wintab,winink"), ""); + GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "input_devices/pen_tablet/driver.windows", PROPERTY_HINT_ENUM, "winink,wintab,dummy"), ""); if (tablet_driver.is_empty()) { // specified in project.godot tablet_driver = GLOBAL_GET("input_devices/pen_tablet/driver"); diff --git a/modules/csg/doc_classes/CSGPolygon3D.xml b/modules/csg/doc_classes/CSGPolygon3D.xml index 338adc9b52..5d35c04e25 100644 --- a/modules/csg/doc_classes/CSGPolygon3D.xml +++ b/modules/csg/doc_classes/CSGPolygon3D.xml @@ -39,7 +39,7 @@ When [member mode] is [constant MODE_PATH], the location of the [Path3D] object used to extrude the [member polygon]. </member> <member name="path_rotation" type="int" setter="set_path_rotation" getter="get_path_rotation" enum="CSGPolygon3D.PathRotation"> - When [member mode] is [constant MODE_PATH], the [enum PathRotation] method used to rotate the [member polygon] as it is extruded. + When [member mode] is [constant MODE_PATH], the path rotation method used to rotate the [member polygon] as it is extruded. </member> <member name="path_simplify_angle" type="float" setter="set_path_simplify_angle" getter="get_path_simplify_angle"> When [member mode] is [constant MODE_PATH], extrusions that are less than this angle, will be merged together to reduce polygon count. diff --git a/modules/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp index ebf0f5a91f..ea7b6d225e 100644 --- a/modules/csg/editor/csg_gizmos.cpp +++ b/modules/csg/editor/csg_gizmos.cpp @@ -44,7 +44,7 @@ CSGShape3DGizmoPlugin::CSGShape3DGizmoPlugin() { helper.instantiate(); - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.0, 0.4, 1, 0.15)); + Color gizmo_color = EDITOR_DEF_RST("editors/3d_gizmos/gizmo_colors/csg", Color(0.0, 0.4, 1, 0.15)); create_material("shape_union_material", gizmo_color); create_material("shape_union_solid_material", gizmo_color); gizmo_color.invert(); diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp index 9128f104b8..f55b00ebe1 100644 --- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp +++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp @@ -80,7 +80,7 @@ bool GDScriptEditorTranslationParserPlugin::_is_constant_string(const GDScriptPa void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser::ClassNode *p_class) { for (int i = 0; i < p_class->members.size(); i++) { const GDScriptParser::ClassNode::Member &m = p_class->members[i]; - // There are 7 types of Member, but only class, function and variable can contain translatable strings. + // Other member types can't contain translatable strings. switch (m.type) { case GDScriptParser::ClassNode::Member::CLASS: _traverse_class(m.m_class); @@ -89,7 +89,11 @@ void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser _traverse_function(m.function); break; case GDScriptParser::ClassNode::Member::VARIABLE: - _read_variable(m.variable); + _assess_expression(m.variable->initializer); + if (m.variable->property == GDScriptParser::VariableNode::PROP_INLINE) { + _traverse_function(m.variable->setter); + _traverse_function(m.variable->getter); + } break; default: break; @@ -98,11 +102,14 @@ void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser } void GDScriptEditorTranslationParserPlugin::_traverse_function(const GDScriptParser::FunctionNode *p_func) { - _traverse_block(p_func->body); -} + if (!p_func) { + return; + } -void GDScriptEditorTranslationParserPlugin::_read_variable(const GDScriptParser::VariableNode *p_var) { - _assess_expression(p_var->initializer); + for (int i = 0; i < p_func->parameters.size(); i++) { + _assess_expression(p_func->parameters[i]->initializer); + } + _traverse_block(p_func->body); } void GDScriptEditorTranslationParserPlugin::_traverse_block(const GDScriptParser::SuiteNode *p_suite) { @@ -114,53 +121,51 @@ void GDScriptEditorTranslationParserPlugin::_traverse_block(const GDScriptParser for (int i = 0; i < statements.size(); i++) { const GDScriptParser::Node *statement = statements[i]; - // Statements with Node type constant, break, continue, pass, breakpoint are skipped because they can't contain translatable strings. + // BREAK, BREAKPOINT, CONSTANT, CONTINUE, and PASS are skipped because they can't contain translatable strings. switch (statement->type) { - case GDScriptParser::Node::VARIABLE: - _assess_expression(static_cast<const GDScriptParser::VariableNode *>(statement)->initializer); - break; + case GDScriptParser::Node::ASSERT: { + const GDScriptParser::AssertNode *assert_node = static_cast<const GDScriptParser::AssertNode *>(statement); + _assess_expression(assert_node->condition); + _assess_expression(assert_node->message); + } break; + case GDScriptParser::Node::ASSIGNMENT: { + _assess_assignment(static_cast<const GDScriptParser::AssignmentNode *>(statement)); + } break; + case GDScriptParser::Node::FOR: { + const GDScriptParser::ForNode *for_node = static_cast<const GDScriptParser::ForNode *>(statement); + _assess_expression(for_node->list); + _traverse_block(for_node->loop); + } break; case GDScriptParser::Node::IF: { const GDScriptParser::IfNode *if_node = static_cast<const GDScriptParser::IfNode *>(statement); _assess_expression(if_node->condition); - //FIXME : if the elif logic is changed in GDScriptParser, then this probably will have to change as well. See GDScriptParser::TreePrinter::print_if(). _traverse_block(if_node->true_block); _traverse_block(if_node->false_block); - break; - } - case GDScriptParser::Node::FOR: { - const GDScriptParser::ForNode *for_node = static_cast<const GDScriptParser::ForNode *>(statement); - _assess_expression(for_node->list); - _traverse_block(for_node->loop); - break; - } - case GDScriptParser::Node::WHILE: { - const GDScriptParser::WhileNode *while_node = static_cast<const GDScriptParser::WhileNode *>(statement); - _assess_expression(while_node->condition); - _traverse_block(while_node->loop); - break; - } + } break; case GDScriptParser::Node::MATCH: { const GDScriptParser::MatchNode *match_node = static_cast<const GDScriptParser::MatchNode *>(statement); _assess_expression(match_node->test); for (int j = 0; j < match_node->branches.size(); j++) { + _traverse_block(match_node->branches[j]->guard_body); _traverse_block(match_node->branches[j]->block); } - break; - } - case GDScriptParser::Node::RETURN: + } break; + case GDScriptParser::Node::RETURN: { _assess_expression(static_cast<const GDScriptParser::ReturnNode *>(statement)->return_value); - break; - case GDScriptParser::Node::ASSERT: - _assess_expression((static_cast<const GDScriptParser::AssertNode *>(statement))->condition); - break; - case GDScriptParser::Node::ASSIGNMENT: - _assess_assignment(static_cast<const GDScriptParser::AssignmentNode *>(statement)); - break; - default: + } break; + case GDScriptParser::Node::VARIABLE: { + _assess_expression(static_cast<const GDScriptParser::VariableNode *>(statement)->initializer); + } break; + case GDScriptParser::Node::WHILE: { + const GDScriptParser::WhileNode *while_node = static_cast<const GDScriptParser::WhileNode *>(statement); + _assess_expression(while_node->condition); + _traverse_block(while_node->loop); + } break; + default: { if (statement->is_expression()) { _assess_expression(static_cast<const GDScriptParser::ExpressionNode *>(statement)); } - break; + } break; } } } @@ -172,25 +177,25 @@ void GDScriptEditorTranslationParserPlugin::_assess_expression(const GDScriptPar return; } - // ExpressionNode of type await, cast, get_node, identifier, literal, preload, self, subscript, unary are ignored as they can't be CallNode - // containing translation strings. + // GET_NODE, IDENTIFIER, LITERAL, PRELOAD, SELF, and TYPE are skipped because they can't contain translatable strings. switch (p_expression->type) { case GDScriptParser::Node::ARRAY: { const GDScriptParser::ArrayNode *array_node = static_cast<const GDScriptParser::ArrayNode *>(p_expression); for (int i = 0; i < array_node->elements.size(); i++) { _assess_expression(array_node->elements[i]); } - break; - } - case GDScriptParser::Node::ASSIGNMENT: + } break; + case GDScriptParser::Node::ASSIGNMENT: { _assess_assignment(static_cast<const GDScriptParser::AssignmentNode *>(p_expression)); - break; + } break; + case GDScriptParser::Node::AWAIT: { + _assess_expression(static_cast<const GDScriptParser::AwaitNode *>(p_expression)->to_await); + } break; case GDScriptParser::Node::BINARY_OPERATOR: { const GDScriptParser::BinaryOpNode *binary_op_node = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression); _assess_expression(binary_op_node->left_operand); _assess_expression(binary_op_node->right_operand); - break; - } + } break; case GDScriptParser::Node::CALL: { const GDScriptParser::CallNode *call_node = static_cast<const GDScriptParser::CallNode *>(p_expression); _extract_from_call(call_node); @@ -198,23 +203,40 @@ void GDScriptEditorTranslationParserPlugin::_assess_expression(const GDScriptPar _assess_expression(call_node->arguments[i]); } } break; + case GDScriptParser::Node::CAST: { + _assess_expression(static_cast<const GDScriptParser::CastNode *>(p_expression)->operand); + } break; case GDScriptParser::Node::DICTIONARY: { const GDScriptParser::DictionaryNode *dict_node = static_cast<const GDScriptParser::DictionaryNode *>(p_expression); for (int i = 0; i < dict_node->elements.size(); i++) { _assess_expression(dict_node->elements[i].key); _assess_expression(dict_node->elements[i].value); } - break; - } + } break; + case GDScriptParser::Node::LAMBDA: { + _traverse_function(static_cast<const GDScriptParser::LambdaNode *>(p_expression)->function); + } break; + case GDScriptParser::Node::SUBSCRIPT: { + const GDScriptParser::SubscriptNode *subscript_node = static_cast<const GDScriptParser::SubscriptNode *>(p_expression); + _assess_expression(subscript_node->base); + if (!subscript_node->is_attribute) { + _assess_expression(subscript_node->index); + } + } break; case GDScriptParser::Node::TERNARY_OPERATOR: { const GDScriptParser::TernaryOpNode *ternary_op_node = static_cast<const GDScriptParser::TernaryOpNode *>(p_expression); _assess_expression(ternary_op_node->condition); _assess_expression(ternary_op_node->true_expr); _assess_expression(ternary_op_node->false_expr); - break; - } - default: - break; + } break; + case GDScriptParser::Node::TYPE_TEST: { + _assess_expression(static_cast<const GDScriptParser::TypeTestNode *>(p_expression)->operand); + } break; + case GDScriptParser::Node::UNARY_OPERATOR: { + _assess_expression(static_cast<const GDScriptParser::UnaryOpNode *>(p_expression)->operand); + } break; + default: { + } break; } } diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h index 580c2a80cd..fab79a925f 100644 --- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h +++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h @@ -59,7 +59,6 @@ class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlug void _traverse_function(const GDScriptParser::FunctionNode *p_func); void _traverse_block(const GDScriptParser::SuiteNode *p_suite); - void _read_variable(const GDScriptParser::VariableNode *p_var); void _assess_expression(const GDScriptParser::ExpressionNode *p_expression); void _assess_assignment(const GDScriptParser::AssignmentNode *p_assignment); void _extract_from_call(const GDScriptParser::CallNode *p_call); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 52235b1854..a999acd1bd 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2220,6 +2220,8 @@ void GDScriptLanguage::profiling_start() { elem->self()->profile.last_frame_call_count = 0; elem->self()->profile.last_frame_self_time = 0; elem->self()->profile.last_frame_total_time = 0; + elem->self()->profile.native_calls.clear(); + elem->self()->profile.last_native_calls.clear(); elem = elem->next(); } @@ -2227,6 +2229,13 @@ void GDScriptLanguage::profiling_start() { #endif } +void GDScriptLanguage::profiling_set_save_native_calls(bool p_enable) { +#ifdef DEBUG_ENABLED + MutexLock lock(mutex); + profile_native_calls = p_enable; +#endif +} + void GDScriptLanguage::profiling_stop() { #ifdef DEBUG_ENABLED MutexLock lock(this->mutex); @@ -2241,17 +2250,32 @@ int GDScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr, MutexLock lock(this->mutex); + profiling_collate_native_call_data(true); SelfList<GDScriptFunction> *elem = function_list.first(); while (elem) { if (current >= p_info_max) { break; } + int last_non_internal = current; p_info_arr[current].call_count = elem->self()->profile.call_count.get(); p_info_arr[current].self_time = elem->self()->profile.self_time.get(); p_info_arr[current].total_time = elem->self()->profile.total_time.get(); p_info_arr[current].signature = elem->self()->profile.signature; - elem = elem->next(); current++; + + int nat_time = 0; + HashMap<String, GDScriptFunction::Profile::NativeProfile>::ConstIterator nat_calls = elem->self()->profile.native_calls.begin(); + while (nat_calls) { + p_info_arr[current].call_count = nat_calls->value.call_count; + p_info_arr[current].total_time = nat_calls->value.total_time; + p_info_arr[current].self_time = nat_calls->value.total_time; + p_info_arr[current].signature = nat_calls->value.signature; + nat_time += nat_calls->value.total_time; + current++; + ++nat_calls; + } + p_info_arr[last_non_internal].internal_time = nat_time; + elem = elem->next(); } #endif @@ -2264,17 +2288,33 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_ #ifdef DEBUG_ENABLED MutexLock lock(this->mutex); + profiling_collate_native_call_data(false); SelfList<GDScriptFunction> *elem = function_list.first(); while (elem) { if (current >= p_info_max) { break; } if (elem->self()->profile.last_frame_call_count > 0) { + int last_non_internal = current; p_info_arr[current].call_count = elem->self()->profile.last_frame_call_count; p_info_arr[current].self_time = elem->self()->profile.last_frame_self_time; p_info_arr[current].total_time = elem->self()->profile.last_frame_total_time; p_info_arr[current].signature = elem->self()->profile.signature; current++; + + int nat_time = 0; + HashMap<String, GDScriptFunction::Profile::NativeProfile>::ConstIterator nat_calls = elem->self()->profile.last_native_calls.begin(); + while (nat_calls) { + p_info_arr[current].call_count = nat_calls->value.call_count; + p_info_arr[current].total_time = nat_calls->value.total_time; + p_info_arr[current].self_time = nat_calls->value.total_time; + p_info_arr[current].internal_time = nat_calls->value.total_time; + p_info_arr[current].signature = nat_calls->value.signature; + nat_time += nat_calls->value.total_time; + current++; + ++nat_calls; + } + p_info_arr[last_non_internal].internal_time = nat_time; } elem = elem->next(); } @@ -2283,6 +2323,33 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_ return current; } +void GDScriptLanguage::profiling_collate_native_call_data(bool p_accumulated) { +#ifdef DEBUG_ENABLED + // The same native call can be called from multiple functions, so join them together here. + // Only use the name of the function (ie signature.split[2]). + HashMap<String, GDScriptFunction::Profile::NativeProfile *> seen_nat_calls; + SelfList<GDScriptFunction> *elem = function_list.first(); + while (elem) { + HashMap<String, GDScriptFunction::Profile::NativeProfile> *nat_calls = p_accumulated ? &elem->self()->profile.native_calls : &elem->self()->profile.last_native_calls; + HashMap<String, GDScriptFunction::Profile::NativeProfile>::Iterator it = nat_calls->begin(); + + while (it != nat_calls->end()) { + Vector<String> sig = it->value.signature.split("::"); + HashMap<String, GDScriptFunction::Profile::NativeProfile *>::ConstIterator already_found = seen_nat_calls.find(sig[2]); + if (already_found) { + already_found->value->total_time += it->value.total_time; + already_found->value->call_count += it->value.call_count; + elem->self()->profile.last_native_calls.remove(it); + } else { + seen_nat_calls.insert(sig[2], &it->value); + } + ++it; + } + elem = elem->next(); + } +#endif +} + struct GDScriptDepSort { //must support sorting so inheritance works properly (parent must be reloaded first) bool operator()(const Ref<GDScript> &A, const Ref<GDScript> &B) const { @@ -2482,9 +2549,11 @@ void GDScriptLanguage::frame() { elem->self()->profile.last_frame_call_count = elem->self()->profile.frame_call_count.get(); elem->self()->profile.last_frame_self_time = elem->self()->profile.frame_self_time.get(); elem->self()->profile.last_frame_total_time = elem->self()->profile.frame_total_time.get(); + elem->self()->profile.last_native_calls = elem->self()->profile.native_calls; elem->self()->profile.frame_call_count.set(0); elem->self()->profile.frame_self_time.set(0); elem->self()->profile.frame_total_time.set(0); + elem->self()->profile.native_calls.clear(); elem = elem->next(); } } diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 5165ec1b06..31811bba47 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -439,6 +439,7 @@ class GDScriptLanguage : public ScriptLanguage { SelfList<GDScriptFunction>::List function_list; bool profiling; + bool profile_native_calls; uint64_t script_frame_time; HashMap<String, ObjectID> orphan_subclasses; @@ -590,6 +591,8 @@ public: virtual void profiling_start() override; virtual void profiling_stop() override; + virtual void profiling_set_save_native_calls(bool p_enable) override; + void profiling_collate_native_call_data(bool p_accumulated); virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override; virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 55bd0f97d5..5478a46bbc 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3777,6 +3777,60 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod } } + // Check non-GDScript scripts. + Ref<Script> script_type = base.script_type; + + if (base_class == nullptr && script_type.is_valid()) { + List<PropertyInfo> property_list; + script_type->get_script_property_list(&property_list); + + for (const PropertyInfo &property_info : property_list) { + if (property_info.name != p_identifier->name) { + continue; + } + + const GDScriptParser::DataType property_type = GDScriptAnalyzer::type_from_property(property_info, false, false); + + p_identifier->set_datatype(property_type); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE; + return; + } + + MethodInfo method_info = script_type->get_method_info(p_identifier->name); + + if (method_info.name == p_identifier->name) { + p_identifier->set_datatype(make_callable_type(method_info)); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION; + return; + } + + List<MethodInfo> signal_list; + script_type->get_script_signal_list(&signal_list); + + for (const MethodInfo &signal_info : signal_list) { + if (signal_info.name != p_identifier->name) { + continue; + } + + const GDScriptParser::DataType signal_type = make_signal_type(signal_info); + + p_identifier->set_datatype(signal_type); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL; + return; + } + + HashMap<StringName, Variant> constant_map; + script_type->get_constants(&constant_map); + + if (constant_map.has(p_identifier->name)) { + Variant constant = constant_map.get(p_identifier->name); + + p_identifier->set_datatype(make_builtin_meta_type(constant.get_type())); + p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT; + return; + } + } + // Check native members. No need for native class recursion because Node exposes all Object's properties. const StringName &native = base.native_type; diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index e63fa17cfe..177c68533e 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -474,6 +474,13 @@ private: uint64_t last_frame_call_count = 0; uint64_t last_frame_self_time = 0; uint64_t last_frame_total_time = 0; + typedef struct NativeProfile { + uint64_t call_count; + uint64_t total_time; + String signature; + } NativeProfile; + HashMap<String, NativeProfile> native_calls; + HashMap<String, NativeProfile> last_native_calls; } profile; #endif @@ -514,6 +521,7 @@ public: void debug_get_stack_member_state(int p_line, List<Pair<StringName, int>> *r_stackvars) const; #ifdef DEBUG_ENABLED + void _profile_native_call(uint64_t p_t_taken, const String &p_function_name, const String &p_instance_class_name = String()); void disassemble(const Vector<String> &p_code_lines) const; #endif diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index ea7abc9ded..f3a4f2eaa6 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -73,8 +73,11 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) { HashMap<String, String> GDScriptParser::theme_color_names; #endif +HashMap<StringName, GDScriptParser::AnnotationInfo> GDScriptParser::valid_annotations; + void GDScriptParser::cleanup() { builtin_types.clear(); + valid_annotations.clear(); } void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const { @@ -89,41 +92,42 @@ bool GDScriptParser::annotation_exists(const String &p_annotation_name) const { GDScriptParser::GDScriptParser() { // Register valid annotations. - // TODO: Should this be static? - register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_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); - - register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation); - // Export annotations. - register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>); - register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true); - register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true); - register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>); - register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, varray(""), true); - register_annotation(MethodInfo("@export_global_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_DIR, Variant::STRING>); - register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>); - register_annotation(MethodInfo("@export_placeholder", PropertyInfo(Variant::STRING, "placeholder")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>); - register_annotation(MethodInfo("@export_range", PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"), PropertyInfo(Variant::FLOAT, "step"), PropertyInfo(Variant::STRING, "extra_hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, varray(1.0, ""), true); - register_annotation(MethodInfo("@export_exp_easing", PropertyInfo(Variant::STRING, "hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, varray(""), true); - register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>); - register_annotation(MethodInfo("@export_node_path", PropertyInfo(Variant::STRING, "type")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, varray(""), true); - register_annotation(MethodInfo("@export_flags", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, varray(), true); - register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>); - register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>); - register_annotation(MethodInfo("@export_flags_2d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_NAVIGATION, Variant::INT>); - register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>); - register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>); - register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>); - register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>); - // Export grouping annotations. - register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>); - register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray("")); - register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, varray("")); - // Warning annotations. - register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true); - // Networking. - register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("authority", "call_remote", "unreliable", 0)); + if (unlikely(valid_annotations.is_empty())) { + register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_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); + + register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation); + // Export annotations. + register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>); + register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true); + register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true); + register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>); + register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, varray(""), true); + register_annotation(MethodInfo("@export_global_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_DIR, Variant::STRING>); + register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>); + register_annotation(MethodInfo("@export_placeholder", PropertyInfo(Variant::STRING, "placeholder")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>); + register_annotation(MethodInfo("@export_range", PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"), PropertyInfo(Variant::FLOAT, "step"), PropertyInfo(Variant::STRING, "extra_hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, varray(1.0, ""), true); + register_annotation(MethodInfo("@export_exp_easing", PropertyInfo(Variant::STRING, "hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, varray(""), true); + register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>); + register_annotation(MethodInfo("@export_node_path", PropertyInfo(Variant::STRING, "type")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, varray(""), true); + register_annotation(MethodInfo("@export_flags", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, varray(), true); + register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>); + register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>); + register_annotation(MethodInfo("@export_flags_2d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_NAVIGATION, Variant::INT>); + register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>); + register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>); + register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>); + register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>); + // Export grouping annotations. + register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>); + register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray("")); + register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, varray("")); + // Warning annotations. + register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true); + // Networking. + register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("authority", "call_remote", "unreliable", 0)); + } #ifdef DEBUG_ENABLED is_ignoring_warnings = !(bool)GLOBAL_GET("debug/gdscript/warnings/enable"); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index f48ad48de0..88b5bdc43f 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1370,7 +1370,7 @@ private: AnnotationAction apply = nullptr; MethodInfo info; }; - HashMap<StringName, AnnotationInfo> valid_annotations; + static HashMap<StringName, AnnotationInfo> valid_annotations; List<AnnotationNode *> annotation_stack; typedef ExpressionNode *(GDScriptParser::*ParseFunction)(ExpressionNode *p_previous_operand, bool p_can_assign); @@ -1470,7 +1470,7 @@ private: SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr, bool p_for_lambda = false); // Annotations AnnotationNode *parse_annotation(uint32_t p_valid_targets); - bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false); + static bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false); bool validate_annotation_arguments(AnnotationNode *p_annotation); void clear_unused_annotations(); bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class); diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 2c0b8df9ac..3abfc7f8e3 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -36,6 +36,18 @@ #include "core/os/os.h" #ifdef DEBUG_ENABLED + +static bool _profile_count_as_native(const Object *p_base_obj, const StringName &p_methodname) { + if (!p_base_obj) { + return false; + } + StringName cname = p_base_obj->get_class_name(); + if ((p_methodname == "new" && cname == "GDScript") || p_methodname == "call") { + return false; + } + return ClassDB::class_exists(cname) && ClassDB::has_method(cname, p_methodname, false); +} + static String _get_element_type(Variant::Type builtin_type, const StringName &native_type, const Ref<Script> &script_type) { if (script_type.is_valid() && script_type->is_valid()) { return GDScript::debug_get_script_name(script_type); @@ -84,6 +96,18 @@ static String _get_var_type(const Variant *p_var) { return basestr; } + +void GDScriptFunction::_profile_native_call(uint64_t p_t_taken, const String &p_func_name, const String &p_instance_class_name) { + HashMap<String, Profile::NativeProfile>::Iterator inner_prof = profile.native_calls.find(p_func_name); + if (inner_prof) { + inner_prof->value.call_count += 1; + } else { + String sig = vformat("%s::0::%s%s%s", get_script()->get_script_path(), p_instance_class_name, p_instance_class_name.is_empty() ? "" : ".", p_func_name); + inner_prof = profile.native_calls.insert(p_func_name, Profile::NativeProfile{ 1, 0, sig }); + } + inner_prof->value.total_time += p_t_taken; +} + #endif // DEBUG_ENABLED Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataType &p_data_type) { @@ -631,9 +655,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } bool exit_ok = false; bool awaited = false; -#endif - -#ifdef DEBUG_ENABLED int variant_address_limits[ADDR_TYPE_MAX] = { _stack_size, _constant_count, p_instance ? p_instance->members.size() : 0 }; #endif @@ -1661,16 +1682,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (GDScriptLanguage::get_singleton()->profiling) { call_time = OS::get_singleton()->get_ticks_usec(); } - + Variant::Type base_type = base->get_type(); + Object *base_obj = base->get_validated_object(); + StringName base_class = base_obj ? base_obj->get_class_name() : StringName(); #endif + Callable::CallError err; if (call_ret) { GET_INSTRUCTION_ARG(ret, argc + 1); -#ifdef DEBUG_ENABLED - Variant::Type base_type = base->get_type(); - Object *base_obj = base->get_validated_object(); - StringName base_class = base_obj ? base_obj->get_class_name() : StringName(); -#endif base->callp(*methodname, (const Variant **)argptrs, argc, *ret, err); #ifdef DEBUG_ENABLED if (ret->get_type() == Variant::NIL) { @@ -1704,8 +1723,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a base->callp(*methodname, (const Variant **)argptrs, argc, ret, err); } #ifdef DEBUG_ENABLED + if (GDScriptLanguage::get_singleton()->profiling) { - function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; + uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time; + if (GDScriptLanguage::get_singleton()->profile_native_calls && _profile_count_as_native(base_obj, *methodname)) { + _profile_native_call(t_taken, *methodname, base_class); + } + function_call_time += t_taken; } if (err.error != Callable::CallError::CALL_OK) { @@ -1782,8 +1806,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED uint64_t call_time = 0; - - if (GDScriptLanguage::get_singleton()->profiling) { + if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) { call_time = OS::get_singleton()->get_ticks_usec(); } #endif @@ -1797,8 +1820,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } #ifdef DEBUG_ENABLED - if (GDScriptLanguage::get_singleton()->profiling) { - function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; + + if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) { + uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time; + _profile_native_call(t_taken, method->get_name(), method->get_instance_class()); + function_call_time += t_taken; } if (err.error != Callable::CallError::CALL_OK) { @@ -1851,22 +1877,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a const Variant **argptrs = const_cast<const Variant **>(instruction_args); -#ifdef DEBUG_ENABLED - uint64_t call_time = 0; - - if (GDScriptLanguage::get_singleton()->profiling) { - call_time = OS::get_singleton()->get_ticks_usec(); - } -#endif - Callable::CallError err; Variant::call_static(builtin_type, *methodname, argptrs, argc, *ret, err); #ifdef DEBUG_ENABLED - if (GDScriptLanguage::get_singleton()->profiling) { - function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; - } - if (err.error != Callable::CallError::CALL_OK) { err_text = _get_call_error(err, "static function '" + methodname->operator String() + "' in type '" + Variant::get_type_name(builtin_type) + "'", argptrs); OPCODE_BREAK; @@ -1895,8 +1909,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED uint64_t call_time = 0; - - if (GDScriptLanguage::get_singleton()->profiling) { + if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) { call_time = OS::get_singleton()->get_ticks_usec(); } #endif @@ -1905,15 +1918,17 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a *ret = method->call(nullptr, argptrs, argc, err); #ifdef DEBUG_ENABLED - if (GDScriptLanguage::get_singleton()->profiling) { - function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; + if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) { + uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time; + _profile_native_call(t_taken, method->get_name(), method->get_instance_class()); + function_call_time += t_taken; } +#endif if (err.error != Callable::CallError::CALL_OK) { err_text = _get_call_error(err, "static function '" + method->get_name().operator String() + "' in type '" + method->get_instance_class().operator String() + "'", argptrs); OPCODE_BREAK; } -#endif ip += 3; } @@ -1951,8 +1966,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED uint64_t call_time = 0; - - if (GDScriptLanguage::get_singleton()->profiling) { + if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) { call_time = OS::get_singleton()->get_ticks_usec(); } #endif @@ -1961,10 +1975,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a method->validated_call(base_obj, (const Variant **)argptrs, ret); #ifdef DEBUG_ENABLED - if (GDScriptLanguage::get_singleton()->profiling) { - function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; + if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) { + uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time; + _profile_native_call(t_taken, method->get_name(), method->get_instance_class()); + function_call_time += t_taken; } #endif + ip += 3; } DISPATCH_OPCODE; @@ -1998,8 +2015,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Variant **argptrs = instruction_args; #ifdef DEBUG_ENABLED uint64_t call_time = 0; - - if (GDScriptLanguage::get_singleton()->profiling) { + if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) { call_time = OS::get_singleton()->get_ticks_usec(); } #endif @@ -2009,10 +2025,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a method->validated_call(base_obj, (const Variant **)argptrs, nullptr); #ifdef DEBUG_ENABLED - if (GDScriptLanguage::get_singleton()->profiling) { - function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; + if (GDScriptLanguage::get_singleton()->profiling && GDScriptLanguage::get_singleton()->profile_native_calls) { + uint64_t t_taken = OS::get_singleton()->get_ticks_usec() - call_time; + _profile_native_call(t_taken, method->get_name(), method->get_instance_class()); + function_call_time += t_taken; } #endif + ip += 3; } DISPATCH_OPCODE; @@ -2033,22 +2052,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Variant::ValidatedBuiltInMethod method = _builtin_methods_ptr[_code_ptr[ip + 2]]; Variant **argptrs = instruction_args; -#ifdef DEBUG_ENABLED - uint64_t call_time = 0; - if (GDScriptLanguage::get_singleton()->profiling) { - call_time = OS::get_singleton()->get_ticks_usec(); - } -#endif - GET_INSTRUCTION_ARG(ret, argc + 1); method(base, (const Variant **)argptrs, argc, ret); -#ifdef DEBUG_ENABLED - if (GDScriptLanguage::get_singleton()->profiling) { - function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; - } -#endif - ip += 3; } DISPATCH_OPCODE; diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index e05979efbc..348ac5194c 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -155,7 +155,6 @@ class GridMap : public Node3D { Ref<PhysicsMaterial> physics_material; bool bake_navigation = false; RID map_override; - uint32_t navigation_layers = 1; Transform3D last_transform; diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index e381f0e840..401f5c62e5 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -463,6 +463,7 @@ public: /* PROFILING FUNCTIONS */ /* TODO */ void profiling_start() override {} /* TODO */ void profiling_stop() override {} + /* TODO */ void profiling_set_save_native_calls(bool p_enable) override {} /* TODO */ int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override { return 0; } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/CSharpSourceGeneratorVerifier.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/CSharpSourceGeneratorVerifier.cs new file mode 100644 index 0000000000..d75922481c --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/CSharpSourceGeneratorVerifier.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Testing.Verifiers; +using Microsoft.CodeAnalysis.Text; + +namespace Godot.SourceGenerators.Tests; + +public static class CSharpSourceGeneratorVerifier<TSourceGenerator> +where TSourceGenerator : ISourceGenerator, new() +{ + public class Test : CSharpSourceGeneratorTest<TSourceGenerator, XUnitVerifier> + { + public Test() + { + ReferenceAssemblies = ReferenceAssemblies.Net.Net60; + + SolutionTransforms.Add((Solution solution, ProjectId projectId) => + { + Project project = solution.GetProject(projectId)! + .AddMetadataReference(Constants.GodotSharpAssembly.CreateMetadataReference()); + + return project.Solution; + }); + } + } + + public static Task Verify(string source, params string[] generatedSources) + { + return Verify(new string[] { source }, generatedSources); + } + + public static Task VerifyNoCompilerDiagnostics(string source, params string[] generatedSources) + { + return VerifyNoCompilerDiagnostics(new string[] { source }, generatedSources); + } + + public static Task Verify(ICollection<string> sources, params string[] generatedSources) + { + return MakeVerifier(sources, generatedSources).RunAsync(); + } + + public static Task VerifyNoCompilerDiagnostics(ICollection<string> sources, params string[] generatedSources) + { + var verifier = MakeVerifier(sources, generatedSources); + verifier.CompilerDiagnostics = CompilerDiagnostics.None; + return verifier.RunAsync(); + } + + public static Test MakeVerifier(ICollection<string> sources, ICollection<string> generatedSources) + { + var verifier = new Test(); + + verifier.TestState.AnalyzerConfigFiles.Add(("/.globalconfig", $""" + is_global = true + build_property.GodotProjectDir = {Constants.ExecutingAssemblyPath} + """)); + + verifier.TestState.Sources.AddRange(sources.Select(source => + { + return (source, SourceText.From(File.ReadAllText(Path.Combine(Constants.SourceFolderPath, source)))); + })); + + verifier.TestState.GeneratedSources.AddRange(generatedSources.Select(generatedSource => + { + return (FullGeneratedSourceName(generatedSource), SourceText.From(File.ReadAllText(Path.Combine(Constants.GeneratedSourceFolderPath, generatedSource)), Encoding.UTF8)); + })); + + return verifier; + } + + private static string FullGeneratedSourceName(string name) + { + var generatorType = typeof(TSourceGenerator); + return Path.Combine(generatorType.Namespace!, generatorType.FullName!, name); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Constants.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Constants.cs new file mode 100644 index 0000000000..783b1e4298 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Constants.cs @@ -0,0 +1,23 @@ +using System.IO; +using System.Reflection; + +namespace Godot.SourceGenerators.Tests; + +public static class Constants +{ + public static Assembly GodotSharpAssembly => typeof(GodotObject).Assembly; + + public static string ExecutingAssemblyPath { get; } + public static string SourceFolderPath { get; } + public static string GeneratedSourceFolderPath { get; } + + static Constants() + { + ExecutingAssemblyPath = Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location!)!); + + var testDataPath = Path.Combine(ExecutingAssemblyPath, "TestData"); + + SourceFolderPath = Path.Combine(testDataPath, "Sources"); + GeneratedSourceFolderPath = Path.Combine(testDataPath, "GeneratedSources"); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Extensions.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Extensions.cs new file mode 100644 index 0000000000..6ff890e5d8 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Extensions.cs @@ -0,0 +1,12 @@ +using System.Reflection; +using Microsoft.CodeAnalysis; + +namespace Godot.SourceGenerators.Tests; + +public static class Extensions +{ + public static MetadataReference CreateMetadataReference(this Assembly assembly) + { + return MetadataReference.CreateFromFile(assembly.Location); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj new file mode 100644 index 0000000000..e39c14f049 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/Godot.SourceGenerators.Tests.csproj @@ -0,0 +1,40 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net6.0</TargetFramework> + + <LangVersion>11</LangVersion> + + <Nullable>enable</Nullable> + <IsPackable>false</IsPackable> + <IsTestProject>true</IsTestProject> + </PropertyGroup> + + <PropertyGroup> + <DefaultItemExcludesInProjectFolder>$(DefaultItemExcludesInProjectFolder);TestData\**</DefaultItemExcludesInProjectFolder> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.1" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" /> + <PackageReference Include="xunit" Version="2.4.2" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + <PackageReference Include="coverlet.collector" Version="3.2.0"> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\..\glue\GodotSharp\GodotSharp\GodotSharp.csproj" /> + <ProjectReference Include="..\Godot.SourceGenerators\Godot.SourceGenerators.csproj" /> + </ItemGroup> + + <ItemGroup> + <None Include="TestData\**\*.cs" CopyToOutputDirectory="PreserveNewest" /> + </ItemGroup> + +</Project> diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptMethodsGeneratorTests.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptMethodsGeneratorTests.cs new file mode 100644 index 0000000000..294932eb9a --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptMethodsGeneratorTests.cs @@ -0,0 +1,24 @@ +using Xunit; + +namespace Godot.SourceGenerators.Tests; + +public class ScriptMethodsGeneratorTests +{ + [Fact] + public async void Methods() + { + await CSharpSourceGeneratorVerifier<ScriptMethodsGenerator>.Verify( + "Methods.cs", + "Methods_ScriptMethods.generated.cs" + ); + } + + [Fact] + public async void ScriptBoilerplate() + { + await CSharpSourceGeneratorVerifier<ScriptMethodsGenerator>.Verify( + "ScriptBoilerplate.cs", + "ScriptBoilerplate_ScriptMethods.generated.cs", "OuterClass.NestedClass_ScriptMethods.generated.cs" + ); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPathAttributeGeneratorTests.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPathAttributeGeneratorTests.cs new file mode 100644 index 0000000000..b7ad4217aa --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPathAttributeGeneratorTests.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis.Text; +using Xunit; + +namespace Godot.SourceGenerators.Tests; + +public class ScriptPathAttributeGeneratorTests +{ + private static (string, SourceText) MakeAssemblyScriptTypesGeneratedSource(ICollection<string> types) + { + return ( + Path.Combine("Godot.SourceGenerators", "Godot.SourceGenerators.ScriptPathAttributeGenerator", "AssemblyScriptTypes.generated.cs"), + SourceText.From($$""" + [assembly:Godot.AssemblyHasScriptsAttribute(new System.Type[] {{{string.Join(", ", types.Select(type => $"typeof({type})"))}}})] + + """, Encoding.UTF8) + ); + } + + [Fact] + public async void ScriptBoilerplate() + { + var verifier = CSharpSourceGeneratorVerifier<ScriptPathAttributeGenerator>.MakeVerifier( + new string[] { "ScriptBoilerplate.cs" }, + new string[] { "ScriptBoilerplate_ScriptPath.generated.cs" } + ); + verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::ScriptBoilerplate" })); + await verifier.RunAsync(); + } + + [Fact] + public async void FooBar() + { + var verifier = CSharpSourceGeneratorVerifier<ScriptPathAttributeGenerator>.MakeVerifier( + new string[] { "Foo.cs", "Bar.cs" }, + new string[] { "Foo_ScriptPath.generated.cs", "Bar_ScriptPath.generated.cs" } + ); + verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::Foo", "global::Bar" })); + await verifier.RunAsync(); + } + + [Fact] + public async void Generic() + { + var verifier = CSharpSourceGeneratorVerifier<ScriptPathAttributeGenerator>.MakeVerifier( + new string[] { "Generic.cs" }, + new string[] { "Generic_ScriptPath.generated.cs" } + ); + verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::Generic" })); + await verifier.RunAsync(); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertiesGeneratorTests.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertiesGeneratorTests.cs new file mode 100644 index 0000000000..d20b354b9e --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertiesGeneratorTests.cs @@ -0,0 +1,60 @@ +using Xunit; + +namespace Godot.SourceGenerators.Tests; + +public class ScriptPropertiesGeneratorTests +{ + [Fact] + public async void ExportedFields() + { + await CSharpSourceGeneratorVerifier<ScriptPropertiesGenerator>.Verify( + new string[] { "ExportedFields.cs", "MoreExportedFields.cs" }, + new string[] { "ExportedFields_ScriptProperties.generated.cs" } + ); + } + + [Fact] + public async void ExportedProperties() + { + await CSharpSourceGeneratorVerifier<ScriptPropertiesGenerator>.Verify( + "ExportedProperties.cs", + "ExportedProperties_ScriptProperties.generated.cs" + ); + } + + [Fact] + public async void OneWayPropertiesAllReadOnly() + { + await CSharpSourceGeneratorVerifier<ScriptPropertiesGenerator>.Verify( + "AllReadOnly.cs", + "AllReadOnly_ScriptProperties.generated.cs" + ); + } + + [Fact] + public async void OneWayPropertiesAllWriteOnly() + { + await CSharpSourceGeneratorVerifier<ScriptPropertiesGenerator>.Verify( + "AllWriteOnly.cs", + "AllWriteOnly_ScriptProperties.generated.cs" + ); + } + + [Fact] + public async void OneWayPropertiesMixedReadonlyWriteOnly() + { + await CSharpSourceGeneratorVerifier<ScriptPropertiesGenerator>.Verify( + "MixedReadOnlyWriteOnly.cs", + "MixedReadOnlyWriteOnly_ScriptProperties.generated.cs" + ); + } + + [Fact] + public async void ScriptBoilerplate() + { + await CSharpSourceGeneratorVerifier<ScriptPropertiesGenerator>.Verify( + "ScriptBoilerplate.cs", + "ScriptBoilerplate_ScriptProperties.generated.cs", "OuterClass.NestedClass_ScriptProperties.generated.cs" + ); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertyDefValGeneratorTests.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertyDefValGeneratorTests.cs new file mode 100644 index 0000000000..ae5fb86d77 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertyDefValGeneratorTests.cs @@ -0,0 +1,24 @@ +using Xunit; + +namespace Godot.SourceGenerators.Tests; + +public class ScriptPropertyDefValGeneratorTests +{ + [Fact] + public async void ExportedFields() + { + await CSharpSourceGeneratorVerifier<ScriptPropertyDefValGenerator>.Verify( + new string[] { "ExportedFields.cs", "MoreExportedFields.cs" }, + new string[] { "ExportedFields_ScriptPropertyDefVal.generated.cs" } + ); + } + + [Fact] + public async void ExportedProperties() + { + await CSharpSourceGeneratorVerifier<ScriptPropertyDefValGenerator>.Verify( + "ExportedProperties.cs", + "ExportedProperties_ScriptPropertyDefVal.generated.cs" + ); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptSerializationGeneratorTests.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptSerializationGeneratorTests.cs new file mode 100644 index 0000000000..24cd446087 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptSerializationGeneratorTests.cs @@ -0,0 +1,15 @@ +using Xunit; + +namespace Godot.SourceGenerators.Tests; + +public class ScriptSerializationGeneratorTests +{ + [Fact] + public async void ScriptBoilerplate() + { + await CSharpSourceGeneratorVerifier<ScriptSerializationGenerator>.VerifyNoCompilerDiagnostics( + "ScriptBoilerplate.cs", + "ScriptBoilerplate_ScriptSerialization.generated.cs", "OuterClass.NestedClass_ScriptSerialization.generated.cs" + ); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptSignalsGeneratorTests.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptSignalsGeneratorTests.cs new file mode 100644 index 0000000000..2a783cedce --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptSignalsGeneratorTests.cs @@ -0,0 +1,15 @@ +using Xunit; + +namespace Godot.SourceGenerators.Tests; + +public class ScriptSignalsGeneratorTests +{ + [Fact] + public async void EventSignals() + { + await CSharpSourceGeneratorVerifier<ScriptSignalsGenerator>.Verify( + "EventSignals.cs", + "EventSignals_ScriptSignals.generated.cs" + ); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/.editorconfig b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/.editorconfig new file mode 100644 index 0000000000..9e2014e16c --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*.cs] +exclude = true +generated_code = true diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/AllReadOnly_ScriptProperties.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/AllReadOnly_ScriptProperties.generated.cs new file mode 100644 index 0000000000..db56ac30a3 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/AllReadOnly_ScriptProperties.generated.cs @@ -0,0 +1,66 @@ +using Godot; +using Godot.NativeInterop; + +partial class AllReadOnly +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword + /// <summary> + /// Cached StringNames for the properties and fields contained in this class, for fast lookup. + /// </summary> + public new class PropertyName : global::Godot.GodotObject.PropertyName { + /// <summary> + /// Cached name for the 'readonly_auto_property' property. + /// </summary> + public new static readonly global::Godot.StringName readonly_auto_property = "readonly_auto_property"; + /// <summary> + /// Cached name for the 'readonly_property' property. + /// </summary> + public new static readonly global::Godot.StringName readonly_property = "readonly_property"; + /// <summary> + /// Cached name for the 'initonly_auto_property' property. + /// </summary> + public new static readonly global::Godot.StringName initonly_auto_property = "initonly_auto_property"; + /// <summary> + /// Cached name for the 'readonly_field' field. + /// </summary> + public new static readonly global::Godot.StringName readonly_field = "readonly_field"; + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value) + { + if (name == PropertyName.readonly_auto_property) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.readonly_auto_property); + return true; + } + else if (name == PropertyName.readonly_property) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.readonly_property); + return true; + } + else if (name == PropertyName.initonly_auto_property) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.initonly_auto_property); + return true; + } + else if (name == PropertyName.readonly_field) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.readonly_field); + return true; + } + return base.GetGodotClassPropertyValue(name, out value); + } + /// <summary> + /// Get the property information for all the properties declared in this class. + /// This method is used by Godot to register the available properties in the editor. + /// Do not call this method. + /// </summary> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new static global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo> GetGodotPropertyList() + { + var properties = new global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>(); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.readonly_field, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.readonly_auto_property, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.readonly_property, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.initonly_auto_property, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + return properties; + } +#pragma warning restore CS0109 +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/AllWriteOnly_ScriptProperties.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/AllWriteOnly_ScriptProperties.generated.cs new file mode 100644 index 0000000000..256420fe87 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/AllWriteOnly_ScriptProperties.generated.cs @@ -0,0 +1,58 @@ +using Godot; +using Godot.NativeInterop; + +partial class AllWriteOnly +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword + /// <summary> + /// Cached StringNames for the properties and fields contained in this class, for fast lookup. + /// </summary> + public new class PropertyName : global::Godot.GodotObject.PropertyName { + /// <summary> + /// Cached name for the 'writeonly_property' property. + /// </summary> + public new static readonly global::Godot.StringName writeonly_property = "writeonly_property"; + /// <summary> + /// Cached name for the 'writeonly_backing_field' field. + /// </summary> + public new static readonly global::Godot.StringName writeonly_backing_field = "writeonly_backing_field"; + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value) + { + if (name == PropertyName.writeonly_property) { + this.writeonly_property = global::Godot.NativeInterop.VariantUtils.ConvertTo<bool>(value); + return true; + } + else if (name == PropertyName.writeonly_backing_field) { + this.writeonly_backing_field = global::Godot.NativeInterop.VariantUtils.ConvertTo<bool>(value); + return true; + } + return base.SetGodotClassPropertyValue(name, value); + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value) + { + if (name == PropertyName.writeonly_backing_field) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<bool>(this.writeonly_backing_field); + return true; + } + return base.GetGodotClassPropertyValue(name, out value); + } + /// <summary> + /// Get the property information for all the properties declared in this class. + /// This method is used by Godot to register the available properties in the editor. + /// Do not call this method. + /// </summary> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new static global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo> GetGodotPropertyList() + { + var properties = new global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>(); + properties.Add(new(type: (global::Godot.Variant.Type)1, name: PropertyName.writeonly_backing_field, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)1, name: PropertyName.writeonly_property, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + return properties; + } +#pragma warning restore CS0109 +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Bar_ScriptPath.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Bar_ScriptPath.generated.cs new file mode 100644 index 0000000000..9096da1b42 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Bar_ScriptPath.generated.cs @@ -0,0 +1,5 @@ +using Godot; +[ScriptPathAttribute("res://Bar.cs")] +partial class Bar +{ +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs new file mode 100644 index 0000000000..b1c57e6b26 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs @@ -0,0 +1,54 @@ +using Godot; +using Godot.NativeInterop; + +partial class EventSignals +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword + /// <summary> + /// Cached StringNames for the signals contained in this class, for fast lookup. + /// </summary> + public new class SignalName : global::Godot.GodotObject.SignalName { + /// <summary> + /// Cached name for the 'MySignal' signal. + /// </summary> + public new static readonly global::Godot.StringName MySignal = "MySignal"; + } + /// <summary> + /// Get the signal information for all the signals declared in this class. + /// This method is used by Godot to register the available signals in the editor. + /// Do not call this method. + /// </summary> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new static global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo> GetGodotSignalList() + { + var signals = new global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>(1); + signals.Add(new(name: SignalName.MySignal, returnVal: new(type: (global::Godot.Variant.Type)0, name: "", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), flags: (global::Godot.MethodFlags)1, arguments: new() { new(type: (global::Godot.Variant.Type)4, name: "str", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), new(type: (global::Godot.Variant.Type)2, name: "num", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), }, defaultArguments: null)); + return signals; + } +#pragma warning restore CS0109 + private global::EventSignals.MySignalEventHandler backing_MySignal; + /// <inheritdoc cref="global::EventSignals.MySignalEventHandler"/> + public event global::EventSignals.MySignalEventHandler MySignal { + add => backing_MySignal += value; + remove => backing_MySignal -= value; +} + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override void RaiseGodotClassSignalCallbacks(in godot_string_name signal, NativeVariantPtrArgs args) + { + if (signal == SignalName.MySignal && args.Count == 2) { + backing_MySignal?.Invoke(global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(args[0]), global::Godot.NativeInterop.VariantUtils.ConvertTo<int>(args[1])); + return; + } + base.RaiseGodotClassSignalCallbacks(signal, args); + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool HasGodotClassSignal(in godot_string_name signal) + { + if (signal == SignalName.MySignal) { + return true; + } + return base.HasGodotClassSignal(signal); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptProperties.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptProperties.generated.cs new file mode 100644 index 0000000000..915c36525b --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptProperties.generated.cs @@ -0,0 +1,816 @@ +using Godot; +using Godot.NativeInterop; + +partial class ExportedFields +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword + /// <summary> + /// Cached StringNames for the properties and fields contained in this class, for fast lookup. + /// </summary> + public new class PropertyName : global::Godot.GodotObject.PropertyName { + /// <summary> + /// Cached name for the 'field_Boolean' field. + /// </summary> + public new static readonly global::Godot.StringName field_Boolean = "field_Boolean"; + /// <summary> + /// Cached name for the 'field_Char' field. + /// </summary> + public new static readonly global::Godot.StringName field_Char = "field_Char"; + /// <summary> + /// Cached name for the 'field_SByte' field. + /// </summary> + public new static readonly global::Godot.StringName field_SByte = "field_SByte"; + /// <summary> + /// Cached name for the 'field_Int16' field. + /// </summary> + public new static readonly global::Godot.StringName field_Int16 = "field_Int16"; + /// <summary> + /// Cached name for the 'field_Int32' field. + /// </summary> + public new static readonly global::Godot.StringName field_Int32 = "field_Int32"; + /// <summary> + /// Cached name for the 'field_Int64' field. + /// </summary> + public new static readonly global::Godot.StringName field_Int64 = "field_Int64"; + /// <summary> + /// Cached name for the 'field_Byte' field. + /// </summary> + public new static readonly global::Godot.StringName field_Byte = "field_Byte"; + /// <summary> + /// Cached name for the 'field_UInt16' field. + /// </summary> + public new static readonly global::Godot.StringName field_UInt16 = "field_UInt16"; + /// <summary> + /// Cached name for the 'field_UInt32' field. + /// </summary> + public new static readonly global::Godot.StringName field_UInt32 = "field_UInt32"; + /// <summary> + /// Cached name for the 'field_UInt64' field. + /// </summary> + public new static readonly global::Godot.StringName field_UInt64 = "field_UInt64"; + /// <summary> + /// Cached name for the 'field_Single' field. + /// </summary> + public new static readonly global::Godot.StringName field_Single = "field_Single"; + /// <summary> + /// Cached name for the 'field_Double' field. + /// </summary> + public new static readonly global::Godot.StringName field_Double = "field_Double"; + /// <summary> + /// Cached name for the 'field_String' field. + /// </summary> + public new static readonly global::Godot.StringName field_String = "field_String"; + /// <summary> + /// Cached name for the 'field_Vector2' field. + /// </summary> + public new static readonly global::Godot.StringName field_Vector2 = "field_Vector2"; + /// <summary> + /// Cached name for the 'field_Vector2I' field. + /// </summary> + public new static readonly global::Godot.StringName field_Vector2I = "field_Vector2I"; + /// <summary> + /// Cached name for the 'field_Rect2' field. + /// </summary> + public new static readonly global::Godot.StringName field_Rect2 = "field_Rect2"; + /// <summary> + /// Cached name for the 'field_Rect2I' field. + /// </summary> + public new static readonly global::Godot.StringName field_Rect2I = "field_Rect2I"; + /// <summary> + /// Cached name for the 'field_Transform2D' field. + /// </summary> + public new static readonly global::Godot.StringName field_Transform2D = "field_Transform2D"; + /// <summary> + /// Cached name for the 'field_Vector3' field. + /// </summary> + public new static readonly global::Godot.StringName field_Vector3 = "field_Vector3"; + /// <summary> + /// Cached name for the 'field_Vector3I' field. + /// </summary> + public new static readonly global::Godot.StringName field_Vector3I = "field_Vector3I"; + /// <summary> + /// Cached name for the 'field_Basis' field. + /// </summary> + public new static readonly global::Godot.StringName field_Basis = "field_Basis"; + /// <summary> + /// Cached name for the 'field_Quaternion' field. + /// </summary> + public new static readonly global::Godot.StringName field_Quaternion = "field_Quaternion"; + /// <summary> + /// Cached name for the 'field_Transform3D' field. + /// </summary> + public new static readonly global::Godot.StringName field_Transform3D = "field_Transform3D"; + /// <summary> + /// Cached name for the 'field_Vector4' field. + /// </summary> + public new static readonly global::Godot.StringName field_Vector4 = "field_Vector4"; + /// <summary> + /// Cached name for the 'field_Vector4I' field. + /// </summary> + public new static readonly global::Godot.StringName field_Vector4I = "field_Vector4I"; + /// <summary> + /// Cached name for the 'field_Projection' field. + /// </summary> + public new static readonly global::Godot.StringName field_Projection = "field_Projection"; + /// <summary> + /// Cached name for the 'field_Aabb' field. + /// </summary> + public new static readonly global::Godot.StringName field_Aabb = "field_Aabb"; + /// <summary> + /// Cached name for the 'field_Color' field. + /// </summary> + public new static readonly global::Godot.StringName field_Color = "field_Color"; + /// <summary> + /// Cached name for the 'field_Plane' field. + /// </summary> + public new static readonly global::Godot.StringName field_Plane = "field_Plane"; + /// <summary> + /// Cached name for the 'field_Callable' field. + /// </summary> + public new static readonly global::Godot.StringName field_Callable = "field_Callable"; + /// <summary> + /// Cached name for the 'field_Signal' field. + /// </summary> + public new static readonly global::Godot.StringName field_Signal = "field_Signal"; + /// <summary> + /// Cached name for the 'field_Enum' field. + /// </summary> + public new static readonly global::Godot.StringName field_Enum = "field_Enum"; + /// <summary> + /// Cached name for the 'field_FlagsEnum' field. + /// </summary> + public new static readonly global::Godot.StringName field_FlagsEnum = "field_FlagsEnum"; + /// <summary> + /// Cached name for the 'field_ByteArray' field. + /// </summary> + public new static readonly global::Godot.StringName field_ByteArray = "field_ByteArray"; + /// <summary> + /// Cached name for the 'field_Int32Array' field. + /// </summary> + public new static readonly global::Godot.StringName field_Int32Array = "field_Int32Array"; + /// <summary> + /// Cached name for the 'field_Int64Array' field. + /// </summary> + public new static readonly global::Godot.StringName field_Int64Array = "field_Int64Array"; + /// <summary> + /// Cached name for the 'field_SingleArray' field. + /// </summary> + public new static readonly global::Godot.StringName field_SingleArray = "field_SingleArray"; + /// <summary> + /// Cached name for the 'field_DoubleArray' field. + /// </summary> + public new static readonly global::Godot.StringName field_DoubleArray = "field_DoubleArray"; + /// <summary> + /// Cached name for the 'field_StringArray' field. + /// </summary> + public new static readonly global::Godot.StringName field_StringArray = "field_StringArray"; + /// <summary> + /// Cached name for the 'field_StringArrayEnum' field. + /// </summary> + public new static readonly global::Godot.StringName field_StringArrayEnum = "field_StringArrayEnum"; + /// <summary> + /// Cached name for the 'field_Vector2Array' field. + /// </summary> + public new static readonly global::Godot.StringName field_Vector2Array = "field_Vector2Array"; + /// <summary> + /// Cached name for the 'field_Vector3Array' field. + /// </summary> + public new static readonly global::Godot.StringName field_Vector3Array = "field_Vector3Array"; + /// <summary> + /// Cached name for the 'field_ColorArray' field. + /// </summary> + public new static readonly global::Godot.StringName field_ColorArray = "field_ColorArray"; + /// <summary> + /// Cached name for the 'field_GodotObjectOrDerivedArray' field. + /// </summary> + public new static readonly global::Godot.StringName field_GodotObjectOrDerivedArray = "field_GodotObjectOrDerivedArray"; + /// <summary> + /// Cached name for the 'field_StringNameArray' field. + /// </summary> + public new static readonly global::Godot.StringName field_StringNameArray = "field_StringNameArray"; + /// <summary> + /// Cached name for the 'field_NodePathArray' field. + /// </summary> + public new static readonly global::Godot.StringName field_NodePathArray = "field_NodePathArray"; + /// <summary> + /// Cached name for the 'field_RidArray' field. + /// </summary> + public new static readonly global::Godot.StringName field_RidArray = "field_RidArray"; + /// <summary> + /// Cached name for the 'field_empty_Int32Array' field. + /// </summary> + public new static readonly global::Godot.StringName field_empty_Int32Array = "field_empty_Int32Array"; + /// <summary> + /// Cached name for the 'field_array_from_list' field. + /// </summary> + public new static readonly global::Godot.StringName field_array_from_list = "field_array_from_list"; + /// <summary> + /// Cached name for the 'field_Variant' field. + /// </summary> + public new static readonly global::Godot.StringName field_Variant = "field_Variant"; + /// <summary> + /// Cached name for the 'field_GodotObjectOrDerived' field. + /// </summary> + public new static readonly global::Godot.StringName field_GodotObjectOrDerived = "field_GodotObjectOrDerived"; + /// <summary> + /// Cached name for the 'field_GodotResourceTexture' field. + /// </summary> + public new static readonly global::Godot.StringName field_GodotResourceTexture = "field_GodotResourceTexture"; + /// <summary> + /// Cached name for the 'field_StringName' field. + /// </summary> + public new static readonly global::Godot.StringName field_StringName = "field_StringName"; + /// <summary> + /// Cached name for the 'field_NodePath' field. + /// </summary> + public new static readonly global::Godot.StringName field_NodePath = "field_NodePath"; + /// <summary> + /// Cached name for the 'field_Rid' field. + /// </summary> + public new static readonly global::Godot.StringName field_Rid = "field_Rid"; + /// <summary> + /// Cached name for the 'field_GodotDictionary' field. + /// </summary> + public new static readonly global::Godot.StringName field_GodotDictionary = "field_GodotDictionary"; + /// <summary> + /// Cached name for the 'field_GodotArray' field. + /// </summary> + public new static readonly global::Godot.StringName field_GodotArray = "field_GodotArray"; + /// <summary> + /// Cached name for the 'field_GodotGenericDictionary' field. + /// </summary> + public new static readonly global::Godot.StringName field_GodotGenericDictionary = "field_GodotGenericDictionary"; + /// <summary> + /// Cached name for the 'field_GodotGenericArray' field. + /// </summary> + public new static readonly global::Godot.StringName field_GodotGenericArray = "field_GodotGenericArray"; + /// <summary> + /// Cached name for the 'field_empty_Int64Array' field. + /// </summary> + public new static readonly global::Godot.StringName field_empty_Int64Array = "field_empty_Int64Array"; + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value) + { + if (name == PropertyName.field_Boolean) { + this.field_Boolean = global::Godot.NativeInterop.VariantUtils.ConvertTo<bool>(value); + return true; + } + else if (name == PropertyName.field_Char) { + this.field_Char = global::Godot.NativeInterop.VariantUtils.ConvertTo<char>(value); + return true; + } + else if (name == PropertyName.field_SByte) { + this.field_SByte = global::Godot.NativeInterop.VariantUtils.ConvertTo<sbyte>(value); + return true; + } + else if (name == PropertyName.field_Int16) { + this.field_Int16 = global::Godot.NativeInterop.VariantUtils.ConvertTo<short>(value); + return true; + } + else if (name == PropertyName.field_Int32) { + this.field_Int32 = global::Godot.NativeInterop.VariantUtils.ConvertTo<int>(value); + return true; + } + else if (name == PropertyName.field_Int64) { + this.field_Int64 = global::Godot.NativeInterop.VariantUtils.ConvertTo<long>(value); + return true; + } + else if (name == PropertyName.field_Byte) { + this.field_Byte = global::Godot.NativeInterop.VariantUtils.ConvertTo<byte>(value); + return true; + } + else if (name == PropertyName.field_UInt16) { + this.field_UInt16 = global::Godot.NativeInterop.VariantUtils.ConvertTo<ushort>(value); + return true; + } + else if (name == PropertyName.field_UInt32) { + this.field_UInt32 = global::Godot.NativeInterop.VariantUtils.ConvertTo<uint>(value); + return true; + } + else if (name == PropertyName.field_UInt64) { + this.field_UInt64 = global::Godot.NativeInterop.VariantUtils.ConvertTo<ulong>(value); + return true; + } + else if (name == PropertyName.field_Single) { + this.field_Single = global::Godot.NativeInterop.VariantUtils.ConvertTo<float>(value); + return true; + } + else if (name == PropertyName.field_Double) { + this.field_Double = global::Godot.NativeInterop.VariantUtils.ConvertTo<double>(value); + return true; + } + else if (name == PropertyName.field_String) { + this.field_String = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + else if (name == PropertyName.field_Vector2) { + this.field_Vector2 = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector2>(value); + return true; + } + else if (name == PropertyName.field_Vector2I) { + this.field_Vector2I = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector2I>(value); + return true; + } + else if (name == PropertyName.field_Rect2) { + this.field_Rect2 = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Rect2>(value); + return true; + } + else if (name == PropertyName.field_Rect2I) { + this.field_Rect2I = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Rect2I>(value); + return true; + } + else if (name == PropertyName.field_Transform2D) { + this.field_Transform2D = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Transform2D>(value); + return true; + } + else if (name == PropertyName.field_Vector3) { + this.field_Vector3 = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector3>(value); + return true; + } + else if (name == PropertyName.field_Vector3I) { + this.field_Vector3I = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector3I>(value); + return true; + } + else if (name == PropertyName.field_Basis) { + this.field_Basis = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Basis>(value); + return true; + } + else if (name == PropertyName.field_Quaternion) { + this.field_Quaternion = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Quaternion>(value); + return true; + } + else if (name == PropertyName.field_Transform3D) { + this.field_Transform3D = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Transform3D>(value); + return true; + } + else if (name == PropertyName.field_Vector4) { + this.field_Vector4 = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector4>(value); + return true; + } + else if (name == PropertyName.field_Vector4I) { + this.field_Vector4I = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector4I>(value); + return true; + } + else if (name == PropertyName.field_Projection) { + this.field_Projection = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Projection>(value); + return true; + } + else if (name == PropertyName.field_Aabb) { + this.field_Aabb = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Aabb>(value); + return true; + } + else if (name == PropertyName.field_Color) { + this.field_Color = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Color>(value); + return true; + } + else if (name == PropertyName.field_Plane) { + this.field_Plane = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Plane>(value); + return true; + } + else if (name == PropertyName.field_Callable) { + this.field_Callable = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Callable>(value); + return true; + } + else if (name == PropertyName.field_Signal) { + this.field_Signal = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Signal>(value); + return true; + } + else if (name == PropertyName.field_Enum) { + this.field_Enum = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::ExportedFields.MyEnum>(value); + return true; + } + else if (name == PropertyName.field_FlagsEnum) { + this.field_FlagsEnum = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::ExportedFields.MyFlagsEnum>(value); + return true; + } + else if (name == PropertyName.field_ByteArray) { + this.field_ByteArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<byte[]>(value); + return true; + } + else if (name == PropertyName.field_Int32Array) { + this.field_Int32Array = global::Godot.NativeInterop.VariantUtils.ConvertTo<int[]>(value); + return true; + } + else if (name == PropertyName.field_Int64Array) { + this.field_Int64Array = global::Godot.NativeInterop.VariantUtils.ConvertTo<long[]>(value); + return true; + } + else if (name == PropertyName.field_SingleArray) { + this.field_SingleArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<float[]>(value); + return true; + } + else if (name == PropertyName.field_DoubleArray) { + this.field_DoubleArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<double[]>(value); + return true; + } + else if (name == PropertyName.field_StringArray) { + this.field_StringArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<string[]>(value); + return true; + } + else if (name == PropertyName.field_StringArrayEnum) { + this.field_StringArrayEnum = global::Godot.NativeInterop.VariantUtils.ConvertTo<string[]>(value); + return true; + } + else if (name == PropertyName.field_Vector2Array) { + this.field_Vector2Array = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector2[]>(value); + return true; + } + else if (name == PropertyName.field_Vector3Array) { + this.field_Vector3Array = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector3[]>(value); + return true; + } + else if (name == PropertyName.field_ColorArray) { + this.field_ColorArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Color[]>(value); + return true; + } + else if (name == PropertyName.field_GodotObjectOrDerivedArray) { + this.field_GodotObjectOrDerivedArray = global::Godot.NativeInterop.VariantUtils.ConvertToSystemArrayOfGodotObject<global::Godot.GodotObject>(value); + return true; + } + else if (name == PropertyName.field_StringNameArray) { + this.field_StringNameArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.StringName[]>(value); + return true; + } + else if (name == PropertyName.field_NodePathArray) { + this.field_NodePathArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.NodePath[]>(value); + return true; + } + else if (name == PropertyName.field_RidArray) { + this.field_RidArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Rid[]>(value); + return true; + } + else if (name == PropertyName.field_empty_Int32Array) { + this.field_empty_Int32Array = global::Godot.NativeInterop.VariantUtils.ConvertTo<int[]>(value); + return true; + } + else if (name == PropertyName.field_array_from_list) { + this.field_array_from_list = global::Godot.NativeInterop.VariantUtils.ConvertTo<int[]>(value); + return true; + } + else if (name == PropertyName.field_Variant) { + this.field_Variant = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Variant>(value); + return true; + } + else if (name == PropertyName.field_GodotObjectOrDerived) { + this.field_GodotObjectOrDerived = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.GodotObject>(value); + return true; + } + else if (name == PropertyName.field_GodotResourceTexture) { + this.field_GodotResourceTexture = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Texture>(value); + return true; + } + else if (name == PropertyName.field_StringName) { + this.field_StringName = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.StringName>(value); + return true; + } + else if (name == PropertyName.field_NodePath) { + this.field_NodePath = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.NodePath>(value); + return true; + } + else if (name == PropertyName.field_Rid) { + this.field_Rid = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Rid>(value); + return true; + } + else if (name == PropertyName.field_GodotDictionary) { + this.field_GodotDictionary = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Collections.Dictionary>(value); + return true; + } + else if (name == PropertyName.field_GodotArray) { + this.field_GodotArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Collections.Array>(value); + return true; + } + else if (name == PropertyName.field_GodotGenericDictionary) { + this.field_GodotGenericDictionary = global::Godot.NativeInterop.VariantUtils.ConvertToDictionary<string, bool>(value); + return true; + } + else if (name == PropertyName.field_GodotGenericArray) { + this.field_GodotGenericArray = global::Godot.NativeInterop.VariantUtils.ConvertToArray<int>(value); + return true; + } + else if (name == PropertyName.field_empty_Int64Array) { + this.field_empty_Int64Array = global::Godot.NativeInterop.VariantUtils.ConvertTo<long[]>(value); + return true; + } + return base.SetGodotClassPropertyValue(name, value); + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value) + { + if (name == PropertyName.field_Boolean) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<bool>(this.field_Boolean); + return true; + } + else if (name == PropertyName.field_Char) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<char>(this.field_Char); + return true; + } + else if (name == PropertyName.field_SByte) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<sbyte>(this.field_SByte); + return true; + } + else if (name == PropertyName.field_Int16) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<short>(this.field_Int16); + return true; + } + else if (name == PropertyName.field_Int32) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<int>(this.field_Int32); + return true; + } + else if (name == PropertyName.field_Int64) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<long>(this.field_Int64); + return true; + } + else if (name == PropertyName.field_Byte) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<byte>(this.field_Byte); + return true; + } + else if (name == PropertyName.field_UInt16) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<ushort>(this.field_UInt16); + return true; + } + else if (name == PropertyName.field_UInt32) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<uint>(this.field_UInt32); + return true; + } + else if (name == PropertyName.field_UInt64) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<ulong>(this.field_UInt64); + return true; + } + else if (name == PropertyName.field_Single) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<float>(this.field_Single); + return true; + } + else if (name == PropertyName.field_Double) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<double>(this.field_Double); + return true; + } + else if (name == PropertyName.field_String) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.field_String); + return true; + } + else if (name == PropertyName.field_Vector2) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector2>(this.field_Vector2); + return true; + } + else if (name == PropertyName.field_Vector2I) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector2I>(this.field_Vector2I); + return true; + } + else if (name == PropertyName.field_Rect2) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Rect2>(this.field_Rect2); + return true; + } + else if (name == PropertyName.field_Rect2I) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Rect2I>(this.field_Rect2I); + return true; + } + else if (name == PropertyName.field_Transform2D) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Transform2D>(this.field_Transform2D); + return true; + } + else if (name == PropertyName.field_Vector3) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector3>(this.field_Vector3); + return true; + } + else if (name == PropertyName.field_Vector3I) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector3I>(this.field_Vector3I); + return true; + } + else if (name == PropertyName.field_Basis) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Basis>(this.field_Basis); + return true; + } + else if (name == PropertyName.field_Quaternion) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Quaternion>(this.field_Quaternion); + return true; + } + else if (name == PropertyName.field_Transform3D) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Transform3D>(this.field_Transform3D); + return true; + } + else if (name == PropertyName.field_Vector4) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector4>(this.field_Vector4); + return true; + } + else if (name == PropertyName.field_Vector4I) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector4I>(this.field_Vector4I); + return true; + } + else if (name == PropertyName.field_Projection) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Projection>(this.field_Projection); + return true; + } + else if (name == PropertyName.field_Aabb) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Aabb>(this.field_Aabb); + return true; + } + else if (name == PropertyName.field_Color) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Color>(this.field_Color); + return true; + } + else if (name == PropertyName.field_Plane) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Plane>(this.field_Plane); + return true; + } + else if (name == PropertyName.field_Callable) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Callable>(this.field_Callable); + return true; + } + else if (name == PropertyName.field_Signal) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Signal>(this.field_Signal); + return true; + } + else if (name == PropertyName.field_Enum) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::ExportedFields.MyEnum>(this.field_Enum); + return true; + } + else if (name == PropertyName.field_FlagsEnum) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::ExportedFields.MyFlagsEnum>(this.field_FlagsEnum); + return true; + } + else if (name == PropertyName.field_ByteArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<byte[]>(this.field_ByteArray); + return true; + } + else if (name == PropertyName.field_Int32Array) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<int[]>(this.field_Int32Array); + return true; + } + else if (name == PropertyName.field_Int64Array) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<long[]>(this.field_Int64Array); + return true; + } + else if (name == PropertyName.field_SingleArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<float[]>(this.field_SingleArray); + return true; + } + else if (name == PropertyName.field_DoubleArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<double[]>(this.field_DoubleArray); + return true; + } + else if (name == PropertyName.field_StringArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string[]>(this.field_StringArray); + return true; + } + else if (name == PropertyName.field_StringArrayEnum) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string[]>(this.field_StringArrayEnum); + return true; + } + else if (name == PropertyName.field_Vector2Array) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector2[]>(this.field_Vector2Array); + return true; + } + else if (name == PropertyName.field_Vector3Array) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector3[]>(this.field_Vector3Array); + return true; + } + else if (name == PropertyName.field_ColorArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Color[]>(this.field_ColorArray); + return true; + } + else if (name == PropertyName.field_GodotObjectOrDerivedArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFromSystemArrayOfGodotObject(this.field_GodotObjectOrDerivedArray); + return true; + } + else if (name == PropertyName.field_StringNameArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.StringName[]>(this.field_StringNameArray); + return true; + } + else if (name == PropertyName.field_NodePathArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.NodePath[]>(this.field_NodePathArray); + return true; + } + else if (name == PropertyName.field_RidArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Rid[]>(this.field_RidArray); + return true; + } + else if (name == PropertyName.field_empty_Int32Array) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<int[]>(this.field_empty_Int32Array); + return true; + } + else if (name == PropertyName.field_array_from_list) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<int[]>(this.field_array_from_list); + return true; + } + else if (name == PropertyName.field_Variant) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Variant>(this.field_Variant); + return true; + } + else if (name == PropertyName.field_GodotObjectOrDerived) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.GodotObject>(this.field_GodotObjectOrDerived); + return true; + } + else if (name == PropertyName.field_GodotResourceTexture) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Texture>(this.field_GodotResourceTexture); + return true; + } + else if (name == PropertyName.field_StringName) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.StringName>(this.field_StringName); + return true; + } + else if (name == PropertyName.field_NodePath) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.NodePath>(this.field_NodePath); + return true; + } + else if (name == PropertyName.field_Rid) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Rid>(this.field_Rid); + return true; + } + else if (name == PropertyName.field_GodotDictionary) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Collections.Dictionary>(this.field_GodotDictionary); + return true; + } + else if (name == PropertyName.field_GodotArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Collections.Array>(this.field_GodotArray); + return true; + } + else if (name == PropertyName.field_GodotGenericDictionary) { + value = global::Godot.NativeInterop.VariantUtils.CreateFromDictionary(this.field_GodotGenericDictionary); + return true; + } + else if (name == PropertyName.field_GodotGenericArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFromArray(this.field_GodotGenericArray); + return true; + } + else if (name == PropertyName.field_empty_Int64Array) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<long[]>(this.field_empty_Int64Array); + return true; + } + return base.GetGodotClassPropertyValue(name, out value); + } + /// <summary> + /// Get the property information for all the properties declared in this class. + /// This method is used by Godot to register the available properties in the editor. + /// Do not call this method. + /// </summary> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new static global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo> GetGodotPropertyList() + { + var properties = new global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>(); + properties.Add(new(type: (global::Godot.Variant.Type)1, name: PropertyName.field_Boolean, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.field_Char, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.field_SByte, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.field_Int16, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.field_Int32, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.field_Int64, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.field_Byte, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.field_UInt16, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.field_UInt32, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.field_UInt64, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)3, name: PropertyName.field_Single, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)3, name: PropertyName.field_Double, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.field_String, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)5, name: PropertyName.field_Vector2, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)6, name: PropertyName.field_Vector2I, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)7, name: PropertyName.field_Rect2, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)8, name: PropertyName.field_Rect2I, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)11, name: PropertyName.field_Transform2D, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)9, name: PropertyName.field_Vector3, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)10, name: PropertyName.field_Vector3I, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)17, name: PropertyName.field_Basis, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)15, name: PropertyName.field_Quaternion, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)18, name: PropertyName.field_Transform3D, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)12, name: PropertyName.field_Vector4, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)13, name: PropertyName.field_Vector4I, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)19, name: PropertyName.field_Projection, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)16, name: PropertyName.field_Aabb, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)20, name: PropertyName.field_Color, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)14, name: PropertyName.field_Plane, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)25, name: PropertyName.field_Callable, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)26, name: PropertyName.field_Signal, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.field_Enum, hint: (global::Godot.PropertyHint)2, hintString: "A,B,C", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.field_FlagsEnum, hint: (global::Godot.PropertyHint)6, hintString: "A:0,B:1,C:2", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)29, name: PropertyName.field_ByteArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)30, name: PropertyName.field_Int32Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)31, name: PropertyName.field_Int64Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)32, name: PropertyName.field_SingleArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)33, name: PropertyName.field_DoubleArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)34, name: PropertyName.field_StringArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)34, name: PropertyName.field_StringArrayEnum, hint: (global::Godot.PropertyHint)23, hintString: "4/2:A,B,C", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)35, name: PropertyName.field_Vector2Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)36, name: PropertyName.field_Vector3Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)37, name: PropertyName.field_ColorArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.field_GodotObjectOrDerivedArray, hint: (global::Godot.PropertyHint)23, hintString: "24/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.field_StringNameArray, hint: (global::Godot.PropertyHint)23, hintString: "21/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.field_NodePathArray, hint: (global::Godot.PropertyHint)23, hintString: "22/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.field_RidArray, hint: (global::Godot.PropertyHint)23, hintString: "23/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)30, name: PropertyName.field_empty_Int32Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)30, name: PropertyName.field_array_from_list, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)0, name: PropertyName.field_Variant, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)135174, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)24, name: PropertyName.field_GodotObjectOrDerived, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)24, name: PropertyName.field_GodotResourceTexture, hint: (global::Godot.PropertyHint)17, hintString: "Texture", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)21, name: PropertyName.field_StringName, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)22, name: PropertyName.field_NodePath, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)23, name: PropertyName.field_Rid, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.field_GodotDictionary, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.field_GodotArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.field_GodotGenericDictionary, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.field_GodotGenericArray, hint: (global::Godot.PropertyHint)23, hintString: "2/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)31, name: PropertyName.field_empty_Int64Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + return properties; + } +#pragma warning restore CS0109 +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptPropertyDefVal.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptPropertyDefVal.generated.cs new file mode 100644 index 0000000000..185b8e4ac6 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptPropertyDefVal.generated.cs @@ -0,0 +1,139 @@ +partial class ExportedFields +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword +#if TOOLS + /// <summary> + /// Get the default values for all properties declared in this class. + /// This method is used by Godot to determine the value that will be + /// used by the inspector when resetting properties. + /// Do not call this method. + /// </summary> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new static global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant> GetGodotPropertyDefaultValues() + { + var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(60); + bool __field_Boolean_default_value = true; + values.Add(PropertyName.field_Boolean, global::Godot.Variant.From<bool>(__field_Boolean_default_value)); + char __field_Char_default_value = 'f'; + values.Add(PropertyName.field_Char, global::Godot.Variant.From<char>(__field_Char_default_value)); + sbyte __field_SByte_default_value = 10; + values.Add(PropertyName.field_SByte, global::Godot.Variant.From<sbyte>(__field_SByte_default_value)); + short __field_Int16_default_value = 10; + values.Add(PropertyName.field_Int16, global::Godot.Variant.From<short>(__field_Int16_default_value)); + int __field_Int32_default_value = 10; + values.Add(PropertyName.field_Int32, global::Godot.Variant.From<int>(__field_Int32_default_value)); + long __field_Int64_default_value = 10; + values.Add(PropertyName.field_Int64, global::Godot.Variant.From<long>(__field_Int64_default_value)); + byte __field_Byte_default_value = 10; + values.Add(PropertyName.field_Byte, global::Godot.Variant.From<byte>(__field_Byte_default_value)); + ushort __field_UInt16_default_value = 10; + values.Add(PropertyName.field_UInt16, global::Godot.Variant.From<ushort>(__field_UInt16_default_value)); + uint __field_UInt32_default_value = 10; + values.Add(PropertyName.field_UInt32, global::Godot.Variant.From<uint>(__field_UInt32_default_value)); + ulong __field_UInt64_default_value = 10; + values.Add(PropertyName.field_UInt64, global::Godot.Variant.From<ulong>(__field_UInt64_default_value)); + float __field_Single_default_value = 10; + values.Add(PropertyName.field_Single, global::Godot.Variant.From<float>(__field_Single_default_value)); + double __field_Double_default_value = 10; + values.Add(PropertyName.field_Double, global::Godot.Variant.From<double>(__field_Double_default_value)); + string __field_String_default_value = "foo"; + values.Add(PropertyName.field_String, global::Godot.Variant.From<string>(__field_String_default_value)); + global::Godot.Vector2 __field_Vector2_default_value = new(10f, 10f); + values.Add(PropertyName.field_Vector2, global::Godot.Variant.From<global::Godot.Vector2>(__field_Vector2_default_value)); + global::Godot.Vector2I __field_Vector2I_default_value = global::Godot.Vector2I.Up; + values.Add(PropertyName.field_Vector2I, global::Godot.Variant.From<global::Godot.Vector2I>(__field_Vector2I_default_value)); + global::Godot.Rect2 __field_Rect2_default_value = new(new global::Godot.Vector2(10f, 10f), new global::Godot.Vector2(10f, 10f)); + values.Add(PropertyName.field_Rect2, global::Godot.Variant.From<global::Godot.Rect2>(__field_Rect2_default_value)); + global::Godot.Rect2I __field_Rect2I_default_value = new(new global::Godot.Vector2I(10, 10), new global::Godot.Vector2I(10, 10)); + values.Add(PropertyName.field_Rect2I, global::Godot.Variant.From<global::Godot.Rect2I>(__field_Rect2I_default_value)); + global::Godot.Transform2D __field_Transform2D_default_value = global::Godot.Transform2D.Identity; + values.Add(PropertyName.field_Transform2D, global::Godot.Variant.From<global::Godot.Transform2D>(__field_Transform2D_default_value)); + global::Godot.Vector3 __field_Vector3_default_value = new(10f, 10f, 10f); + values.Add(PropertyName.field_Vector3, global::Godot.Variant.From<global::Godot.Vector3>(__field_Vector3_default_value)); + global::Godot.Vector3I __field_Vector3I_default_value = global::Godot.Vector3I.Back; + values.Add(PropertyName.field_Vector3I, global::Godot.Variant.From<global::Godot.Vector3I>(__field_Vector3I_default_value)); + global::Godot.Basis __field_Basis_default_value = new global::Godot.Basis(global::Godot.Quaternion.Identity); + values.Add(PropertyName.field_Basis, global::Godot.Variant.From<global::Godot.Basis>(__field_Basis_default_value)); + global::Godot.Quaternion __field_Quaternion_default_value = new global::Godot.Quaternion(global::Godot.Basis.Identity); + values.Add(PropertyName.field_Quaternion, global::Godot.Variant.From<global::Godot.Quaternion>(__field_Quaternion_default_value)); + global::Godot.Transform3D __field_Transform3D_default_value = global::Godot.Transform3D.Identity; + values.Add(PropertyName.field_Transform3D, global::Godot.Variant.From<global::Godot.Transform3D>(__field_Transform3D_default_value)); + global::Godot.Vector4 __field_Vector4_default_value = new(10f, 10f, 10f, 10f); + values.Add(PropertyName.field_Vector4, global::Godot.Variant.From<global::Godot.Vector4>(__field_Vector4_default_value)); + global::Godot.Vector4I __field_Vector4I_default_value = global::Godot.Vector4I.One; + values.Add(PropertyName.field_Vector4I, global::Godot.Variant.From<global::Godot.Vector4I>(__field_Vector4I_default_value)); + global::Godot.Projection __field_Projection_default_value = global::Godot.Projection.Identity; + values.Add(PropertyName.field_Projection, global::Godot.Variant.From<global::Godot.Projection>(__field_Projection_default_value)); + global::Godot.Aabb __field_Aabb_default_value = new global::Godot.Aabb(10f, 10f, 10f, new global::Godot.Vector3(1f, 1f, 1f)); + values.Add(PropertyName.field_Aabb, global::Godot.Variant.From<global::Godot.Aabb>(__field_Aabb_default_value)); + global::Godot.Color __field_Color_default_value = global::Godot.Colors.Aquamarine; + values.Add(PropertyName.field_Color, global::Godot.Variant.From<global::Godot.Color>(__field_Color_default_value)); + global::Godot.Plane __field_Plane_default_value = global::Godot.Plane.PlaneXZ; + values.Add(PropertyName.field_Plane, global::Godot.Variant.From<global::Godot.Plane>(__field_Plane_default_value)); + global::Godot.Callable __field_Callable_default_value = new global::Godot.Callable(global::Godot.Engine.GetMainLoop(), "_process"); + values.Add(PropertyName.field_Callable, global::Godot.Variant.From<global::Godot.Callable>(__field_Callable_default_value)); + global::Godot.Signal __field_Signal_default_value = new global::Godot.Signal(global::Godot.Engine.GetMainLoop(), "property_list_changed"); + values.Add(PropertyName.field_Signal, global::Godot.Variant.From<global::Godot.Signal>(__field_Signal_default_value)); + global::ExportedFields.MyEnum __field_Enum_default_value = global::ExportedFields.MyEnum.C; + values.Add(PropertyName.field_Enum, global::Godot.Variant.From<global::ExportedFields.MyEnum>(__field_Enum_default_value)); + global::ExportedFields.MyFlagsEnum __field_FlagsEnum_default_value = global::ExportedFields.MyFlagsEnum.C; + values.Add(PropertyName.field_FlagsEnum, global::Godot.Variant.From<global::ExportedFields.MyFlagsEnum>(__field_FlagsEnum_default_value)); + byte[] __field_ByteArray_default_value = { 0, 1, 2, 3, 4, 5, 6 }; + values.Add(PropertyName.field_ByteArray, global::Godot.Variant.From<byte[]>(__field_ByteArray_default_value)); + int[] __field_Int32Array_default_value = { 0, 1, 2, 3, 4, 5, 6 }; + values.Add(PropertyName.field_Int32Array, global::Godot.Variant.From<int[]>(__field_Int32Array_default_value)); + long[] __field_Int64Array_default_value = { 0, 1, 2, 3, 4, 5, 6 }; + values.Add(PropertyName.field_Int64Array, global::Godot.Variant.From<long[]>(__field_Int64Array_default_value)); + float[] __field_SingleArray_default_value = { 0f, 1f, 2f, 3f, 4f, 5f, 6f }; + values.Add(PropertyName.field_SingleArray, global::Godot.Variant.From<float[]>(__field_SingleArray_default_value)); + double[] __field_DoubleArray_default_value = { 0d, 1d, 2d, 3d, 4d, 5d, 6d }; + values.Add(PropertyName.field_DoubleArray, global::Godot.Variant.From<double[]>(__field_DoubleArray_default_value)); + string[] __field_StringArray_default_value = { "foo", "bar" }; + values.Add(PropertyName.field_StringArray, global::Godot.Variant.From<string[]>(__field_StringArray_default_value)); + string[] __field_StringArrayEnum_default_value = { "foo", "bar" }; + values.Add(PropertyName.field_StringArrayEnum, global::Godot.Variant.From<string[]>(__field_StringArrayEnum_default_value)); + global::Godot.Vector2[] __field_Vector2Array_default_value = { global::Godot.Vector2.Up, global::Godot.Vector2.Down, global::Godot.Vector2.Left, global::Godot.Vector2.Right }; + values.Add(PropertyName.field_Vector2Array, global::Godot.Variant.From<global::Godot.Vector2[]>(__field_Vector2Array_default_value)); + global::Godot.Vector3[] __field_Vector3Array_default_value = { global::Godot.Vector3.Up, global::Godot.Vector3.Down, global::Godot.Vector3.Left, global::Godot.Vector3.Right }; + values.Add(PropertyName.field_Vector3Array, global::Godot.Variant.From<global::Godot.Vector3[]>(__field_Vector3Array_default_value)); + global::Godot.Color[] __field_ColorArray_default_value = { global::Godot.Colors.Aqua, global::Godot.Colors.Aquamarine, global::Godot.Colors.Azure, global::Godot.Colors.Beige }; + values.Add(PropertyName.field_ColorArray, global::Godot.Variant.From<global::Godot.Color[]>(__field_ColorArray_default_value)); + global::Godot.GodotObject[] __field_GodotObjectOrDerivedArray_default_value = { null }; + values.Add(PropertyName.field_GodotObjectOrDerivedArray, global::Godot.Variant.CreateFrom(__field_GodotObjectOrDerivedArray_default_value)); + global::Godot.StringName[] __field_StringNameArray_default_value = { "foo", "bar" }; + values.Add(PropertyName.field_StringNameArray, global::Godot.Variant.From<global::Godot.StringName[]>(__field_StringNameArray_default_value)); + global::Godot.NodePath[] __field_NodePathArray_default_value = { "foo", "bar" }; + values.Add(PropertyName.field_NodePathArray, global::Godot.Variant.From<global::Godot.NodePath[]>(__field_NodePathArray_default_value)); + global::Godot.Rid[] __field_RidArray_default_value = { default, default, default }; + values.Add(PropertyName.field_RidArray, global::Godot.Variant.From<global::Godot.Rid[]>(__field_RidArray_default_value)); + int[] __field_empty_Int32Array_default_value = global::System.Array.Empty<int>(); + values.Add(PropertyName.field_empty_Int32Array, global::Godot.Variant.From<int[]>(__field_empty_Int32Array_default_value)); + int[] __field_array_from_list_default_value = new global::System.Collections.Generic.List<int>(global::System.Array.Empty<int>()).ToArray(); + values.Add(PropertyName.field_array_from_list, global::Godot.Variant.From<int[]>(__field_array_from_list_default_value)); + global::Godot.Variant __field_Variant_default_value = "foo"; + values.Add(PropertyName.field_Variant, global::Godot.Variant.From<global::Godot.Variant>(__field_Variant_default_value)); + global::Godot.GodotObject __field_GodotObjectOrDerived_default_value = default; + values.Add(PropertyName.field_GodotObjectOrDerived, global::Godot.Variant.From<global::Godot.GodotObject>(__field_GodotObjectOrDerived_default_value)); + global::Godot.Texture __field_GodotResourceTexture_default_value = default; + values.Add(PropertyName.field_GodotResourceTexture, global::Godot.Variant.From<global::Godot.Texture>(__field_GodotResourceTexture_default_value)); + global::Godot.StringName __field_StringName_default_value = new global::Godot.StringName("foo"); + values.Add(PropertyName.field_StringName, global::Godot.Variant.From<global::Godot.StringName>(__field_StringName_default_value)); + global::Godot.NodePath __field_NodePath_default_value = new global::Godot.NodePath("foo"); + values.Add(PropertyName.field_NodePath, global::Godot.Variant.From<global::Godot.NodePath>(__field_NodePath_default_value)); + global::Godot.Rid __field_Rid_default_value = default; + values.Add(PropertyName.field_Rid, global::Godot.Variant.From<global::Godot.Rid>(__field_Rid_default_value)); + global::Godot.Collections.Dictionary __field_GodotDictionary_default_value = new() { { "foo", 10 }, { global::Godot.Vector2.Up, global::Godot.Colors.Chocolate } }; + values.Add(PropertyName.field_GodotDictionary, global::Godot.Variant.From<global::Godot.Collections.Dictionary>(__field_GodotDictionary_default_value)); + global::Godot.Collections.Array __field_GodotArray_default_value = new() { "foo", 10, global::Godot.Vector2.Up, global::Godot.Colors.Chocolate }; + values.Add(PropertyName.field_GodotArray, global::Godot.Variant.From<global::Godot.Collections.Array>(__field_GodotArray_default_value)); + global::Godot.Collections.Dictionary<string, bool> __field_GodotGenericDictionary_default_value = new() { { "foo", true }, { "bar", false } }; + values.Add(PropertyName.field_GodotGenericDictionary, global::Godot.Variant.CreateFrom(__field_GodotGenericDictionary_default_value)); + global::Godot.Collections.Array<int> __field_GodotGenericArray_default_value = new() { 0, 1, 2, 3, 4, 5, 6 }; + values.Add(PropertyName.field_GodotGenericArray, global::Godot.Variant.CreateFrom(__field_GodotGenericArray_default_value)); + long[] __field_empty_Int64Array_default_value = global::System.Array.Empty<long>(); + values.Add(PropertyName.field_empty_Int64Array, global::Godot.Variant.From<long[]>(__field_empty_Int64Array_default_value)); + return values; + } +#endif // TOOLS +#pragma warning restore CS0109 +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptProperties.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptProperties.generated.cs new file mode 100644 index 0000000000..b5ec9b5b49 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptProperties.generated.cs @@ -0,0 +1,933 @@ +using Godot; +using Godot.NativeInterop; + +partial class ExportedProperties +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword + /// <summary> + /// Cached StringNames for the properties and fields contained in this class, for fast lookup. + /// </summary> + public new class PropertyName : global::Godot.GodotObject.PropertyName { + /// <summary> + /// Cached name for the 'NotGenerate_Complex_Lamda_Property' property. + /// </summary> + public new static readonly global::Godot.StringName NotGenerate_Complex_Lamda_Property = "NotGenerate_Complex_Lamda_Property"; + /// <summary> + /// Cached name for the 'NotGenerate_Lamda_NoField_Property' property. + /// </summary> + public new static readonly global::Godot.StringName NotGenerate_Lamda_NoField_Property = "NotGenerate_Lamda_NoField_Property"; + /// <summary> + /// Cached name for the 'NotGenerate_Complex_Return_Property' property. + /// </summary> + public new static readonly global::Godot.StringName NotGenerate_Complex_Return_Property = "NotGenerate_Complex_Return_Property"; + /// <summary> + /// Cached name for the 'NotGenerate_Returns_Property' property. + /// </summary> + public new static readonly global::Godot.StringName NotGenerate_Returns_Property = "NotGenerate_Returns_Property"; + /// <summary> + /// Cached name for the 'FullProperty_String' property. + /// </summary> + public new static readonly global::Godot.StringName FullProperty_String = "FullProperty_String"; + /// <summary> + /// Cached name for the 'FullProperty_String_Complex' property. + /// </summary> + public new static readonly global::Godot.StringName FullProperty_String_Complex = "FullProperty_String_Complex"; + /// <summary> + /// Cached name for the 'LamdaProperty_String' property. + /// </summary> + public new static readonly global::Godot.StringName LamdaProperty_String = "LamdaProperty_String"; + /// <summary> + /// Cached name for the 'property_Boolean' property. + /// </summary> + public new static readonly global::Godot.StringName property_Boolean = "property_Boolean"; + /// <summary> + /// Cached name for the 'property_Char' property. + /// </summary> + public new static readonly global::Godot.StringName property_Char = "property_Char"; + /// <summary> + /// Cached name for the 'property_SByte' property. + /// </summary> + public new static readonly global::Godot.StringName property_SByte = "property_SByte"; + /// <summary> + /// Cached name for the 'property_Int16' property. + /// </summary> + public new static readonly global::Godot.StringName property_Int16 = "property_Int16"; + /// <summary> + /// Cached name for the 'property_Int32' property. + /// </summary> + public new static readonly global::Godot.StringName property_Int32 = "property_Int32"; + /// <summary> + /// Cached name for the 'property_Int64' property. + /// </summary> + public new static readonly global::Godot.StringName property_Int64 = "property_Int64"; + /// <summary> + /// Cached name for the 'property_Byte' property. + /// </summary> + public new static readonly global::Godot.StringName property_Byte = "property_Byte"; + /// <summary> + /// Cached name for the 'property_UInt16' property. + /// </summary> + public new static readonly global::Godot.StringName property_UInt16 = "property_UInt16"; + /// <summary> + /// Cached name for the 'property_UInt32' property. + /// </summary> + public new static readonly global::Godot.StringName property_UInt32 = "property_UInt32"; + /// <summary> + /// Cached name for the 'property_UInt64' property. + /// </summary> + public new static readonly global::Godot.StringName property_UInt64 = "property_UInt64"; + /// <summary> + /// Cached name for the 'property_Single' property. + /// </summary> + public new static readonly global::Godot.StringName property_Single = "property_Single"; + /// <summary> + /// Cached name for the 'property_Double' property. + /// </summary> + public new static readonly global::Godot.StringName property_Double = "property_Double"; + /// <summary> + /// Cached name for the 'property_String' property. + /// </summary> + public new static readonly global::Godot.StringName property_String = "property_String"; + /// <summary> + /// Cached name for the 'property_Vector2' property. + /// </summary> + public new static readonly global::Godot.StringName property_Vector2 = "property_Vector2"; + /// <summary> + /// Cached name for the 'property_Vector2I' property. + /// </summary> + public new static readonly global::Godot.StringName property_Vector2I = "property_Vector2I"; + /// <summary> + /// Cached name for the 'property_Rect2' property. + /// </summary> + public new static readonly global::Godot.StringName property_Rect2 = "property_Rect2"; + /// <summary> + /// Cached name for the 'property_Rect2I' property. + /// </summary> + public new static readonly global::Godot.StringName property_Rect2I = "property_Rect2I"; + /// <summary> + /// Cached name for the 'property_Transform2D' property. + /// </summary> + public new static readonly global::Godot.StringName property_Transform2D = "property_Transform2D"; + /// <summary> + /// Cached name for the 'property_Vector3' property. + /// </summary> + public new static readonly global::Godot.StringName property_Vector3 = "property_Vector3"; + /// <summary> + /// Cached name for the 'property_Vector3I' property. + /// </summary> + public new static readonly global::Godot.StringName property_Vector3I = "property_Vector3I"; + /// <summary> + /// Cached name for the 'property_Basis' property. + /// </summary> + public new static readonly global::Godot.StringName property_Basis = "property_Basis"; + /// <summary> + /// Cached name for the 'property_Quaternion' property. + /// </summary> + public new static readonly global::Godot.StringName property_Quaternion = "property_Quaternion"; + /// <summary> + /// Cached name for the 'property_Transform3D' property. + /// </summary> + public new static readonly global::Godot.StringName property_Transform3D = "property_Transform3D"; + /// <summary> + /// Cached name for the 'property_Vector4' property. + /// </summary> + public new static readonly global::Godot.StringName property_Vector4 = "property_Vector4"; + /// <summary> + /// Cached name for the 'property_Vector4I' property. + /// </summary> + public new static readonly global::Godot.StringName property_Vector4I = "property_Vector4I"; + /// <summary> + /// Cached name for the 'property_Projection' property. + /// </summary> + public new static readonly global::Godot.StringName property_Projection = "property_Projection"; + /// <summary> + /// Cached name for the 'property_Aabb' property. + /// </summary> + public new static readonly global::Godot.StringName property_Aabb = "property_Aabb"; + /// <summary> + /// Cached name for the 'property_Color' property. + /// </summary> + public new static readonly global::Godot.StringName property_Color = "property_Color"; + /// <summary> + /// Cached name for the 'property_Plane' property. + /// </summary> + public new static readonly global::Godot.StringName property_Plane = "property_Plane"; + /// <summary> + /// Cached name for the 'property_Callable' property. + /// </summary> + public new static readonly global::Godot.StringName property_Callable = "property_Callable"; + /// <summary> + /// Cached name for the 'property_Signal' property. + /// </summary> + public new static readonly global::Godot.StringName property_Signal = "property_Signal"; + /// <summary> + /// Cached name for the 'property_Enum' property. + /// </summary> + public new static readonly global::Godot.StringName property_Enum = "property_Enum"; + /// <summary> + /// Cached name for the 'property_FlagsEnum' property. + /// </summary> + public new static readonly global::Godot.StringName property_FlagsEnum = "property_FlagsEnum"; + /// <summary> + /// Cached name for the 'property_ByteArray' property. + /// </summary> + public new static readonly global::Godot.StringName property_ByteArray = "property_ByteArray"; + /// <summary> + /// Cached name for the 'property_Int32Array' property. + /// </summary> + public new static readonly global::Godot.StringName property_Int32Array = "property_Int32Array"; + /// <summary> + /// Cached name for the 'property_Int64Array' property. + /// </summary> + public new static readonly global::Godot.StringName property_Int64Array = "property_Int64Array"; + /// <summary> + /// Cached name for the 'property_SingleArray' property. + /// </summary> + public new static readonly global::Godot.StringName property_SingleArray = "property_SingleArray"; + /// <summary> + /// Cached name for the 'property_DoubleArray' property. + /// </summary> + public new static readonly global::Godot.StringName property_DoubleArray = "property_DoubleArray"; + /// <summary> + /// Cached name for the 'property_StringArray' property. + /// </summary> + public new static readonly global::Godot.StringName property_StringArray = "property_StringArray"; + /// <summary> + /// Cached name for the 'property_StringArrayEnum' property. + /// </summary> + public new static readonly global::Godot.StringName property_StringArrayEnum = "property_StringArrayEnum"; + /// <summary> + /// Cached name for the 'property_Vector2Array' property. + /// </summary> + public new static readonly global::Godot.StringName property_Vector2Array = "property_Vector2Array"; + /// <summary> + /// Cached name for the 'property_Vector3Array' property. + /// </summary> + public new static readonly global::Godot.StringName property_Vector3Array = "property_Vector3Array"; + /// <summary> + /// Cached name for the 'property_ColorArray' property. + /// </summary> + public new static readonly global::Godot.StringName property_ColorArray = "property_ColorArray"; + /// <summary> + /// Cached name for the 'property_GodotObjectOrDerivedArray' property. + /// </summary> + public new static readonly global::Godot.StringName property_GodotObjectOrDerivedArray = "property_GodotObjectOrDerivedArray"; + /// <summary> + /// Cached name for the 'field_StringNameArray' property. + /// </summary> + public new static readonly global::Godot.StringName field_StringNameArray = "field_StringNameArray"; + /// <summary> + /// Cached name for the 'field_NodePathArray' property. + /// </summary> + public new static readonly global::Godot.StringName field_NodePathArray = "field_NodePathArray"; + /// <summary> + /// Cached name for the 'field_RidArray' property. + /// </summary> + public new static readonly global::Godot.StringName field_RidArray = "field_RidArray"; + /// <summary> + /// Cached name for the 'property_Variant' property. + /// </summary> + public new static readonly global::Godot.StringName property_Variant = "property_Variant"; + /// <summary> + /// Cached name for the 'property_GodotObjectOrDerived' property. + /// </summary> + public new static readonly global::Godot.StringName property_GodotObjectOrDerived = "property_GodotObjectOrDerived"; + /// <summary> + /// Cached name for the 'property_GodotResourceTexture' property. + /// </summary> + public new static readonly global::Godot.StringName property_GodotResourceTexture = "property_GodotResourceTexture"; + /// <summary> + /// Cached name for the 'property_StringName' property. + /// </summary> + public new static readonly global::Godot.StringName property_StringName = "property_StringName"; + /// <summary> + /// Cached name for the 'property_NodePath' property. + /// </summary> + public new static readonly global::Godot.StringName property_NodePath = "property_NodePath"; + /// <summary> + /// Cached name for the 'property_Rid' property. + /// </summary> + public new static readonly global::Godot.StringName property_Rid = "property_Rid"; + /// <summary> + /// Cached name for the 'property_GodotDictionary' property. + /// </summary> + public new static readonly global::Godot.StringName property_GodotDictionary = "property_GodotDictionary"; + /// <summary> + /// Cached name for the 'property_GodotArray' property. + /// </summary> + public new static readonly global::Godot.StringName property_GodotArray = "property_GodotArray"; + /// <summary> + /// Cached name for the 'property_GodotGenericDictionary' property. + /// </summary> + public new static readonly global::Godot.StringName property_GodotGenericDictionary = "property_GodotGenericDictionary"; + /// <summary> + /// Cached name for the 'property_GodotGenericArray' property. + /// </summary> + public new static readonly global::Godot.StringName property_GodotGenericArray = "property_GodotGenericArray"; + /// <summary> + /// Cached name for the '_notGenerate_Property_String' field. + /// </summary> + public new static readonly global::Godot.StringName _notGenerate_Property_String = "_notGenerate_Property_String"; + /// <summary> + /// Cached name for the '_notGenerate_Property_Int' field. + /// </summary> + public new static readonly global::Godot.StringName _notGenerate_Property_Int = "_notGenerate_Property_Int"; + /// <summary> + /// Cached name for the '_fullProperty_String' field. + /// </summary> + public new static readonly global::Godot.StringName _fullProperty_String = "_fullProperty_String"; + /// <summary> + /// Cached name for the '_fullProperty_String_Complex' field. + /// </summary> + public new static readonly global::Godot.StringName _fullProperty_String_Complex = "_fullProperty_String_Complex"; + /// <summary> + /// Cached name for the '_lamdaProperty_String' field. + /// </summary> + public new static readonly global::Godot.StringName _lamdaProperty_String = "_lamdaProperty_String"; + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value) + { + if (name == PropertyName.NotGenerate_Complex_Lamda_Property) { + this.NotGenerate_Complex_Lamda_Property = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + else if (name == PropertyName.NotGenerate_Lamda_NoField_Property) { + this.NotGenerate_Lamda_NoField_Property = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + else if (name == PropertyName.NotGenerate_Complex_Return_Property) { + this.NotGenerate_Complex_Return_Property = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + else if (name == PropertyName.NotGenerate_Returns_Property) { + this.NotGenerate_Returns_Property = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + else if (name == PropertyName.FullProperty_String) { + this.FullProperty_String = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + else if (name == PropertyName.FullProperty_String_Complex) { + this.FullProperty_String_Complex = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + else if (name == PropertyName.LamdaProperty_String) { + this.LamdaProperty_String = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + else if (name == PropertyName.property_Boolean) { + this.property_Boolean = global::Godot.NativeInterop.VariantUtils.ConvertTo<bool>(value); + return true; + } + else if (name == PropertyName.property_Char) { + this.property_Char = global::Godot.NativeInterop.VariantUtils.ConvertTo<char>(value); + return true; + } + else if (name == PropertyName.property_SByte) { + this.property_SByte = global::Godot.NativeInterop.VariantUtils.ConvertTo<sbyte>(value); + return true; + } + else if (name == PropertyName.property_Int16) { + this.property_Int16 = global::Godot.NativeInterop.VariantUtils.ConvertTo<short>(value); + return true; + } + else if (name == PropertyName.property_Int32) { + this.property_Int32 = global::Godot.NativeInterop.VariantUtils.ConvertTo<int>(value); + return true; + } + else if (name == PropertyName.property_Int64) { + this.property_Int64 = global::Godot.NativeInterop.VariantUtils.ConvertTo<long>(value); + return true; + } + else if (name == PropertyName.property_Byte) { + this.property_Byte = global::Godot.NativeInterop.VariantUtils.ConvertTo<byte>(value); + return true; + } + else if (name == PropertyName.property_UInt16) { + this.property_UInt16 = global::Godot.NativeInterop.VariantUtils.ConvertTo<ushort>(value); + return true; + } + else if (name == PropertyName.property_UInt32) { + this.property_UInt32 = global::Godot.NativeInterop.VariantUtils.ConvertTo<uint>(value); + return true; + } + else if (name == PropertyName.property_UInt64) { + this.property_UInt64 = global::Godot.NativeInterop.VariantUtils.ConvertTo<ulong>(value); + return true; + } + else if (name == PropertyName.property_Single) { + this.property_Single = global::Godot.NativeInterop.VariantUtils.ConvertTo<float>(value); + return true; + } + else if (name == PropertyName.property_Double) { + this.property_Double = global::Godot.NativeInterop.VariantUtils.ConvertTo<double>(value); + return true; + } + else if (name == PropertyName.property_String) { + this.property_String = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + else if (name == PropertyName.property_Vector2) { + this.property_Vector2 = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector2>(value); + return true; + } + else if (name == PropertyName.property_Vector2I) { + this.property_Vector2I = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector2I>(value); + return true; + } + else if (name == PropertyName.property_Rect2) { + this.property_Rect2 = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Rect2>(value); + return true; + } + else if (name == PropertyName.property_Rect2I) { + this.property_Rect2I = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Rect2I>(value); + return true; + } + else if (name == PropertyName.property_Transform2D) { + this.property_Transform2D = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Transform2D>(value); + return true; + } + else if (name == PropertyName.property_Vector3) { + this.property_Vector3 = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector3>(value); + return true; + } + else if (name == PropertyName.property_Vector3I) { + this.property_Vector3I = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector3I>(value); + return true; + } + else if (name == PropertyName.property_Basis) { + this.property_Basis = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Basis>(value); + return true; + } + else if (name == PropertyName.property_Quaternion) { + this.property_Quaternion = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Quaternion>(value); + return true; + } + else if (name == PropertyName.property_Transform3D) { + this.property_Transform3D = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Transform3D>(value); + return true; + } + else if (name == PropertyName.property_Vector4) { + this.property_Vector4 = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector4>(value); + return true; + } + else if (name == PropertyName.property_Vector4I) { + this.property_Vector4I = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector4I>(value); + return true; + } + else if (name == PropertyName.property_Projection) { + this.property_Projection = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Projection>(value); + return true; + } + else if (name == PropertyName.property_Aabb) { + this.property_Aabb = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Aabb>(value); + return true; + } + else if (name == PropertyName.property_Color) { + this.property_Color = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Color>(value); + return true; + } + else if (name == PropertyName.property_Plane) { + this.property_Plane = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Plane>(value); + return true; + } + else if (name == PropertyName.property_Callable) { + this.property_Callable = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Callable>(value); + return true; + } + else if (name == PropertyName.property_Signal) { + this.property_Signal = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Signal>(value); + return true; + } + else if (name == PropertyName.property_Enum) { + this.property_Enum = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::ExportedProperties.MyEnum>(value); + return true; + } + else if (name == PropertyName.property_FlagsEnum) { + this.property_FlagsEnum = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::ExportedProperties.MyFlagsEnum>(value); + return true; + } + else if (name == PropertyName.property_ByteArray) { + this.property_ByteArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<byte[]>(value); + return true; + } + else if (name == PropertyName.property_Int32Array) { + this.property_Int32Array = global::Godot.NativeInterop.VariantUtils.ConvertTo<int[]>(value); + return true; + } + else if (name == PropertyName.property_Int64Array) { + this.property_Int64Array = global::Godot.NativeInterop.VariantUtils.ConvertTo<long[]>(value); + return true; + } + else if (name == PropertyName.property_SingleArray) { + this.property_SingleArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<float[]>(value); + return true; + } + else if (name == PropertyName.property_DoubleArray) { + this.property_DoubleArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<double[]>(value); + return true; + } + else if (name == PropertyName.property_StringArray) { + this.property_StringArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<string[]>(value); + return true; + } + else if (name == PropertyName.property_StringArrayEnum) { + this.property_StringArrayEnum = global::Godot.NativeInterop.VariantUtils.ConvertTo<string[]>(value); + return true; + } + else if (name == PropertyName.property_Vector2Array) { + this.property_Vector2Array = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector2[]>(value); + return true; + } + else if (name == PropertyName.property_Vector3Array) { + this.property_Vector3Array = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Vector3[]>(value); + return true; + } + else if (name == PropertyName.property_ColorArray) { + this.property_ColorArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Color[]>(value); + return true; + } + else if (name == PropertyName.property_GodotObjectOrDerivedArray) { + this.property_GodotObjectOrDerivedArray = global::Godot.NativeInterop.VariantUtils.ConvertToSystemArrayOfGodotObject<global::Godot.GodotObject>(value); + return true; + } + else if (name == PropertyName.field_StringNameArray) { + this.field_StringNameArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.StringName[]>(value); + return true; + } + else if (name == PropertyName.field_NodePathArray) { + this.field_NodePathArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.NodePath[]>(value); + return true; + } + else if (name == PropertyName.field_RidArray) { + this.field_RidArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Rid[]>(value); + return true; + } + else if (name == PropertyName.property_Variant) { + this.property_Variant = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Variant>(value); + return true; + } + else if (name == PropertyName.property_GodotObjectOrDerived) { + this.property_GodotObjectOrDerived = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.GodotObject>(value); + return true; + } + else if (name == PropertyName.property_GodotResourceTexture) { + this.property_GodotResourceTexture = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Texture>(value); + return true; + } + else if (name == PropertyName.property_StringName) { + this.property_StringName = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.StringName>(value); + return true; + } + else if (name == PropertyName.property_NodePath) { + this.property_NodePath = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.NodePath>(value); + return true; + } + else if (name == PropertyName.property_Rid) { + this.property_Rid = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Rid>(value); + return true; + } + else if (name == PropertyName.property_GodotDictionary) { + this.property_GodotDictionary = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Collections.Dictionary>(value); + return true; + } + else if (name == PropertyName.property_GodotArray) { + this.property_GodotArray = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Collections.Array>(value); + return true; + } + else if (name == PropertyName.property_GodotGenericDictionary) { + this.property_GodotGenericDictionary = global::Godot.NativeInterop.VariantUtils.ConvertToDictionary<string, bool>(value); + return true; + } + else if (name == PropertyName.property_GodotGenericArray) { + this.property_GodotGenericArray = global::Godot.NativeInterop.VariantUtils.ConvertToArray<int>(value); + return true; + } + else if (name == PropertyName._notGenerate_Property_String) { + this._notGenerate_Property_String = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + else if (name == PropertyName._notGenerate_Property_Int) { + this._notGenerate_Property_Int = global::Godot.NativeInterop.VariantUtils.ConvertTo<int>(value); + return true; + } + else if (name == PropertyName._fullProperty_String) { + this._fullProperty_String = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + else if (name == PropertyName._fullProperty_String_Complex) { + this._fullProperty_String_Complex = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + else if (name == PropertyName._lamdaProperty_String) { + this._lamdaProperty_String = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value); + return true; + } + return base.SetGodotClassPropertyValue(name, value); + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value) + { + if (name == PropertyName.NotGenerate_Complex_Lamda_Property) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.NotGenerate_Complex_Lamda_Property); + return true; + } + else if (name == PropertyName.NotGenerate_Lamda_NoField_Property) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.NotGenerate_Lamda_NoField_Property); + return true; + } + else if (name == PropertyName.NotGenerate_Complex_Return_Property) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.NotGenerate_Complex_Return_Property); + return true; + } + else if (name == PropertyName.NotGenerate_Returns_Property) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.NotGenerate_Returns_Property); + return true; + } + else if (name == PropertyName.FullProperty_String) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.FullProperty_String); + return true; + } + else if (name == PropertyName.FullProperty_String_Complex) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.FullProperty_String_Complex); + return true; + } + else if (name == PropertyName.LamdaProperty_String) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.LamdaProperty_String); + return true; + } + else if (name == PropertyName.property_Boolean) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<bool>(this.property_Boolean); + return true; + } + else if (name == PropertyName.property_Char) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<char>(this.property_Char); + return true; + } + else if (name == PropertyName.property_SByte) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<sbyte>(this.property_SByte); + return true; + } + else if (name == PropertyName.property_Int16) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<short>(this.property_Int16); + return true; + } + else if (name == PropertyName.property_Int32) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<int>(this.property_Int32); + return true; + } + else if (name == PropertyName.property_Int64) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<long>(this.property_Int64); + return true; + } + else if (name == PropertyName.property_Byte) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<byte>(this.property_Byte); + return true; + } + else if (name == PropertyName.property_UInt16) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<ushort>(this.property_UInt16); + return true; + } + else if (name == PropertyName.property_UInt32) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<uint>(this.property_UInt32); + return true; + } + else if (name == PropertyName.property_UInt64) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<ulong>(this.property_UInt64); + return true; + } + else if (name == PropertyName.property_Single) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<float>(this.property_Single); + return true; + } + else if (name == PropertyName.property_Double) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<double>(this.property_Double); + return true; + } + else if (name == PropertyName.property_String) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.property_String); + return true; + } + else if (name == PropertyName.property_Vector2) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector2>(this.property_Vector2); + return true; + } + else if (name == PropertyName.property_Vector2I) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector2I>(this.property_Vector2I); + return true; + } + else if (name == PropertyName.property_Rect2) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Rect2>(this.property_Rect2); + return true; + } + else if (name == PropertyName.property_Rect2I) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Rect2I>(this.property_Rect2I); + return true; + } + else if (name == PropertyName.property_Transform2D) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Transform2D>(this.property_Transform2D); + return true; + } + else if (name == PropertyName.property_Vector3) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector3>(this.property_Vector3); + return true; + } + else if (name == PropertyName.property_Vector3I) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector3I>(this.property_Vector3I); + return true; + } + else if (name == PropertyName.property_Basis) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Basis>(this.property_Basis); + return true; + } + else if (name == PropertyName.property_Quaternion) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Quaternion>(this.property_Quaternion); + return true; + } + else if (name == PropertyName.property_Transform3D) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Transform3D>(this.property_Transform3D); + return true; + } + else if (name == PropertyName.property_Vector4) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector4>(this.property_Vector4); + return true; + } + else if (name == PropertyName.property_Vector4I) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector4I>(this.property_Vector4I); + return true; + } + else if (name == PropertyName.property_Projection) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Projection>(this.property_Projection); + return true; + } + else if (name == PropertyName.property_Aabb) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Aabb>(this.property_Aabb); + return true; + } + else if (name == PropertyName.property_Color) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Color>(this.property_Color); + return true; + } + else if (name == PropertyName.property_Plane) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Plane>(this.property_Plane); + return true; + } + else if (name == PropertyName.property_Callable) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Callable>(this.property_Callable); + return true; + } + else if (name == PropertyName.property_Signal) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Signal>(this.property_Signal); + return true; + } + else if (name == PropertyName.property_Enum) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::ExportedProperties.MyEnum>(this.property_Enum); + return true; + } + else if (name == PropertyName.property_FlagsEnum) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::ExportedProperties.MyFlagsEnum>(this.property_FlagsEnum); + return true; + } + else if (name == PropertyName.property_ByteArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<byte[]>(this.property_ByteArray); + return true; + } + else if (name == PropertyName.property_Int32Array) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<int[]>(this.property_Int32Array); + return true; + } + else if (name == PropertyName.property_Int64Array) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<long[]>(this.property_Int64Array); + return true; + } + else if (name == PropertyName.property_SingleArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<float[]>(this.property_SingleArray); + return true; + } + else if (name == PropertyName.property_DoubleArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<double[]>(this.property_DoubleArray); + return true; + } + else if (name == PropertyName.property_StringArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string[]>(this.property_StringArray); + return true; + } + else if (name == PropertyName.property_StringArrayEnum) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string[]>(this.property_StringArrayEnum); + return true; + } + else if (name == PropertyName.property_Vector2Array) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector2[]>(this.property_Vector2Array); + return true; + } + else if (name == PropertyName.property_Vector3Array) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Vector3[]>(this.property_Vector3Array); + return true; + } + else if (name == PropertyName.property_ColorArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Color[]>(this.property_ColorArray); + return true; + } + else if (name == PropertyName.property_GodotObjectOrDerivedArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFromSystemArrayOfGodotObject(this.property_GodotObjectOrDerivedArray); + return true; + } + else if (name == PropertyName.field_StringNameArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.StringName[]>(this.field_StringNameArray); + return true; + } + else if (name == PropertyName.field_NodePathArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.NodePath[]>(this.field_NodePathArray); + return true; + } + else if (name == PropertyName.field_RidArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Rid[]>(this.field_RidArray); + return true; + } + else if (name == PropertyName.property_Variant) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Variant>(this.property_Variant); + return true; + } + else if (name == PropertyName.property_GodotObjectOrDerived) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.GodotObject>(this.property_GodotObjectOrDerived); + return true; + } + else if (name == PropertyName.property_GodotResourceTexture) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Texture>(this.property_GodotResourceTexture); + return true; + } + else if (name == PropertyName.property_StringName) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.StringName>(this.property_StringName); + return true; + } + else if (name == PropertyName.property_NodePath) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.NodePath>(this.property_NodePath); + return true; + } + else if (name == PropertyName.property_Rid) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Rid>(this.property_Rid); + return true; + } + else if (name == PropertyName.property_GodotDictionary) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Collections.Dictionary>(this.property_GodotDictionary); + return true; + } + else if (name == PropertyName.property_GodotArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Collections.Array>(this.property_GodotArray); + return true; + } + else if (name == PropertyName.property_GodotGenericDictionary) { + value = global::Godot.NativeInterop.VariantUtils.CreateFromDictionary(this.property_GodotGenericDictionary); + return true; + } + else if (name == PropertyName.property_GodotGenericArray) { + value = global::Godot.NativeInterop.VariantUtils.CreateFromArray(this.property_GodotGenericArray); + return true; + } + else if (name == PropertyName._notGenerate_Property_String) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this._notGenerate_Property_String); + return true; + } + else if (name == PropertyName._notGenerate_Property_Int) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<int>(this._notGenerate_Property_Int); + return true; + } + else if (name == PropertyName._fullProperty_String) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this._fullProperty_String); + return true; + } + else if (name == PropertyName._fullProperty_String_Complex) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this._fullProperty_String_Complex); + return true; + } + else if (name == PropertyName._lamdaProperty_String) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this._lamdaProperty_String); + return true; + } + return base.GetGodotClassPropertyValue(name, out value); + } + /// <summary> + /// Get the property information for all the properties declared in this class. + /// This method is used by Godot to register the available properties in the editor. + /// Do not call this method. + /// </summary> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new static global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo> GetGodotPropertyList() + { + var properties = new global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>(); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName._notGenerate_Property_String, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.NotGenerate_Complex_Lamda_Property, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.NotGenerate_Lamda_NoField_Property, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.NotGenerate_Complex_Return_Property, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName._notGenerate_Property_Int, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.NotGenerate_Returns_Property, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName._fullProperty_String, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.FullProperty_String, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName._fullProperty_String_Complex, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.FullProperty_String_Complex, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName._lamdaProperty_String, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.LamdaProperty_String, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)1, name: PropertyName.property_Boolean, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.property_Char, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.property_SByte, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.property_Int16, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.property_Int32, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.property_Int64, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.property_Byte, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.property_UInt16, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.property_UInt32, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.property_UInt64, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)3, name: PropertyName.property_Single, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)3, name: PropertyName.property_Double, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.property_String, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)5, name: PropertyName.property_Vector2, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)6, name: PropertyName.property_Vector2I, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)7, name: PropertyName.property_Rect2, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)8, name: PropertyName.property_Rect2I, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)11, name: PropertyName.property_Transform2D, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)9, name: PropertyName.property_Vector3, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)10, name: PropertyName.property_Vector3I, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)17, name: PropertyName.property_Basis, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)15, name: PropertyName.property_Quaternion, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)18, name: PropertyName.property_Transform3D, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)12, name: PropertyName.property_Vector4, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)13, name: PropertyName.property_Vector4I, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)19, name: PropertyName.property_Projection, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)16, name: PropertyName.property_Aabb, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)20, name: PropertyName.property_Color, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)14, name: PropertyName.property_Plane, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)25, name: PropertyName.property_Callable, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)26, name: PropertyName.property_Signal, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.property_Enum, hint: (global::Godot.PropertyHint)2, hintString: "A,B,C", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.property_FlagsEnum, hint: (global::Godot.PropertyHint)6, hintString: "A:0,B:1,C:2", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)29, name: PropertyName.property_ByteArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)30, name: PropertyName.property_Int32Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)31, name: PropertyName.property_Int64Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)32, name: PropertyName.property_SingleArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)33, name: PropertyName.property_DoubleArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)34, name: PropertyName.property_StringArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)34, name: PropertyName.property_StringArrayEnum, hint: (global::Godot.PropertyHint)23, hintString: "4/2:A,B,C", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)35, name: PropertyName.property_Vector2Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)36, name: PropertyName.property_Vector3Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)37, name: PropertyName.property_ColorArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.property_GodotObjectOrDerivedArray, hint: (global::Godot.PropertyHint)23, hintString: "24/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.field_StringNameArray, hint: (global::Godot.PropertyHint)23, hintString: "21/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.field_NodePathArray, hint: (global::Godot.PropertyHint)23, hintString: "22/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.field_RidArray, hint: (global::Godot.PropertyHint)23, hintString: "23/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)0, name: PropertyName.property_Variant, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)135174, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)24, name: PropertyName.property_GodotObjectOrDerived, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)24, name: PropertyName.property_GodotResourceTexture, hint: (global::Godot.PropertyHint)17, hintString: "Texture", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)21, name: PropertyName.property_StringName, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)22, name: PropertyName.property_NodePath, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)23, name: PropertyName.property_Rid, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.property_GodotDictionary, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.property_GodotArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.property_GodotGenericDictionary, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.property_GodotGenericArray, hint: (global::Godot.PropertyHint)23, hintString: "2/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + return properties; + } +#pragma warning restore CS0109 +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptPropertyDefVal.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptPropertyDefVal.generated.cs new file mode 100644 index 0000000000..a4cd10d080 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptPropertyDefVal.generated.cs @@ -0,0 +1,147 @@ +partial class ExportedProperties +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword +#if TOOLS + /// <summary> + /// Get the default values for all properties declared in this class. + /// This method is used by Godot to determine the value that will be + /// used by the inspector when resetting properties. + /// Do not call this method. + /// </summary> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new static global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant> GetGodotPropertyDefaultValues() + { + var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(64); + string __NotGenerate_Complex_Lamda_Property_default_value = default; + values.Add(PropertyName.NotGenerate_Complex_Lamda_Property, global::Godot.Variant.From<string>(__NotGenerate_Complex_Lamda_Property_default_value)); + string __NotGenerate_Lamda_NoField_Property_default_value = default; + values.Add(PropertyName.NotGenerate_Lamda_NoField_Property, global::Godot.Variant.From<string>(__NotGenerate_Lamda_NoField_Property_default_value)); + string __NotGenerate_Complex_Return_Property_default_value = default; + values.Add(PropertyName.NotGenerate_Complex_Return_Property, global::Godot.Variant.From<string>(__NotGenerate_Complex_Return_Property_default_value)); + string __NotGenerate_Returns_Property_default_value = default; + values.Add(PropertyName.NotGenerate_Returns_Property, global::Godot.Variant.From<string>(__NotGenerate_Returns_Property_default_value)); + string __FullProperty_String_default_value = "FullProperty_String"; + values.Add(PropertyName.FullProperty_String, global::Godot.Variant.From<string>(__FullProperty_String_default_value)); + string __FullProperty_String_Complex_default_value = new string("FullProperty_String_Complex") + global::System.Convert.ToInt32("1"); + values.Add(PropertyName.FullProperty_String_Complex, global::Godot.Variant.From<string>(__FullProperty_String_Complex_default_value)); + string __LamdaProperty_String_default_value = "LamdaProperty_String"; + values.Add(PropertyName.LamdaProperty_String, global::Godot.Variant.From<string>(__LamdaProperty_String_default_value)); + bool __property_Boolean_default_value = true; + values.Add(PropertyName.property_Boolean, global::Godot.Variant.From<bool>(__property_Boolean_default_value)); + char __property_Char_default_value = 'f'; + values.Add(PropertyName.property_Char, global::Godot.Variant.From<char>(__property_Char_default_value)); + sbyte __property_SByte_default_value = 10; + values.Add(PropertyName.property_SByte, global::Godot.Variant.From<sbyte>(__property_SByte_default_value)); + short __property_Int16_default_value = 10; + values.Add(PropertyName.property_Int16, global::Godot.Variant.From<short>(__property_Int16_default_value)); + int __property_Int32_default_value = 10; + values.Add(PropertyName.property_Int32, global::Godot.Variant.From<int>(__property_Int32_default_value)); + long __property_Int64_default_value = 10; + values.Add(PropertyName.property_Int64, global::Godot.Variant.From<long>(__property_Int64_default_value)); + byte __property_Byte_default_value = 10; + values.Add(PropertyName.property_Byte, global::Godot.Variant.From<byte>(__property_Byte_default_value)); + ushort __property_UInt16_default_value = 10; + values.Add(PropertyName.property_UInt16, global::Godot.Variant.From<ushort>(__property_UInt16_default_value)); + uint __property_UInt32_default_value = 10; + values.Add(PropertyName.property_UInt32, global::Godot.Variant.From<uint>(__property_UInt32_default_value)); + ulong __property_UInt64_default_value = 10; + values.Add(PropertyName.property_UInt64, global::Godot.Variant.From<ulong>(__property_UInt64_default_value)); + float __property_Single_default_value = 10; + values.Add(PropertyName.property_Single, global::Godot.Variant.From<float>(__property_Single_default_value)); + double __property_Double_default_value = 10; + values.Add(PropertyName.property_Double, global::Godot.Variant.From<double>(__property_Double_default_value)); + string __property_String_default_value = "foo"; + values.Add(PropertyName.property_String, global::Godot.Variant.From<string>(__property_String_default_value)); + global::Godot.Vector2 __property_Vector2_default_value = new(10f, 10f); + values.Add(PropertyName.property_Vector2, global::Godot.Variant.From<global::Godot.Vector2>(__property_Vector2_default_value)); + global::Godot.Vector2I __property_Vector2I_default_value = global::Godot.Vector2I.Up; + values.Add(PropertyName.property_Vector2I, global::Godot.Variant.From<global::Godot.Vector2I>(__property_Vector2I_default_value)); + global::Godot.Rect2 __property_Rect2_default_value = new(new global::Godot.Vector2(10f, 10f), new global::Godot.Vector2(10f, 10f)); + values.Add(PropertyName.property_Rect2, global::Godot.Variant.From<global::Godot.Rect2>(__property_Rect2_default_value)); + global::Godot.Rect2I __property_Rect2I_default_value = new(new global::Godot.Vector2I(10, 10), new global::Godot.Vector2I(10, 10)); + values.Add(PropertyName.property_Rect2I, global::Godot.Variant.From<global::Godot.Rect2I>(__property_Rect2I_default_value)); + global::Godot.Transform2D __property_Transform2D_default_value = global::Godot.Transform2D.Identity; + values.Add(PropertyName.property_Transform2D, global::Godot.Variant.From<global::Godot.Transform2D>(__property_Transform2D_default_value)); + global::Godot.Vector3 __property_Vector3_default_value = new(10f, 10f, 10f); + values.Add(PropertyName.property_Vector3, global::Godot.Variant.From<global::Godot.Vector3>(__property_Vector3_default_value)); + global::Godot.Vector3I __property_Vector3I_default_value = global::Godot.Vector3I.Back; + values.Add(PropertyName.property_Vector3I, global::Godot.Variant.From<global::Godot.Vector3I>(__property_Vector3I_default_value)); + global::Godot.Basis __property_Basis_default_value = new global::Godot.Basis(global::Godot.Quaternion.Identity); + values.Add(PropertyName.property_Basis, global::Godot.Variant.From<global::Godot.Basis>(__property_Basis_default_value)); + global::Godot.Quaternion __property_Quaternion_default_value = new global::Godot.Quaternion(global::Godot.Basis.Identity); + values.Add(PropertyName.property_Quaternion, global::Godot.Variant.From<global::Godot.Quaternion>(__property_Quaternion_default_value)); + global::Godot.Transform3D __property_Transform3D_default_value = global::Godot.Transform3D.Identity; + values.Add(PropertyName.property_Transform3D, global::Godot.Variant.From<global::Godot.Transform3D>(__property_Transform3D_default_value)); + global::Godot.Vector4 __property_Vector4_default_value = new(10f, 10f, 10f, 10f); + values.Add(PropertyName.property_Vector4, global::Godot.Variant.From<global::Godot.Vector4>(__property_Vector4_default_value)); + global::Godot.Vector4I __property_Vector4I_default_value = global::Godot.Vector4I.One; + values.Add(PropertyName.property_Vector4I, global::Godot.Variant.From<global::Godot.Vector4I>(__property_Vector4I_default_value)); + global::Godot.Projection __property_Projection_default_value = global::Godot.Projection.Identity; + values.Add(PropertyName.property_Projection, global::Godot.Variant.From<global::Godot.Projection>(__property_Projection_default_value)); + global::Godot.Aabb __property_Aabb_default_value = new global::Godot.Aabb(10f, 10f, 10f, new global::Godot.Vector3(1f, 1f, 1f)); + values.Add(PropertyName.property_Aabb, global::Godot.Variant.From<global::Godot.Aabb>(__property_Aabb_default_value)); + global::Godot.Color __property_Color_default_value = global::Godot.Colors.Aquamarine; + values.Add(PropertyName.property_Color, global::Godot.Variant.From<global::Godot.Color>(__property_Color_default_value)); + global::Godot.Plane __property_Plane_default_value = global::Godot.Plane.PlaneXZ; + values.Add(PropertyName.property_Plane, global::Godot.Variant.From<global::Godot.Plane>(__property_Plane_default_value)); + global::Godot.Callable __property_Callable_default_value = new global::Godot.Callable(global::Godot.Engine.GetMainLoop(), "_process"); + values.Add(PropertyName.property_Callable, global::Godot.Variant.From<global::Godot.Callable>(__property_Callable_default_value)); + global::Godot.Signal __property_Signal_default_value = new global::Godot.Signal(global::Godot.Engine.GetMainLoop(), "property_list_changed"); + values.Add(PropertyName.property_Signal, global::Godot.Variant.From<global::Godot.Signal>(__property_Signal_default_value)); + global::ExportedProperties.MyEnum __property_Enum_default_value = global::ExportedProperties.MyEnum.C; + values.Add(PropertyName.property_Enum, global::Godot.Variant.From<global::ExportedProperties.MyEnum>(__property_Enum_default_value)); + global::ExportedProperties.MyFlagsEnum __property_FlagsEnum_default_value = global::ExportedProperties.MyFlagsEnum.C; + values.Add(PropertyName.property_FlagsEnum, global::Godot.Variant.From<global::ExportedProperties.MyFlagsEnum>(__property_FlagsEnum_default_value)); + byte[] __property_ByteArray_default_value = { 0, 1, 2, 3, 4, 5, 6 }; + values.Add(PropertyName.property_ByteArray, global::Godot.Variant.From<byte[]>(__property_ByteArray_default_value)); + int[] __property_Int32Array_default_value = { 0, 1, 2, 3, 4, 5, 6 }; + values.Add(PropertyName.property_Int32Array, global::Godot.Variant.From<int[]>(__property_Int32Array_default_value)); + long[] __property_Int64Array_default_value = { 0, 1, 2, 3, 4, 5, 6 }; + values.Add(PropertyName.property_Int64Array, global::Godot.Variant.From<long[]>(__property_Int64Array_default_value)); + float[] __property_SingleArray_default_value = { 0f, 1f, 2f, 3f, 4f, 5f, 6f }; + values.Add(PropertyName.property_SingleArray, global::Godot.Variant.From<float[]>(__property_SingleArray_default_value)); + double[] __property_DoubleArray_default_value = { 0d, 1d, 2d, 3d, 4d, 5d, 6d }; + values.Add(PropertyName.property_DoubleArray, global::Godot.Variant.From<double[]>(__property_DoubleArray_default_value)); + string[] __property_StringArray_default_value = { "foo", "bar" }; + values.Add(PropertyName.property_StringArray, global::Godot.Variant.From<string[]>(__property_StringArray_default_value)); + string[] __property_StringArrayEnum_default_value = { "foo", "bar" }; + values.Add(PropertyName.property_StringArrayEnum, global::Godot.Variant.From<string[]>(__property_StringArrayEnum_default_value)); + global::Godot.Vector2[] __property_Vector2Array_default_value = { global::Godot.Vector2.Up, global::Godot.Vector2.Down, global::Godot.Vector2.Left, global::Godot.Vector2.Right }; + values.Add(PropertyName.property_Vector2Array, global::Godot.Variant.From<global::Godot.Vector2[]>(__property_Vector2Array_default_value)); + global::Godot.Vector3[] __property_Vector3Array_default_value = { global::Godot.Vector3.Up, global::Godot.Vector3.Down, global::Godot.Vector3.Left, global::Godot.Vector3.Right }; + values.Add(PropertyName.property_Vector3Array, global::Godot.Variant.From<global::Godot.Vector3[]>(__property_Vector3Array_default_value)); + global::Godot.Color[] __property_ColorArray_default_value = { global::Godot.Colors.Aqua, global::Godot.Colors.Aquamarine, global::Godot.Colors.Azure, global::Godot.Colors.Beige }; + values.Add(PropertyName.property_ColorArray, global::Godot.Variant.From<global::Godot.Color[]>(__property_ColorArray_default_value)); + global::Godot.GodotObject[] __property_GodotObjectOrDerivedArray_default_value = { null }; + values.Add(PropertyName.property_GodotObjectOrDerivedArray, global::Godot.Variant.CreateFrom(__property_GodotObjectOrDerivedArray_default_value)); + global::Godot.StringName[] __field_StringNameArray_default_value = { "foo", "bar" }; + values.Add(PropertyName.field_StringNameArray, global::Godot.Variant.From<global::Godot.StringName[]>(__field_StringNameArray_default_value)); + global::Godot.NodePath[] __field_NodePathArray_default_value = { "foo", "bar" }; + values.Add(PropertyName.field_NodePathArray, global::Godot.Variant.From<global::Godot.NodePath[]>(__field_NodePathArray_default_value)); + global::Godot.Rid[] __field_RidArray_default_value = { default, default, default }; + values.Add(PropertyName.field_RidArray, global::Godot.Variant.From<global::Godot.Rid[]>(__field_RidArray_default_value)); + global::Godot.Variant __property_Variant_default_value = "foo"; + values.Add(PropertyName.property_Variant, global::Godot.Variant.From<global::Godot.Variant>(__property_Variant_default_value)); + global::Godot.GodotObject __property_GodotObjectOrDerived_default_value = default; + values.Add(PropertyName.property_GodotObjectOrDerived, global::Godot.Variant.From<global::Godot.GodotObject>(__property_GodotObjectOrDerived_default_value)); + global::Godot.Texture __property_GodotResourceTexture_default_value = default; + values.Add(PropertyName.property_GodotResourceTexture, global::Godot.Variant.From<global::Godot.Texture>(__property_GodotResourceTexture_default_value)); + global::Godot.StringName __property_StringName_default_value = new global::Godot.StringName("foo"); + values.Add(PropertyName.property_StringName, global::Godot.Variant.From<global::Godot.StringName>(__property_StringName_default_value)); + global::Godot.NodePath __property_NodePath_default_value = new global::Godot.NodePath("foo"); + values.Add(PropertyName.property_NodePath, global::Godot.Variant.From<global::Godot.NodePath>(__property_NodePath_default_value)); + global::Godot.Rid __property_Rid_default_value = default; + values.Add(PropertyName.property_Rid, global::Godot.Variant.From<global::Godot.Rid>(__property_Rid_default_value)); + global::Godot.Collections.Dictionary __property_GodotDictionary_default_value = new() { { "foo", 10 }, { global::Godot.Vector2.Up, global::Godot.Colors.Chocolate } }; + values.Add(PropertyName.property_GodotDictionary, global::Godot.Variant.From<global::Godot.Collections.Dictionary>(__property_GodotDictionary_default_value)); + global::Godot.Collections.Array __property_GodotArray_default_value = new() { "foo", 10, global::Godot.Vector2.Up, global::Godot.Colors.Chocolate }; + values.Add(PropertyName.property_GodotArray, global::Godot.Variant.From<global::Godot.Collections.Array>(__property_GodotArray_default_value)); + global::Godot.Collections.Dictionary<string, bool> __property_GodotGenericDictionary_default_value = new() { { "foo", true }, { "bar", false } }; + values.Add(PropertyName.property_GodotGenericDictionary, global::Godot.Variant.CreateFrom(__property_GodotGenericDictionary_default_value)); + global::Godot.Collections.Array<int> __property_GodotGenericArray_default_value = new() { 0, 1, 2, 3, 4, 5, 6 }; + values.Add(PropertyName.property_GodotGenericArray, global::Godot.Variant.CreateFrom(__property_GodotGenericArray_default_value)); + return values; + } +#endif // TOOLS +#pragma warning restore CS0109 +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Foo_ScriptPath.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Foo_ScriptPath.generated.cs new file mode 100644 index 0000000000..9092ad9938 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Foo_ScriptPath.generated.cs @@ -0,0 +1,5 @@ +using Godot; +[ScriptPathAttribute("res://Foo.cs")] +partial class Foo +{ +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic_ScriptPath.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic_ScriptPath.generated.cs new file mode 100644 index 0000000000..72c48595a2 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic_ScriptPath.generated.cs @@ -0,0 +1,5 @@ +using Godot; +[ScriptPathAttribute("res://Generic.cs")] +partial class Generic +{ +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Methods_ScriptMethods.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Methods_ScriptMethods.generated.cs new file mode 100644 index 0000000000..f757497618 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Methods_ScriptMethods.generated.cs @@ -0,0 +1,61 @@ +using Godot; +using Godot.NativeInterop; + +partial class Methods +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword + /// <summary> + /// Cached StringNames for the methods contained in this class, for fast lookup. + /// </summary> + public new class MethodName : global::Godot.GodotObject.MethodName { + /// <summary> + /// Cached name for the 'MethodWithOverload' method. + /// </summary> + public new static readonly global::Godot.StringName MethodWithOverload = "MethodWithOverload"; + } + /// <summary> + /// Get the method information for all the methods declared in this class. + /// This method is used by Godot to register the available methods in the editor. + /// Do not call this method. + /// </summary> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new static global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo> GetGodotMethodList() + { + var methods = new global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>(3); + methods.Add(new(name: MethodName.MethodWithOverload, returnVal: new(type: (global::Godot.Variant.Type)0, name: "", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), flags: (global::Godot.MethodFlags)1, arguments: null, defaultArguments: null)); + methods.Add(new(name: MethodName.MethodWithOverload, returnVal: new(type: (global::Godot.Variant.Type)0, name: "", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), flags: (global::Godot.MethodFlags)1, arguments: new() { new(type: (global::Godot.Variant.Type)2, name: "a", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), }, defaultArguments: null)); + methods.Add(new(name: MethodName.MethodWithOverload, returnVal: new(type: (global::Godot.Variant.Type)0, name: "", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), flags: (global::Godot.MethodFlags)1, arguments: new() { new(type: (global::Godot.Variant.Type)2, name: "a", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), new(type: (global::Godot.Variant.Type)2, name: "b", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), }, defaultArguments: null)); + return methods; + } +#pragma warning restore CS0109 + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool InvokeGodotClassMethod(in godot_string_name method, NativeVariantPtrArgs args, out godot_variant ret) + { + if (method == MethodName.MethodWithOverload && args.Count == 0) { + MethodWithOverload(); + ret = default; + return true; + } + if (method == MethodName.MethodWithOverload && args.Count == 1) { + MethodWithOverload(global::Godot.NativeInterop.VariantUtils.ConvertTo<int>(args[0])); + ret = default; + return true; + } + if (method == MethodName.MethodWithOverload && args.Count == 2) { + MethodWithOverload(global::Godot.NativeInterop.VariantUtils.ConvertTo<int>(args[0]), global::Godot.NativeInterop.VariantUtils.ConvertTo<int>(args[1])); + ret = default; + return true; + } + return base.InvokeGodotClassMethod(method, args, out ret); + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool HasGodotClassMethod(in godot_string_name method) + { + if (method == MethodName.MethodWithOverload) { + return true; + } + return base.HasGodotClassMethod(method); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/MixedReadOnlyWriteOnly_ScriptProperties.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/MixedReadOnlyWriteOnly_ScriptProperties.generated.cs new file mode 100644 index 0000000000..f812457aa5 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/MixedReadOnlyWriteOnly_ScriptProperties.generated.cs @@ -0,0 +1,94 @@ +using Godot; +using Godot.NativeInterop; + +partial class MixedReadOnlyWriteOnly +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword + /// <summary> + /// Cached StringNames for the properties and fields contained in this class, for fast lookup. + /// </summary> + public new class PropertyName : global::Godot.GodotObject.PropertyName { + /// <summary> + /// Cached name for the 'readonly_auto_property' property. + /// </summary> + public new static readonly global::Godot.StringName readonly_auto_property = "readonly_auto_property"; + /// <summary> + /// Cached name for the 'readonly_property' property. + /// </summary> + public new static readonly global::Godot.StringName readonly_property = "readonly_property"; + /// <summary> + /// Cached name for the 'initonly_auto_property' property. + /// </summary> + public new static readonly global::Godot.StringName initonly_auto_property = "initonly_auto_property"; + /// <summary> + /// Cached name for the 'writeonly_property' property. + /// </summary> + public new static readonly global::Godot.StringName writeonly_property = "writeonly_property"; + /// <summary> + /// Cached name for the 'readonly_field' field. + /// </summary> + public new static readonly global::Godot.StringName readonly_field = "readonly_field"; + /// <summary> + /// Cached name for the 'writeonly_backing_field' field. + /// </summary> + public new static readonly global::Godot.StringName writeonly_backing_field = "writeonly_backing_field"; + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value) + { + if (name == PropertyName.writeonly_property) { + this.writeonly_property = global::Godot.NativeInterop.VariantUtils.ConvertTo<bool>(value); + return true; + } + else if (name == PropertyName.writeonly_backing_field) { + this.writeonly_backing_field = global::Godot.NativeInterop.VariantUtils.ConvertTo<bool>(value); + return true; + } + return base.SetGodotClassPropertyValue(name, value); + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value) + { + if (name == PropertyName.readonly_auto_property) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.readonly_auto_property); + return true; + } + else if (name == PropertyName.readonly_property) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.readonly_property); + return true; + } + else if (name == PropertyName.initonly_auto_property) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.initonly_auto_property); + return true; + } + else if (name == PropertyName.readonly_field) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.readonly_field); + return true; + } + else if (name == PropertyName.writeonly_backing_field) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<bool>(this.writeonly_backing_field); + return true; + } + return base.GetGodotClassPropertyValue(name, out value); + } + /// <summary> + /// Get the property information for all the properties declared in this class. + /// This method is used by Godot to register the available properties in the editor. + /// Do not call this method. + /// </summary> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new static global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo> GetGodotPropertyList() + { + var properties = new global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>(); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.readonly_field, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.readonly_auto_property, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.readonly_property, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.initonly_auto_property, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)1, name: PropertyName.writeonly_backing_field, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)1, name: PropertyName.writeonly_property, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + return properties; + } +#pragma warning restore CS0109 +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/OuterClass.NestedClass_ScriptMethods.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/OuterClass.NestedClass_ScriptMethods.generated.cs new file mode 100644 index 0000000000..328107a237 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/OuterClass.NestedClass_ScriptMethods.generated.cs @@ -0,0 +1,52 @@ +using Godot; +using Godot.NativeInterop; + +partial struct OuterClass +{ +partial class NestedClass +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword + /// <summary> + /// Cached StringNames for the methods contained in this class, for fast lookup. + /// </summary> + public new class MethodName : global::Godot.RefCounted.MethodName { + /// <summary> + /// Cached name for the '_Get' method. + /// </summary> + public new static readonly global::Godot.StringName _Get = "_Get"; + } + /// <summary> + /// Get the method information for all the methods declared in this class. + /// This method is used by Godot to register the available methods in the editor. + /// Do not call this method. + /// </summary> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new static global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo> GetGodotMethodList() + { + var methods = new global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>(1); + methods.Add(new(name: MethodName._Get, returnVal: new(type: (global::Godot.Variant.Type)0, name: "", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)131078, exported: false), flags: (global::Godot.MethodFlags)1, arguments: new() { new(type: (global::Godot.Variant.Type)21, name: "property", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), }, defaultArguments: null)); + return methods; + } +#pragma warning restore CS0109 + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool InvokeGodotClassMethod(in godot_string_name method, NativeVariantPtrArgs args, out godot_variant ret) + { + if (method == MethodName._Get && args.Count == 1) { + var callRet = _Get(global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.StringName>(args[0])); + ret = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Variant>(callRet); + return true; + } + return base.InvokeGodotClassMethod(method, args, out ret); + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool HasGodotClassMethod(in godot_string_name method) + { + if (method == MethodName._Get) { + return true; + } + return base.HasGodotClassMethod(method); + } +} +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/OuterClass.NestedClass_ScriptProperties.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/OuterClass.NestedClass_ScriptProperties.generated.cs new file mode 100644 index 0000000000..79f014a41e --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/OuterClass.NestedClass_ScriptProperties.generated.cs @@ -0,0 +1,15 @@ +using Godot; +using Godot.NativeInterop; + +partial struct OuterClass +{ +partial class NestedClass +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword + /// <summary> + /// Cached StringNames for the properties and fields contained in this class, for fast lookup. + /// </summary> + public new class PropertyName : global::Godot.RefCounted.PropertyName { + } +} +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/OuterClass.NestedClass_ScriptSerialization.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/OuterClass.NestedClass_ScriptSerialization.generated.cs new file mode 100644 index 0000000000..4ecce50178 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/OuterClass.NestedClass_ScriptSerialization.generated.cs @@ -0,0 +1,21 @@ +using Godot; +using Godot.NativeInterop; + +partial struct OuterClass +{ +partial class NestedClass +{ + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override void SaveGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info) + { + base.SaveGodotObjectData(info); + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override void RestoreGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info) + { + base.RestoreGodotObjectData(info); + } +} +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ScriptBoilerplate_ScriptMethods.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ScriptBoilerplate_ScriptMethods.generated.cs new file mode 100644 index 0000000000..8656f4617e --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ScriptBoilerplate_ScriptMethods.generated.cs @@ -0,0 +1,62 @@ +using Godot; +using Godot.NativeInterop; + +partial class ScriptBoilerplate +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword + /// <summary> + /// Cached StringNames for the methods contained in this class, for fast lookup. + /// </summary> + public new class MethodName : global::Godot.Node.MethodName { + /// <summary> + /// Cached name for the '_Process' method. + /// </summary> + public new static readonly global::Godot.StringName _Process = "_Process"; + /// <summary> + /// Cached name for the 'Bazz' method. + /// </summary> + public new static readonly global::Godot.StringName Bazz = "Bazz"; + } + /// <summary> + /// Get the method information for all the methods declared in this class. + /// This method is used by Godot to register the available methods in the editor. + /// Do not call this method. + /// </summary> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new static global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo> GetGodotMethodList() + { + var methods = new global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>(2); + methods.Add(new(name: MethodName._Process, returnVal: new(type: (global::Godot.Variant.Type)0, name: "", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), flags: (global::Godot.MethodFlags)1, arguments: new() { new(type: (global::Godot.Variant.Type)3, name: "delta", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), }, defaultArguments: null)); + methods.Add(new(name: MethodName.Bazz, returnVal: new(type: (global::Godot.Variant.Type)2, name: "", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), flags: (global::Godot.MethodFlags)1, arguments: new() { new(type: (global::Godot.Variant.Type)21, name: "name", hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)6, exported: false), }, defaultArguments: null)); + return methods; + } +#pragma warning restore CS0109 + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool InvokeGodotClassMethod(in godot_string_name method, NativeVariantPtrArgs args, out godot_variant ret) + { + if (method == MethodName._Process && args.Count == 1) { + _Process(global::Godot.NativeInterop.VariantUtils.ConvertTo<double>(args[0])); + ret = default; + return true; + } + if (method == MethodName.Bazz && args.Count == 1) { + var callRet = Bazz(global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.StringName>(args[0])); + ret = global::Godot.NativeInterop.VariantUtils.CreateFrom<int>(callRet); + return true; + } + return base.InvokeGodotClassMethod(method, args, out ret); + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool HasGodotClassMethod(in godot_string_name method) + { + if (method == MethodName._Process) { + return true; + } + else if (method == MethodName.Bazz) { + return true; + } + return base.HasGodotClassMethod(method); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ScriptBoilerplate_ScriptPath.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ScriptBoilerplate_ScriptPath.generated.cs new file mode 100644 index 0000000000..ffcd29f7cd --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ScriptBoilerplate_ScriptPath.generated.cs @@ -0,0 +1,5 @@ +using Godot; +[ScriptPathAttribute("res://ScriptBoilerplate.cs")] +partial class ScriptBoilerplate +{ +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ScriptBoilerplate_ScriptProperties.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ScriptBoilerplate_ScriptProperties.generated.cs new file mode 100644 index 0000000000..09368b7ab6 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ScriptBoilerplate_ScriptProperties.generated.cs @@ -0,0 +1,62 @@ +using Godot; +using Godot.NativeInterop; + +partial class ScriptBoilerplate +{ +#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword + /// <summary> + /// Cached StringNames for the properties and fields contained in this class, for fast lookup. + /// </summary> + public new class PropertyName : global::Godot.Node.PropertyName { + /// <summary> + /// Cached name for the '_nodePath' field. + /// </summary> + public new static readonly global::Godot.StringName _nodePath = "_nodePath"; + /// <summary> + /// Cached name for the '_velocity' field. + /// </summary> + public new static readonly global::Godot.StringName _velocity = "_velocity"; + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value) + { + if (name == PropertyName._nodePath) { + this._nodePath = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.NodePath>(value); + return true; + } + else if (name == PropertyName._velocity) { + this._velocity = global::Godot.NativeInterop.VariantUtils.ConvertTo<int>(value); + return true; + } + return base.SetGodotClassPropertyValue(name, value); + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value) + { + if (name == PropertyName._nodePath) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.NodePath>(this._nodePath); + return true; + } + else if (name == PropertyName._velocity) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom<int>(this._velocity); + return true; + } + return base.GetGodotClassPropertyValue(name, out value); + } + /// <summary> + /// Get the property information for all the properties declared in this class. + /// This method is used by Godot to register the available properties in the editor. + /// Do not call this method. + /// </summary> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new static global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo> GetGodotPropertyList() + { + var properties = new global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>(); + properties.Add(new(type: (global::Godot.Variant.Type)22, name: PropertyName._nodePath, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName._velocity, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); + return properties; + } +#pragma warning restore CS0109 +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ScriptBoilerplate_ScriptSerialization.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ScriptBoilerplate_ScriptSerialization.generated.cs new file mode 100644 index 0000000000..28bc863b0a --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ScriptBoilerplate_ScriptSerialization.generated.cs @@ -0,0 +1,24 @@ +using Godot; +using Godot.NativeInterop; + +partial class ScriptBoilerplate +{ + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override void SaveGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info) + { + base.SaveGodotObjectData(info); + info.AddProperty(PropertyName._nodePath, global::Godot.Variant.From<global::Godot.NodePath>(this._nodePath)); + info.AddProperty(PropertyName._velocity, global::Godot.Variant.From<int>(this._velocity)); + } + /// <inheritdoc/> + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + protected override void RestoreGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info) + { + base.RestoreGodotObjectData(info); + if (info.TryGetProperty(PropertyName._nodePath, out var _value__nodePath)) + this._nodePath = _value__nodePath.As<global::Godot.NodePath>(); + if (info.TryGetProperty(PropertyName._velocity, out var _value__velocity)) + this._velocity = _value__velocity.As<int>(); + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/AllReadOnly.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/AllReadOnly.cs new file mode 100644 index 0000000000..94c2bda363 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/AllReadOnly.cs @@ -0,0 +1,9 @@ +using Godot; + +public partial class AllReadOnly : GodotObject +{ + public readonly string readonly_field = "foo"; + public string readonly_auto_property { get; } = "foo"; + public string readonly_property { get => "foo"; } + public string initonly_auto_property { get; init; } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/AllWriteOnly.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/AllWriteOnly.cs new file mode 100644 index 0000000000..156d6bb6a5 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/AllWriteOnly.cs @@ -0,0 +1,7 @@ +using Godot; + +public partial class AllWriteOnly : GodotObject +{ + bool writeonly_backing_field = false; + public bool writeonly_property { set => writeonly_backing_field = value; } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Bar.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Bar.cs new file mode 100644 index 0000000000..dfe2217c26 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Bar.cs @@ -0,0 +1,14 @@ +using Godot; + +partial class Bar : GodotObject +{ +} + +// Foo in another file +partial class Foo +{ +} + +partial class NotSameNameAsFile : GodotObject +{ +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/EventSignals.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/EventSignals.cs new file mode 100644 index 0000000000..160c5d193d --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/EventSignals.cs @@ -0,0 +1,7 @@ +using Godot; + +public partial class EventSignals : GodotObject +{ + [Signal] + public delegate void MySignalEventHandler(string str, int num); +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedFields.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedFields.cs new file mode 100644 index 0000000000..09d654ffcb --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedFields.cs @@ -0,0 +1,102 @@ +using Godot; +using System; +using System.Collections.Generic; + +public partial class ExportedFields : GodotObject +{ + [Export] private Boolean field_Boolean = true; + [Export] private Char field_Char = 'f'; + [Export] private SByte field_SByte = 10; + [Export] private Int16 field_Int16 = 10; + [Export] private Int32 field_Int32 = 10; + [Export] private Int64 field_Int64 = 10; + [Export] private Byte field_Byte = 10; + [Export] private UInt16 field_UInt16 = 10; + [Export] private UInt32 field_UInt32 = 10; + [Export] private UInt64 field_UInt64 = 10; + [Export] private Single field_Single = 10; + [Export] private Double field_Double = 10; + [Export] private String field_String = "foo"; + + // Godot structs + [Export] private Vector2 field_Vector2 = new(10f, 10f); + [Export] private Vector2I field_Vector2I = Vector2I.Up; + [Export] private Rect2 field_Rect2 = new(new Vector2(10f, 10f), new Vector2(10f, 10f)); + [Export] private Rect2I field_Rect2I = new(new Vector2I(10, 10), new Vector2I(10, 10)); + [Export] private Transform2D field_Transform2D = Transform2D.Identity; + [Export] private Vector3 field_Vector3 = new(10f, 10f, 10f); + [Export] private Vector3I field_Vector3I = Vector3I.Back; + [Export] private Basis field_Basis = new Basis(Quaternion.Identity); + [Export] private Quaternion field_Quaternion = new Quaternion(Basis.Identity); + [Export] private Transform3D field_Transform3D = Transform3D.Identity; + [Export] private Vector4 field_Vector4 = new(10f, 10f, 10f, 10f); + [Export] private Vector4I field_Vector4I = Vector4I.One; + [Export] private Projection field_Projection = Projection.Identity; + [Export] private Aabb field_Aabb = new Aabb(10f, 10f, 10f, new Vector3(1f, 1f, 1f)); + [Export] private Color field_Color = Colors.Aquamarine; + [Export] private Plane field_Plane = Plane.PlaneXZ; + [Export] private Callable field_Callable = new Callable(Engine.GetMainLoop(), "_process"); + [Export] private Signal field_Signal = new Signal(Engine.GetMainLoop(), "property_list_changed"); + + // Enums + enum MyEnum + { + A, + B, + C + } + + [Export] private MyEnum field_Enum = MyEnum.C; + + [Flags] + enum MyFlagsEnum + { + A, + B, + C + } + + [Export] private MyFlagsEnum field_FlagsEnum = MyFlagsEnum.C; + + // Arrays + [Export] private Byte[] field_ByteArray = { 0, 1, 2, 3, 4, 5, 6 }; + [Export] private Int32[] field_Int32Array = { 0, 1, 2, 3, 4, 5, 6 }; + [Export] private Int64[] field_Int64Array = { 0, 1, 2, 3, 4, 5, 6 }; + [Export] private Single[] field_SingleArray = { 0f, 1f, 2f, 3f, 4f, 5f, 6f }; + [Export] private Double[] field_DoubleArray = { 0d, 1d, 2d, 3d, 4d, 5d, 6d }; + [Export] private String[] field_StringArray = { "foo", "bar" }; + [Export(PropertyHint.Enum, "A,B,C")] private String[] field_StringArrayEnum = { "foo", "bar" }; + [Export] private Vector2[] field_Vector2Array = { Vector2.Up, Vector2.Down, Vector2.Left, Vector2.Right }; + [Export] private Vector3[] field_Vector3Array = { Vector3.Up, Vector3.Down, Vector3.Left, Vector3.Right }; + [Export] private Color[] field_ColorArray = { Colors.Aqua, Colors.Aquamarine, Colors.Azure, Colors.Beige }; + [Export] private GodotObject[] field_GodotObjectOrDerivedArray = { null }; + [Export] private StringName[] field_StringNameArray = { "foo", "bar" }; + [Export] private NodePath[] field_NodePathArray = { "foo", "bar" }; + [Export] private Rid[] field_RidArray = { default, default, default }; + // Note we use Array and not System.Array. This tests the generated namespace qualification. + [Export] private Int32[] field_empty_Int32Array = Array.Empty<Int32>(); + // Note we use List and not System.Collections.Generic. + [Export] private int[] field_array_from_list = new List<int>(Array.Empty<int>()).ToArray(); + + // Variant + [Export] private Variant field_Variant = "foo"; + + // Classes + [Export] private GodotObject field_GodotObjectOrDerived; + [Export] private Godot.Texture field_GodotResourceTexture; + [Export] private StringName field_StringName = new StringName("foo"); + [Export] private NodePath field_NodePath = new NodePath("foo"); + [Export] private Rid field_Rid; + + [Export] + private Godot.Collections.Dictionary field_GodotDictionary = new() { { "foo", 10 }, { Vector2.Up, Colors.Chocolate } }; + + [Export] + private Godot.Collections.Array field_GodotArray = new() { "foo", 10, Vector2.Up, Colors.Chocolate }; + + [Export] + private Godot.Collections.Dictionary<string, bool> field_GodotGenericDictionary = new() { { "foo", true }, { "bar", false } }; + + [Export] + private Godot.Collections.Array<int> field_GodotGenericArray = new() { 0, 1, 2, 3, 4, 5, 6 }; +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedProperties.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedProperties.cs new file mode 100644 index 0000000000..3783838dae --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedProperties.cs @@ -0,0 +1,186 @@ +using Godot; +using System; + +public partial class ExportedProperties : GodotObject +{ + // Do not generate default value + private String _notGenerate_Property_String = new string("not generate"); + [Export] + public String NotGenerate_Complex_Lamda_Property + { + get => _notGenerate_Property_String + Convert.ToInt32("1"); + set => _notGenerate_Property_String = value; + } + + [Export] + public String NotGenerate_Lamda_NoField_Property + { + get => new string("not generate"); + set => _notGenerate_Property_String = value; + } + + [Export] + public String NotGenerate_Complex_Return_Property + { + get + { + return _notGenerate_Property_String + Convert.ToInt32("1"); + } + set + { + _notGenerate_Property_String = value; + } + } + + private int _notGenerate_Property_Int = 1; + [Export] + public string NotGenerate_Returns_Property + { + get + { + if (_notGenerate_Property_Int == 1) + { + return "a"; + } + else + { + return "b"; + } + } + set + { + _notGenerate_Property_Int = value == "a" ? 1 : 2; + } + } + + // Full Property + private String _fullProperty_String = "FullProperty_String"; + [Export] + public String FullProperty_String + { + get + { + return _fullProperty_String; + } + set + { + _fullProperty_String = value; + } + } + + private String _fullProperty_String_Complex = new string("FullProperty_String_Complex") + Convert.ToInt32("1"); + [Export] + public String FullProperty_String_Complex + { + get + { + return _fullProperty_String_Complex; + } + set + { + _fullProperty_String_Complex = value; + } + } + + // Lambda Property + private String _lamdaProperty_String = "LamdaProperty_String"; + [Export] + public String LamdaProperty_String + { + get => _lamdaProperty_String; + set => _lamdaProperty_String = value; + } + + // Auto Property + [Export] private Boolean property_Boolean { get; set; } = true; + [Export] private Char property_Char { get; set; } = 'f'; + [Export] private SByte property_SByte { get; set; } = 10; + [Export] private Int16 property_Int16 { get; set; } = 10; + [Export] private Int32 property_Int32 { get; set; } = 10; + [Export] private Int64 property_Int64 { get; set; } = 10; + [Export] private Byte property_Byte { get; set; } = 10; + [Export] private UInt16 property_UInt16 { get; set; } = 10; + [Export] private UInt32 property_UInt32 { get; set; } = 10; + [Export] private UInt64 property_UInt64 { get; set; } = 10; + [Export] private Single property_Single { get; set; } = 10; + [Export] private Double property_Double { get; set; } = 10; + [Export] private String property_String { get; set; } = "foo"; + + // Godot structs + [Export] private Vector2 property_Vector2 { get; set; } = new(10f, 10f); + [Export] private Vector2I property_Vector2I { get; set; } = Vector2I.Up; + [Export] private Rect2 property_Rect2 { get; set; } = new(new Vector2(10f, 10f), new Vector2(10f, 10f)); + [Export] private Rect2I property_Rect2I { get; set; } = new(new Vector2I(10, 10), new Vector2I(10, 10)); + [Export] private Transform2D property_Transform2D { get; set; } = Transform2D.Identity; + [Export] private Vector3 property_Vector3 { get; set; } = new(10f, 10f, 10f); + [Export] private Vector3I property_Vector3I { get; set; } = Vector3I.Back; + [Export] private Basis property_Basis { get; set; } = new Basis(Quaternion.Identity); + [Export] private Quaternion property_Quaternion { get; set; } = new Quaternion(Basis.Identity); + [Export] private Transform3D property_Transform3D { get; set; } = Transform3D.Identity; + [Export] private Vector4 property_Vector4 { get; set; } = new(10f, 10f, 10f, 10f); + [Export] private Vector4I property_Vector4I { get; set; } = Vector4I.One; + [Export] private Projection property_Projection { get; set; } = Projection.Identity; + [Export] private Aabb property_Aabb { get; set; } = new Aabb(10f, 10f, 10f, new Vector3(1f, 1f, 1f)); + [Export] private Color property_Color { get; set; } = Colors.Aquamarine; + [Export] private Plane property_Plane { get; set; } = Plane.PlaneXZ; + [Export] private Callable property_Callable { get; set; } = new Callable(Engine.GetMainLoop(), "_process"); + [Export] private Signal property_Signal { get; set; } = new Signal(Engine.GetMainLoop(), "property_list_changed"); + + // Enums + enum MyEnum + { + A, + B, + C + } + + [Export] private MyEnum property_Enum { get; set; } = MyEnum.C; + + [Flags] + enum MyFlagsEnum + { + A, + B, + C + } + + [Export] private MyFlagsEnum property_FlagsEnum { get; set; } = MyFlagsEnum.C; + + // Arrays + [Export] private Byte[] property_ByteArray { get; set; } = { 0, 1, 2, 3, 4, 5, 6 }; + [Export] private Int32[] property_Int32Array { get; set; } = { 0, 1, 2, 3, 4, 5, 6 }; + [Export] private Int64[] property_Int64Array { get; set; } = { 0, 1, 2, 3, 4, 5, 6 }; + [Export] private Single[] property_SingleArray { get; set; } = { 0f, 1f, 2f, 3f, 4f, 5f, 6f }; + [Export] private Double[] property_DoubleArray { get; set; } = { 0d, 1d, 2d, 3d, 4d, 5d, 6d }; + [Export] private String[] property_StringArray { get; set; } = { "foo", "bar" }; + [Export(PropertyHint.Enum, "A,B,C")] private String[] property_StringArrayEnum { get; set; } = { "foo", "bar" }; + [Export] private Vector2[] property_Vector2Array { get; set; } = { Vector2.Up, Vector2.Down, Vector2.Left, Vector2.Right }; + [Export] private Vector3[] property_Vector3Array { get; set; } = { Vector3.Up, Vector3.Down, Vector3.Left, Vector3.Right }; + [Export] private Color[] property_ColorArray { get; set; } = { Colors.Aqua, Colors.Aquamarine, Colors.Azure, Colors.Beige }; + [Export] private GodotObject[] property_GodotObjectOrDerivedArray { get; set; } = { null }; + [Export] private StringName[] field_StringNameArray { get; set; } = { "foo", "bar" }; + [Export] private NodePath[] field_NodePathArray { get; set; } = { "foo", "bar" }; + [Export] private Rid[] field_RidArray { get; set; } = { default, default, default }; + + // Variant + [Export] private Variant property_Variant { get; set; } = "foo"; + + // Classes + [Export] private GodotObject property_GodotObjectOrDerived { get; set; } + [Export] private Godot.Texture property_GodotResourceTexture { get; set; } + [Export] private StringName property_StringName { get; set; } = new StringName("foo"); + [Export] private NodePath property_NodePath { get; set; } = new NodePath("foo"); + [Export] private Rid property_Rid { get; set; } + + [Export] + private Godot.Collections.Dictionary property_GodotDictionary { get; set; } = new() { { "foo", 10 }, { Vector2.Up, Colors.Chocolate } }; + + [Export] + private Godot.Collections.Array property_GodotArray { get; set; } = new() { "foo", 10, Vector2.Up, Colors.Chocolate }; + + [Export] + private Godot.Collections.Dictionary<string, bool> property_GodotGenericDictionary { get; set; } = new() { { "foo", true }, { "bar", false } }; + + [Export] + private Godot.Collections.Array<int> property_GodotGenericArray { get; set; } = new() { 0, 1, 2, 3, 4, 5, 6 }; +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Foo.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Foo.cs new file mode 100644 index 0000000000..26853553c7 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Foo.cs @@ -0,0 +1,10 @@ +using Godot; + +partial class Foo : GodotObject +{ +} + +// Foo again in the same file +partial class Foo +{ +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.cs new file mode 100644 index 0000000000..84d1ede065 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.cs @@ -0,0 +1,18 @@ +using Godot; + +partial class Generic<T> : GodotObject +{ + private int _field; +} + +// Generic again but different generic parameters +partial class Generic<T, R> : GodotObject +{ + private int _field; +} + +// Generic again but without generic parameters +partial class Generic : GodotObject +{ + private int _field; +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Methods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Methods.cs new file mode 100644 index 0000000000..1da9db8204 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Methods.cs @@ -0,0 +1,26 @@ +using Godot; + +public partial class Methods : GodotObject +{ + private void MethodWithOverload() + { + } + + private void MethodWithOverload(int a) + { + } + + private void MethodWithOverload(int a, int b) + { + } + + // Should be ignored. The previous one is picked. + private void MethodWithOverload(float a, float b) + { + } + + // Generic methods should be ignored. + private void GenericMethod<T>(T t) + { + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MixedReadOnlyWriteOnly.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MixedReadOnlyWriteOnly.cs new file mode 100644 index 0000000000..61a48cefc9 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MixedReadOnlyWriteOnly.cs @@ -0,0 +1,12 @@ +using Godot; + +public partial class MixedReadOnlyWriteOnly : GodotObject +{ + public readonly string readonly_field = "foo"; + public string readonly_auto_property { get; } = "foo"; + public string readonly_property { get => "foo"; } + public string initonly_auto_property { get; init; } + + bool writeonly_backing_field = false; + public bool writeonly_property { set => writeonly_backing_field = value; } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MoreExportedFields.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MoreExportedFields.cs new file mode 100644 index 0000000000..47063a9cdf --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/MoreExportedFields.cs @@ -0,0 +1,8 @@ +using Godot; +using System; + +public partial class ExportedFields : GodotObject +{ + // Note we use Array and not System.Array. This tests the generated namespace qualification. + [Export] private Int64[] field_empty_Int64Array = Array.Empty<Int64>(); +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ScriptBoilerplate.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ScriptBoilerplate.cs new file mode 100644 index 0000000000..5506465b92 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ScriptBoilerplate.cs @@ -0,0 +1,33 @@ +using Godot; + +public partial class ScriptBoilerplate : Node +{ + private NodePath _nodePath; + private int _velocity; + + public override void _Process(double delta) + { + _ = delta; + + base._Process(delta); + } + + public int Bazz(StringName name) + { + _ = name; + return 1; + } + + public void IgnoreThisMethodWithByRefParams(ref int a) + { + _ = a; + } +} + +partial struct OuterClass +{ + public partial class NestedClass : RefCounted + { + public override Variant _Get(StringName property) => default; + } +} diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 36fdda4625..25a5720bc4 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -69,6 +69,7 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) { #define OPEN_BLOCK_L1 INDENT1 OPEN_BLOCK #define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK +#define OPEN_BLOCK_L3 INDENT3 OPEN_BLOCK #define CLOSE_BLOCK_L1 INDENT1 CLOSE_BLOCK #define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK #define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK @@ -2591,7 +2592,11 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall, // Generate icall function r_output << MEMBER_BEGIN "internal static unsafe " << (ret_void ? "void" : return_type->c_type_out) << " " - << icall_method << "(" << c_func_sig.as_string() << ") " OPEN_BLOCK; + << icall_method << "(" << c_func_sig.as_string() << ")\n" OPEN_BLOCK_L1; + + if (!p_icall.is_static) { + r_output << INDENT2 "ExceptionUtils.ThrowIfNullPtr(" CS_PARAM_INSTANCE ");\n"; + } if (!ret_void && (!p_icall.is_vararg || return_type->cname != name_cache.type_Variant)) { String ptrcall_return_type; @@ -2619,11 +2624,6 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall, r_output << ptrcall_return_type << " " C_LOCAL_RET << initialization << ";\n"; } - if (!p_icall.is_static) { - r_output << INDENT2 "if (" CS_PARAM_INSTANCE " == IntPtr.Zero)\n" - << INDENT3 "throw new ArgumentNullException(nameof(" CS_PARAM_INSTANCE "));\n"; - } - String argc_str = itos(p_icall.get_arguments_count()); auto generate_call_and_return_stmts = [&](const char *base_indent) { @@ -2714,7 +2714,7 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall, r_output << c_in_statements.as_string(); - r_output << INDENT3 "for (int i = 0; i < vararg_length; i++) " OPEN_BLOCK + r_output << INDENT3 "for (int i = 0; i < vararg_length; i++)\n" OPEN_BLOCK_L3 << INDENT4 "varargs[i] = " << vararg_arg << "[i].NativeVar;\n" << INDENT4 C_LOCAL_PTRCALL_ARGS "[" << real_argc_str << " + i] = new IntPtr(&varargs[i]);\n" << CLOSE_BLOCK_L3; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 13c0cde1ef..6024073f8a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -6,6 +6,8 @@ using System.Linq; using System.Runtime.CompilerServices; using Godot.NativeInterop; +#nullable enable + namespace Godot.Collections { /// <summary> @@ -22,7 +24,7 @@ namespace Godot.Collections { internal godot_array.movable NativeValue; - private WeakReference<IDisposable> _weakReferenceToSelf; + private WeakReference<IDisposable>? _weakReferenceToSelf; /// <summary> /// Constructs a new empty <see cref="Array"/>. @@ -1140,7 +1142,8 @@ namespace Godot.Collections /// </summary> /// <param name="from">The typed array to convert.</param> /// <returns>A new Godot Array, or <see langword="null"/> if <see paramref="from"/> was null.</returns> - public static explicit operator Array(Array<T> from) + [return: NotNullIfNotNull(nameof(from))] + public static explicit operator Array?(Array<T>? from) { return from?._underlyingArray; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 80c26e5708..a087b20308 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -194,7 +194,7 @@ namespace Godot.Bridge var native = GodotObject.InternalGetClassNativeBase(scriptType); - var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | + var field = native.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) @@ -253,11 +253,15 @@ namespace Godot.Bridge { var editorAssembly = AppDomain.CurrentDomain.GetAssemblies() .FirstOrDefault(a => a.GetName().Name == "GodotSharpEditor"); - wrapperType = editorAssembly?.GetType("Godot." + nativeTypeNameStr); - if (wrapperType == null) + if (editorAssembly != null) { - wrapperType = GetTypeByGodotClassAttr(editorAssembly, nativeTypeNameStr); + wrapperType = editorAssembly.GetType("Godot." + nativeTypeNameStr); + + if (wrapperType == null) + { + wrapperType = GetTypeByGodotClassAttr(editorAssembly, nativeTypeNameStr); + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 6c2fb7374c..ab2e0a78b9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -414,7 +414,7 @@ namespace Godot { ulong objectId = reader.ReadUInt64(); // ReSharper disable once RedundantNameQualifier - GodotObject godotObject = GodotObject.InstanceFromId(objectId); + GodotObject? godotObject = GodotObject.InstanceFromId(objectId); if (godotObject == null) return false; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 2a72ebc32b..405f280cbb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -5,6 +5,8 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Godot.NativeInterop; +#nullable enable + namespace Godot.Collections { /// <summary> @@ -19,7 +21,7 @@ namespace Godot.Collections { internal godot_dictionary.movable NativeValue; - private WeakReference<IDisposable> _weakReferenceToSelf; + private WeakReference<IDisposable>? _weakReferenceToSelf; /// <summary> /// Constructs a new empty <see cref="Dictionary"/>. @@ -559,7 +561,8 @@ namespace Godot.Collections /// </summary> /// <param name="from">The typed dictionary to convert.</param> /// <returns>A new Godot Dictionary, or <see langword="null"/> if <see paramref="from"/> was null.</returns> - public static explicit operator Dictionary(Dictionary<TKey, TValue> from) + [return: NotNullIfNotNull(nameof(from))] + public static explicit operator Dictionary?(Dictionary<TKey, TValue>? from) { return from?._underlyingDict; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs index 6c90c17078..563a6abe9b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs @@ -1,6 +1,8 @@ using System; using Godot.NativeInterop; +#nullable enable + namespace Godot { public partial class GodotObject @@ -26,7 +28,7 @@ namespace Godot /// </example> /// <param name="instanceId">Instance ID of the Object to retrieve.</param> /// <returns>The <see cref="GodotObject"/> instance.</returns> - public static GodotObject InstanceFromId(ulong instanceId) + public static GodotObject? InstanceFromId(ulong instanceId) { return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId)); } @@ -49,7 +51,7 @@ namespace Godot /// </summary> /// <param name="instance">The instance to check.</param> /// <returns>If the instance is a valid object.</returns> - public static bool IsInstanceValid(GodotObject instance) + public static bool IsInstanceValid(GodotObject? instance) { return instance != null && instance.NativeInstance != IntPtr.Zero; } @@ -66,9 +68,9 @@ namespace Godot /// </summary> /// <param name="obj">The object.</param> /// <returns> - /// The <see cref="WeakRef"/> reference to the object or <see langword="null"/>. + /// The <see cref="Godot.WeakRef"/> reference to the object or <see langword="null"/>. /// </returns> - public static WeakRef WeakRef(GodotObject obj) + public static WeakRef? WeakRef(GodotObject? obj) { if (!IsInstanceValid(obj)) return null; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs index 43598ca84d..8f8e884b8c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs @@ -1,8 +1,11 @@ using System; +using System.Diagnostics; using System.Runtime.InteropServices; using Godot.Bridge; using Godot.NativeInterop; +#nullable enable + namespace Godot { public partial class GodotObject : IDisposable @@ -13,7 +16,7 @@ namespace Godot internal IntPtr NativePtr; private bool _memoryOwn; - private WeakReference<GodotObject> _weakReferenceToSelf; + private WeakReference<GodotObject>? _weakReferenceToSelf; /// <summary> /// Constructs a new <see cref="GodotObject"/>. @@ -59,7 +62,7 @@ namespace Godot /// </summary> public IntPtr NativeInstance => NativePtr; - internal static IntPtr GetPtr(GodotObject instance) + internal static IntPtr GetPtr(GodotObject? instance) { if (instance == null) return IntPtr.Zero; @@ -105,7 +108,7 @@ namespace Godot if (gcHandleToFree != IntPtr.Zero) { - object target = GCHandle.FromIntPtr(gcHandleToFree).Target; + object? target = GCHandle.FromIntPtr(gcHandleToFree).Target; // The GC handle may have been replaced in another thread. Release it only if // it's associated to this managed instance, or if the target is no longer alive. if (target != this && target != null) @@ -176,18 +179,14 @@ namespace Godot internal static Type InternalGetClassNativeBase(Type t) { - do - { - var assemblyName = t.Assembly.GetName(); + var name = t.Assembly.GetName().Name; - if (assemblyName.Name == "GodotSharp") - return t; + if (name == "GodotSharp" || name == "GodotSharpEditor") + return t; - if (assemblyName.Name == "GodotSharpEditor") - return t; - } while ((t = t.BaseType) != null); + Debug.Assert(t.BaseType is not null, "Script types must derive from a native Godot type."); - return null; + return InternalGetClassNativeBase(t.BaseType); } // ReSharper disable once VirtualMemberNeverOverridden.Global diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs index dc53e48bd0..537d863ef2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Text; #nullable enable @@ -239,5 +240,13 @@ namespace Godot.NativeInterop return variant->Type.ToString(); } + + internal static void ThrowIfNullPtr(IntPtr ptr, [CallerArgumentExpression(nameof(ptr))] string? paramName = null) + { + if (ptr == IntPtr.Zero) + { + throw new ArgumentNullException(paramName); + } + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index f216fb7ea3..2ab6904417 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -1,6 +1,9 @@ using System; +using System.Diagnostics.CodeAnalysis; using Godot.NativeInterop; +#nullable enable + namespace Godot { /// <summary> @@ -39,11 +42,11 @@ namespace Godot /// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene. /// </code> /// </example> - public sealed class NodePath : IDisposable, IEquatable<NodePath> + public sealed class NodePath : IDisposable, IEquatable<NodePath?> { internal godot_node_path.movable NativeValue; - private WeakReference<IDisposable> _weakReferenceToSelf; + private WeakReference<IDisposable>? _weakReferenceToSelf; ~NodePath() { @@ -135,7 +138,8 @@ namespace Godot /// Converts this <see cref="NodePath"/> to a string. /// </summary> /// <param name="from">The <see cref="NodePath"/> to convert.</param> - public static implicit operator string(NodePath from) => from?.ToString(); + [return: NotNullIfNotNull(nameof(from))] + public static implicit operator string?(NodePath? from) => from?.ToString(); /// <summary> /// Converts this <see cref="NodePath"/> to a string. @@ -289,19 +293,19 @@ namespace Godot /// <returns>If the <see cref="NodePath"/> is empty.</returns> public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty; - public static bool operator ==(NodePath left, NodePath right) + public static bool operator ==(NodePath? left, NodePath? right) { if (left is null) return right is null; return left.Equals(right); } - public static bool operator !=(NodePath left, NodePath right) + public static bool operator !=(NodePath? left, NodePath? right) { return !(left == right); } - public bool Equals(NodePath other) + public bool Equals([NotNullWhen(true)] NodePath? other) { if (other is null) return false; @@ -310,7 +314,7 @@ namespace Godot return NativeFuncs.godotsharp_node_path_equals(self, otherNative).ToBool(); } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { return ReferenceEquals(this, obj) || (obj is NodePath other && Equals(other)); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs index 97d28f9ee9..51e97e0fb8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs @@ -1,6 +1,9 @@ using System; +using System.Diagnostics.CodeAnalysis; using Godot.NativeInterop; +#nullable enable + namespace Godot { /// <summary> @@ -10,11 +13,11 @@ namespace Godot /// Comparing them is much faster than with regular strings, because only the pointers are compared, /// not the whole strings. /// </summary> - public sealed class StringName : IDisposable, IEquatable<StringName> + public sealed class StringName : IDisposable, IEquatable<StringName?> { internal godot_string_name.movable NativeValue; - private WeakReference<IDisposable> _weakReferenceToSelf; + private WeakReference<IDisposable>? _weakReferenceToSelf; ~StringName() { @@ -81,7 +84,8 @@ namespace Godot /// Converts a <see cref="StringName"/> to a string. /// </summary> /// <param name="from">The <see cref="StringName"/> to convert.</param> - public static implicit operator string(StringName from) => from?.ToString(); + [return: NotNullIfNotNull(nameof(from))] + public static implicit operator string?(StringName? from) => from?.ToString(); /// <summary> /// Converts this <see cref="StringName"/> to a string. @@ -104,43 +108,43 @@ namespace Godot /// <returns>If the <see cref="StringName"/> is empty.</returns> public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty; - public static bool operator ==(StringName left, StringName right) + public static bool operator ==(StringName? left, StringName? right) { if (left is null) return right is null; return left.Equals(right); } - public static bool operator !=(StringName left, StringName right) + public static bool operator !=(StringName? left, StringName? right) { return !(left == right); } - public bool Equals(StringName other) + public bool Equals([NotNullWhen(true)] StringName? other) { if (other is null) return false; return NativeValue.DangerousSelfRef == other.NativeValue.DangerousSelfRef; } - public static bool operator ==(StringName left, in godot_string_name right) + public static bool operator ==(StringName? left, in godot_string_name right) { if (left is null) return right.IsEmpty; return left.Equals(right); } - public static bool operator !=(StringName left, in godot_string_name right) + public static bool operator !=(StringName? left, in godot_string_name right) { return !(left == right); } - public static bool operator ==(in godot_string_name left, StringName right) + public static bool operator ==(in godot_string_name left, StringName? right) { return right == left; } - public static bool operator !=(in godot_string_name left, StringName right) + public static bool operator !=(in godot_string_name left, StringName? right) { return !(right == left); } @@ -150,7 +154,7 @@ namespace Godot return NativeValue.DangerousSelfRef == other; } - public override bool Equals(object obj) + public override bool Equals([NotNullWhen(true)] object? obj) { return ReferenceEquals(this, obj) || (obj is StringName other && Equals(other)); } diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp index 5a28f8b8ef..5a27f315b9 100644 --- a/modules/navigation/godot_navigation_server.cpp +++ b/modules/navigation/godot_navigation_server.cpp @@ -391,6 +391,13 @@ COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform) { region->set_transform(p_transform); } +Transform3D GodotNavigationServer::region_get_transform(RID p_region) const { + NavRegion *region = region_owner.get_or_null(p_region); + ERR_FAIL_NULL_V(region, Transform3D()); + + return region->get_transform(); +} + COMMAND_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost) { NavRegion *region = region_owner.get_or_null(p_region); ERR_FAIL_NULL(region); @@ -719,6 +726,13 @@ COMMAND_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_distance) { agent->set_neighbor_distance(p_distance); } +real_t GodotNavigationServer::agent_get_neighbor_distance(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, 0); + + return agent->get_neighbor_distance(); +} + COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) { NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_NULL(agent); @@ -726,22 +740,43 @@ COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) { agent->set_max_neighbors(p_count); } +int GodotNavigationServer::agent_get_max_neighbors(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, 0); + + return agent->get_max_neighbors(); +} + COMMAND_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon) { - ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive."); + ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizon must be positive."); NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_NULL(agent); agent->set_time_horizon_agents(p_time_horizon); } +real_t GodotNavigationServer::agent_get_time_horizon_agents(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, 0); + + return agent->get_time_horizon_agents(); +} + COMMAND_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon) { - ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive."); + ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizon must be positive."); NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_NULL(agent); agent->set_time_horizon_obstacles(p_time_horizon); } +real_t GodotNavigationServer::agent_get_time_horizon_obstacles(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, 0); + + return agent->get_time_horizon_obstacles(); +} + COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius) { ERR_FAIL_COND_MSG(p_radius < 0.0, "Radius must be positive."); NavAgent *agent = agent_owner.get_or_null(p_agent); @@ -750,6 +785,13 @@ COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius) { agent->set_radius(p_radius); } +real_t GodotNavigationServer::agent_get_radius(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, 0); + + return agent->get_radius(); +} + COMMAND_2(agent_set_height, RID, p_agent, real_t, p_height) { ERR_FAIL_COND_MSG(p_height < 0.0, "Height must be positive."); NavAgent *agent = agent_owner.get_or_null(p_agent); @@ -758,6 +800,13 @@ COMMAND_2(agent_set_height, RID, p_agent, real_t, p_height) { agent->set_height(p_height); } +real_t GodotNavigationServer::agent_get_height(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, 0); + + return agent->get_height(); +} + COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) { ERR_FAIL_COND_MSG(p_max_speed < 0.0, "Max speed must be positive."); NavAgent *agent = agent_owner.get_or_null(p_agent); @@ -766,6 +815,13 @@ COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) { agent->set_max_speed(p_max_speed); } +real_t GodotNavigationServer::agent_get_max_speed(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, 0); + + return agent->get_max_speed(); +} + COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) { NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_NULL(agent); @@ -773,6 +829,13 @@ COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) { agent->set_velocity(p_velocity); } +Vector3 GodotNavigationServer::agent_get_velocity(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, Vector3()); + + return agent->get_velocity(); +} + COMMAND_2(agent_set_velocity_forced, RID, p_agent, Vector3, p_velocity) { NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_NULL(agent); @@ -787,6 +850,13 @@ COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position) { agent->set_position(p_position); } +Vector3 GodotNavigationServer::agent_get_position(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, Vector3()); + + return agent->get_position(); +} + bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const { NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_NULL_V(agent, false); @@ -809,18 +879,39 @@ COMMAND_2(agent_set_avoidance_callback, RID, p_agent, Callable, p_callback) { } } +bool GodotNavigationServer::agent_has_avoidance_callback(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, false); + + return agent->has_avoidance_callback(); +} + COMMAND_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers) { NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_NULL(agent); agent->set_avoidance_layers(p_layers); } +uint32_t GodotNavigationServer::agent_get_avoidance_layers(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, 0); + + return agent->get_avoidance_layers(); +} + COMMAND_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask) { NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_NULL(agent); agent->set_avoidance_mask(p_mask); } +uint32_t GodotNavigationServer::agent_get_avoidance_mask(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, 0); + + return agent->get_avoidance_mask(); +} + COMMAND_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority) { ERR_FAIL_COND_MSG(p_priority < 0.0, "Avoidance priority must be between 0.0 and 1.0 inclusive."); ERR_FAIL_COND_MSG(p_priority > 1.0, "Avoidance priority must be between 0.0 and 1.0 inclusive."); @@ -829,6 +920,13 @@ COMMAND_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority) { agent->set_avoidance_priority(p_priority); } +real_t GodotNavigationServer::agent_get_avoidance_priority(RID p_agent) const { + NavAgent *agent = agent_owner.get_or_null(p_agent); + ERR_FAIL_NULL_V(agent, 0); + + return agent->get_avoidance_priority(); +} + RID GodotNavigationServer::obstacle_create() { MutexLock lock(operations_mutex); @@ -913,12 +1011,26 @@ COMMAND_2(obstacle_set_radius, RID, p_obstacle, real_t, p_radius) { obstacle->set_radius(p_radius); } +real_t GodotNavigationServer::obstacle_get_radius(RID p_obstacle) const { + NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle); + ERR_FAIL_NULL_V(obstacle, 0); + + return obstacle->get_radius(); +} + COMMAND_2(obstacle_set_height, RID, p_obstacle, real_t, p_height) { NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle); ERR_FAIL_NULL(obstacle); obstacle->set_height(p_height); } +real_t GodotNavigationServer::obstacle_get_height(RID p_obstacle) const { + NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle); + ERR_FAIL_NULL_V(obstacle, 0); + + return obstacle->get_height(); +} + COMMAND_2(obstacle_set_velocity, RID, p_obstacle, Vector3, p_velocity) { NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle); ERR_FAIL_NULL(obstacle); @@ -926,24 +1038,52 @@ COMMAND_2(obstacle_set_velocity, RID, p_obstacle, Vector3, p_velocity) { obstacle->set_velocity(p_velocity); } +Vector3 GodotNavigationServer::obstacle_get_velocity(RID p_obstacle) const { + NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle); + ERR_FAIL_NULL_V(obstacle, Vector3()); + + return obstacle->get_velocity(); +} + COMMAND_2(obstacle_set_position, RID, p_obstacle, Vector3, p_position) { NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle); ERR_FAIL_NULL(obstacle); obstacle->set_position(p_position); } +Vector3 GodotNavigationServer::obstacle_get_position(RID p_obstacle) const { + NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle); + ERR_FAIL_NULL_V(obstacle, Vector3()); + + return obstacle->get_position(); +} + void GodotNavigationServer::obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) { NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle); ERR_FAIL_NULL(obstacle); obstacle->set_vertices(p_vertices); } +Vector<Vector3> GodotNavigationServer::obstacle_get_vertices(RID p_obstacle) const { + NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle); + ERR_FAIL_NULL_V(obstacle, Vector<Vector3>()); + + return obstacle->get_vertices(); +} + COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers) { NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle); ERR_FAIL_NULL(obstacle); obstacle->set_avoidance_layers(p_layers); } +uint32_t GodotNavigationServer::obstacle_get_avoidance_layers(RID p_obstacle) const { + NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle); + ERR_FAIL_NULL_V(obstacle, 0); + + return obstacle->get_avoidance_layers(); +} + void GodotNavigationServer::parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback) { #ifndef _3D_DISABLED ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred()."); diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h index 3a76f83b09..f19450db27 100644 --- a/modules/navigation/godot_navigation_server.h +++ b/modules/navigation/godot_navigation_server.h @@ -165,6 +165,7 @@ public: COMMAND_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers); virtual uint32_t region_get_navigation_layers(RID p_region) const override; COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform); + virtual Transform3D region_get_transform(RID p_region) const override; COMMAND_2(region_set_navigation_mesh, RID, p_region, Ref<NavigationMesh>, p_navigation_mesh); #ifndef DISABLE_DEPRECATED virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) override; @@ -204,20 +205,33 @@ public: COMMAND_2(agent_set_paused, RID, p_agent, bool, p_paused); virtual bool agent_get_paused(RID p_agent) const override; COMMAND_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_distance); + virtual real_t agent_get_neighbor_distance(RID p_agent) const override; COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count); + virtual int agent_get_max_neighbors(RID p_agent) const override; COMMAND_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon); + virtual real_t agent_get_time_horizon_agents(RID p_agent) const override; COMMAND_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon); + virtual real_t agent_get_time_horizon_obstacles(RID p_agent) const override; COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius); + virtual real_t agent_get_radius(RID p_agent) const override; COMMAND_2(agent_set_height, RID, p_agent, real_t, p_height); + virtual real_t agent_get_height(RID p_agent) const override; COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed); + virtual real_t agent_get_max_speed(RID p_agent) const override; COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity); + virtual Vector3 agent_get_velocity(RID p_agent) const override; COMMAND_2(agent_set_velocity_forced, RID, p_agent, Vector3, p_velocity); COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position); + virtual Vector3 agent_get_position(RID p_agent) const override; virtual bool agent_is_map_changed(RID p_agent) const override; COMMAND_2(agent_set_avoidance_callback, RID, p_agent, Callable, p_callback); + virtual bool agent_has_avoidance_callback(RID p_agent) const override; COMMAND_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers); + virtual uint32_t agent_get_avoidance_layers(RID p_agent) const override; COMMAND_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask); + virtual uint32_t agent_get_avoidance_mask(RID p_agent) const override; COMMAND_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority); + virtual real_t agent_get_avoidance_priority(RID p_agent) const override; virtual RID obstacle_create() override; COMMAND_2(obstacle_set_avoidance_enabled, RID, p_obstacle, bool, p_enabled); @@ -229,11 +243,17 @@ public: COMMAND_2(obstacle_set_paused, RID, p_obstacle, bool, p_paused); virtual bool obstacle_get_paused(RID p_obstacle) const override; COMMAND_2(obstacle_set_radius, RID, p_obstacle, real_t, p_radius); + virtual real_t obstacle_get_radius(RID p_obstacle) const override; COMMAND_2(obstacle_set_velocity, RID, p_obstacle, Vector3, p_velocity); + virtual Vector3 obstacle_get_velocity(RID p_obstacle) const override; COMMAND_2(obstacle_set_position, RID, p_obstacle, Vector3, p_position); + virtual Vector3 obstacle_get_position(RID p_obstacle) const override; COMMAND_2(obstacle_set_height, RID, p_obstacle, real_t, p_height); + virtual real_t obstacle_get_height(RID p_obstacle) const override; virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override; + virtual Vector<Vector3> obstacle_get_vertices(RID p_obstacle) const override; COMMAND_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers); + virtual uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const override; virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override; virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override; diff --git a/modules/navigation/godot_navigation_server_2d.cpp b/modules/navigation/godot_navigation_server_2d.cpp index 142d6181a1..76bfd3a101 100644 --- a/modules/navigation/godot_navigation_server_2d.cpp +++ b/modules/navigation/godot_navigation_server_2d.cpp @@ -141,6 +141,13 @@ static Transform3D trf2_to_trf3(const Transform2D &d) { return Transform3D(b, o); } +static Transform2D trf3_to_trf2(const Transform3D &d) { + Vector3 o = d.get_origin(); + Vector3 nx = d.xform(Vector3(1, 0, 0)) - o; + Vector3 nz = d.xform(Vector3(0, 0, 1)) - o; + return Transform2D(nx.x, nx.z, nz.x, nz.z, o.x, o.z); +} + static ObjectID id_to_id(const ObjectID &id) { return id; } @@ -283,6 +290,10 @@ void FORWARD_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigati uint32_t FORWARD_1_C(region_get_navigation_layers, RID, p_region, rid_to_rid); void FORWARD_2(region_set_transform, RID, p_region, Transform2D, p_transform, rid_to_rid, trf2_to_trf3); +Transform2D GodotNavigationServer2D::region_get_transform(RID p_region) const { + return trf3_to_trf2(NavigationServer3D::get_singleton()->region_get_transform(p_region)); +} + void GodotNavigationServer2D::region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) { NavigationServer3D::get_singleton()->region_set_navigation_mesh(p_region, poly_to_mesh(p_navigation_polygon)); } @@ -326,25 +337,60 @@ void FORWARD_2(agent_set_avoidance_enabled, RID, p_agent, bool, p_enabled, rid_t bool FORWARD_1_C(agent_get_avoidance_enabled, RID, p_agent, rid_to_rid); void FORWARD_2(agent_set_map, RID, p_agent, RID, p_map, rid_to_rid, rid_to_rid); void FORWARD_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_dist, rid_to_rid, real_to_real); +real_t GodotNavigationServer2D::agent_get_neighbor_distance(RID p_agent) const { + return NavigationServer3D::get_singleton()->agent_get_neighbor_distance(p_agent); +} void FORWARD_2(agent_set_max_neighbors, RID, p_agent, int, p_count, rid_to_rid, int_to_int); +int GodotNavigationServer2D::agent_get_max_neighbors(RID p_agent) const { + return NavigationServer3D::get_singleton()->agent_get_max_neighbors(p_agent); +} void FORWARD_2(agent_set_time_horizon_agents, RID, p_agent, real_t, p_time_horizon, rid_to_rid, real_to_real); +real_t GodotNavigationServer2D::agent_get_time_horizon_agents(RID p_agent) const { + return NavigationServer3D::get_singleton()->agent_get_time_horizon_agents(p_agent); +} void FORWARD_2(agent_set_time_horizon_obstacles, RID, p_agent, real_t, p_time_horizon, rid_to_rid, real_to_real); +real_t GodotNavigationServer2D::agent_get_time_horizon_obstacles(RID p_agent) const { + return NavigationServer3D::get_singleton()->agent_get_time_horizon_obstacles(p_agent); +} void FORWARD_2(agent_set_radius, RID, p_agent, real_t, p_radius, rid_to_rid, real_to_real); +real_t GodotNavigationServer2D::agent_get_radius(RID p_agent) const { + return NavigationServer3D::get_singleton()->agent_get_radius(p_agent); +} void FORWARD_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed, rid_to_rid, real_to_real); +real_t GodotNavigationServer2D::agent_get_max_speed(RID p_agent) const { + return NavigationServer3D::get_singleton()->agent_get_max_speed(p_agent); +} void FORWARD_2(agent_set_velocity_forced, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3); void FORWARD_2(agent_set_velocity, RID, p_agent, Vector2, p_velocity, rid_to_rid, v2_to_v3); +Vector2 GodotNavigationServer2D::agent_get_velocity(RID p_agent) const { + return v3_to_v2(NavigationServer3D::get_singleton()->agent_get_velocity(p_agent)); +} void FORWARD_2(agent_set_position, RID, p_agent, Vector2, p_position, rid_to_rid, v2_to_v3); - +Vector2 GodotNavigationServer2D::agent_get_position(RID p_agent) const { + return v3_to_v2(NavigationServer3D::get_singleton()->agent_get_position(p_agent)); +} bool FORWARD_1_C(agent_is_map_changed, RID, p_agent, rid_to_rid); void FORWARD_2(agent_set_paused, RID, p_agent, bool, p_paused, rid_to_rid, bool_to_bool); bool FORWARD_1_C(agent_get_paused, RID, p_agent, rid_to_rid); void FORWARD_1(free, RID, p_object, rid_to_rid); void FORWARD_2(agent_set_avoidance_callback, RID, p_agent, Callable, p_callback, rid_to_rid, callable_to_callable); +bool GodotNavigationServer2D::agent_has_avoidance_callback(RID p_agent) const { + return NavigationServer3D::get_singleton()->agent_has_avoidance_callback(p_agent); +} void FORWARD_2(agent_set_avoidance_layers, RID, p_agent, uint32_t, p_layers, rid_to_rid, uint32_to_uint32); +uint32_t GodotNavigationServer2D::agent_get_avoidance_layers(RID p_agent) const { + return NavigationServer3D::get_singleton()->agent_get_avoidance_layers(p_agent); +} void FORWARD_2(agent_set_avoidance_mask, RID, p_agent, uint32_t, p_mask, rid_to_rid, uint32_to_uint32); +uint32_t GodotNavigationServer2D::agent_get_avoidance_mask(RID p_agent) const { + return NavigationServer3D::get_singleton()->agent_get_avoidance_mask(p_agent); +} void FORWARD_2(agent_set_avoidance_priority, RID, p_agent, real_t, p_priority, rid_to_rid, real_to_real); +real_t GodotNavigationServer2D::agent_get_avoidance_priority(RID p_agent) const { + return NavigationServer3D::get_singleton()->agent_get_avoidance_priority(p_agent); +} RID GodotNavigationServer2D::obstacle_create() { RID obstacle = NavigationServer3D::get_singleton()->obstacle_create(); @@ -357,13 +403,28 @@ RID FORWARD_1_C(obstacle_get_map, RID, p_obstacle, rid_to_rid); void FORWARD_2(obstacle_set_paused, RID, p_obstacle, bool, p_paused, rid_to_rid, bool_to_bool); bool FORWARD_1_C(obstacle_get_paused, RID, p_obstacle, rid_to_rid); void FORWARD_2(obstacle_set_radius, RID, p_obstacle, real_t, p_radius, rid_to_rid, real_to_real); +real_t GodotNavigationServer2D::obstacle_get_radius(RID p_obstacle) const { + return NavigationServer3D::get_singleton()->obstacle_get_radius(p_obstacle); +} void FORWARD_2(obstacle_set_velocity, RID, p_obstacle, Vector2, p_velocity, rid_to_rid, v2_to_v3); +Vector2 GodotNavigationServer2D::obstacle_get_velocity(RID p_obstacle) const { + return v3_to_v2(NavigationServer3D::get_singleton()->obstacle_get_velocity(p_obstacle)); +} void FORWARD_2(obstacle_set_position, RID, p_obstacle, Vector2, p_position, rid_to_rid, v2_to_v3); +Vector2 GodotNavigationServer2D::obstacle_get_position(RID p_obstacle) const { + return v3_to_v2(NavigationServer3D::get_singleton()->obstacle_get_position(p_obstacle)); +} void FORWARD_2(obstacle_set_avoidance_layers, RID, p_obstacle, uint32_t, p_layers, rid_to_rid, uint32_to_uint32); +uint32_t GodotNavigationServer2D::obstacle_get_avoidance_layers(RID p_obstacle) const { + return NavigationServer3D::get_singleton()->obstacle_get_avoidance_layers(p_obstacle); +} void GodotNavigationServer2D::obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) { NavigationServer3D::get_singleton()->obstacle_set_vertices(p_obstacle, vector_v2_to_v3(p_vertices)); } +Vector<Vector2> GodotNavigationServer2D::obstacle_get_vertices(RID p_obstacle) const { + return vector_v3_to_v2(NavigationServer3D::get_singleton()->obstacle_get_vertices(p_obstacle)); +} void GodotNavigationServer2D::query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const { ERR_FAIL_COND(!p_query_parameters.is_valid()); diff --git a/modules/navigation/godot_navigation_server_2d.h b/modules/navigation/godot_navigation_server_2d.h index 88dee0ce69..2f473da1ab 100644 --- a/modules/navigation/godot_navigation_server_2d.h +++ b/modules/navigation/godot_navigation_server_2d.h @@ -95,6 +95,7 @@ public: virtual void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) override; virtual uint32_t region_get_navigation_layers(RID p_region) const override; virtual void region_set_transform(RID p_region, Transform2D p_transform) override; + virtual Transform2D region_get_transform(RID p_region) const override; virtual void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) override; virtual int region_get_connections_count(RID p_region) const override; virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override; @@ -159,6 +160,7 @@ public: /// low, the simulation will not be safe. /// Must be non-negative. virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance) override; + virtual real_t agent_get_neighbor_distance(RID p_agent) const override; /// The maximum number of other agents this /// agent takes into account in the navigation. @@ -167,6 +169,7 @@ public: /// number is too low, the simulation will not /// be safe. virtual void agent_set_max_neighbors(RID p_agent, int p_count) override; + virtual int agent_get_max_neighbors(RID p_agent) const override; /// The minimal amount of time for which this /// agent's velocities that are computed by the @@ -176,17 +179,20 @@ public: /// other agents, but the less freedom this /// agent has in choosing its velocities. /// Must be positive. - virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) override; + virtual real_t agent_get_time_horizon_agents(RID p_agent) const override; virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) override; + virtual real_t agent_get_time_horizon_obstacles(RID p_agent) const override; /// The radius of this agent. /// Must be non-negative. virtual void agent_set_radius(RID p_agent, real_t p_radius) override; + virtual real_t agent_get_radius(RID p_agent) const override; /// The maximum speed of this agent. /// Must be non-negative. virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) override; + virtual real_t agent_get_max_speed(RID p_agent) const override; /// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) override; @@ -194,19 +200,27 @@ public: /// The wanted velocity for the agent as a "suggestion" to the avoidance simulation. /// The simulation will try to fulfill this velocity wish if possible but may change the velocity depending on other agent's and obstacles'. virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) override; + virtual Vector2 agent_get_velocity(RID p_agent) const override; /// Position of the agent in world space. virtual void agent_set_position(RID p_agent, Vector2 p_position) override; + virtual Vector2 agent_get_position(RID p_agent) const override; /// Returns true if the map got changed the previous frame. virtual bool agent_is_map_changed(RID p_agent) const override; /// Callback called at the end of the RVO process virtual void agent_set_avoidance_callback(RID p_agent, Callable p_callback) override; + virtual bool agent_has_avoidance_callback(RID p_agent) const override; virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) override; + virtual uint32_t agent_get_avoidance_layers(RID p_agent) const override; + virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) override; + virtual uint32_t agent_get_avoidance_mask(RID p_agent) const override; + virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) override; + virtual real_t agent_get_avoidance_priority(RID p_agent) const override; virtual RID obstacle_create() override; virtual void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled) override; @@ -216,10 +230,15 @@ public: virtual void obstacle_set_paused(RID p_obstacle, bool p_paused) override; virtual bool obstacle_get_paused(RID p_obstacle) const override; virtual void obstacle_set_radius(RID p_obstacle, real_t p_radius) override; + virtual real_t obstacle_get_radius(RID p_obstacle) const override; virtual void obstacle_set_velocity(RID p_obstacle, Vector2 p_velocity) override; + virtual Vector2 obstacle_get_velocity(RID p_obstacle) const override; virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position) override; + virtual Vector2 obstacle_get_position(RID p_obstacle) const override; virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) override; + virtual Vector<Vector2> obstacle_get_vertices(RID p_obstacle) const override; virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override; + virtual uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const override; virtual void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const override; diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 3b875b7fa7..6429513b53 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -372,7 +372,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p // Stores the further reachable end polygon, in case our goal is not reachable. if (is_reachable) { - real_t d = navigation_polys[least_cost_id].entry.distance_to(p_destination) * navigation_polys[least_cost_id].poly->owner->get_travel_cost(); + real_t d = navigation_polys[least_cost_id].entry.distance_to(p_destination); if (reachable_d > d) { reachable_d = d; reachable_end = navigation_polys[least_cost_id].poly; diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml index 454f8f2ed4..8698c5755a 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -76,7 +76,7 @@ <method name="get_signaling_state" qualifiers="const"> <return type="int" enum="WebRTCPeerConnection.SignalingState" /> <description> - Returns the [enum SignalingState] on the local end of the connection while connecting or reconnecting to another peer. + Returns the signaling state on the local end of the connection while connecting or reconnecting to another peer. </description> </method> <method name="initialize"> diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index e1d842422c..bbb4b04508 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -6108,7 +6108,8 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode if (p_screen == SCREEN_OF_MAIN_WINDOW) { p_screen = SCREEN_PRIMARY; } - window_position = screen_get_position(p_screen) + (screen_get_size(p_screen) - p_resolution) / 2; + Rect2i scr_rect = screen_get_usable_rect(p_screen); + window_position = scr_rect.position + (scr_rect.size - p_resolution) / 2; } WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution)); diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index f012292f84..a19e02eb21 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -140,6 +140,8 @@ private: String rendering_driver; NSMenu *apple_menu = nullptr; + NSMenu *window_menu = nullptr; + NSMenu *help_menu = nullptr; NSMenu *dock_menu = nullptr; struct MenuData { Callable open; @@ -226,7 +228,8 @@ private: static NSCursor *_cursor_from_selector(SEL p_selector, SEL p_fallback = nil); - bool _has_help_menu() const; + int _get_system_menu_start(const NSMenu *p_menu) const; + int _get_system_menu_count(const NSMenu *p_menu) const; NSMenuItem *_menu_add_item(const String &p_menu_root, const String &p_label, Key p_accel, int p_index, int *r_out); public: @@ -321,6 +324,8 @@ public: virtual void global_menu_remove_item(const String &p_menu_root, int p_idx) override; virtual void global_menu_clear(const String &p_menu_root) override; + virtual Dictionary global_menu_get_system_menu_roots() const override; + virtual bool tts_is_speaking() const override; virtual bool tts_is_paused() const override; virtual TypedArray<Dictionary> tts_get_voices() const override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 407a315827..4936b83b59 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -64,6 +64,9 @@ #import <IOKit/hid/IOHIDKeys.h> #import <IOKit/hid/IOHIDLib.h> +#define MENU_TAG_START 0x00004447 +#define MENU_TAG_END 0xFFFF4447 + const NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) const { const NSMenu *menu = nullptr; if (p_menu_root == "" || p_menu_root.to_lower() == "_main") { @@ -72,16 +75,21 @@ const NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) cons } else if (p_menu_root.to_lower() == "_dock") { // macOS dock menu. menu = dock_menu; + } else if (p_menu_root.to_lower() == "_apple") { + // macOS Apple menu. + menu = apple_menu; + } else if (p_menu_root.to_lower() == "_window") { + // macOS Window menu. + menu = window_menu; + } else if (p_menu_root.to_lower() == "_help") { + // macOS Help menu. + menu = help_menu; } else { // Submenu. if (submenu.has(p_menu_root)) { menu = submenu[p_menu_root].menu; } } - if (menu == apple_menu) { - // Do not allow to change Apple menu. - return nullptr; - } return menu; } @@ -93,6 +101,15 @@ NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) { } else if (p_menu_root.to_lower() == "_dock") { // macOS dock menu. menu = dock_menu; + } else if (p_menu_root.to_lower() == "_apple") { + // macOS Apple menu. + menu = apple_menu; + } else if (p_menu_root.to_lower() == "_window") { + // macOS Window menu. + menu = window_menu; + } else if (p_menu_root.to_lower() == "_help") { + // macOS Help menu. + menu = help_menu; } else { // Submenu. if (!submenu.has(p_menu_root)) { @@ -104,10 +121,6 @@ NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) { } menu = submenu[p_menu_root].menu; } - if (menu == apple_menu) { - // Do not allow to change Apple menu. - return nullptr; - } return menu; } @@ -820,22 +833,6 @@ String DisplayServerMacOS::get_name() const { return "macOS"; } -bool DisplayServerMacOS::_has_help_menu() const { - if ([NSApp helpMenu]) { - return true; - } else { - NSMenu *menu = [NSApp mainMenu]; - const NSMenuItem *menu_item = [menu itemAtIndex:[menu numberOfItems] - 1]; - if (menu_item) { - String menu_name = String::utf8([[menu_item title] UTF8String]); - if (menu_name == "Help" || menu_name == RTR("Help")) { - return true; - } - } - return false; - } -} - bool DisplayServerMacOS::_is_menu_opened(NSMenu *p_menu) const { if (submenu_inv.has(p_menu)) { const MenuData &md = submenu[submenu_inv[p_menu]]; @@ -854,24 +851,57 @@ bool DisplayServerMacOS::_is_menu_opened(NSMenu *p_menu) const { return false; } +int DisplayServerMacOS::_get_system_menu_start(const NSMenu *p_menu) const { + if (p_menu == [NSApp mainMenu]) { // Skip Apple menu. + return 1; + } + if (p_menu == apple_menu || p_menu == window_menu || p_menu == help_menu) { + int count = [p_menu numberOfItems]; + for (int i = 0; i < count; i++) { + NSMenuItem *menu_item = [p_menu itemAtIndex:i]; + if (menu_item.tag == MENU_TAG_START) { + return i + 1; + } + } + } + return 0; +} + +int DisplayServerMacOS::_get_system_menu_count(const NSMenu *p_menu) const { + if (p_menu == [NSApp mainMenu]) { // Skip Apple, Window and Help menu. + return [p_menu numberOfItems] - 3; + } + if (p_menu == apple_menu || p_menu == window_menu || p_menu == help_menu) { + int start = 0; + int count = [p_menu numberOfItems]; + for (int i = 0; i < count; i++) { + NSMenuItem *menu_item = [p_menu itemAtIndex:i]; + if (menu_item.tag == MENU_TAG_START) { + start = i + 1; + } + if (menu_item.tag == MENU_TAG_END) { + return i - start; + } + } + } + return [p_menu numberOfItems]; +} + NSMenuItem *DisplayServerMacOS::_menu_add_item(const String &p_menu_root, const String &p_label, Key p_accel, int p_index, int *r_out) { NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { String keycode = KeyMappingMacOS::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); NSMenuItem *menu_item; - int item_count = ((menu == [NSApp mainMenu]) && _has_help_menu()) ? [menu numberOfItems] - 1 : [menu numberOfItems]; - if ((menu == [NSApp mainMenu]) && (p_label == "Help" || p_label == RTR("Help"))) { - p_index = [menu numberOfItems]; - } else if (p_index < 0) { - p_index = item_count; + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + if (p_index < 0) { + p_index = item_start + item_count; } else { - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_index++; - } - p_index = CLAMP(p_index, 0, item_count); + p_index += item_start; + p_index = CLAMP(p_index, item_start, item_start + item_count); } menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; - *r_out = (menu == [NSApp mainMenu]) ? p_index - 1 : p_index; + *r_out = p_index - item_start; return menu_item; } return nullptr; @@ -1070,19 +1100,16 @@ int DisplayServerMacOS::global_menu_add_submenu_item(const String &p_menu_root, return -1; } NSMenuItem *menu_item; - int item_count = ((menu == [NSApp mainMenu]) && _has_help_menu()) ? [menu numberOfItems] - 1 : [menu numberOfItems]; - if ((menu == [NSApp mainMenu]) && (p_label == "Help" || p_label == RTR("Help"))) { - p_index = [menu numberOfItems]; - } else if (p_index < 0) { - p_index = item_count; + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + if (p_index < 0) { + p_index = item_start + item_count; } else { - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_index++; - } - p_index = CLAMP(p_index, 0, item_count); + p_index += item_start; + p_index = CLAMP(p_index, item_start, item_start + item_count); } menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@"" atIndex:p_index]; - out = (menu == [NSApp mainMenu]) ? p_index - 1 : p_index; + out = p_index - item_start; GodotMenuItem *obj = [[GodotMenuItem alloc] init]; obj->callback = Callable(); @@ -1105,13 +1132,16 @@ int DisplayServerMacOS::global_menu_add_separator(const String &p_menu_root, int if (menu == [NSApp mainMenu]) { // Do not add separators into main menu. return -1; } + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); if (p_index < 0) { - p_index = [menu numberOfItems]; + p_index = item_start + item_count; } else { - p_index = CLAMP(p_index, 0, [menu numberOfItems]); + p_index += item_start; + p_index = CLAMP(p_index, item_start, item_start + item_count); } [menu insertItem:[NSMenuItem separatorItem] atIndex:p_index]; - return p_index; + return p_index - item_start; } return -1; } @@ -1121,11 +1151,8 @@ int DisplayServerMacOS::global_menu_get_item_index_from_text(const String &p_men const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]] - 1; - } else { - return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; - } + int item_start = _get_system_menu_start(menu); + return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]] - item_start; } return -1; @@ -1136,16 +1163,14 @@ int DisplayServerMacOS::global_menu_get_item_index_from_tag(const String &p_menu const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { - for (NSInteger i = (menu == [NSApp mainMenu]) ? 1 : 0; i < [menu numberOfItems]; i++) { + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + for (NSInteger i = item_start; i < item_start + item_count; i++) { const NSMenuItem *menu_item = [menu itemAtIndex:i]; if (menu_item) { const GodotMenuItem *obj = [menu_item representedObject]; if (obj && obj->meta == p_tag) { - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - return i - 1; - } else { - return i; - } + return i - item_start; } } } @@ -1160,10 +1185,10 @@ bool DisplayServerMacOS::global_menu_is_item_checked(const String &p_menu_root, const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, false); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { return ([menu_item state] == NSControlStateValueOn); @@ -1178,10 +1203,10 @@ bool DisplayServerMacOS::global_menu_is_item_checkable(const String &p_menu_root const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, false); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1199,10 +1224,10 @@ bool DisplayServerMacOS::global_menu_is_item_radio_checkable(const String &p_men const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, false); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1220,10 +1245,10 @@ Callable DisplayServerMacOS::global_menu_get_item_callback(const String &p_menu_ const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, Callable()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Callable()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Callable()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1241,10 +1266,10 @@ Callable DisplayServerMacOS::global_menu_get_item_key_callback(const String &p_m const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, Callable()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Callable()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Callable()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1262,10 +1287,10 @@ Variant DisplayServerMacOS::global_menu_get_item_tag(const String &p_menu_root, const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, Variant()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Variant()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Variant()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1283,10 +1308,10 @@ String DisplayServerMacOS::global_menu_get_item_text(const String &p_menu_root, const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, String()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], String()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, String()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { return String::utf8([[menu_item title] UTF8String]); @@ -1301,10 +1326,10 @@ String DisplayServerMacOS::global_menu_get_item_submenu(const String &p_menu_roo const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, String()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], String()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, String()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { NSMenu *sub_menu = [menu_item submenu]; @@ -1322,10 +1347,10 @@ Key DisplayServerMacOS::global_menu_get_item_accelerator(const String &p_menu_ro const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, Key::NONE); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Key::NONE); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Key::NONE); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { String ret = String::utf8([[menu_item keyEquivalent] UTF8String]); @@ -1358,10 +1383,10 @@ bool DisplayServerMacOS::global_menu_is_item_disabled(const String &p_menu_root, const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, false); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { return ![menu_item isEnabled]; @@ -1376,10 +1401,10 @@ bool DisplayServerMacOS::global_menu_is_item_hidden(const String &p_menu_root, i const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, false); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], false); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, false); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { return [menu_item isHidden]; @@ -1394,10 +1419,10 @@ String DisplayServerMacOS::global_menu_get_item_tooltip(const String &p_menu_roo const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, String()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], String()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, String()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { return String::utf8([[menu_item toolTip] UTF8String]); @@ -1412,10 +1437,10 @@ int DisplayServerMacOS::global_menu_get_item_state(const String &p_menu_root, in const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], 0); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, 0); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1433,10 +1458,10 @@ int DisplayServerMacOS::global_menu_get_item_max_states(const String &p_menu_roo const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], 0); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, 0); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1454,10 +1479,10 @@ Ref<Texture2D> DisplayServerMacOS::global_menu_get_item_icon(const String &p_men const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>()); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], Ref<Texture2D>()); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, Ref<Texture2D>()); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1477,10 +1502,10 @@ int DisplayServerMacOS::global_menu_get_item_indentation_level(const String &p_m const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND_V(p_idx < 0, 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND_V(p_idx >= [menu numberOfItems], 0); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND_V(p_idx >= item_start + item_count, 0); const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { return [menu_item indentationLevel]; @@ -1495,10 +1520,10 @@ void DisplayServerMacOS::global_menu_set_item_checked(const String &p_menu_root, NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { if (p_checked) { @@ -1516,10 +1541,10 @@ void DisplayServerMacOS::global_menu_set_item_checkable(const String &p_menu_roo NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1535,10 +1560,10 @@ void DisplayServerMacOS::global_menu_set_item_radio_checkable(const String &p_me NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1554,10 +1579,10 @@ void DisplayServerMacOS::global_menu_set_item_callback(const String &p_menu_root NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1573,10 +1598,10 @@ void DisplayServerMacOS::global_menu_set_item_hover_callbacks(const String &p_me NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1592,10 +1617,10 @@ void DisplayServerMacOS::global_menu_set_item_key_callback(const String &p_menu_ NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1611,10 +1636,10 @@ void DisplayServerMacOS::global_menu_set_item_tag(const String &p_menu_root, int NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1630,10 +1655,10 @@ void DisplayServerMacOS::global_menu_set_item_text(const String &p_menu_root, in NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { [menu_item setTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; @@ -1647,10 +1672,10 @@ void DisplayServerMacOS::global_menu_set_item_submenu(const String &p_menu_root, NSMenu *menu = _get_menu_root(p_menu_root); if (menu && p_submenu.is_empty()) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) { @@ -1673,10 +1698,10 @@ void DisplayServerMacOS::global_menu_set_item_submenu(const String &p_menu_root, return; } ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { [menu setSubmenu:sub_menu forItem:menu_item]; @@ -1690,10 +1715,10 @@ void DisplayServerMacOS::global_menu_set_item_accelerator(const String &p_menu_r NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { if (p_keycode == Key::NONE) { @@ -1713,10 +1738,10 @@ void DisplayServerMacOS::global_menu_set_item_disabled(const String &p_menu_root NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { [menu_item setEnabled:(!p_disabled)]; @@ -1730,10 +1755,10 @@ void DisplayServerMacOS::global_menu_set_item_hidden(const String &p_menu_root, NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { [menu_item setHidden:p_hidden]; @@ -1747,10 +1772,10 @@ void DisplayServerMacOS::global_menu_set_item_tooltip(const String &p_menu_root, NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { [menu_item setToolTip:[NSString stringWithUTF8String:p_tooltip.utf8().get_data()]]; @@ -1764,10 +1789,10 @@ void DisplayServerMacOS::global_menu_set_item_state(const String &p_menu_root, i NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1783,10 +1808,10 @@ void DisplayServerMacOS::global_menu_set_item_max_states(const String &p_menu_ro NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1802,10 +1827,10 @@ void DisplayServerMacOS::global_menu_set_item_icon(const String &p_menu_root, in NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { GodotMenuItem *obj = [menu_item representedObject]; @@ -1832,10 +1857,10 @@ void DisplayServerMacOS::global_menu_set_item_indentation_level(const String &p_ NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if (menu_item) { [menu_item setIndentationLevel:p_level]; @@ -1848,11 +1873,7 @@ int DisplayServerMacOS::global_menu_get_item_count(const String &p_menu_root) co const NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - return [menu numberOfItems] - 1; - } else { - return [menu numberOfItems]; - } + return _get_system_menu_count(menu); } else { return 0; } @@ -1864,10 +1885,10 @@ void DisplayServerMacOS::global_menu_remove_item(const String &p_menu_root, int NSMenu *menu = _get_menu_root(p_menu_root); if (menu) { ERR_FAIL_COND(p_idx < 0); - if (menu == [NSApp mainMenu]) { // Skip Apple menu. - p_idx++; - } - ERR_FAIL_COND(p_idx >= [menu numberOfItems]); + int item_start = _get_system_menu_start(menu); + int item_count = _get_system_menu_count(menu); + p_idx += item_start; + ERR_FAIL_COND(p_idx >= item_start + item_count); NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; if ([menu_item submenu] && _is_menu_opened([menu_item submenu])) { ERR_PRINT("Can't remove open menu!"); @@ -1886,12 +1907,41 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) { ERR_PRINT("Can't remove open menu!"); return; } - [menu removeAllItems]; - // Restore Apple menu. + + if (menu == apple_menu) { + int start = _get_system_menu_start(apple_menu); + int count = _get_system_menu_count(apple_menu); + for (int i = start + count - 1; i >= start; i--) { + [apple_menu removeItemAtIndex:i]; + } + } else if (menu == window_menu) { + int start = _get_system_menu_start(window_menu); + int count = _get_system_menu_count(window_menu); + for (int i = start + count - 1; i >= start; i--) { + [window_menu removeItemAtIndex:i]; + } + } else if (menu == help_menu) { + int start = _get_system_menu_start(help_menu); + int count = _get_system_menu_count(help_menu); + for (int i = start + count - 1; i >= start; i--) { + [help_menu removeItemAtIndex:i]; + } + } else { + [menu removeAllItems]; + } + + // Restore Apple, Window and Help menu. if (menu == [NSApp mainMenu]) { NSMenuItem *menu_item = [menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; [menu setSubmenu:apple_menu forItem:menu_item]; + + menu_item = [menu addItemWithTitle:@"Window" action:nil keyEquivalent:@""]; + [menu setSubmenu:window_menu forItem:menu_item]; + + menu_item = [menu addItemWithTitle:@"Help" action:nil keyEquivalent:@""]; + [menu setSubmenu:help_menu forItem:menu_item]; } + if (submenu.has(p_menu_root)) { submenu_inv.erase(submenu[p_menu_root].menu); submenu.erase(p_menu_root); @@ -1899,6 +1949,15 @@ void DisplayServerMacOS::global_menu_clear(const String &p_menu_root) { } } +Dictionary DisplayServerMacOS::global_menu_get_system_menu_roots() const { + Dictionary out; + out["_dock"] = "@Dock"; + out["_apple"] = "@Apple"; + out["_window"] = "Window"; + out["_help"] = "Help"; + return out; +} + bool DisplayServerMacOS::tts_is_speaking() const { ERR_FAIL_NULL_V_MSG(tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); return [tts isSpeaking]; @@ -3564,6 +3623,9 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win } break; case WINDOW_FLAG_NO_FOCUS: { wd.no_focus = p_enabled; + + NSWindow *w = wd.window_object; + w.excludedFromWindowsMenu = wd.is_popup || wd.no_focus; } break; case WINDOW_FLAG_MOUSE_PASSTHROUGH: { wd.mpass = p_enabled; @@ -3572,6 +3634,9 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup."); ERR_FAIL_COND_MSG([wd.window_object isVisible] && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened."); wd.is_popup = p_enabled; + + NSWindow *w = wd.window_object; + w.excludedFromWindowsMenu = wd.is_popup || wd.no_focus; } break; default: { } @@ -4488,6 +4553,13 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM [apple_menu addItem:[NSMenuItem separatorItem]]; + menu_item = [apple_menu addItemWithTitle:@"_start_" action:nil keyEquivalent:@""]; + menu_item.hidden = YES; + menu_item.tag = MENU_TAG_START; + menu_item = [apple_menu addItemWithTitle:@"_end_" action:nil keyEquivalent:@""]; + menu_item.hidden = YES; + menu_item.tag = MENU_TAG_END; + NSMenu *services = [[NSMenu alloc] initWithTitle:@""]; menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""]; [apple_menu setSubmenu:services forItem:menu_item]; @@ -4508,10 +4580,41 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname]; [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + window_menu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Window", nil)]; + [window_menu addItemWithTitle:NSLocalizedString(@"Minimize", nil) action:@selector(performMiniaturize:) keyEquivalent:@"m"]; + [window_menu addItemWithTitle:NSLocalizedString(@"Zoom", nil) action:@selector(performZoom:) keyEquivalent:@""]; + [window_menu addItem:[NSMenuItem separatorItem]]; + [window_menu addItemWithTitle:NSLocalizedString(@"Bring All to Front", nil) action:@selector(bringAllToFront:) keyEquivalent:@""]; + [window_menu addItem:[NSMenuItem separatorItem]]; + menu_item = [window_menu addItemWithTitle:@"_start_" action:nil keyEquivalent:@""]; + menu_item.hidden = YES; + menu_item.tag = MENU_TAG_START; + menu_item = [window_menu addItemWithTitle:@"_end_" action:nil keyEquivalent:@""]; + menu_item.hidden = YES; + menu_item.tag = MENU_TAG_END; + + help_menu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Help", nil)]; + menu_item = [help_menu addItemWithTitle:@"_start_" action:nil keyEquivalent:@""]; + menu_item.hidden = YES; + menu_item.tag = MENU_TAG_START; + menu_item = [help_menu addItemWithTitle:@"_end_" action:nil keyEquivalent:@""]; + menu_item.hidden = YES; + menu_item.tag = MENU_TAG_END; + + [NSApp setWindowsMenu:window_menu]; + [NSApp setHelpMenu:help_menu]; + // Add items to the menu bar. NSMenu *main_menu = [NSApp mainMenu]; menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; [main_menu setSubmenu:apple_menu forItem:menu_item]; + + menu_item = [main_menu addItemWithTitle:NSLocalizedString(@"Window", nil) action:nil keyEquivalent:@""]; + [main_menu setSubmenu:window_menu forItem:menu_item]; + + menu_item = [main_menu addItemWithTitle:NSLocalizedString(@"Help", nil) action:nil keyEquivalent:@""]; + [main_menu setSubmenu:help_menu forItem:menu_item]; + [main_menu setAutoenablesItems:NO]; //!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -4564,7 +4667,8 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM if (p_screen == SCREEN_OF_MAIN_WINDOW) { p_screen = SCREEN_PRIMARY; } - window_position = screen_get_position(p_screen) + (screen_get_size(p_screen) - p_resolution) / 2; + Rect2i scr_rect = screen_get_usable_rect(p_screen); + window_position = scr_rect.position + (scr_rect.size - p_resolution) / 2; } WindowID main_window = _create_window(p_mode, p_vsync_mode, Rect2i(window_position, p_resolution)); diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 77dfff2e5d..c35e8d9f96 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -4606,6 +4606,22 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win } } + // Note: Windows Ink API for pen input, available on Windows 8+ only. + // Note: DPI conversion API, available on Windows 8.1+ only. + HMODULE user32_lib = LoadLibraryW(L"user32.dll"); + if (user32_lib) { + win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType"); + win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo"); + win81p_LogicalToPhysicalPointForPerMonitorDPI = (LogicalToPhysicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "LogicalToPhysicalPointForPerMonitorDPI"); + win81p_PhysicalToLogicalPointForPerMonitorDPI = (PhysicalToLogicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "PhysicalToLogicalPointForPerMonitorDPI"); + + winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo; + } + + if (winink_available) { + tablet_drivers.push_back("winink"); + } + // Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink. HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll"); if (wintab_lib) { @@ -4622,21 +4638,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win tablet_drivers.push_back("wintab"); } - // Note: Windows Ink API for pen input, available on Windows 8+ only. - // Note: DPI conversion API, available on Windows 8.1+ only. - HMODULE user32_lib = LoadLibraryW(L"user32.dll"); - if (user32_lib) { - win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType"); - win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo"); - win81p_LogicalToPhysicalPointForPerMonitorDPI = (LogicalToPhysicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "LogicalToPhysicalPointForPerMonitorDPI"); - win81p_PhysicalToLogicalPointForPerMonitorDPI = (PhysicalToLogicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "PhysicalToLogicalPointForPerMonitorDPI"); - - winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo; - } - - if (winink_available) { - tablet_drivers.push_back("winink"); - } + tablet_drivers.push_back("dummy"); if (OS::get_singleton()->is_hidpi_allowed()) { HMODULE Shcore = LoadLibraryW(L"Shcore.dll"); @@ -4761,7 +4763,8 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win if (p_screen == SCREEN_OF_MAIN_WINDOW) { p_screen = SCREEN_PRIMARY; } - window_position = screen_get_position(p_screen) + (screen_get_size(p_screen) - p_resolution) / 2; + Rect2i scr_rect = screen_get_usable_rect(p_screen); + window_position = scr_rect.position + (scr_rect.size - p_resolution) / 2; } WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution)); diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index bf3783966b..08b315fa6c 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -381,6 +381,10 @@ float AnimatedSprite2D::get_playing_speed() const { } void AnimatedSprite2D::set_centered(bool p_center) { + if (centered == p_center) { + return; + } + centered = p_center; queue_redraw(); item_rect_changed(); @@ -391,6 +395,10 @@ bool AnimatedSprite2D::is_centered() const { } void AnimatedSprite2D::set_offset(const Point2 &p_offset) { + if (offset == p_offset) { + return; + } + offset = p_offset; queue_redraw(); item_rect_changed(); @@ -401,6 +409,10 @@ Point2 AnimatedSprite2D::get_offset() const { } void AnimatedSprite2D::set_flip_h(bool p_flip) { + if (hflip == p_flip) { + return; + } + hflip = p_flip; queue_redraw(); } @@ -410,6 +422,10 @@ bool AnimatedSprite2D::is_flipped_h() const { } void AnimatedSprite2D::set_flip_v(bool p_flip) { + if (vflip == p_flip) { + return; + } + vflip = p_flip; queue_redraw(); } diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 2c4bf08f34..b1ff94dda4 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -166,6 +166,21 @@ void Area2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i bool body_in = p_status == PhysicsServer2D::AREA_BODY_ADDED; ObjectID objid = p_instance; + // Exit early if instance is invalid. + if (objid.is_null()) { + // Emit the appropriate signals. + lock_callback(); + locked = true; + if (body_in) { + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, (Node *)nullptr, p_body_shape, p_area_shape); + } else { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, (Node *)nullptr, p_body_shape, p_area_shape); + } + locked = false; + unlock_callback(); + return; + } + Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); @@ -262,6 +277,21 @@ void Area2D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i bool area_in = p_status == PhysicsServer2D::AREA_BODY_ADDED; ObjectID objid = p_instance; + // Exit early if instance is invalid. + if (objid.is_null()) { + // Emit the appropriate signals. + lock_callback(); + locked = true; + if (area_in) { + emit_signal(SceneStringNames::get_singleton()->area_shape_entered, p_area, (Node *)nullptr, p_area_shape, p_self_shape); + } else { + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, p_area, (Node *)nullptr, p_area_shape, p_self_shape); + } + locked = false; + unlock_callback(); + return; + } + Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 1c193f991e..e04e6d7dce 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -881,7 +881,7 @@ void CPUParticles2D::_particles_process(double p_delta) { force += diff.length() > 0.0 ? diff.normalized() * (tex_radial_accel)*Math::lerp(parameters_min[PARAM_RADIAL_ACCEL], parameters_max[PARAM_RADIAL_ACCEL], rand_from_seed(alt_seed)) : Vector2(); //apply tangential acceleration; Vector2 yx = Vector2(diff.y, diff.x); - force += yx.length() > 0.0 ? yx.normalized() * (tex_tangential_accel * Math::lerp(parameters_min[PARAM_TANGENTIAL_ACCEL], parameters_max[PARAM_TANGENTIAL_ACCEL], rand_from_seed(alt_seed))) : Vector2(); + force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)).normalized() * (tex_tangential_accel * Math::lerp(parameters_min[PARAM_TANGENTIAL_ACCEL], parameters_max[PARAM_TANGENTIAL_ACCEL], rand_from_seed(alt_seed))) : Vector2(); //apply attractor forces p.velocity += force * local_delta; //orbit velocity diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index dbbba315d4..fee774fe2e 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -503,7 +503,7 @@ void NavigationAgent2D::set_max_neighbors(int p_count) { } void NavigationAgent2D::set_time_horizon_agents(real_t p_time_horizon) { - ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive."); + ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizon must be positive."); if (Math::is_equal_approx(time_horizon_agents, p_time_horizon)) { return; } @@ -512,7 +512,7 @@ void NavigationAgent2D::set_time_horizon_agents(real_t p_time_horizon) { } void NavigationAgent2D::set_time_horizon_obstacles(real_t p_time_horizon) { - ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive."); + ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizon must be positive."); if (Math::is_equal_approx(time_horizon_obstacles, p_time_horizon)) { return; } diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp index 6d0a2968d7..d3be5b9c65 100644 --- a/scene/2d/sprite_2d.cpp +++ b/scene/2d/sprite_2d.cpp @@ -155,6 +155,10 @@ Ref<Texture2D> Sprite2D::get_texture() const { } void Sprite2D::set_centered(bool p_center) { + if (centered == p_center) { + return; + } + centered = p_center; queue_redraw(); item_rect_changed(); @@ -165,6 +169,10 @@ bool Sprite2D::is_centered() const { } void Sprite2D::set_offset(const Point2 &p_offset) { + if (offset == p_offset) { + return; + } + offset = p_offset; queue_redraw(); item_rect_changed(); @@ -175,6 +183,10 @@ Point2 Sprite2D::get_offset() const { } void Sprite2D::set_flip_h(bool p_flip) { + if (hflip == p_flip) { + return; + } + hflip = p_flip; queue_redraw(); } @@ -184,6 +196,10 @@ bool Sprite2D::is_flipped_h() const { } void Sprite2D::set_flip_v(bool p_flip) { + if (vflip == p_flip) { + return; + } + vflip = p_flip; queue_redraw(); } @@ -193,7 +209,7 @@ bool Sprite2D::is_flipped_v() const { } void Sprite2D::set_region_enabled(bool p_region_enabled) { - if (p_region_enabled == region_enabled) { + if (region_enabled == p_region_enabled) { return; } @@ -223,6 +239,10 @@ Rect2 Sprite2D::get_region_rect() const { } void Sprite2D::set_region_filter_clip_enabled(bool p_region_filter_clip_enabled) { + if (region_filter_clip_enabled == p_region_filter_clip_enabled) { + return; + } + region_filter_clip_enabled = p_region_filter_clip_enabled; queue_redraw(); } @@ -234,12 +254,12 @@ bool Sprite2D::is_region_filter_clip_enabled() const { void Sprite2D::set_frame(int p_frame) { ERR_FAIL_INDEX(p_frame, vframes * hframes); - if (frame != p_frame) { - item_rect_changed(); + if (frame == p_frame) { + return; } frame = p_frame; - + item_rect_changed(); emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -260,6 +280,11 @@ Vector2i Sprite2D::get_frame_coords() const { void Sprite2D::set_vframes(int p_amount) { ERR_FAIL_COND_MSG(p_amount < 1, "Amount of vframes cannot be smaller than 1."); + + if (vframes == p_amount) { + return; + } + vframes = p_amount; if (frame >= vframes * hframes) { frame = 0; @@ -275,6 +300,11 @@ int Sprite2D::get_vframes() const { void Sprite2D::set_hframes(int p_amount) { ERR_FAIL_COND_MSG(p_amount < 1, "Amount of hframes cannot be smaller than 1."); + + if (hframes == p_amount) { + return; + } + if (vframes > 1) { // Adjust the frame to fit new sheet dimensions. int original_column = frame % hframes; diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index 585bce09fb..014c33cad0 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -223,6 +223,21 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i bool body_in = p_status == PhysicsServer3D::AREA_BODY_ADDED; ObjectID objid = p_instance; + // Exit early if instance is invalid. + if (objid.is_null()) { + lock_callback(); + locked = true; + // Emit the appropriate signals. + if (body_in) { + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, (Node *)nullptr, p_body_shape, p_area_shape); + } else { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, (Node *)nullptr, p_body_shape, p_area_shape); + } + locked = false; + unlock_callback(); + return; + } + Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); @@ -254,7 +269,7 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i E->value.shapes.insert(ShapePair(p_body_shape, p_area_shape)); } - if (E->value.in_tree) { + if (!node || E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_area_shape); } @@ -276,7 +291,7 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i } } } - if (node && in_tree) { + if (!node || in_tree) { emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, obj, p_body_shape, p_area_shape); } } @@ -414,6 +429,21 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i bool area_in = p_status == PhysicsServer3D::AREA_BODY_ADDED; ObjectID objid = p_instance; + // Exit if instance is invalid. + if (objid.is_null()) { + lock_callback(); + locked = true; + // Emit the appropriate signals. + if (area_in) { + emit_signal(SceneStringNames::get_singleton()->area_shape_entered, p_area, (Node *)nullptr, p_area_shape, p_self_shape); + } else { + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, p_area, (Node *)nullptr, p_area_shape, p_self_shape); + } + locked = false; + unlock_callback(); + return; + } + Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 8dc17a1c62..c66d9bd2c9 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -272,7 +272,7 @@ void Camera3D::clear_current(bool p_enable_next) { if (get_viewport()->get_camera_3d() == this) { get_viewport()->_camera_3d_set(nullptr); - if (p_enable_next) { + if (p_enable_next && !Engine::get_singleton()->is_editor_hint()) { get_viewport()->_camera_3d_make_next_current(this); } } diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 187ce90284..ebcd69455f 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -377,7 +377,7 @@ void Light3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01,degrees"), "set_param", "get_param", PARAM_SIZE); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_specular", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_SPECULAR); - ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI/SDFGI only)"), "set_bake_mode", "get_bake_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Static,Dynamic"), "set_bake_mode", "get_bake_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); ADD_GROUP("Shadow", "shadow_"); diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index b67935018c..eb52d4540e 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -567,7 +567,7 @@ void NavigationAgent3D::set_max_neighbors(int p_count) { } void NavigationAgent3D::set_time_horizon_agents(real_t p_time_horizon) { - ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive."); + ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizon must be positive."); if (Math::is_equal_approx(time_horizon_agents, p_time_horizon)) { return; } @@ -576,7 +576,7 @@ void NavigationAgent3D::set_time_horizon_agents(real_t p_time_horizon) { } void NavigationAgent3D::set_time_horizon_obstacles(real_t p_time_horizon) { - ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizion must be positive."); + ERR_FAIL_COND_MSG(p_time_horizon < 0.0, "Time horizon must be positive."); if (Math::is_equal_approx(time_horizon_obstacles, p_time_horizon)) { return; } diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index ffd328c37e..b8b0d31d45 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -286,6 +286,10 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, } void SpriteBase3D::set_centered(bool p_center) { + if (centered == p_center) { + return; + } + centered = p_center; _queue_redraw(); } @@ -295,6 +299,10 @@ bool SpriteBase3D::is_centered() const { } void SpriteBase3D::set_offset(const Point2 &p_offset) { + if (offset == p_offset) { + return; + } + offset = p_offset; _queue_redraw(); } @@ -304,6 +312,10 @@ Point2 SpriteBase3D::get_offset() const { } void SpriteBase3D::set_flip_h(bool p_flip) { + if (hflip == p_flip) { + return; + } + hflip = p_flip; _queue_redraw(); } @@ -313,6 +325,10 @@ bool SpriteBase3D::is_flipped_h() const { } void SpriteBase3D::set_flip_v(bool p_flip) { + if (vflip == p_flip) { + return; + } + vflip = p_flip; _queue_redraw(); } @@ -322,6 +338,10 @@ bool SpriteBase3D::is_flipped_v() const { } void SpriteBase3D::set_modulate(const Color &p_color) { + if (modulate == p_color) { + return; + } + modulate = p_color; _propagate_color_changed(); _queue_redraw(); @@ -333,6 +353,11 @@ Color SpriteBase3D::get_modulate() const { void SpriteBase3D::set_render_priority(int p_priority) { ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX); + + if (render_priority == p_priority) { + return; + } + render_priority = p_priority; _queue_redraw(); } @@ -342,6 +367,10 @@ int SpriteBase3D::get_render_priority() const { } void SpriteBase3D::set_pixel_size(real_t p_amount) { + if (pixel_size == p_amount) { + return; + } + pixel_size = p_amount; _queue_redraw(); } @@ -352,6 +381,11 @@ real_t SpriteBase3D::get_pixel_size() const { void SpriteBase3D::set_axis(Vector3::Axis p_axis) { ERR_FAIL_INDEX(p_axis, 3); + + if (axis == p_axis) { + return; + } + axis = p_axis; _queue_redraw(); } @@ -445,6 +479,11 @@ Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const { void SpriteBase3D::set_draw_flag(DrawFlags p_flag, bool p_enable) { ERR_FAIL_INDEX(p_flag, FLAG_MAX); + + if (flags[p_flag] == p_enable) { + return; + } + flags[p_flag] = p_enable; _queue_redraw(); } @@ -456,6 +495,11 @@ bool SpriteBase3D::get_draw_flag(DrawFlags p_flag) const { void SpriteBase3D::set_alpha_cut_mode(AlphaCutMode p_mode) { ERR_FAIL_INDEX(p_mode, ALPHA_CUT_MAX); + + if (alpha_cut == p_mode) { + return; + } + alpha_cut = p_mode; _queue_redraw(); } @@ -465,10 +509,12 @@ SpriteBase3D::AlphaCutMode SpriteBase3D::get_alpha_cut_mode() const { } void SpriteBase3D::set_alpha_hash_scale(float p_hash_scale) { - if (alpha_hash_scale != p_hash_scale) { - alpha_hash_scale = p_hash_scale; - _queue_redraw(); + if (alpha_hash_scale == p_hash_scale) { + return; } + + alpha_hash_scale = p_hash_scale; + _queue_redraw(); } float SpriteBase3D::get_alpha_hash_scale() const { @@ -476,10 +522,12 @@ float SpriteBase3D::get_alpha_hash_scale() const { } void SpriteBase3D::set_alpha_scissor_threshold(float p_threshold) { - if (alpha_scissor_threshold != p_threshold) { - alpha_scissor_threshold = p_threshold; - _queue_redraw(); + if (alpha_scissor_threshold == p_threshold) { + return; } + + alpha_scissor_threshold = p_threshold; + _queue_redraw(); } float SpriteBase3D::get_alpha_scissor_threshold() const { @@ -487,10 +535,12 @@ float SpriteBase3D::get_alpha_scissor_threshold() const { } void SpriteBase3D::set_alpha_antialiasing(BaseMaterial3D::AlphaAntiAliasing p_alpha_aa) { - if (alpha_antialiasing_mode != p_alpha_aa) { - alpha_antialiasing_mode = p_alpha_aa; - _queue_redraw(); + if (alpha_antialiasing_mode == p_alpha_aa) { + return; } + + alpha_antialiasing_mode = p_alpha_aa; + _queue_redraw(); } BaseMaterial3D::AlphaAntiAliasing SpriteBase3D::get_alpha_antialiasing() const { @@ -498,10 +548,12 @@ BaseMaterial3D::AlphaAntiAliasing SpriteBase3D::get_alpha_antialiasing() const { } void SpriteBase3D::set_alpha_antialiasing_edge(float p_edge) { - if (alpha_antialiasing_edge != p_edge) { - alpha_antialiasing_edge = p_edge; - _queue_redraw(); + if (alpha_antialiasing_edge == p_edge) { + return; } + + alpha_antialiasing_edge = p_edge; + _queue_redraw(); } float SpriteBase3D::get_alpha_antialiasing_edge() const { @@ -510,6 +562,11 @@ float SpriteBase3D::get_alpha_antialiasing_edge() const { void SpriteBase3D::set_billboard_mode(StandardMaterial3D::BillboardMode p_mode) { ERR_FAIL_INDEX(p_mode, 3); // Cannot use BILLBOARD_PARTICLES. + + if (billboard_mode == p_mode) { + return; + } + billboard_mode = p_mode; _queue_redraw(); } @@ -519,10 +576,12 @@ StandardMaterial3D::BillboardMode SpriteBase3D::get_billboard_mode() const { } void SpriteBase3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) { - if (texture_filter != p_filter) { - texture_filter = p_filter; - _queue_redraw(); + if (texture_filter == p_filter) { + return; } + + texture_filter = p_filter; + _queue_redraw(); } StandardMaterial3D::TextureFilter SpriteBase3D::get_texture_filter() const { @@ -767,9 +826,12 @@ bool Sprite3D::is_region_enabled() const { } void Sprite3D::set_region_rect(const Rect2 &p_region_rect) { - bool changed = region_rect != p_region_rect; + if (region_rect == p_region_rect) { + return; + } + region_rect = p_region_rect; - if (region && changed) { + if (region) { _queue_redraw(); } } @@ -781,10 +843,12 @@ Rect2 Sprite3D::get_region_rect() const { void Sprite3D::set_frame(int p_frame) { ERR_FAIL_INDEX(p_frame, int64_t(vframes) * hframes); - frame = p_frame; + if (frame == p_frame) { + return; + } + frame = p_frame; _queue_redraw(); - emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -805,6 +869,11 @@ Vector2i Sprite3D::get_frame_coords() const { void Sprite3D::set_vframes(int p_amount) { ERR_FAIL_COND_MSG(p_amount < 1, "Amount of vframes cannot be smaller than 1."); + + if (vframes == p_amount) { + return; + } + vframes = p_amount; if (frame >= vframes * hframes) { frame = 0; @@ -819,6 +888,11 @@ int Sprite3D::get_vframes() const { void Sprite3D::set_hframes(int p_amount) { ERR_FAIL_COND_MSG(p_amount < 1, "Amount of hframes cannot be smaller than 1."); + + if (hframes == p_amount) { + return; + } + if (vframes > 1) { // Adjust the frame to fit new sheet dimensions. int original_column = frame % hframes; diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 3b1faca17e..33f865382d 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -511,7 +511,7 @@ void GeometryInstance3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_occlusion_culling"), "set_ignore_occlusion_culling", "is_ignoring_occlusion_culling"); ADD_GROUP("Global Illumination", "gi_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)"), "set_gi_mode", "get_gi_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Static,Dynamic"), "set_gi_mode", "get_gi_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, String::utf8("1×,2×,4×,8×")), "set_lightmap_scale", "get_lightmap_scale"); ADD_GROUP("Visibility Range", "visibility_range_"); diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 9ee7d01d0e..e63e1ee42a 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -69,152 +69,173 @@ void CodeEdit::_notification(int p_what) { } } - bool code_completion_below = false; - if (caret_visible && code_completion_active && code_completion_options.size() > 0) { - const int code_completion_options_count = code_completion_options.size(); - const int lines = MIN(code_completion_options_count, theme_cache.code_completion_max_lines); - const Size2 icon_area_size(row_height, row_height); - - code_completion_rect.size.width = code_completion_longest_line + theme_cache.code_completion_icon_separation + icon_area_size.width + 2; - code_completion_rect.size.height = lines * row_height; - - const Point2 caret_pos = get_caret_draw_pos(); - const int total_height = theme_cache.code_completion_style->get_minimum_size().y + code_completion_rect.size.height; - const bool can_fit_completion_above = (caret_pos.y - row_height > total_height); - const bool can_fit_completion_below = (caret_pos.y + row_height + total_height <= get_size().height); - if (!can_fit_completion_below && can_fit_completion_above) { - code_completion_rect.position.y = (caret_pos.y - total_height - row_height) + theme_cache.line_spacing; - } else { - code_completion_rect.position.y = caret_pos.y + (theme_cache.line_spacing / 2.0f); - code_completion_below = true; - } + if (caret_visible) { + const bool draw_code_completion = code_completion_active && !code_completion_options.is_empty(); + const bool draw_code_hint = !code_hint.is_empty(); - const int scroll_width = code_completion_options_count > theme_cache.code_completion_max_lines ? theme_cache.code_completion_scroll_width : 0; - const int code_completion_base_width = theme_cache.font->get_string_size(code_completion_base, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width; - if (caret_pos.x - code_completion_base_width + code_completion_rect.size.width + scroll_width > get_size().width) { - code_completion_rect.position.x = get_size().width - code_completion_rect.size.width - scroll_width; - } else { - code_completion_rect.position.x = caret_pos.x - code_completion_base_width; - } + /* Code hint */ + Size2 code_hint_minsize; + if (draw_code_hint) { + const int font_height = theme_cache.font->get_height(theme_cache.font_size); - draw_style_box(theme_cache.code_completion_style, Rect2(code_completion_rect.position - theme_cache.code_completion_style->get_offset(), code_completion_rect.size + theme_cache.code_completion_style->get_minimum_size() + Size2(scroll_width, 0))); - if (theme_cache.code_completion_background_color.a > 0.01) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(code_completion_rect.position, code_completion_rect.size + Size2(scroll_width, 0)), theme_cache.code_completion_background_color); - } + Vector<String> code_hint_lines = code_hint.split("\n"); + int line_count = code_hint_lines.size(); - code_completion_scroll_rect.position = code_completion_rect.position + Vector2(code_completion_rect.size.width, 0); - code_completion_scroll_rect.size = Vector2(scroll_width, code_completion_rect.size.height); + int max_width = 0; + for (int i = 0; i < line_count; i++) { + max_width = MAX(max_width, theme_cache.font->get_string_size(code_hint_lines[i], HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x); + } + code_hint_minsize = theme_cache.code_hint_style->get_minimum_size() + Size2(max_width, line_count * font_height + (theme_cache.line_spacing * line_count - 1)); - code_completion_line_ofs = CLAMP((code_completion_force_item_center < 0 ? code_completion_current_selected : code_completion_force_item_center) - lines / 2, 0, code_completion_options_count - lines); - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(code_completion_rect.position.x, code_completion_rect.position.y + (code_completion_current_selected - code_completion_line_ofs) * row_height), Size2(code_completion_rect.size.width, row_height)), theme_cache.code_completion_selected_color); + int offset = theme_cache.font->get_string_size(code_hint_lines[0].substr(0, code_hint_lines[0].find(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x; + if (code_hint_xpos == -0xFFFF) { + code_hint_xpos = get_caret_draw_pos().x - offset; + } + Point2 hint_ofs = Vector2(code_hint_xpos, get_caret_draw_pos().y); + if (code_hint_draw_below) { + hint_ofs.y += theme_cache.line_spacing / 2.0f; + } else { + hint_ofs.y -= (code_hint_minsize.y + row_height) - theme_cache.line_spacing; + } - for (int i = 0; i < lines; i++) { - int l = code_completion_line_ofs + i; - ERR_CONTINUE(l < 0 || l >= code_completion_options_count); + draw_style_box(theme_cache.code_hint_style, Rect2(hint_ofs, code_hint_minsize)); - Ref<TextLine> tl; - tl.instantiate(); - tl->add_string(code_completion_options[l].display, theme_cache.font, theme_cache.font_size); + int yofs = 0; + for (int i = 0; i < line_count; i++) { + const String &line = code_hint_lines[i]; - int yofs = (row_height - tl->get_size().y) / 2; - Point2 title_pos(code_completion_rect.position.x, code_completion_rect.position.y + i * row_height + yofs); + int begin = 0; + int end = 0; + if (line.contains(String::chr(0xFFFF))) { + begin = theme_cache.font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x; + end = theme_cache.font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x; + } + + Point2 round_ofs = hint_ofs + theme_cache.code_hint_style->get_offset() + Vector2(0, theme_cache.font->get_ascent(theme_cache.font_size) + font_height * i + yofs); + round_ofs = round_ofs.round(); + draw_string(theme_cache.font, round_ofs, line.replace(String::chr(0xFFFF), ""), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size, theme_cache.code_hint_color); + if (end > 0) { + // Draw an underline for the currently edited function parameter. + const Vector2 b = hint_ofs + theme_cache.code_hint_style->get_offset() + Vector2(begin, font_height + font_height * i + yofs); + draw_line(b, b + Vector2(end - begin, 0), theme_cache.code_hint_color, 2); + + // Draw a translucent text highlight as well. + const Rect2 highlight_rect = Rect2( + b - Vector2(0, font_height), + Vector2(end - begin, font_height)); + draw_rect(highlight_rect, theme_cache.code_hint_color * Color(1, 1, 1, 0.2)); + } + yofs += theme_cache.line_spacing; + } + } - /* Draw completion icon if it is valid. */ - const Ref<Texture2D> &icon = code_completion_options[l].icon; - Rect2 icon_area(code_completion_rect.position.x, code_completion_rect.position.y + i * row_height, icon_area_size.width, icon_area_size.height); - if (icon.is_valid()) { - Size2 icon_size = icon_area.size * 0.7; - icon->draw_rect(ci, Rect2(icon_area.position + (icon_area.size - icon_size) / 2, icon_size)); + /* Code completion */ + if (draw_code_completion) { + const int code_completion_options_count = code_completion_options.size(); + const int lines = MIN(code_completion_options_count, theme_cache.code_completion_max_lines); + const Size2 icon_area_size(row_height, row_height); + + code_completion_rect.size.width = code_completion_longest_line + theme_cache.code_completion_icon_separation + icon_area_size.width + 2; + code_completion_rect.size.height = lines * row_height; + + const Point2 caret_pos = get_caret_draw_pos(); + const int total_height = theme_cache.code_completion_style->get_minimum_size().y + code_completion_rect.size.height; + int min_y = caret_pos.y - row_height; + int max_y = caret_pos.y + row_height + total_height; + if (draw_code_hint) { + if (code_hint_draw_below) { + max_y += code_hint_minsize.y; + } else { + min_y -= code_hint_minsize.y; + } } - title_pos.x = icon_area.position.x + icon_area.size.width + theme_cache.code_completion_icon_separation; - tl->set_width(code_completion_rect.size.width - (icon_area_size.x + theme_cache.code_completion_icon_separation)); - if (rtl) { - if (code_completion_options[l].default_value.get_type() == Variant::COLOR) { - draw_rect(Rect2(Point2(code_completion_rect.position.x, icon_area.position.y), icon_area_size), (Color)code_completion_options[l].default_value); + const bool can_fit_completion_above = (min_y > total_height); + const bool can_fit_completion_below = (max_y <= get_size().height); + if (!can_fit_completion_below && can_fit_completion_above) { + code_completion_rect.position.y = (caret_pos.y - total_height - row_height) + theme_cache.line_spacing; + if (draw_code_hint && !code_hint_draw_below) { + code_completion_rect.position.y -= code_hint_minsize.y; } - tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); } else { - if (code_completion_options[l].default_value.get_type() == Variant::COLOR) { - draw_rect(Rect2(Point2(code_completion_rect.position.x + code_completion_rect.size.width - icon_area_size.x, icon_area.position.y), icon_area_size), (Color)code_completion_options[l].default_value); + code_completion_rect.position.y = caret_pos.y + (theme_cache.line_spacing / 2.0f); + if (draw_code_hint && code_hint_draw_below) { + code_completion_rect.position.y += code_hint_minsize.y; } - tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT); } - Point2 match_pos = Point2(code_completion_rect.position.x + icon_area_size.x + theme_cache.code_completion_icon_separation, code_completion_rect.position.y + i * row_height); - - for (int j = 0; j < code_completion_options[l].matches.size(); j++) { - Pair<int, int> match_segment = code_completion_options[l].matches[j]; - int match_offset = theme_cache.font->get_string_size(code_completion_options[l].display.substr(0, match_segment.first), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width; - int match_len = theme_cache.font->get_string_size(code_completion_options[l].display.substr(match_segment.first, match_segment.second), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width; + const int scroll_width = code_completion_options_count > theme_cache.code_completion_max_lines ? theme_cache.code_completion_scroll_width : 0; + const int code_completion_base_width = theme_cache.font->get_string_size(code_completion_base, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width; + if (caret_pos.x - code_completion_base_width + code_completion_rect.size.width + scroll_width > get_size().width) { + code_completion_rect.position.x = get_size().width - code_completion_rect.size.width - scroll_width; + } else { + code_completion_rect.position.x = caret_pos.x - code_completion_base_width; + } - draw_rect(Rect2(match_pos + Point2(match_offset, 0), Size2(match_len, row_height)), theme_cache.code_completion_existing_color); + draw_style_box(theme_cache.code_completion_style, Rect2(code_completion_rect.position - theme_cache.code_completion_style->get_offset(), code_completion_rect.size + theme_cache.code_completion_style->get_minimum_size() + Size2(scroll_width, 0))); + if (theme_cache.code_completion_background_color.a > 0.01) { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(code_completion_rect.position, code_completion_rect.size + Size2(scroll_width, 0)), theme_cache.code_completion_background_color); } - tl->draw(ci, title_pos, code_completion_options[l].font_color); - } - /* Draw a small scroll rectangle to show a position in the options. */ - if (scroll_width) { - Color scroll_color = is_code_completion_scroll_hovered || is_code_completion_scroll_pressed ? theme_cache.code_completion_scroll_hovered_color : theme_cache.code_completion_scroll_color; + code_completion_scroll_rect.position = code_completion_rect.position + Vector2(code_completion_rect.size.width, 0); + code_completion_scroll_rect.size = Vector2(scroll_width, code_completion_rect.size.height); - float r = (float)theme_cache.code_completion_max_lines / code_completion_options_count; - float o = (float)code_completion_line_ofs / code_completion_options_count; - draw_rect(Rect2(code_completion_rect.position.x + code_completion_rect.size.width, code_completion_rect.position.y + o * code_completion_rect.size.y, scroll_width, code_completion_rect.size.y * r), scroll_color); - } - } + code_completion_line_ofs = CLAMP((code_completion_force_item_center < 0 ? code_completion_current_selected : code_completion_force_item_center) - lines / 2, 0, code_completion_options_count - lines); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(code_completion_rect.position.x, code_completion_rect.position.y + (code_completion_current_selected - code_completion_line_ofs) * row_height), Size2(code_completion_rect.size.width, row_height)), theme_cache.code_completion_selected_color); - /* Code hint */ - if (caret_visible && !code_hint.is_empty() && (!code_completion_active || (code_completion_below != code_hint_draw_below))) { - const int font_height = theme_cache.font->get_height(theme_cache.font_size); + for (int i = 0; i < lines; i++) { + int l = code_completion_line_ofs + i; + ERR_CONTINUE(l < 0 || l >= code_completion_options_count); - Vector<String> code_hint_lines = code_hint.split("\n"); - int line_count = code_hint_lines.size(); + Ref<TextLine> tl; + tl.instantiate(); + tl->add_string(code_completion_options[l].display, theme_cache.font, theme_cache.font_size); - int max_width = 0; - for (int i = 0; i < line_count; i++) { - max_width = MAX(max_width, theme_cache.font->get_string_size(code_hint_lines[i], HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x); - } - Size2 minsize = theme_cache.code_hint_style->get_minimum_size() + Size2(max_width, line_count * font_height + (theme_cache.line_spacing * line_count - 1)); + int yofs = (row_height - tl->get_size().y) / 2; + Point2 title_pos(code_completion_rect.position.x, code_completion_rect.position.y + i * row_height + yofs); - int offset = theme_cache.font->get_string_size(code_hint_lines[0].substr(0, code_hint_lines[0].find(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x; - if (code_hint_xpos == -0xFFFF) { - code_hint_xpos = get_caret_draw_pos().x - offset; - } - Point2 hint_ofs = Vector2(code_hint_xpos, get_caret_draw_pos().y); - if (code_hint_draw_below) { - hint_ofs.y += theme_cache.line_spacing / 2.0f; - } else { - hint_ofs.y -= (minsize.y + row_height) - theme_cache.line_spacing; - } + /* Draw completion icon if it is valid. */ + const Ref<Texture2D> &icon = code_completion_options[l].icon; + Rect2 icon_area(code_completion_rect.position.x, code_completion_rect.position.y + i * row_height, icon_area_size.width, icon_area_size.height); + if (icon.is_valid()) { + Size2 icon_size = icon_area.size * 0.7; + icon->draw_rect(ci, Rect2(icon_area.position + (icon_area.size - icon_size) / 2, icon_size)); + } + title_pos.x = icon_area.position.x + icon_area.size.width + theme_cache.code_completion_icon_separation; + + tl->set_width(code_completion_rect.size.width - (icon_area_size.x + theme_cache.code_completion_icon_separation)); + if (rtl) { + if (code_completion_options[l].default_value.get_type() == Variant::COLOR) { + draw_rect(Rect2(Point2(code_completion_rect.position.x, icon_area.position.y), icon_area_size), (Color)code_completion_options[l].default_value); + } + tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); + } else { + if (code_completion_options[l].default_value.get_type() == Variant::COLOR) { + draw_rect(Rect2(Point2(code_completion_rect.position.x + code_completion_rect.size.width - icon_area_size.x, icon_area.position.y), icon_area_size), (Color)code_completion_options[l].default_value); + } + tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT); + } - draw_style_box(theme_cache.code_hint_style, Rect2(hint_ofs, minsize)); + Point2 match_pos = Point2(code_completion_rect.position.x + icon_area_size.x + theme_cache.code_completion_icon_separation, code_completion_rect.position.y + i * row_height); - int yofs = 0; - for (int i = 0; i < line_count; i++) { - const String &line = code_hint_lines[i]; + for (int j = 0; j < code_completion_options[l].matches.size(); j++) { + Pair<int, int> match_segment = code_completion_options[l].matches[j]; + int match_offset = theme_cache.font->get_string_size(code_completion_options[l].display.substr(0, match_segment.first), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width; + int match_len = theme_cache.font->get_string_size(code_completion_options[l].display.substr(match_segment.first, match_segment.second), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width; - int begin = 0; - int end = 0; - if (line.contains(String::chr(0xFFFF))) { - begin = theme_cache.font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x; - end = theme_cache.font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x; + draw_rect(Rect2(match_pos + Point2(match_offset, 0), Size2(match_len, row_height)), theme_cache.code_completion_existing_color); + } + tl->draw(ci, title_pos, code_completion_options[l].font_color); } - Point2 round_ofs = hint_ofs + theme_cache.code_hint_style->get_offset() + Vector2(0, theme_cache.font->get_ascent(theme_cache.font_size) + font_height * i + yofs); - round_ofs = round_ofs.round(); - draw_string(theme_cache.font, round_ofs, line.replace(String::chr(0xFFFF), ""), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size, theme_cache.code_hint_color); - if (end > 0) { - // Draw an underline for the currently edited function parameter. - const Vector2 b = hint_ofs + theme_cache.code_hint_style->get_offset() + Vector2(begin, font_height + font_height * i + yofs); - draw_line(b, b + Vector2(end - begin, 0), theme_cache.code_hint_color, 2); - - // Draw a translucent text highlight as well. - const Rect2 highlight_rect = Rect2( - b - Vector2(0, font_height), - Vector2(end - begin, font_height)); - draw_rect(highlight_rect, theme_cache.code_hint_color * Color(1, 1, 1, 0.2)); + /* Draw a small scroll rectangle to show a position in the options. */ + if (scroll_width) { + Color scroll_color = is_code_completion_scroll_hovered || is_code_completion_scroll_pressed ? theme_cache.code_completion_scroll_hovered_color : theme_cache.code_completion_scroll_color; + + float r = (float)theme_cache.code_completion_max_lines / code_completion_options_count; + float o = (float)code_completion_line_ofs / code_completion_options_count; + draw_rect(Rect2(code_completion_rect.position.x + code_completion_rect.size.width, code_completion_rect.position.y + o * code_completion_rect.size.y, scroll_width, code_completion_rect.size.y * r), scroll_color); } - yofs += theme_cache.line_spacing; } } } break; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index d31806fe97..1d5c1c0ade 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -556,16 +556,17 @@ void ColorPicker::_html_submitted(const String &p_html) { return; } - const Color previous_color = color; - color = Color::from_string(p_html.strip_edges(), previous_color); + Color new_color = Color::from_string(p_html.strip_edges(), color); if (!is_editing_alpha()) { - color.a = previous_color.a; + new_color.a = color.a; } - if (color == previous_color) { + if (new_color.to_argb32() == color.to_argb32()) { return; } + color = new_color; + if (!is_inside_tree()) { return; } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index ed54bd000c..84ca6bac5b 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -197,6 +197,12 @@ void Control::reparent(Node *p_parent, bool p_keep_global_transform) { // Editor integration. +int Control::root_layout_direction = 0; + +void Control::set_root_layout_direction(int p_root_dir) { + root_layout_direction = p_root_dir; +} + void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { ERR_READ_THREAD_GUARD; Node::get_argument_options(p_function, p_idx, r_options); @@ -3024,10 +3030,35 @@ bool Control::is_layout_rtl() const { if (data.is_rtl_dirty) { const_cast<Control *>(this)->data.is_rtl_dirty = false; if (data.layout_dir == LAYOUT_DIRECTION_INHERITED) { +#ifdef TOOLS_ENABLED + if (is_part_of_edited_scene() && GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) { + const_cast<Control *>(this)->data.is_rtl = true; + return data.is_rtl; + } + if (is_inside_tree()) { + Node *edited_scene_root = get_tree()->get_edited_scene_root(); + if (edited_scene_root == this) { + int proj_root_layout_direction = GLOBAL_GET(SNAME("internationalization/rendering/root_node_layout_direction")); + if (proj_root_layout_direction == 1) { + const_cast<Control *>(this)->data.is_rtl = false; + } else if (proj_root_layout_direction == 2) { + const_cast<Control *>(this)->data.is_rtl = true; + } else if (proj_root_layout_direction == 3) { + String locale = OS::get_singleton()->get_locale(); + const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale); + } else { + String locale = TranslationServer::get_singleton()->get_tool_locale(); + const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale); + } + return data.is_rtl; + } + } +#else if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) { const_cast<Control *>(this)->data.is_rtl = true; return data.is_rtl; } +#endif Node *parent_node = get_parent(); while (parent_node) { Control *parent_control = Object::cast_to<Control>(parent_node); @@ -3044,11 +3075,13 @@ bool Control::is_layout_rtl() const { parent_node = parent_node->get_parent(); } - int root_dir = GLOBAL_GET(SNAME("internationalization/rendering/root_node_layout_direction")); - if (root_dir == 1) { + if (root_layout_direction == 1) { const_cast<Control *>(this)->data.is_rtl = false; - } else if (root_dir == 2) { + } else if (root_layout_direction == 2) { const_cast<Control *>(this)->data.is_rtl = true; + } else if (root_layout_direction == 3) { + String locale = OS::get_singleton()->get_locale(); + const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale); } else { String locale = TranslationServer::get_singleton()->get_tool_locale(); const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale); diff --git a/scene/gui/control.h b/scene/gui/control.h index db1bd3a346..aec61dc2fe 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -317,6 +317,8 @@ private: // Extra properties. + static int root_layout_direction; + String get_tooltip_text() const; protected: @@ -403,6 +405,8 @@ public: // Editor integration. + static void set_root_layout_direction(int p_root_dir); + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; PackedStringArray get_configuration_warnings() const override; diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index 957a8f276e..3e827e76dc 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -42,6 +42,7 @@ void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) { if (close_on_escape && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) { _cancel_pressed(); } + Window::_input_from_window(p_event); } void AcceptDialog::_parent_focused() { @@ -428,8 +429,6 @@ AcceptDialog::AcceptDialog() { ok_button->connect("pressed", callable_mp(this, &AcceptDialog::_ok_pressed)); set_title(TTRC("Alert!")); - - connect("window_input", callable_mp(this, &AcceptDialog::_input_from_window)); } AcceptDialog::~AcceptDialog() { diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index d5cbaaeef8..e28d6b7467 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -65,11 +65,11 @@ class AcceptDialog : public Window { static bool swap_cancel_ok; - void _input_from_window(const Ref<InputEvent> &p_event); void _parent_focused(); protected: virtual Size2 _get_contents_minimum_size() const override; + virtual void _input_from_window(const Ref<InputEvent> &p_event) override; void _notification(int p_what); static void _bind_methods(); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 69023d2056..f5cf7eb59d 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -542,40 +542,9 @@ void GraphEdit::_notification(int p_what) { // Draw background grid. if (show_grid) { - Vector2 offset = get_scroll_offset() / zoom; - Size2 size = get_size() / zoom; - - Point2i from_pos = (offset / float(snapping_distance)).floor(); - Point2i len = (size / float(snapping_distance)).floor() + Vector2(1, 1); - - for (int i = from_pos.x; i < from_pos.x + len.x; i++) { - Color color; - - if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) { - color = theme_cache.grid_major; - } else { - color = theme_cache.grid_minor; - } - - float base_offset = i * snapping_distance * zoom - offset.x * zoom; - draw_line(Vector2(base_offset, 0), Vector2(base_offset, get_size().height), color); - } - - for (int i = from_pos.y; i < from_pos.y + len.y; i++) { - Color color; - - if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) { - color = theme_cache.grid_major; - } else { - color = theme_cache.grid_minor; - } - - float base_offset = i * snapping_distance * zoom - offset.y * zoom; - draw_line(Vector2(0, base_offset), Vector2(get_size().width, base_offset), color); - } + _draw_grid(); } } break; - case NOTIFICATION_RESIZED: { _update_scroll(); top_layer->queue_redraw(); @@ -1128,6 +1097,67 @@ void GraphEdit::_minimap_draw() { minimap->draw_texture(resizer, Point2(), resizer_color); } +void GraphEdit::_draw_grid() { + Vector2 offset = get_scroll_offset() / zoom; + Size2 size = get_size() / zoom; + + Point2i from_pos = (offset / float(snapping_distance)).floor(); + Point2i len = (size / float(snapping_distance)).floor() + Vector2(1, 1); + + switch (grid_pattern) { + case GRID_PATTERN_LINES: { + for (int i = from_pos.x; i < from_pos.x + len.x; i++) { + Color color; + + if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) { + color = theme_cache.grid_major; + } else { + color = theme_cache.grid_minor; + } + + float base_offset = i * snapping_distance * zoom - offset.x * zoom; + draw_line(Vector2(base_offset, 0), Vector2(base_offset, get_size().height), color); + } + + for (int i = from_pos.y; i < from_pos.y + len.y; i++) { + Color color; + + if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) { + color = theme_cache.grid_major; + } else { + color = theme_cache.grid_minor; + } + + float base_offset = i * snapping_distance * zoom - offset.y * zoom; + draw_line(Vector2(0, base_offset), Vector2(get_size().width, base_offset), color); + } + } break; + case GRID_PATTERN_DOTS: { + Color transparent_grid_minor = theme_cache.grid_minor; + transparent_grid_minor.a *= CLAMP(2 * (zoom - 0.4), 0, 1); + + for (int i = from_pos.x; i < from_pos.x + len.x; i++) { + for (int j = from_pos.y; j < from_pos.y + len.y; j++) { + Color color = transparent_grid_minor; + + if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0 && ABS(j) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) { + color = theme_cache.grid_major; + } + + if (color.a == 0) { + continue; + } + + float base_offset_x = i * snapping_distance * zoom - offset.x * zoom; + float base_offset_y = j * snapping_distance * zoom - offset.y * zoom; + + draw_rect(Rect2(base_offset_x - 1, base_offset_y - 1, 3, 3), color); + } + } + } break; + } +} + void GraphEdit::set_selected(Node *p_child) { for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i)); @@ -1647,6 +1677,19 @@ bool GraphEdit::is_showing_grid() const { return show_grid; } +void GraphEdit::set_grid_pattern(GridPattern p_pattern) { + if (grid_pattern == p_pattern) { + return; + } + + grid_pattern = p_pattern; + queue_redraw(); +} + +GraphEdit::GridPattern GraphEdit::get_grid_pattern() const { + return grid_pattern; +} + void GraphEdit::_snapping_toggled() { snapping_enabled = toggle_snapping_button->is_pressed(); } @@ -1859,6 +1902,9 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_show_grid", "enable"), &GraphEdit::set_show_grid); ClassDB::bind_method(D_METHOD("is_showing_grid"), &GraphEdit::is_showing_grid); + ClassDB::bind_method(D_METHOD("set_grid_pattern", "pattern"), &GraphEdit::set_grid_pattern); + ClassDB::bind_method(D_METHOD("get_grid_pattern"), &GraphEdit::get_grid_pattern); + ClassDB::bind_method(D_METHOD("set_snapping_enabled", "enable"), &GraphEdit::set_snapping_enabled); ClassDB::bind_method(D_METHOD("is_snapping_enabled"), &GraphEdit::is_snapping_enabled); @@ -1917,6 +1963,7 @@ void GraphEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_scroll_offset", "get_scroll_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_grid"), "set_show_grid", "is_showing_grid"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "grid_pattern", PROPERTY_HINT_ENUM, "Lines,Dots"), "set_grid_pattern", "get_grid_pattern"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snapping_enabled"), "set_snapping_enabled", "is_snapping_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "snapping_distance", PROPERTY_HINT_NONE, "suffix:px"), "set_snapping_distance", "get_snapping_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans"), "set_panning_scheme", "get_panning_scheme"); @@ -1970,6 +2017,9 @@ void GraphEdit::_bind_methods() { BIND_ENUM_CONSTANT(SCROLL_ZOOMS); BIND_ENUM_CONSTANT(SCROLL_PANS); + BIND_ENUM_CONSTANT(GRID_PATTERN_LINES); + BIND_ENUM_CONSTANT(GRID_PATTERN_DOTS); + BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphEdit, panel); BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, grid_major); BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, grid_minor); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index a7be9ac0b3..31cb495bf8 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -126,6 +126,11 @@ public: SCROLL_PANS, }; + enum GridPattern { + GRID_PATTERN_LINES, + GRID_PATTERN_DOTS + }; + private: struct ConnectionType { union { @@ -176,6 +181,7 @@ private: bool snapping_enabled = true; int snapping_distance = 20; bool show_grid = true; + GridPattern grid_pattern = GRID_PATTERN_LINES; bool connecting = false; String connecting_from; @@ -289,6 +295,8 @@ private: void _connections_layer_draw(); void _minimap_draw(); + void _draw_grid(); + TypedArray<Dictionary> _get_connection_list() const; friend class GraphEditFilter; @@ -412,6 +420,9 @@ public: void set_show_grid(bool p_enable); bool is_showing_grid() const; + void set_grid_pattern(GridPattern p_pattern); + GridPattern get_grid_pattern() const; + void set_connection_lines_curvature(float p_curvature); float get_connection_lines_curvature() const; @@ -431,5 +442,6 @@ public: }; VARIANT_ENUM_CAST(GraphEdit::PanningScheme); +VARIANT_ENUM_CAST(GraphEdit::GridPattern); #endif // GRAPH_EDIT_H diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 3df0d97160..1b1905e793 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -668,6 +668,127 @@ void Label::_notification(int p_what) { } } +Rect2 Label::get_character_bounds(int p_pos) const { + if (dirty || font_dirty || lines_dirty) { + const_cast<Label *>(this)->_shape(); + } + + bool has_settings = settings.is_valid(); + Size2 size = get_size(); + Ref<StyleBox> style = theme_cache.normal_style; + int line_spacing = has_settings ? settings->get_line_spacing() : theme_cache.line_spacing; + bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL); + bool rtl_layout = is_layout_rtl(); + + float total_h = 0.0; + int lines_visible = 0; + + // Get number of lines to fit to the height. + for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; + if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { + break; + } + lines_visible++; + } + + if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { + lines_visible = max_lines_visible; + } + + int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); + + // Get real total height. + total_h = 0; + for (int64_t i = lines_skipped; i < last_line; i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; + } + + total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM); + + int vbegin = 0, vsep = 0; + if (lines_visible > 0) { + switch (vertical_alignment) { + case VERTICAL_ALIGNMENT_TOP: { + // Nothing. + } break; + case VERTICAL_ALIGNMENT_CENTER: { + vbegin = (size.y - (total_h - line_spacing)) / 2; + vsep = 0; + + } break; + case VERTICAL_ALIGNMENT_BOTTOM: { + vbegin = size.y - (total_h - line_spacing); + vsep = 0; + + } break; + case VERTICAL_ALIGNMENT_FILL: { + vbegin = 0; + if (lines_visible > 1) { + vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1); + } else { + vsep = 0; + } + + } break; + } + } + + Vector2 ofs; + ofs.y = style->get_offset().y + vbegin; + for (int i = lines_skipped; i < last_line; i++) { + Size2 line_size = TS->shaped_text_get_size(lines_rid[i]); + ofs.x = 0; + switch (horizontal_alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + if (rtl && autowrap_mode != TextServer::AUTOWRAP_OFF) { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } else { + ofs.x = style->get_offset().x; + } + break; + case HORIZONTAL_ALIGNMENT_LEFT: { + if (rtl_layout) { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } else { + ofs.x = style->get_offset().x; + } + } break; + case HORIZONTAL_ALIGNMENT_CENTER: { + ofs.x = int(size.width - line_size.width) / 2; + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + if (rtl_layout) { + ofs.x = style->get_offset().x; + } else { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } + } break; + } + int v_size = TS->shaped_text_get_glyph_count(lines_rid[i]); + const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); + + float gl_off = 0.0f; + for (int j = 0; j < v_size; j++) { + if ((glyphs[j].count > 0) && ((glyphs[j].index != 0) || ((glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE))) { + if (p_pos >= glyphs[j].start && p_pos < glyphs[j].end) { + float advance = 0.f; + for (int k = 0; k < glyphs[j].count; k++) { + advance += glyphs[j + k].advance; + } + Rect2 rect; + rect.position = ofs + Vector2(gl_off, 0); + rect.size = Vector2(advance, TS->shaped_text_get_size(lines_rid[i]).y); + return rect; + } + } + gl_off += glyphs[j].advance * glyphs[j].repeat; + } + ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing; + } + return Rect2(); +} + Size2 Label::get_minimum_size() const { // don't want to mutable everything if (dirty || font_dirty || lines_dirty) { @@ -1055,6 +1176,8 @@ void Label::_bind_methods() { ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &Label::set_structured_text_bidi_override_options); ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label::get_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("get_character_bounds", "pos"), &Label::get_character_bounds); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "label_settings", PROPERTY_HINT_RESOURCE_TYPE, "LabelSettings"), "set_label_settings", "get_label_settings"); ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); diff --git a/scene/gui/label.h b/scene/gui/label.h index 44443e3eb4..9306033dd0 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -161,6 +161,8 @@ public: int get_line_count() const; int get_visible_line_count() const; + Rect2 get_character_bounds(int p_pos) const; + Label(const String &p_text = String()); ~Label(); }; diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 7418ba7333..7fa2653ed9 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -249,11 +249,13 @@ String MenuBar::bind_global_menu() { Vector<PopupMenu *> popups = _get_popups(); for (int i = 0; i < menu_cache.size(); i++) { String submenu_name = popups[i]->bind_global_menu(); - int index = ds->global_menu_add_submenu_item("_main", menu_cache[i].name, submenu_name, global_start_idx + i); - ds->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(i)); - ds->global_menu_set_item_hidden("_main", index, menu_cache[i].hidden); - ds->global_menu_set_item_disabled("_main", index, menu_cache[i].disabled); - ds->global_menu_set_item_tooltip("_main", index, menu_cache[i].tooltip); + if (!popups[i]->is_system_menu()) { + int index = ds->global_menu_add_submenu_item("_main", menu_cache[i].name, submenu_name, global_start_idx + i); + ds->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(i)); + ds->global_menu_set_item_hidden("_main", index, menu_cache[i].hidden); + ds->global_menu_set_item_disabled("_main", index, menu_cache[i].disabled); + ds->global_menu_set_item_tooltip("_main", index, menu_cache[i].tooltip); + } } return global_menu_name; @@ -268,8 +270,10 @@ void MenuBar::unbind_global_menu() { int global_start = _find_global_start_index(); Vector<PopupMenu *> popups = _get_popups(); for (int i = menu_cache.size() - 1; i >= 0; i--) { - popups[i]->unbind_global_menu(); - ds->global_menu_remove_item("_main", global_start + i); + if (!popups[i]->is_system_menu()) { + popups[i]->unbind_global_menu(); + ds->global_menu_remove_item("_main", global_start + i); + } } global_menu_name = String(); @@ -558,8 +562,10 @@ void MenuBar::add_child_notify(Node *p_child) { if (!global_menu_name.is_empty()) { String submenu_name = pm->bind_global_menu(); - int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, _find_global_start_index() + menu_cache.size() - 1); - DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(menu_cache.size() - 1)); + if (!pm->is_system_menu()) { + int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, _find_global_start_index() + menu_cache.size() - 1); + DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(menu_cache.size() - 1)); + } } update_minimum_size(); } @@ -587,14 +593,16 @@ void MenuBar::move_child_notify(Node *p_child) { menu_cache.insert(new_idx, menu); if (!global_menu_name.is_empty()) { - int global_start = _find_global_start_index(); - if (old_idx != -1) { - DisplayServer::get_singleton()->global_menu_remove_item("_main", global_start + old_idx); - } - if (new_idx != -1) { - String submenu_name = pm->bind_global_menu(); - int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, global_start + new_idx); - DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(new_idx)); + if (!pm->is_system_menu()) { + int global_start = _find_global_start_index(); + if (old_idx != -1) { + DisplayServer::get_singleton()->global_menu_remove_item("_main", global_start + old_idx); + } + if (new_idx != -1) { + String submenu_name = pm->bind_global_menu(); + int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, global_start + new_idx); + DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(new_idx)); + } } } } @@ -612,8 +620,10 @@ void MenuBar::remove_child_notify(Node *p_child) { menu_cache.remove_at(idx); if (!global_menu_name.is_empty()) { - pm->unbind_global_menu(); - DisplayServer::get_singleton()->global_menu_remove_item("_main", _find_global_start_index() + idx); + if (!pm->is_system_menu()) { + pm->unbind_global_menu(); + DisplayServer::get_singleton()->global_menu_remove_item("_main", _find_global_start_index() + idx); + } } p_child->remove_meta("_menu_name"); diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index b16e8371a2..8369bedda9 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -39,6 +39,7 @@ void Popup::_input_from_window(const Ref<InputEvent> &p_event) { if (get_flag(FLAG_POPUP) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) { _close_pressed(); } + Window::_input_from_window(p_event); } void Popup::_initialize_visible_parents() { @@ -204,8 +205,6 @@ Popup::Popup() { set_flag(FLAG_BORDERLESS, true); set_flag(FLAG_RESIZE_DISABLED, true); set_flag(FLAG_POPUP, true); - - connect("window_input", callable_mp(this, &Popup::_input_from_window)); } Popup::~Popup() { diff --git a/scene/gui/popup.h b/scene/gui/popup.h index d524e448dd..25edca3657 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -47,14 +47,13 @@ class Popup : public Window { Ref<StyleBox> panel_style; } theme_cache; - void _input_from_window(const Ref<InputEvent> &p_event); - void _initialize_visible_parents(); void _deinitialize_visible_parents(); protected: void _close_pressed(); virtual Rect2i _popup_adjust_rect() const override; + virtual void _input_from_window(const Ref<InputEvent> &p_event) override; void _notification(int p_what); void _validate_property(PropertyInfo &p_property) const; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index d6b8dd0202..d9c633b238 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -40,6 +40,8 @@ #include "scene/gui/menu_bar.h" #include "scene/theme/theme_db.h" +HashMap<String, PopupMenu *> PopupMenu::system_menus; + String PopupMenu::bind_global_menu() { #ifdef TOOLS_ENABLED if (is_part_of_edited_scene()) { @@ -54,8 +56,20 @@ String PopupMenu::bind_global_menu() { return global_menu_name; // Already bound; } - DisplayServer *ds = DisplayServer::get_singleton(); global_menu_name = "__PopupMenu#" + itos(get_instance_id()); + if (system_menu_name.length() > 0) { + if (system_menus.has(system_menu_name)) { + WARN_PRINT(vformat("Attempting to bind PopupMenu to the special menu %s, but another menu is already bound to it. This menu: %s, current menu: %s", system_menu_name, this->get_description(), system_menus[system_menu_name]->get_description())); + } else { + const Dictionary &supported_special_names = DisplayServer::get_singleton()->global_menu_get_system_menu_roots(); + if (supported_special_names.has(system_menu_name)) { + system_menus[system_menu_name] = this; + global_menu_name = system_menu_name; + } + } + } + + DisplayServer *ds = DisplayServer::get_singleton(); ds->global_menu_set_popup_callbacks(global_menu_name, callable_mp(this, &PopupMenu::_about_to_popup), callable_mp(this, &PopupMenu::_about_to_close)); for (int i = 0; i < items.size(); i++) { Item &item = items.write[i]; @@ -105,6 +119,10 @@ void PopupMenu::unbind_global_menu() { return; } + if (global_menu_name == system_menu_name && system_menus[system_menu_name] == this) { + system_menus.erase(system_menu_name); + } + for (int i = 0; i < items.size(); i++) { Item &item = items.write[i]; if (!item.submenu.is_empty()) { @@ -120,6 +138,24 @@ void PopupMenu::unbind_global_menu() { global_menu_name = String(); } +bool PopupMenu::is_system_menu() const { + return (global_menu_name == system_menu_name) && (system_menu_name.length() > 0); +} + +void PopupMenu::set_system_menu_root(const String &p_special) { + if (is_inside_tree() && system_menu_name.length() > 0) { + unbind_global_menu(); + } + system_menu_name = p_special; + if (is_inside_tree() && system_menu_name.length() > 0) { + bind_global_menu(); + } +} + +String PopupMenu::get_system_menu_root() const { + return system_menu_name; +} + String PopupMenu::_get_accel_text(const Item &p_item) const { if (p_item.shortcut.is_valid()) { return p_item.shortcut->get_as_text(); @@ -378,9 +414,16 @@ void PopupMenu::_submenu_timeout() { submenu_over = -1; } -void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { - ERR_FAIL_COND(p_event.is_null()); +void PopupMenu::_input_from_window(const Ref<InputEvent> &p_event) { + if (p_event.is_valid()) { + _input_from_window_internal(p_event); + } else { + WARN_PRINT_ONCE("PopupMenu has received an invalid InputEvent. Consider filtering invalid events out."); + } + Popup::_input_from_window(p_event); +} +void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) { if (!items.is_empty()) { Input *input = Input::get_singleton(); Ref<InputEventJoypadMotion> joypadmotion_event = p_event; @@ -947,6 +990,15 @@ void PopupMenu::_notification(int p_what) { if (!is_embedded()) { set_flag(FLAG_NO_FOCUS, true); } + if (system_menu_name.length() > 0) { + bind_global_menu(); + } + } break; + + case NOTIFICATION_EXIT_TREE: { + if (system_menu_name.length() > 0) { + unbind_global_menu(); + } } break; case NOTIFICATION_THEME_CHANGED: @@ -2716,11 +2768,16 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &PopupMenu::set_allow_search); ClassDB::bind_method(D_METHOD("get_allow_search"), &PopupMenu::get_allow_search); + ClassDB::bind_method(D_METHOD("is_system_menu"), &PopupMenu::is_system_menu); + ClassDB::bind_method(D_METHOD("set_system_menu_root", "special"), &PopupMenu::set_system_menu_root); + ClassDB::bind_method(D_METHOD("get_system_menu_root"), &PopupMenu::get_system_menu_root); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "submenu_popup_delay", PROPERTY_HINT_NONE, "suffix:s"), "set_submenu_popup_delay", "get_submenu_popup_delay"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "system_menu_root", PROPERTY_HINT_ENUM, "Dock (macOS):_dock,Apple Menu(macOS):_apple,Window Menu(macOS):_window,Help Menu(macOS):_help"), "set_system_menu_root", "get_system_menu_root"); ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "item_"); @@ -2799,8 +2856,6 @@ PopupMenu::PopupMenu() { scroll_container->add_child(control, false, INTERNAL_MODE_FRONT); control->connect("draw", callable_mp(this, &PopupMenu::_draw_items)); - connect("window_input", callable_mp(this, &PopupMenu::gui_input)); - submenu_timer = memnew(Timer); submenu_timer->set_wait_time(0.3); submenu_timer->set_one_shot(true); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 5d5f4a8322..c1ab9544ea 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -40,6 +40,8 @@ class PopupMenu : public Popup { GDCLASS(PopupMenu, Popup); + static HashMap<String, PopupMenu *> system_menus; + struct Item { Ref<Texture2D> icon; int icon_max_width = 0; @@ -90,6 +92,7 @@ class PopupMenu : public Popup { }; String global_menu_name; + String system_menu_name; bool close_allowed = false; bool activated_by_keyboard = false; @@ -112,7 +115,6 @@ class PopupMenu : public Popup { void _shape_item(int p_idx); - virtual void gui_input(const Ref<InputEvent> &p_event); void _activate_submenu(int p_over, bool p_by_keyboard = false); void _submenu_timeout(); @@ -191,10 +193,12 @@ class PopupMenu : public Popup { void _minimum_lifetime_timeout(); void _close_pressed(); void _menu_changed(); + void _input_from_window_internal(const Ref<InputEvent> &p_event); protected: virtual void add_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; + virtual void _input_from_window(const Ref<InputEvent> &p_event) override; void _notification(int p_what); bool _set(const StringName &p_name, const Variant &p_value); @@ -218,6 +222,9 @@ public: String bind_global_menu(); void unbind_global_menu(); + bool is_system_menu() const; + void set_system_menu_root(const String &p_special); + String get_system_menu_root() const; void add_item(const String &p_label, int p_id = -1, Key p_accel = Key::NONE); void add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id = -1, Key p_accel = Key::NONE); diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index b45319422c..b86309353b 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -91,12 +91,32 @@ void ProgressBar::_notification(int p_what) { } if (show_percentage) { - String txt = itos(int(get_as_ratio() * 100)); + double ratio = 0; + + // Avoid division by zero. + if (Math::is_equal_approx(get_max(), get_min())) { + ratio = 1; + } else if (is_ratio_exp() && get_min() >= 0 && get_value() >= 0) { + double exp_min = get_min() == 0 ? 0.0 : Math::log(get_min()) / Math::log((double)2); + double exp_max = Math::log(get_max()) / Math::log((double)2); + double exp_value = get_value() == 0 ? 0.0 : Math::log(get_value()) / Math::log((double)2); + double percentage = (exp_value - exp_min) / (exp_max - exp_min); + + ratio = CLAMP(percentage, is_lesser_allowed() ? percentage : 0, is_greater_allowed() ? percentage : 1); + } else { + double percentage = (get_value() - get_min()) / (get_max() - get_min()); + + ratio = CLAMP(percentage, is_lesser_allowed() ? percentage : 0, is_greater_allowed() ? percentage : 1); + } + + String txt = itos(int(ratio * 100)); + if (is_localizing_numeral_system()) { txt = TS->format_number(txt) + TS->percent_sign(); } else { txt += String("%"); } + TextLine tl = TextLine(txt, theme_cache.font, theme_cache.font_size); Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round(); diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 8bb8eb1d30..586334a0b4 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -88,10 +88,14 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) { } } else if (scrollable) { if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) { - grab_focus(); + if (get_focus_mode() != FOCUS_NONE) { + grab_focus(); + } set_value(get_value() + get_step()); } else if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) { - grab_focus(); + if (get_focus_mode() != FOCUS_NONE) { + grab_focus(); + } set_value(get_value() - get_step()); } } diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index 70c2cc9d65..248260a7d2 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -74,6 +74,7 @@ void TextureProgressBar::set_nine_patch_stretch(bool p_stretch) { nine_patch_stretch = p_stretch; queue_redraw(); update_minimum_size(); + notify_property_list_changed(); } bool TextureProgressBar::get_nine_patch_stretch() const { @@ -615,6 +616,7 @@ void TextureProgressBar::set_fill_mode(int p_fill) { mode = (FillMode)p_fill; queue_redraw(); + notify_property_list_changed(); } int TextureProgressBar::get_fill_mode() { @@ -669,6 +671,16 @@ Point2 TextureProgressBar::get_radial_center_offset() { return rad_center_off; } +void TextureProgressBar::_validate_property(PropertyInfo &p_property) const { + if (p_property.name.begins_with("stretch_margin_") && !nine_patch_stretch) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if (p_property.name.begins_with("radial_") && (mode != FillMode::FILL_CLOCKWISE && mode != FillMode::FILL_COUNTER_CLOCKWISE && mode != FillMode::FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE)) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } +} + void TextureProgressBar::_bind_methods() { ClassDB::bind_method(D_METHOD("set_under_texture", "tex"), &TextureProgressBar::set_under_texture); ClassDB::bind_method(D_METHOD("get_under_texture"), &TextureProgressBar::get_under_texture); @@ -710,8 +722,13 @@ void TextureProgressBar::_bind_methods() { ClassDB::bind_method(D_METHOD("get_nine_patch_stretch"), &TextureProgressBar::get_nine_patch_stretch); ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom),Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "nine_patch_stretch"), "set_nine_patch_stretch", "get_nine_patch_stretch"); + ADD_GROUP("Radial Fill", "radial_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,degrees"), "set_radial_initial_angle", "get_radial_initial_angle"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,degrees"), "set_fill_degrees", "get_fill_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "radial_center_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_radial_center_offset", "get_radial_center_offset"); + ADD_GROUP("", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "nine_patch_stretch"), "set_nine_patch_stretch", "get_nine_patch_stretch"); ADD_GROUP("Stretch Margin", "stretch_margin_"); ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1,suffix:px"), "set_stretch_margin", "get_stretch_margin", SIDE_LEFT); ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1,suffix:px"), "set_stretch_margin", "get_stretch_margin", SIDE_TOP); @@ -729,11 +746,6 @@ void TextureProgressBar::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over"), "set_tint_over", "get_tint_over"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_progress"), "set_tint_progress", "get_tint_progress"); - ADD_GROUP("Radial Fill", "radial_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider,degrees"), "set_radial_initial_angle", "get_radial_initial_angle"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider,degrees"), "set_fill_degrees", "get_fill_degrees"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "radial_center_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_radial_center_offset", "get_radial_center_offset"); - BIND_ENUM_CONSTANT(FILL_LEFT_TO_RIGHT); BIND_ENUM_CONSTANT(FILL_RIGHT_TO_LEFT); BIND_ENUM_CONSTANT(FILL_TOP_TO_BOTTOM); diff --git a/scene/gui/texture_progress_bar.h b/scene/gui/texture_progress_bar.h index 5999aa986b..d890ce6e36 100644 --- a/scene/gui/texture_progress_bar.h +++ b/scene/gui/texture_progress_bar.h @@ -43,6 +43,7 @@ class TextureProgressBar : public Range { protected: static void _bind_methods(); void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; public: enum FillMode { diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 8a243fd68f..18ee39aeec 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -2195,27 +2195,38 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 draw_item_rect(p_item->cells.write[i], item_rect, cell_color, icon_col, outline_size, font_outline_color); } break; case TreeItem::CELL_MODE_CHECK: { - Ref<Texture2D> checked = theme_cache.checked; - Ref<Texture2D> unchecked = theme_cache.unchecked; - Ref<Texture2D> indeterminate = theme_cache.indeterminate; Point2i check_ofs = item_rect.position; - check_ofs.y += Math::floor((real_t)(item_rect.size.y - checked->get_height()) / 2); + check_ofs.y += Math::floor((real_t)(item_rect.size.y - theme_cache.checked->get_height()) / 2); - if (p_item->cells[i].indeterminate) { - indeterminate->draw(ci, check_ofs); - } else if (p_item->cells[i].checked) { - checked->draw(ci, check_ofs); + if (p_item->cells[i].editable) { + if (p_item->cells[i].indeterminate) { + theme_cache.indeterminate->draw(ci, check_ofs); + } else if (p_item->cells[i].checked) { + theme_cache.checked->draw(ci, check_ofs); + } else { + theme_cache.unchecked->draw(ci, check_ofs); + } } else { - unchecked->draw(ci, check_ofs); + if (p_item->cells[i].indeterminate) { + theme_cache.indeterminate_disabled->draw(ci, check_ofs); + } else if (p_item->cells[i].checked) { + theme_cache.checked_disabled->draw(ci, check_ofs); + } else { + theme_cache.unchecked_disabled->draw(ci, check_ofs); + } } - int check_w = checked->get_width() + theme_cache.h_separation; + int check_w = theme_cache.checked->get_width() + theme_cache.h_separation; text_pos.x += check_w; item_rect.size.x -= check_w; item_rect.position.x += check_w; + if (!p_item->cells[i].editable) { + cell_color = theme_cache.font_disabled_color; + } + draw_item_rect(p_item->cells.write[i], item_rect, cell_color, icon_col, outline_size, font_outline_color); } break; @@ -5535,7 +5546,10 @@ void Tree::_bind_methods() { BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, checked); BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, unchecked); + BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, checked_disabled); + BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, unchecked_disabled); BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, indeterminate); + BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, indeterminate_disabled); BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, arrow); BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, arrow_collapsed); BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, arrow_collapsed_mirrored); @@ -5549,6 +5563,7 @@ void Tree::_bind_methods() { BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, font_color); BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, font_selected_color); + BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, font_disabled_color); BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, drop_position_color); BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, h_separation); BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, v_separation); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 4a5814c45c..2dda408dd7 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -536,7 +536,10 @@ private: Ref<Texture2D> checked; Ref<Texture2D> unchecked; + Ref<Texture2D> checked_disabled; + Ref<Texture2D> unchecked_disabled; Ref<Texture2D> indeterminate; + Ref<Texture2D> indeterminate_disabled; Ref<Texture2D> arrow; Ref<Texture2D> arrow_collapsed; Ref<Texture2D> arrow_collapsed_mirrored; @@ -545,6 +548,7 @@ private: Color font_color; Color font_selected_color; + Color font_disabled_color; Color guide_color; Color drop_position_color; Color relationship_line_color; diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp index c61fa1d9b8..438a228c4f 100644 --- a/scene/gui/view_panner.cpp +++ b/scene/gui/view_panner.cpp @@ -43,12 +43,14 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) if (scroll_vec != Vector2() && mb->is_pressed()) { if (control_scheme == SCROLL_PANS) { if (mb->is_ctrl_pressed()) { - // Compute the zoom factor. - float zoom_factor = mb->get_factor() <= 0 ? 1.0 : mb->get_factor(); - zoom_factor = ((scroll_zoom_factor - 1.0) * zoom_factor) + 1.0; - float zoom = (scroll_vec.x + scroll_vec.y) > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor; - zoom_callback.call(zoom, mb->get_position(), p_event); - return true; + if (scroll_vec.y != 0) { + // Compute the zoom factor. + float zoom_factor = mb->get_factor() <= 0 ? 1.0 : mb->get_factor(); + zoom_factor = ((scroll_zoom_factor - 1.0) * zoom_factor) + 1.0; + float zoom = scroll_vec.y > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor; + zoom_callback.call(zoom, mb->get_position(), p_event); + return true; + } } else { Vector2 panning = scroll_vec * mb->get_factor(); if (pan_axis == PAN_AXIS_HORIZONTAL) { @@ -73,11 +75,11 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) } pan_callback.call(-panning * scroll_speed, p_event); return true; - } else if (!mb->is_shift_pressed()) { + } else if (!mb->is_shift_pressed() && scroll_vec.y != 0) { // Compute the zoom factor. float zoom_factor = mb->get_factor() <= 0 ? 1.0 : mb->get_factor(); zoom_factor = ((scroll_zoom_factor - 1.0) * zoom_factor) + 1.0; - float zoom = (scroll_vec.x + scroll_vec.y) > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor; + float zoom = scroll_vec.y > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor; zoom_callback.call(zoom, mb->get_position(), p_event); return true; } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 820cb7571f..3cb06e769f 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -3069,8 +3069,13 @@ static void _add_nodes_to_options(const Node *p_base, const Node *p_node, List<S void Node::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { String pf = p_function; - if ((pf == "has_node" || pf == "get_node") && p_idx == 0) { + if (p_idx == 0 && (pf == "has_node" || pf == "get_node")) { _add_nodes_to_options(this, this, r_options); + } else if (p_idx == 0 && (pf == "add_to_group" || pf == "remove_from_group" || pf == "is_in_group")) { + HashMap<StringName, String> global_groups = ProjectSettings::get_singleton()->get_global_groups_list(); + for (const KeyValue<StringName, String> &E : global_groups) { + r_options->push_back(E.key.operator String().quote()); + } } Object::get_argument_options(p_function, p_idx, r_options); } diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 4417007b9d..b6fbd70e14 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1714,6 +1714,19 @@ void SceneTree::get_argument_options(const StringName &p_function, int p_idx, Li filename = dir_access->get_next(); } } + } else { + bool add_options = false; + if (p_idx == 0) { + add_options = p_function == "get_nodes_in_group" || p_function == "has_group" || p_function == "get_first_node_in_group" || p_function == "set_group" || p_function == "notify_group" || p_function == "call_group" || p_function == "add_to_group"; + } else if (p_idx == 1) { + add_options = p_function == "set_group_flags" || p_function == "call_group_flags" || p_function == "notify_group_flags"; + } + if (add_options) { + HashMap<StringName, String> global_groups = ProjectSettings::get_singleton()->get_global_groups_list(); + for (const KeyValue<StringName, String> &E : global_groups) { + r_options->push_back(E.key.operator String().quote()); + } + } } } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 8cb7614dfd..89ec5636ab 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -3518,6 +3518,11 @@ Control *Viewport::gui_get_focus_owner() const { return gui.key_focus; } +Control *Viewport::gui_get_hovered_control() const { + ERR_READ_THREAD_GUARD_V(nullptr); + return gui.mouse_over; +} + void Viewport::set_msaa_2d(MSAA p_msaa) { ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_msaa, MSAA_MAX); @@ -4557,6 +4562,7 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("gui_release_focus"), &Viewport::gui_release_focus); ClassDB::bind_method(D_METHOD("gui_get_focus_owner"), &Viewport::gui_get_focus_owner); + ClassDB::bind_method(D_METHOD("gui_get_hovered_control"), &Viewport::gui_get_hovered_control); ClassDB::bind_method(D_METHOD("set_disable_input", "disable"), &Viewport::set_disable_input); ClassDB::bind_method(D_METHOD("is_input_disabled"), &Viewport::is_input_disabled); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 6efa98ece8..43a89c8a0b 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -610,6 +610,7 @@ public: void gui_release_focus(); Control *gui_get_focus_owner() const; + Control *gui_get_hovered_control() const; PackedStringArray get_configuration_warnings() const override; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 36d7d079b2..eb431445ed 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -40,6 +40,14 @@ #include "scene/theme/theme_db.h" #include "scene/theme/theme_owner.h" +// Editor integration. + +int Window::root_layout_direction = 0; + +void Window::set_root_layout_direction(int p_root_dir) { + root_layout_direction = p_root_dir; +} + // Dynamic properties. bool Window::_set(const StringName &p_name, const Variant &p_value) { @@ -1559,7 +1567,12 @@ void Window::_window_input(const Ref<InputEvent> &p_ev) { } } - if (p_ev->get_device() != InputEvent::DEVICE_ID_INTERNAL) { + // If the event needs to be handled in a Window-derived class, then it should overwrite + // `_input_from_window` instead of subscribing to the `window_input` signal, because the signal + // filters out internal events. + _input_from_window(p_ev); + + if (p_ev->get_device() != InputEvent::DEVICE_ID_INTERNAL && is_inside_tree()) { emit_signal(SceneStringNames::get_singleton()->window_input, p_ev); } @@ -2533,9 +2546,32 @@ Window::LayoutDirection Window::get_layout_direction() const { bool Window::is_layout_rtl() const { ERR_READ_THREAD_GUARD_V(false); if (layout_dir == LAYOUT_DIRECTION_INHERITED) { +#ifdef TOOLS_ENABLED + if (is_part_of_edited_scene() && GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) { + return true; + } + if (is_inside_tree()) { + Node *edited_scene_root = get_tree()->get_edited_scene_root(); + if (edited_scene_root == this) { + int proj_root_layout_direction = GLOBAL_GET(SNAME("internationalization/rendering/root_node_layout_direction")); + if (proj_root_layout_direction == 1) { + return false; + } else if (proj_root_layout_direction == 2) { + return true; + } else if (proj_root_layout_direction == 3) { + String locale = OS::get_singleton()->get_locale(); + return TS->is_locale_right_to_left(locale); + } else { + String locale = TranslationServer::get_singleton()->get_tool_locale(); + return TS->is_locale_right_to_left(locale); + } + } + } +#else if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) { return true; } +#endif Node *parent_node = get_parent(); while (parent_node) { Control *parent_control = Object::cast_to<Control>(parent_node); @@ -2550,11 +2586,13 @@ bool Window::is_layout_rtl() const { parent_node = parent_node->get_parent(); } - int root_dir = GLOBAL_GET(SNAME("internationalization/rendering/root_node_layout_direction")); - if (root_dir == 1) { + if (root_layout_direction == 1) { return false; - } else if (root_dir == 2) { + } else if (root_layout_direction == 2) { return true; + } else if (root_layout_direction == 3) { + String locale = OS::get_singleton()->get_locale(); + return TS->is_locale_right_to_left(locale); } else { String locale = TranslationServer::get_singleton()->get_tool_locale(); return TS->is_locale_right_to_left(locale); diff --git a/scene/main/window.h b/scene/main/window.h index 8a54b6c7d3..0682abc3c7 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -234,11 +234,14 @@ private: Ref<Shortcut> debugger_stop_shortcut; + static int root_layout_direction; + protected: virtual Rect2i _popup_adjust_rect() const { return Rect2i(); } virtual void _post_popup() {} virtual void _update_theme_item_cache(); + virtual void _input_from_window(const Ref<InputEvent> &p_event) {} void _notification(int p_what); static void _bind_methods(); @@ -260,6 +263,8 @@ public: NOTIFICATION_THEME_CHANGED = 32 }; + static void set_root_layout_direction(int p_root_dir); + void set_title(const String &p_title); String get_title() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 2e6759063b..baaa78f9f2 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -456,6 +456,10 @@ void register_scene_types() { AcceptDialog::set_swap_cancel_ok(swap_cancel_ok); #endif + int root_dir = GLOBAL_GET("internationalization/rendering/root_node_layout_direction"); + Control::set_root_layout_direction(root_dir); + Window::set_root_layout_direction(root_dir); + /* REGISTER ANIMATION */ GDREGISTER_CLASS(Tween); GDREGISTER_ABSTRACT_CLASS(Tweener); diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 1f6e453e88..de81b9d785 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -1849,6 +1849,44 @@ void SceneState::add_editable_instance(const NodePath &p_path) { editable_instances.push_back(p_path); } +bool SceneState::remove_group_references(const StringName &p_name) { + bool edited = false; + for (NodeData &node : nodes) { + for (const int &group : node.groups) { + if (names[group] == p_name) { + node.groups.erase(group); + edited = true; + break; + } + } + } + return edited; +} + +bool SceneState::rename_group_references(const StringName &p_old_name, const StringName &p_new_name) { + bool edited = false; + for (const NodeData &node : nodes) { + for (const int &group : node.groups) { + if (names[group] == p_old_name) { + names.write[group] = p_new_name; + edited = true; + break; + } + } + } + return edited; +} + +HashSet<StringName> SceneState::get_all_groups() { + HashSet<StringName> ret; + for (const NodeData &node : nodes) { + for (const int &group : node.groups) { + ret.insert(names[group]); + } + } + return ret; +} + Vector<String> SceneState::_get_node_groups(int p_idx) const { Vector<StringName> groups = get_node_groups(p_idx); Vector<String> ret; diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 4b436a8385..e6cbc3e16b 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -204,6 +204,10 @@ public: void add_connection(int p_from, int p_to, int p_signal, int p_method, int p_flags, int p_unbinds, const Vector<int> &p_binds); void add_editable_instance(const NodePath &p_path); + bool remove_group_references(const StringName &p_name); + bool rename_group_references(const StringName &p_old_name, const StringName &p_new_name); + HashSet<StringName> get_all_groups(); + virtual void set_last_modified_time(uint64_t p_time) { last_modified_time = p_time; } uint64_t get_last_modified_time() const { return last_modified_time; } diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index 4ed1980826..5acb08de14 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -35,6 +35,7 @@ Mutex ParticleProcessMaterial::material_mutex; SelfList<ParticleProcessMaterial>::List *ParticleProcessMaterial::dirty_materials = nullptr; HashMap<ParticleProcessMaterial::MaterialKey, ParticleProcessMaterial::ShaderData, ParticleProcessMaterial::MaterialKey> ParticleProcessMaterial::shader_map; +RBSet<String> ParticleProcessMaterial::min_max_properties; ParticleProcessMaterial::ShaderNames *ParticleProcessMaterial::shader_names = nullptr; void ParticleProcessMaterial::init_shaders() { @@ -1181,6 +1182,10 @@ bool ParticleProcessMaterial::_is_shader_dirty() const { return element.in_list(); } +bool ParticleProcessMaterial::has_min_max_property(const String &p_name) { + return min_max_properties.has(p_name); +} + void ParticleProcessMaterial::set_direction(Vector3 p_direction) { direction = p_direction; RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->direction, direction); @@ -1217,6 +1222,15 @@ Vector3 ParticleProcessMaterial::get_velocity_pivot() { return velocity_pivot; } +void ParticleProcessMaterial::set_param(Parameter p_param, const Vector2 &p_value) { + set_param_min(p_param, p_value.x); + set_param_max(p_param, p_value.y); +} + +Vector2 ParticleProcessMaterial::get_param(Parameter p_param) const { + return Vector2(get_param_min(p_param), get_param_max(p_param)); +} + void ParticleProcessMaterial::set_param_min(Parameter p_param, float p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); @@ -1805,11 +1819,9 @@ void ParticleProcessMaterial::_validate_property(PropertyInfo &p_property) const p_property.name == "turbulence_noise_speed" || p_property.name == "turbulence_noise_speed_random" || p_property.name == "turbulence_influence_over_life" || - p_property.name == "turbulence_influence_min" || - p_property.name == "turbulence_influence_max" || - p_property.name == "turbulence_initial_displacement_min" || - p_property.name == "turbulence_initial_displacement_max") { - p_property.usage = PROPERTY_USAGE_NO_EDITOR; + p_property.name == "turbulence_influence" || + p_property.name == "turbulence_initial_displacement") { + p_property.usage &= ~PROPERTY_USAGE_EDITOR; } } @@ -1829,6 +1841,10 @@ void ParticleProcessMaterial::_validate_property(PropertyInfo &p_property) const if ((p_property.name == "orbit_velocity_min" || p_property.name == "orbit_velocity_max") && (!tex_parameters[PARAM_ORBIT_VELOCITY].is_valid() && !particle_flags[PARTICLE_FLAG_DISABLE_Z])) { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } + + if (p_property.usage & PROPERTY_USAGE_EDITOR && (p_property.name.ends_with("_min") || p_property.name.ends_with("_max"))) { + p_property.usage &= ~PROPERTY_USAGE_EDITOR; + } } void ParticleProcessMaterial::set_sub_emitter_mode(SubEmitterMode p_sub_emitter_mode) { @@ -1942,6 +1958,9 @@ void ParticleProcessMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &ParticleProcessMaterial::set_flatness); ClassDB::bind_method(D_METHOD("get_flatness"), &ParticleProcessMaterial::get_flatness); + ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &ParticleProcessMaterial::set_param); + ClassDB::bind_method(D_METHOD("get_param", "param"), &ParticleProcessMaterial::get_param); + ClassDB::bind_method(D_METHOD("set_param_min", "param", "value"), &ParticleProcessMaterial::set_param_min); ClassDB::bind_method(D_METHOD("get_param_min", "param"), &ParticleProcessMaterial::get_param_min); @@ -2064,6 +2083,12 @@ void ParticleProcessMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_bounce", "bounce"), &ParticleProcessMaterial::set_collision_bounce); ClassDB::bind_method(D_METHOD("get_collision_bounce"), &ParticleProcessMaterial::get_collision_bounce); +#define ADD_MIN_MAX_PROPERTY(m_property, m_range, m_parameter_name) \ + ADD_PROPERTYI(PropertyInfo(Variant::VECTOR2, m_property, PROPERTY_HINT_RANGE, m_range, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "set_param", "get_param", m_parameter_name); \ + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, m_property "_min", PROPERTY_HINT_RANGE, m_range), "set_param_min", "get_param_min", m_parameter_name); \ + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, m_property "_max", PROPERTY_HINT_RANGE, m_range), "set_param_max", "get_param_max", m_parameter_name); \ + min_max_properties.insert(m_property); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness"); ADD_GROUP("Particle Flags", "particle_flag_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); @@ -2086,8 +2111,7 @@ void ParticleProcessMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius"), "set_emission_ring_radius", "get_emission_ring_radius"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius"); ADD_SUBGROUP("Angle", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_min", PROPERTY_HINT_RANGE, "-720,720,0.1,or_less,or_greater,degrees"), "set_param_min", "get_param_min", PARAM_ANGLE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_max", PROPERTY_HINT_RANGE, "-720,720,0.1,or_less,or_greater,degrees"), "set_param_max", "get_param_max", PARAM_ANGLE); + ADD_MIN_MAX_PROPERTY("angle", "-720,720,0.1,or_less,or_greater,degrees", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE); ADD_SUBGROUP("Velocity", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inherit_velocity_ratio", PROPERTY_HINT_RANGE, "0.0,1.0,0.001,or_less,or_greater"), "set_inherit_velocity_ratio", "get_inherit_velocity_ratio"); @@ -2095,25 +2119,20 @@ void ParticleProcessMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "direction"), "set_direction", "get_direction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spread", PROPERTY_HINT_RANGE, "0,180,0.001"), "set_spread", "get_spread"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "flatness", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_flatness", "get_flatness"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_INITIAL_LINEAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_MIN_MAX_PROPERTY("initial_velocity", "0,1000,0.01,or_less,or_greater", PARAM_INITIAL_LINEAR_VELOCITY); ADD_GROUP("Animated Velocity", ""); ADD_SUBGROUP("Velocity Limit", ""); ADD_SUBGROUP("Angular Velocity", "angular_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ANGULAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ANGULAR_VELOCITY); + ADD_MIN_MAX_PROPERTY("angular_velocity", "-720,720,0.01,or_less,or_greater", PARAM_ANGULAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGULAR_VELOCITY); ADD_SUBGROUP("Directional Velocity", "directional_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_DIRECTIONAL_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_DIRECTIONAL_VELOCITY); + ADD_MIN_MAX_PROPERTY("directional_velocity", "-720,720,0.01,or_less,or_greater", PARAM_DIRECTIONAL_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "directional_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveXYZTexture"), "set_param_texture", "get_param_texture", PARAM_DIRECTIONAL_VELOCITY); ADD_SUBGROUP("Orbit Velocity", "orbit_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_min", PROPERTY_HINT_RANGE, "-2,2,0.001,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ORBIT_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_max", PROPERTY_HINT_RANGE, "-2,2,0.001,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ORBIT_VELOCITY); + ADD_MIN_MAX_PROPERTY("orbit_velocity", "-2,2,0.001,or_less,or_greater", PARAM_ORBIT_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture,CurveXYZTexture"), "set_param_texture", "get_param_texture", PARAM_ORBIT_VELOCITY); ADD_SUBGROUP("Radial Velocity", "radial_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_velocity_min", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_velocity_max", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_RADIAL_VELOCITY); + ADD_MIN_MAX_PROPERTY("radial_velocity", "-1000,1000,0.01,or_less,or_greater", PARAM_RADIAL_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_RADIAL_VELOCITY); ADD_SUBGROUP("Velocity Limit", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "velocity_limit_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_velocity_limit_curve", "get_velocity_limit_curve"); @@ -2121,32 +2140,26 @@ void ParticleProcessMaterial::_bind_methods() { ADD_SUBGROUP("Gravity", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity"); ADD_SUBGROUP("Linear Accel", "linear_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_LINEAR_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_LINEAR_ACCEL); + ADD_MIN_MAX_PROPERTY("linear_accel", "-100,100,0.01,or_less,or_greater", PARAM_LINEAR_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_LINEAR_ACCEL); ADD_SUBGROUP("Radial Accel", "radial_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_RADIAL_ACCEL); + ADD_MIN_MAX_PROPERTY("radial_accel", "-100,100,0.01,or_less,or_greater", PARAM_RADIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_RADIAL_ACCEL); ADD_SUBGROUP("Tangential Accel", "tangential_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_TANGENTIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_TANGENTIAL_ACCEL); + ADD_MIN_MAX_PROPERTY("tangential_accel", "-100,100,0.01,or_less,or_greater", PARAM_TANGENTIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_TANGENTIAL_ACCEL); ADD_SUBGROUP("Damping", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_min", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_param_min", "get_param_min", PARAM_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_max", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_param_max", "get_param_max", PARAM_DAMPING); + ADD_MIN_MAX_PROPERTY("damping", "0,100,0.001,or_greater", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_DAMPING); ADD_SUBGROUP("Attractor Interaction", "attractor_interaction_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "attractor_interaction_enabled"), "set_attractor_interaction_enabled", "is_attractor_interaction_enabled"); ADD_GROUP("Display", ""); ADD_SUBGROUP("Scale", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_SCALE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_SCALE); + ADD_MIN_MAX_PROPERTY("scale", "0,1000,0.01,or_greater", PARAM_SCALE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture,CurveXYZTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE); ADD_SUBGROUP("Scale Over Velocity", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_over_velocity_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_SCALE_OVER_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_over_velocity_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_SCALE_OVER_VELOCITY); + ADD_MIN_MAX_PROPERTY("scale_over_velocity", "0,1000,0.01,or_greater", PARAM_SCALE_OVER_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_over_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture,CurveXYZTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE_OVER_VELOCITY); ADD_SUBGROUP("Color Curves", ""); @@ -2156,15 +2169,12 @@ void ParticleProcessMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "alpha_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_alpha_curve", "get_alpha_curve"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_emission_curve", "get_emission_curve"); ADD_SUBGROUP("Hue Variation", "hue_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_max", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_max", "get_param_max", PARAM_HUE_VARIATION); + ADD_MIN_MAX_PROPERTY("hue_variation", "-1,1,0.01", PARAM_HUE_VARIATION); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION); ADD_SUBGROUP("Animation", "anim_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,16,0.01,or_less,or_greater"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED); + ADD_MIN_MAX_PROPERTY("anim_speed", "0,16,0.01,or_less,or_greater", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); + ADD_MIN_MAX_PROPERTY("anim_offset", "0,1,0.0001", PARAM_ANIM_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET); ADD_GROUP("Turbulence", "turbulence_"); @@ -2173,10 +2183,8 @@ void ParticleProcessMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "turbulence_noise_scale", PROPERTY_HINT_RANGE, "0,10,0.001,or_greater"), "set_turbulence_noise_scale", "get_turbulence_noise_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "turbulence_noise_speed"), "set_turbulence_noise_speed", "get_turbulence_noise_speed"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "turbulence_noise_speed_random", PROPERTY_HINT_RANGE, "0,4,0.01"), "set_turbulence_noise_speed_random", "get_turbulence_noise_speed_random"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "turbulence_influence_min", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param_min", "get_param_min", PARAM_TURB_VEL_INFLUENCE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "turbulence_influence_max", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param_max", "get_param_max", PARAM_TURB_VEL_INFLUENCE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "turbulence_initial_displacement_min", PROPERTY_HINT_RANGE, "-100,100,0.1"), "set_param_min", "get_param_min", PARAM_TURB_INIT_DISPLACEMENT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "turbulence_initial_displacement_max", PROPERTY_HINT_RANGE, "-100,100,0.1"), "set_param_max", "get_param_max", PARAM_TURB_INIT_DISPLACEMENT); + ADD_MIN_MAX_PROPERTY("turbulence_influence", "0,1,0.001", PARAM_TURB_VEL_INFLUENCE); + ADD_MIN_MAX_PROPERTY("turbulence_initial_displacement", "-100,100,0.1", PARAM_TURB_INIT_DISPLACEMENT); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "turbulence_influence_over_life", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_TURB_INFLUENCE_OVER_LIFE); ADD_GROUP("Collision", "collision_"); @@ -2237,6 +2245,8 @@ void ParticleProcessMaterial::_bind_methods() { BIND_ENUM_CONSTANT(COLLISION_RIGID); BIND_ENUM_CONSTANT(COLLISION_HIDE_ON_CONTACT); BIND_ENUM_CONSTANT(COLLISION_MAX); + +#undef ADD_MIN_MAX_PROPERTY } ParticleProcessMaterial::ParticleProcessMaterial() : diff --git a/scene/resources/particle_process_material.h b/scene/resources/particle_process_material.h index 5ed8b61c77..94b2009654 100644 --- a/scene/resources/particle_process_material.h +++ b/scene/resources/particle_process_material.h @@ -149,6 +149,7 @@ private: }; static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map; + static RBSet<String> min_max_properties; MaterialKey current_key; @@ -361,6 +362,8 @@ protected: void _validate_property(PropertyInfo &p_property) const; public: + static bool has_min_max_property(const String &p_name); + void set_direction(Vector3 p_direction); Vector3 get_direction() const; @@ -373,6 +376,9 @@ public: void set_velocity_pivot(const Vector3 &p_pivot); Vector3 get_velocity_pivot(); + void set_param(Parameter p_param, const Vector2 &p_value); + Vector2 get_param(Parameter p_param) const; + void set_param_min(Parameter p_param, float p_value); float get_param_min(Parameter p_param) const; diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp index ddec5e826b..09fb5aba0d 100644 --- a/scene/theme/default_theme.cpp +++ b/scene/theme/default_theme.cpp @@ -773,8 +773,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("custom_button_hover", "Tree", button_hover); theme->set_icon("checked", "Tree", icons["checked"]); + theme->set_icon("checked_disabled", "Tree", icons["checked_disabled"]); theme->set_icon("unchecked", "Tree", icons["unchecked"]); + theme->set_icon("unchecked_disabled", "Tree", icons["unchecked_disabled"]); theme->set_icon("indeterminate", "Tree", icons["indeterminate"]); + theme->set_icon("indeterminate_disabled", "Tree", icons["indeterminate_disabled"]); theme->set_icon("updown", "Tree", icons["updown"]); theme->set_icon("select_arrow", "Tree", icons["option_button_arrow"]); theme->set_icon("arrow", "Tree", icons["arrow_down"]); @@ -789,6 +792,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("title_button_color", "Tree", control_font_color); theme->set_color("font_color", "Tree", control_font_low_color); theme->set_color("font_selected_color", "Tree", control_font_pressed_color); + theme->set_color("font_disabled_color", "Tree", control_font_disabled_color); theme->set_color("font_outline_color", "Tree", Color(1, 1, 1)); theme->set_color("guide_color", "Tree", Color(0.7, 0.7, 0.7, 0.25)); theme->set_color("drop_position_color", "Tree", Color(1, 1, 1)); diff --git a/scene/theme/icons/indeterminate.svg b/scene/theme/icons/indeterminate.svg index 2a742e1475..e6e8dec5ed 100644 --- a/scene/theme/icons/indeterminate.svg +++ b/scene/theme/icons/indeterminate.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 15.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.3333333 1c-1.2887 0-2.3333333 1.0446683-2.3333333 2.3333333v9.3333337c0 1.2887 1.0446683 2.333333 2.3333333 2.333333h9.3333337c1.2887 0 2.333333-1.044668 2.333333-2.333333v-9.3333337c0-1.2887-1.044668-2.3333333-2.333333-2.3333333z" fill="#fff" fill-opacity=".75" stroke-width="1.16667"/><path d="m3 7h10v2h-10z" fill="#1a1a1a" stroke-linecap="square" stroke-opacity=".75" stroke-width="2"/></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="14" height="14" rx="2.333" fill="#fff" fill-opacity=".75"/><path d="m3 7h10v2h-10z" fill="#1a1a1a"/></svg> diff --git a/scene/theme/icons/indeterminate_disabled.svg b/scene/theme/icons/indeterminate_disabled.svg new file mode 100644 index 0000000000..f238f6f31c --- /dev/null +++ b/scene/theme/icons/indeterminate_disabled.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="14" height="14" rx="2.333" fill="#fff" fill-opacity=".37"/><path d="m3 7h10v2h-10z" fill="#1a1a1a" fill-opacity=".5"/></svg> diff --git a/servers/debugger/servers_debugger.cpp b/servers/debugger/servers_debugger.cpp index 9161c5c8b7..bf7dd6c29e 100644 --- a/servers/debugger/servers_debugger.cpp +++ b/servers/debugger/servers_debugger.cpp @@ -108,12 +108,13 @@ Array ServersDebugger::ServersProfilerFrame::serialize() { } } - arr.push_back(script_functions.size() * 4); + arr.push_back(script_functions.size() * 5); for (int i = 0; i < script_functions.size(); i++) { arr.push_back(script_functions[i].sig_id); arr.push_back(script_functions[i].call_count); arr.push_back(script_functions[i].self_time); arr.push_back(script_functions[i].total_time); + arr.push_back(script_functions[i].internal_time); } return arr; } @@ -149,14 +150,15 @@ bool ServersDebugger::ServersProfilerFrame::deserialize(const Array &p_arr) { int func_size = p_arr[idx]; idx += 1; CHECK_SIZE(p_arr, idx + func_size, "ServersProfilerFrame"); - for (int i = 0; i < func_size / 4; i++) { + for (int i = 0; i < func_size / 5; i++) { ScriptFunctionInfo fi; fi.sig_id = p_arr[idx]; fi.call_count = p_arr[idx + 1]; fi.self_time = p_arr[idx + 2]; fi.total_time = p_arr[idx + 3]; + fi.internal_time = p_arr[idx + 4]; script_functions.push_back(fi); - idx += 4; + idx += 5; } CHECK_END(p_arr, idx, "ServersProfilerFrame"); return true; @@ -210,8 +212,11 @@ public: sig_map.clear(); for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptServer::get_language(i)->profiling_start(); + if (p_opts.size() == 2 && p_opts[1].get_type() == Variant::BOOL) { + ScriptServer::get_language(i)->profiling_set_save_native_calls(p_opts[1]); + } } - if (p_opts.size() == 1 && p_opts[0].get_type() == Variant::INT) { + if (p_opts.size() > 0 && p_opts[0].get_type() == Variant::INT) { max_frame_functions = MAX(0, int(p_opts[0])); } } else { @@ -265,6 +270,7 @@ public: w[i].call_count = ptrs[i]->call_count; w[i].total_time = ptrs[i]->total_time / 1000000.0; w[i].self_time = ptrs[i]->self_time / 1000000.0; + w[i].internal_time = ptrs[i]->internal_time / 1000000.0; } } diff --git a/servers/debugger/servers_debugger.h b/servers/debugger/servers_debugger.h index c7a11439e0..6efed9f8b9 100644 --- a/servers/debugger/servers_debugger.h +++ b/servers/debugger/servers_debugger.h @@ -69,6 +69,7 @@ public: int call_count = 0; double self_time = 0; double total_time = 0; + double internal_time = 0; }; // Servers profiler diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 6459cc7462..bb28bc0eb8 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -267,6 +267,11 @@ void DisplayServer::global_menu_clear(const String &p_menu_root) { WARN_PRINT("Global menus not supported by this display server."); } +Dictionary DisplayServer::global_menu_get_system_menu_roots() const { + WARN_PRINT("Global menus not supported by this display server."); + return Dictionary(); +} + bool DisplayServer::tts_is_speaking() const { WARN_PRINT("TTS is not supported by this display server."); return false; @@ -652,6 +657,8 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("global_menu_remove_item", "menu_root", "idx"), &DisplayServer::global_menu_remove_item); ClassDB::bind_method(D_METHOD("global_menu_clear", "menu_root"), &DisplayServer::global_menu_clear); + ClassDB::bind_method(D_METHOD("global_menu_get_system_menu_roots"), &DisplayServer::global_menu_get_system_menu_roots); + ClassDB::bind_method(D_METHOD("tts_is_speaking"), &DisplayServer::tts_is_speaking); ClassDB::bind_method(D_METHOD("tts_is_paused"), &DisplayServer::tts_is_paused); ClassDB::bind_method(D_METHOD("tts_get_voices"), &DisplayServer::tts_get_voices); diff --git a/servers/display_server.h b/servers/display_server.h index d2e112d224..4450677f71 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -185,6 +185,8 @@ public: virtual void global_menu_remove_item(const String &p_menu_root, int p_idx); virtual void global_menu_clear(const String &p_menu_root); + virtual Dictionary global_menu_get_system_menu_roots() const; + struct TTSUtterance { String text; String voice; diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp index d37bbcfff0..81f389e19b 100644 --- a/servers/navigation_server_2d.cpp +++ b/servers/navigation_server_2d.cpp @@ -80,6 +80,7 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer2D::region_set_navigation_layers); ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer2D::region_get_navigation_layers); ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer2D::region_set_transform); + ClassDB::bind_method(D_METHOD("region_get_transform", "region"), &NavigationServer2D::region_get_transform); ClassDB::bind_method(D_METHOD("region_set_navigation_polygon", "region", "navigation_polygon"), &NavigationServer2D::region_set_navigation_polygon); ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer2D::region_get_connections_count); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_start); @@ -114,19 +115,31 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("agent_set_paused", "agent", "paused"), &NavigationServer2D::agent_set_paused); ClassDB::bind_method(D_METHOD("agent_get_paused", "agent"), &NavigationServer2D::agent_get_paused); ClassDB::bind_method(D_METHOD("agent_set_neighbor_distance", "agent", "distance"), &NavigationServer2D::agent_set_neighbor_distance); + ClassDB::bind_method(D_METHOD("agent_get_neighbor_distance", "agent"), &NavigationServer2D::agent_get_neighbor_distance); ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &NavigationServer2D::agent_set_max_neighbors); + ClassDB::bind_method(D_METHOD("agent_get_max_neighbors", "agent"), &NavigationServer2D::agent_get_max_neighbors); ClassDB::bind_method(D_METHOD("agent_set_time_horizon_agents", "agent", "time_horizon"), &NavigationServer2D::agent_set_time_horizon_agents); + ClassDB::bind_method(D_METHOD("agent_get_time_horizon_agents", "agent"), &NavigationServer2D::agent_get_time_horizon_agents); ClassDB::bind_method(D_METHOD("agent_set_time_horizon_obstacles", "agent", "time_horizon"), &NavigationServer2D::agent_set_time_horizon_obstacles); + ClassDB::bind_method(D_METHOD("agent_get_time_horizon_obstacles", "agent"), &NavigationServer2D::agent_get_time_horizon_obstacles); ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &NavigationServer2D::agent_set_radius); + ClassDB::bind_method(D_METHOD("agent_get_radius", "agent"), &NavigationServer2D::agent_get_radius); ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &NavigationServer2D::agent_set_max_speed); + ClassDB::bind_method(D_METHOD("agent_get_max_speed", "agent"), &NavigationServer2D::agent_get_max_speed); ClassDB::bind_method(D_METHOD("agent_set_velocity_forced", "agent", "velocity"), &NavigationServer2D::agent_set_velocity_forced); ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &NavigationServer2D::agent_set_velocity); + ClassDB::bind_method(D_METHOD("agent_get_velocity", "agent"), &NavigationServer2D::agent_get_velocity); ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer2D::agent_set_position); + ClassDB::bind_method(D_METHOD("agent_get_position", "agent"), &NavigationServer2D::agent_get_position); ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer2D::agent_is_map_changed); ClassDB::bind_method(D_METHOD("agent_set_avoidance_callback", "agent", "callback"), &NavigationServer2D::agent_set_avoidance_callback); + ClassDB::bind_method(D_METHOD("agent_has_avoidance_callback", "agent"), &NavigationServer2D::agent_has_avoidance_callback); ClassDB::bind_method(D_METHOD("agent_set_avoidance_layers", "agent", "layers"), &NavigationServer2D::agent_set_avoidance_layers); + ClassDB::bind_method(D_METHOD("agent_get_avoidance_layers", "agent"), &NavigationServer2D::agent_get_avoidance_layers); ClassDB::bind_method(D_METHOD("agent_set_avoidance_mask", "agent", "mask"), &NavigationServer2D::agent_set_avoidance_mask); + ClassDB::bind_method(D_METHOD("agent_get_avoidance_mask", "agent"), &NavigationServer2D::agent_get_avoidance_mask); ClassDB::bind_method(D_METHOD("agent_set_avoidance_priority", "agent", "priority"), &NavigationServer2D::agent_set_avoidance_priority); + ClassDB::bind_method(D_METHOD("agent_get_avoidance_priority", "agent"), &NavigationServer2D::agent_get_avoidance_priority); ClassDB::bind_method(D_METHOD("obstacle_create"), &NavigationServer2D::obstacle_create); ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_enabled", "obstacle", "enabled"), &NavigationServer2D::obstacle_set_avoidance_enabled); @@ -136,10 +149,15 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("obstacle_set_paused", "obstacle", "paused"), &NavigationServer2D::obstacle_set_paused); ClassDB::bind_method(D_METHOD("obstacle_get_paused", "obstacle"), &NavigationServer2D::obstacle_get_paused); ClassDB::bind_method(D_METHOD("obstacle_set_radius", "obstacle", "radius"), &NavigationServer2D::obstacle_set_radius); + ClassDB::bind_method(D_METHOD("obstacle_get_radius", "obstacle"), &NavigationServer2D::obstacle_get_radius); ClassDB::bind_method(D_METHOD("obstacle_set_velocity", "obstacle", "velocity"), &NavigationServer2D::obstacle_set_velocity); + ClassDB::bind_method(D_METHOD("obstacle_get_velocity", "obstacle"), &NavigationServer2D::obstacle_get_velocity); ClassDB::bind_method(D_METHOD("obstacle_set_position", "obstacle", "position"), &NavigationServer2D::obstacle_set_position); + ClassDB::bind_method(D_METHOD("obstacle_get_position", "obstacle"), &NavigationServer2D::obstacle_get_position); ClassDB::bind_method(D_METHOD("obstacle_set_vertices", "obstacle", "vertices"), &NavigationServer2D::obstacle_set_vertices); + ClassDB::bind_method(D_METHOD("obstacle_get_vertices", "obstacle"), &NavigationServer2D::obstacle_get_vertices); ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer2D::obstacle_set_avoidance_layers); + ClassDB::bind_method(D_METHOD("obstacle_get_avoidance_layers", "obstacle"), &NavigationServer2D::obstacle_get_avoidance_layers); ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_polygon", "source_geometry_data", "root_node", "callback"), &NavigationServer2D::parse_source_geometry_data, DEFVAL(Callable())); ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_polygon", "source_geometry_data", "callback"), &NavigationServer2D::bake_from_source_geometry_data, DEFVAL(Callable())); diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h index 66bfface3e..be3818fe9d 100644 --- a/servers/navigation_server_2d.h +++ b/servers/navigation_server_2d.h @@ -138,6 +138,7 @@ public: /// Set the global transformation of this region. virtual void region_set_transform(RID p_region, Transform2D p_transform) = 0; + virtual Transform2D region_get_transform(RID p_region) const = 0; /// Set the navigation poly of this region. virtual void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) = 0; @@ -208,6 +209,7 @@ public: /// low, the simulation will not be safe. /// Must be non-negative. virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance) = 0; + virtual real_t agent_get_neighbor_distance(RID p_agent) const = 0; /// The maximum number of other agents this /// agent takes into account in the navigation. @@ -216,6 +218,7 @@ public: /// number is too low, the simulation will not /// be safe. virtual void agent_set_max_neighbors(RID p_agent, int p_count) = 0; + virtual int agent_get_max_neighbors(RID p_agent) const = 0; /// The minimal amount of time for which this /// agent's velocities that are computed by the @@ -225,17 +228,20 @@ public: /// other agents, but the less freedom this /// agent has in choosing its velocities. /// Must be positive. - virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) = 0; + virtual real_t agent_get_time_horizon_agents(RID p_agent) const = 0; virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) = 0; + virtual real_t agent_get_time_horizon_obstacles(RID p_agent) const = 0; /// The radius of this agent. /// Must be non-negative. virtual void agent_set_radius(RID p_agent, real_t p_radius) = 0; + virtual real_t agent_get_radius(RID p_agent) const = 0; /// The maximum speed of this agent. /// Must be non-negative. virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) = 0; + virtual real_t agent_get_max_speed(RID p_agent) const = 0; /// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly virtual void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) = 0; @@ -243,19 +249,27 @@ public: /// The wanted velocity for the agent as a "suggestion" to the avoidance simulation. /// The simulation will try to fulfill this velocity wish if possible but may change the velocity depending on other agent's and obstacles'. virtual void agent_set_velocity(RID p_agent, Vector2 p_velocity) = 0; + virtual Vector2 agent_get_velocity(RID p_agent) const = 0; /// Position of the agent in world space. virtual void agent_set_position(RID p_agent, Vector2 p_position) = 0; + virtual Vector2 agent_get_position(RID p_agent) const = 0; /// Returns true if the map got changed the previous frame. virtual bool agent_is_map_changed(RID p_agent) const = 0; /// Callback called at the end of the RVO process virtual void agent_set_avoidance_callback(RID p_agent, Callable p_callback) = 0; + virtual bool agent_has_avoidance_callback(RID p_agent) const = 0; virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) = 0; + virtual uint32_t agent_get_avoidance_layers(RID p_agent) const = 0; + virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) = 0; + virtual uint32_t agent_get_avoidance_mask(RID p_agent) const = 0; + virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) = 0; + virtual real_t agent_get_avoidance_priority(RID p_agent) const = 0; /// Creates the obstacle. virtual RID obstacle_create() = 0; @@ -266,10 +280,15 @@ public: virtual void obstacle_set_paused(RID p_obstacle, bool p_paused) = 0; virtual bool obstacle_get_paused(RID p_obstacle) const = 0; virtual void obstacle_set_radius(RID p_obstacle, real_t p_radius) = 0; + virtual real_t obstacle_get_radius(RID p_obstacle) const = 0; virtual void obstacle_set_velocity(RID p_obstacle, Vector2 p_velocity) = 0; + virtual Vector2 obstacle_get_velocity(RID p_obstacle) const = 0; virtual void obstacle_set_position(RID p_obstacle, Vector2 p_position) = 0; + virtual Vector2 obstacle_get_position(RID p_obstacle) const = 0; virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) = 0; + virtual Vector<Vector2> obstacle_get_vertices(RID p_obstacle) const = 0; virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) = 0; + virtual uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const = 0; /// Returns a customized navigation path using a query parameters object virtual void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const = 0; diff --git a/servers/navigation_server_2d_dummy.h b/servers/navigation_server_2d_dummy.h index d380ca5242..5b33a4f170 100644 --- a/servers/navigation_server_2d_dummy.h +++ b/servers/navigation_server_2d_dummy.h @@ -77,6 +77,7 @@ public: void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) override {} uint32_t region_get_navigation_layers(RID p_region) const override { return 0; } void region_set_transform(RID p_region, Transform2D p_transform) override {} + Transform2D region_get_transform(RID p_region) const override { return Transform2D(); } void region_set_navigation_polygon(RID p_region, Ref<NavigationPolygon> p_navigation_polygon) override {} int region_get_connections_count(RID p_region) const override { return 0; } Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector2(); } @@ -111,19 +112,31 @@ public: void agent_set_avoidance_enabled(RID p_agent, bool p_enabled) override {} bool agent_get_avoidance_enabled(RID p_agent) const override { return false; } void agent_set_neighbor_distance(RID p_agent, real_t p_distance) override {} + real_t agent_get_neighbor_distance(RID p_agent) const override { return 0; } void agent_set_max_neighbors(RID p_agent, int p_count) override {} + int agent_get_max_neighbors(RID p_agent) const override { return 0; } void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) override {} + real_t agent_get_time_horizon_agents(RID p_agent) const override { return 0; } void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) override {} + real_t agent_get_time_horizon_obstacles(RID p_agent) const override { return 0; } void agent_set_radius(RID p_agent, real_t p_radius) override {} + real_t agent_get_radius(RID p_agent) const override { return 0; } void agent_set_max_speed(RID p_agent, real_t p_max_speed) override {} + real_t agent_get_max_speed(RID p_agent) const override { return 0; } void agent_set_velocity_forced(RID p_agent, Vector2 p_velocity) override {} void agent_set_velocity(RID p_agent, Vector2 p_velocity) override {} + Vector2 agent_get_velocity(RID p_agent) const override { return Vector2(); } void agent_set_position(RID p_agent, Vector2 p_position) override {} + Vector2 agent_get_position(RID p_agent) const override { return Vector2(); } bool agent_is_map_changed(RID p_agent) const override { return false; } void agent_set_avoidance_callback(RID p_agent, Callable p_callback) override {} + bool agent_has_avoidance_callback(RID p_agent) const override { return false; } void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) override {} + uint32_t agent_get_avoidance_layers(RID p_agent) const override { return 0; } void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) override {} + uint32_t agent_get_avoidance_mask(RID p_agent) const override { return 0; } void agent_set_avoidance_priority(RID p_agent, real_t p_priority) override {} + real_t agent_get_avoidance_priority(RID p_agent) const override { return 0; } RID obstacle_create() override { return RID(); } void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled) override {} @@ -133,10 +146,15 @@ public: void obstacle_set_paused(RID p_obstacle, bool p_paused) override {} bool obstacle_get_paused(RID p_obstacle) const override { return false; } void obstacle_set_radius(RID p_obstacle, real_t p_radius) override {} + real_t obstacle_get_radius(RID p_agent) const override { return 0; } void obstacle_set_velocity(RID p_obstacle, Vector2 p_velocity) override {} + Vector2 obstacle_get_velocity(RID p_agent) const override { return Vector2(); } void obstacle_set_position(RID p_obstacle, Vector2 p_position) override {} + Vector2 obstacle_get_position(RID p_agent) const override { return Vector2(); } void obstacle_set_vertices(RID p_obstacle, const Vector<Vector2> &p_vertices) override {} + Vector<Vector2> obstacle_get_vertices(RID p_agent) const override { return Vector<Vector2>(); } void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {} + uint32_t obstacle_get_avoidance_layers(RID p_agent) const override { return 0; } void query_path(const Ref<NavigationPathQueryParameters2D> &p_query_parameters, Ref<NavigationPathQueryResult2D> p_query_result) const override {} diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index 874809a6d7..258a799d34 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -85,6 +85,7 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_set_navigation_layers", "region", "navigation_layers"), &NavigationServer3D::region_set_navigation_layers); ClassDB::bind_method(D_METHOD("region_get_navigation_layers", "region"), &NavigationServer3D::region_get_navigation_layers); ClassDB::bind_method(D_METHOD("region_set_transform", "region", "transform"), &NavigationServer3D::region_set_transform); + ClassDB::bind_method(D_METHOD("region_get_transform", "region"), &NavigationServer3D::region_get_transform); ClassDB::bind_method(D_METHOD("region_set_navigation_mesh", "region", "navigation_mesh"), &NavigationServer3D::region_set_navigation_mesh); #ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("region_bake_navigation_mesh", "navigation_mesh", "root_node"), &NavigationServer3D::region_bake_navigation_mesh); @@ -125,20 +126,33 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("agent_set_paused", "agent", "paused"), &NavigationServer3D::agent_set_paused); ClassDB::bind_method(D_METHOD("agent_get_paused", "agent"), &NavigationServer3D::agent_get_paused); ClassDB::bind_method(D_METHOD("agent_set_neighbor_distance", "agent", "distance"), &NavigationServer3D::agent_set_neighbor_distance); + ClassDB::bind_method(D_METHOD("agent_get_neighbor_distance", "agent"), &NavigationServer3D::agent_get_neighbor_distance); ClassDB::bind_method(D_METHOD("agent_set_max_neighbors", "agent", "count"), &NavigationServer3D::agent_set_max_neighbors); + ClassDB::bind_method(D_METHOD("agent_get_max_neighbors", "agent"), &NavigationServer3D::agent_get_max_neighbors); ClassDB::bind_method(D_METHOD("agent_set_time_horizon_agents", "agent", "time_horizon"), &NavigationServer3D::agent_set_time_horizon_agents); + ClassDB::bind_method(D_METHOD("agent_get_time_horizon_agents", "agent"), &NavigationServer3D::agent_get_time_horizon_agents); ClassDB::bind_method(D_METHOD("agent_set_time_horizon_obstacles", "agent", "time_horizon"), &NavigationServer3D::agent_set_time_horizon_obstacles); + ClassDB::bind_method(D_METHOD("agent_get_time_horizon_obstacles", "agent"), &NavigationServer3D::agent_get_time_horizon_obstacles); ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &NavigationServer3D::agent_set_radius); + ClassDB::bind_method(D_METHOD("agent_get_radius", "agent"), &NavigationServer3D::agent_get_radius); ClassDB::bind_method(D_METHOD("agent_set_height", "agent", "height"), &NavigationServer3D::agent_set_height); + ClassDB::bind_method(D_METHOD("agent_get_height", "agent"), &NavigationServer3D::agent_get_height); ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &NavigationServer3D::agent_set_max_speed); + ClassDB::bind_method(D_METHOD("agent_get_max_speed", "agent"), &NavigationServer3D::agent_get_max_speed); ClassDB::bind_method(D_METHOD("agent_set_velocity_forced", "agent", "velocity"), &NavigationServer3D::agent_set_velocity_forced); ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &NavigationServer3D::agent_set_velocity); + ClassDB::bind_method(D_METHOD("agent_get_velocity", "agent"), &NavigationServer3D::agent_get_velocity); ClassDB::bind_method(D_METHOD("agent_set_position", "agent", "position"), &NavigationServer3D::agent_set_position); + ClassDB::bind_method(D_METHOD("agent_get_position", "agent"), &NavigationServer3D::agent_get_position); ClassDB::bind_method(D_METHOD("agent_is_map_changed", "agent"), &NavigationServer3D::agent_is_map_changed); ClassDB::bind_method(D_METHOD("agent_set_avoidance_callback", "agent", "callback"), &NavigationServer3D::agent_set_avoidance_callback); + ClassDB::bind_method(D_METHOD("agent_has_avoidance_callback", "agent"), &NavigationServer3D::agent_has_avoidance_callback); ClassDB::bind_method(D_METHOD("agent_set_avoidance_layers", "agent", "layers"), &NavigationServer3D::agent_set_avoidance_layers); + ClassDB::bind_method(D_METHOD("agent_get_avoidance_layers", "agent"), &NavigationServer3D::agent_get_avoidance_layers); ClassDB::bind_method(D_METHOD("agent_set_avoidance_mask", "agent", "mask"), &NavigationServer3D::agent_set_avoidance_mask); + ClassDB::bind_method(D_METHOD("agent_get_avoidance_mask", "agent"), &NavigationServer3D::agent_get_avoidance_mask); ClassDB::bind_method(D_METHOD("agent_set_avoidance_priority", "agent", "priority"), &NavigationServer3D::agent_set_avoidance_priority); + ClassDB::bind_method(D_METHOD("agent_get_avoidance_priority", "agent"), &NavigationServer3D::agent_get_avoidance_priority); ClassDB::bind_method(D_METHOD("obstacle_create"), &NavigationServer3D::obstacle_create); ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_enabled", "obstacle", "enabled"), &NavigationServer3D::obstacle_set_avoidance_enabled); @@ -150,11 +164,17 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("obstacle_set_paused", "obstacle", "paused"), &NavigationServer3D::obstacle_set_paused); ClassDB::bind_method(D_METHOD("obstacle_get_paused", "obstacle"), &NavigationServer3D::obstacle_get_paused); ClassDB::bind_method(D_METHOD("obstacle_set_radius", "obstacle", "radius"), &NavigationServer3D::obstacle_set_radius); + ClassDB::bind_method(D_METHOD("obstacle_get_radius", "obstacle"), &NavigationServer3D::obstacle_get_radius); ClassDB::bind_method(D_METHOD("obstacle_set_height", "obstacle", "height"), &NavigationServer3D::obstacle_set_height); + ClassDB::bind_method(D_METHOD("obstacle_get_height", "obstacle"), &NavigationServer3D::obstacle_get_height); ClassDB::bind_method(D_METHOD("obstacle_set_velocity", "obstacle", "velocity"), &NavigationServer3D::obstacle_set_velocity); + ClassDB::bind_method(D_METHOD("obstacle_get_velocity", "obstacle"), &NavigationServer3D::obstacle_get_velocity); ClassDB::bind_method(D_METHOD("obstacle_set_position", "obstacle", "position"), &NavigationServer3D::obstacle_set_position); + ClassDB::bind_method(D_METHOD("obstacle_get_position", "obstacle"), &NavigationServer3D::obstacle_get_position); ClassDB::bind_method(D_METHOD("obstacle_set_vertices", "obstacle", "vertices"), &NavigationServer3D::obstacle_set_vertices); + ClassDB::bind_method(D_METHOD("obstacle_get_vertices", "obstacle"), &NavigationServer3D::obstacle_get_vertices); ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer3D::obstacle_set_avoidance_layers); + ClassDB::bind_method(D_METHOD("obstacle_get_avoidance_layers", "obstacle"), &NavigationServer3D::obstacle_get_avoidance_layers); ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_mesh", "source_geometry_data", "root_node", "callback"), &NavigationServer3D::parse_source_geometry_data, DEFVAL(Callable())); ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data, DEFVAL(Callable())); diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index 96587afefa..dc58502b1e 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -149,6 +149,7 @@ public: /// Set the global transformation of this region. virtual void region_set_transform(RID p_region, Transform3D p_transform) = 0; + virtual Transform3D region_get_transform(RID p_region) const = 0; /// Set the navigation mesh of this region. virtual void region_set_navigation_mesh(RID p_region, Ref<NavigationMesh> p_navigation_mesh) = 0; @@ -227,6 +228,7 @@ public: /// low, the simulation will not be safe. /// Must be non-negative. virtual void agent_set_neighbor_distance(RID p_agent, real_t p_distance) = 0; + virtual real_t agent_get_neighbor_distance(RID p_agent) const = 0; /// The maximum number of other agents this /// agent takes into account in the navigation. @@ -235,25 +237,32 @@ public: /// number is too low, the simulation will not /// be safe. virtual void agent_set_max_neighbors(RID p_agent, int p_count) = 0; + virtual int agent_get_max_neighbors(RID p_agent) const = 0; // Sets the minimum amount of time in seconds that an agent's // must be able to stay on the calculated velocity while still avoiding collisions with agent's // if this value is set to high an agent will often fall back to using a very low velocity just to be safe virtual void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) = 0; + virtual real_t agent_get_time_horizon_agents(RID p_agent) const = 0; /// Sets the minimum amount of time in seconds that an agent's // must be able to stay on the calculated velocity while still avoiding collisions with obstacle's // if this value is set to high an agent will often fall back to using a very low velocity just to be safe virtual void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) = 0; + virtual real_t agent_get_time_horizon_obstacles(RID p_agent) const = 0; /// The radius of this agent. /// Must be non-negative. virtual void agent_set_radius(RID p_agent, real_t p_radius) = 0; + virtual real_t agent_get_radius(RID p_agent) const = 0; + virtual void agent_set_height(RID p_agent, real_t p_height) = 0; + virtual real_t agent_get_height(RID p_agent) const = 0; /// The maximum speed of this agent. /// Must be non-negative. virtual void agent_set_max_speed(RID p_agent, real_t p_max_speed) = 0; + virtual real_t agent_get_max_speed(RID p_agent) const = 0; /// forces and agent velocity change in the avoidance simulation, adds simulation instability if done recklessly virtual void agent_set_velocity_forced(RID p_agent, Vector3 p_velocity) = 0; @@ -261,22 +270,31 @@ public: /// The wanted velocity for the agent as a "suggestion" to the avoidance simulation. /// The simulation will try to fulfill this velocity wish if possible but may change the velocity depending on other agent's and obstacles'. virtual void agent_set_velocity(RID p_agent, Vector3 p_velocity) = 0; + virtual Vector3 agent_get_velocity(RID p_agent) const = 0; /// Position of the agent in world space. virtual void agent_set_position(RID p_agent, Vector3 p_position) = 0; + virtual Vector3 agent_get_position(RID p_agent) const = 0; /// Returns true if the map got changed the previous frame. virtual bool agent_is_map_changed(RID p_agent) const = 0; /// Callback called at the end of the RVO process virtual void agent_set_avoidance_callback(RID p_agent, Callable p_callback) = 0; + virtual bool agent_has_avoidance_callback(RID p_agent) const = 0; virtual void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) = 0; + virtual uint32_t agent_get_avoidance_layers(RID p_agent) const = 0; + virtual void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) = 0; + virtual uint32_t agent_get_avoidance_mask(RID p_agent) const = 0; + virtual void agent_set_avoidance_priority(RID p_agent, real_t p_priority) = 0; + virtual real_t agent_get_avoidance_priority(RID p_agent) const = 0; /// Creates the obstacle. virtual RID obstacle_create() = 0; + virtual void obstacle_set_map(RID p_obstacle, RID p_map) = 0; virtual RID obstacle_get_map(RID p_obstacle) const = 0; @@ -285,14 +303,22 @@ public: virtual void obstacle_set_avoidance_enabled(RID p_obstacle, bool p_enabled) = 0; virtual bool obstacle_get_avoidance_enabled(RID p_obstacle) const = 0; + virtual void obstacle_set_use_3d_avoidance(RID p_obstacle, bool p_enabled) = 0; virtual bool obstacle_get_use_3d_avoidance(RID p_obstacle) const = 0; + virtual void obstacle_set_radius(RID p_obstacle, real_t p_radius) = 0; + virtual real_t obstacle_get_radius(RID p_obstacle) const = 0; virtual void obstacle_set_height(RID p_obstacle, real_t p_height) = 0; + virtual real_t obstacle_get_height(RID p_obstacle) const = 0; virtual void obstacle_set_velocity(RID p_obstacle, Vector3 p_velocity) = 0; + virtual Vector3 obstacle_get_velocity(RID p_obstacle) const = 0; virtual void obstacle_set_position(RID p_obstacle, Vector3 p_position) = 0; + virtual Vector3 obstacle_get_position(RID p_obstacle) const = 0; virtual void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) = 0; + virtual Vector<Vector3> obstacle_get_vertices(RID p_obstacle) const = 0; virtual void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) = 0; + virtual uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const = 0; /// Destroy the `RID` virtual void free(RID p_object) = 0; diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h index 7bd3eec937..dd0dfc7348 100644 --- a/servers/navigation_server_3d_dummy.h +++ b/servers/navigation_server_3d_dummy.h @@ -81,6 +81,7 @@ public: void region_set_navigation_layers(RID p_region, uint32_t p_navigation_layers) override {} uint32_t region_get_navigation_layers(RID p_region) const override { return 0; } void region_set_transform(RID p_region, Transform3D p_transform) override {} + Transform3D region_get_transform(RID p_region) const override { return Transform3D(); } void region_set_navigation_mesh(RID p_region, Ref<NavigationMesh> p_navigation_mesh) override {} #ifndef DISABLE_DEPRECATED void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) override {} @@ -118,20 +119,33 @@ public: void agent_set_use_3d_avoidance(RID p_agent, bool p_enabled) override {} bool agent_get_use_3d_avoidance(RID p_agent) const override { return false; } void agent_set_neighbor_distance(RID p_agent, real_t p_distance) override {} + real_t agent_get_neighbor_distance(RID p_agent) const override { return 0; } void agent_set_max_neighbors(RID p_agent, int p_count) override {} + int agent_get_max_neighbors(RID p_agent) const override { return 0; } void agent_set_time_horizon_agents(RID p_agent, real_t p_time_horizon) override {} + real_t agent_get_time_horizon_agents(RID p_agent) const override { return 0; } void agent_set_time_horizon_obstacles(RID p_agent, real_t p_time_horizon) override {} + real_t agent_get_time_horizon_obstacles(RID p_agent) const override { return 0; } void agent_set_radius(RID p_agent, real_t p_radius) override {} + real_t agent_get_radius(RID p_agent) const override { return 0; } void agent_set_height(RID p_agent, real_t p_height) override {} + real_t agent_get_height(RID p_agent) const override { return 0; } void agent_set_max_speed(RID p_agent, real_t p_max_speed) override {} + real_t agent_get_max_speed(RID p_agent) const override { return 0; } void agent_set_velocity_forced(RID p_agent, Vector3 p_velocity) override {} void agent_set_velocity(RID p_agent, Vector3 p_velocity) override {} + Vector3 agent_get_velocity(RID p_agent) const override { return Vector3(); } void agent_set_position(RID p_agent, Vector3 p_position) override {} + Vector3 agent_get_position(RID p_agent) const override { return Vector3(); } bool agent_is_map_changed(RID p_agent) const override { return false; } void agent_set_avoidance_callback(RID p_agent, Callable p_callback) override {} + bool agent_has_avoidance_callback(RID p_agent) const override { return false; } void agent_set_avoidance_layers(RID p_agent, uint32_t p_layers) override {} + uint32_t agent_get_avoidance_layers(RID p_agent) const override { return 0; } void agent_set_avoidance_mask(RID p_agent, uint32_t p_mask) override {} + uint32_t agent_get_avoidance_mask(RID p_agent) const override { return 0; } void agent_set_avoidance_priority(RID p_agent, real_t p_priority) override {} + real_t agent_get_avoidance_priority(RID p_agent) const override { return 0; } RID obstacle_create() override { return RID(); } void obstacle_set_map(RID p_obstacle, RID p_map) override {} RID obstacle_get_map(RID p_obstacle) const override { return RID(); } @@ -142,11 +156,17 @@ public: void obstacle_set_use_3d_avoidance(RID p_obstacle, bool p_enabled) override {} bool obstacle_get_use_3d_avoidance(RID p_obstacle) const override { return false; } void obstacle_set_radius(RID p_obstacle, real_t p_radius) override {} + real_t obstacle_get_radius(RID p_obstacle) const override { return 0; } void obstacle_set_height(RID p_obstacle, real_t p_height) override {} + real_t obstacle_get_height(RID p_obstacle) const override { return 0; } void obstacle_set_velocity(RID p_obstacle, Vector3 p_velocity) override {} + Vector3 obstacle_get_velocity(RID p_obstacle) const override { return Vector3(); } void obstacle_set_position(RID p_obstacle, Vector3 p_position) override {} + Vector3 obstacle_get_position(RID p_obstacle) const override { return Vector3(); } void obstacle_set_vertices(RID p_obstacle, const Vector<Vector3> &p_vertices) override {} + Vector<Vector3> obstacle_get_vertices(RID p_obstacle) const override { return Vector<Vector3>(); } void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {} + uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const override { return 0; } void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {} void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {} void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {} diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 004fe26b85..e17fa0ce90 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -1860,7 +1860,7 @@ void fragment_shader(in SceneData scene_data) { pssm_coord /= pssm_coord.w; - shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * blur_factor, pssm_coord); + shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * float(directional_lights.data[i].blend_splits)), pssm_coord); if (directional_lights.data[i].blend_splits) { float pssm_blend; @@ -1870,21 +1870,21 @@ void fragment_shader(in SceneData scene_data) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 1) pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); - pssm_blend = smoothstep(0.0, directional_lights.data[i].shadow_split_offsets.x, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x - directional_lights.data[i].shadow_split_offsets.x * 0.1, directional_lights.data[i].shadow_split_offsets.x, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 2) pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x, directional_lights.data[i].shadow_split_offsets.y, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y - directional_lights.data[i].shadow_split_offsets.y * 0.1, directional_lights.data[i].shadow_split_offsets.y, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 3) pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y, directional_lights.data[i].shadow_split_offsets.z, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.z - directional_lights.data[i].shadow_split_offsets.z * 0.1, directional_lights.data[i].shadow_split_offsets.z, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; } else { @@ -1894,7 +1894,7 @@ void fragment_shader(in SceneData scene_data) { pssm_coord /= pssm_coord.w; - float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * blur_factor2, pssm_coord); + float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * float(directional_lights.data[i].blend_splits)), pssm_coord); shadow = mix(shadow, shadow2, pssm_blend); } } diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index d620e21cb1..03737e087c 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -1506,7 +1506,7 @@ void main() { pssm_coord /= pssm_coord.w; - shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * blur_factor, pssm_coord); + shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * float(directional_lights.data[i].blend_splits)), pssm_coord); if (directional_lights.data[i].blend_splits) { float pssm_blend; @@ -1516,21 +1516,21 @@ void main() { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 1) pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); - pssm_blend = smoothstep(0.0, directional_lights.data[i].shadow_split_offsets.x, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x - directional_lights.data[i].shadow_split_offsets.x * 0.1, directional_lights.data[i].shadow_split_offsets.x, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 2) pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x, directional_lights.data[i].shadow_split_offsets.y, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y - directional_lights.data[i].shadow_split_offsets.y * 0.1, directional_lights.data[i].shadow_split_offsets.y, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { vec4 v = vec4(vertex, 1.0); BIAS_FUNC(v, 3) pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y, directional_lights.data[i].shadow_split_offsets.z, depth_z); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.z - directional_lights.data[i].shadow_split_offsets.z * 0.1, directional_lights.data[i].shadow_split_offsets.z, depth_z); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; } else { @@ -1540,7 +1540,7 @@ void main() { pssm_coord /= pssm_coord.w; - float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * blur_factor2, pssm_coord); + float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * float(directional_lights.data[i].blend_splits)), pssm_coord); shadow = mix(shadow, shadow2, pssm_blend); } diff --git a/tests/scene/test_camera_3d.h b/tests/scene/test_camera_3d.h new file mode 100644 index 0000000000..169486d108 --- /dev/null +++ b/tests/scene/test_camera_3d.h @@ -0,0 +1,370 @@ +/**************************************************************************/ +/* test_camera_3d.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_CAMERA_3D_H +#define TEST_CAMERA_3D_H + +#include "scene/3d/camera_3d.h" +#include "scene/main/viewport.h" +#include "scene/main/window.h" + +#include "tests/test_macros.h" + +// Constants. +#define SQRT3 (1.7320508f) + +TEST_CASE("[SceneTree][Camera3D] Getters and setters") { + Camera3D *test_camera = memnew(Camera3D); + + SUBCASE("Cull mask") { + constexpr int cull_mask = (1 << 5) | (1 << 7) | (1 << 9); + constexpr int set_enable_layer = 3; + constexpr int set_disable_layer = 5; + test_camera->set_cull_mask(cull_mask); + CHECK(test_camera->get_cull_mask() == cull_mask); + test_camera->set_cull_mask_value(set_enable_layer, true); + CHECK(test_camera->get_cull_mask_value(set_enable_layer)); + test_camera->set_cull_mask_value(set_disable_layer, false); + CHECK_FALSE(test_camera->get_cull_mask_value(set_disable_layer)); + } + + SUBCASE("Attributes") { + Ref<CameraAttributes> attributes = memnew(CameraAttributes); + test_camera->set_attributes(attributes); + CHECK(test_camera->get_attributes() == attributes); + Ref<CameraAttributesPhysical> physical_attributes = memnew(CameraAttributesPhysical); + test_camera->set_attributes(physical_attributes); + CHECK(test_camera->get_attributes() == physical_attributes); + } + + SUBCASE("Camera frustum properties") { + constexpr float near = 0.2f; + constexpr float far = 995.0f; + constexpr float fov = 120.0f; + constexpr float size = 7.0f; + constexpr float h_offset = 1.1f; + constexpr float v_offset = -1.6f; + const Vector2 frustum_offset(5, 7); + test_camera->set_near(near); + CHECK(test_camera->get_near() == near); + test_camera->set_far(far); + CHECK(test_camera->get_far() == far); + test_camera->set_fov(fov); + CHECK(test_camera->get_fov() == fov); + test_camera->set_size(size); + CHECK(test_camera->get_size() == size); + test_camera->set_h_offset(h_offset); + CHECK(test_camera->get_h_offset() == h_offset); + test_camera->set_v_offset(v_offset); + CHECK(test_camera->get_v_offset() == v_offset); + test_camera->set_frustum_offset(frustum_offset); + CHECK(test_camera->get_frustum_offset() == frustum_offset); + test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_HEIGHT); + CHECK(test_camera->get_keep_aspect_mode() == Camera3D::KeepAspect::KEEP_HEIGHT); + test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_WIDTH); + CHECK(test_camera->get_keep_aspect_mode() == Camera3D::KeepAspect::KEEP_WIDTH); + } + + SUBCASE("Projection mode") { + test_camera->set_projection(Camera3D::ProjectionType::PROJECTION_ORTHOGONAL); + CHECK(test_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_ORTHOGONAL); + test_camera->set_projection(Camera3D::ProjectionType::PROJECTION_PERSPECTIVE); + CHECK(test_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE); + } + + SUBCASE("Helper setters") { + constexpr float fov = 90.0f, size = 6.0f; + constexpr float near1 = 0.1f, near2 = 0.5f; + constexpr float far1 = 1001.0f, far2 = 1005.0f; + test_camera->set_perspective(fov, near1, far1); + CHECK(test_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE); + CHECK(test_camera->get_near() == near1); + CHECK(test_camera->get_far() == far1); + CHECK(test_camera->get_fov() == fov); + test_camera->set_orthogonal(size, near2, far2); + CHECK(test_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_ORTHOGONAL); + CHECK(test_camera->get_near() == near2); + CHECK(test_camera->get_far() == far2); + CHECK(test_camera->get_size() == size); + } + + SUBCASE("Doppler tracking") { + test_camera->set_doppler_tracking(Camera3D::DopplerTracking::DOPPLER_TRACKING_IDLE_STEP); + CHECK(test_camera->get_doppler_tracking() == Camera3D::DopplerTracking::DOPPLER_TRACKING_IDLE_STEP); + test_camera->set_doppler_tracking(Camera3D::DopplerTracking::DOPPLER_TRACKING_PHYSICS_STEP); + CHECK(test_camera->get_doppler_tracking() == Camera3D::DopplerTracking::DOPPLER_TRACKING_PHYSICS_STEP); + test_camera->set_doppler_tracking(Camera3D::DopplerTracking::DOPPLER_TRACKING_DISABLED); + CHECK(test_camera->get_doppler_tracking() == Camera3D::DopplerTracking::DOPPLER_TRACKING_DISABLED); + } + + memdelete(test_camera); +} + +TEST_CASE("[SceneTree][Camera3D] Position queries") { + // Cameras need a viewport to know how to compute their frustums, so we make a fake one here. + Camera3D *test_camera = memnew(Camera3D); + SubViewport *mock_viewport = memnew(SubViewport); + // 4:2. + mock_viewport->set_size(Vector2(400, 200)); + SceneTree::get_singleton()->get_root()->add_child(mock_viewport); + mock_viewport->add_child(test_camera); + test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_WIDTH); + REQUIRE_MESSAGE(test_camera->is_current(), "Camera3D should be made current upon entering tree."); + + SUBCASE("Orthogonal projection") { + test_camera->set_projection(Camera3D::ProjectionType::PROJECTION_ORTHOGONAL); + // The orthogonal case is simpler, so we test a more random position + rotation combination here. + // For the other cases we'll use zero translation and rotation instead. + test_camera->set_global_position(Vector3(1, 2, 3)); + test_camera->look_at(Vector3(-4, 5, 1)); + // Width = 5, Aspect Ratio = 400 / 200 = 2, so Height is 2.5. + test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f); + const Basis basis = test_camera->get_global_basis(); + // Subtract near so offset starts from the near plane. + const Vector3 offset1 = basis.xform(Vector3(-1.5f, 3.5f, 0.2f - test_camera->get_near())); + const Vector3 offset2 = basis.xform(Vector3(2.0f, -0.5f, -0.6f - test_camera->get_near())); + const Vector3 offset3 = basis.xform(Vector3(-3.0f, 1.0f, -0.6f - test_camera->get_near())); + const Vector3 offset4 = basis.xform(Vector3(-2.0f, 1.5f, -0.6f - test_camera->get_near())); + const Vector3 offset5 = basis.xform(Vector3(0, 0, 10000.0f - test_camera->get_near())); + + SUBCASE("is_position_behind") { + CHECK(test_camera->is_position_behind(test_camera->get_global_position() + offset1)); + CHECK_FALSE(test_camera->is_position_behind(test_camera->get_global_position() + offset2)); + + SUBCASE("h/v offset should have no effect on the result of is_position_behind") { + test_camera->set_h_offset(-11.0f); + test_camera->set_v_offset(22.1f); + CHECK(test_camera->is_position_behind(test_camera->get_global_position() + offset1)); + test_camera->set_h_offset(4.7f); + test_camera->set_v_offset(-3.0f); + CHECK_FALSE(test_camera->is_position_behind(test_camera->get_global_position() + offset2)); + } + // Reset h/v offsets. + test_camera->set_h_offset(0); + test_camera->set_v_offset(0); + } + + SUBCASE("is_position_in_frustum") { + // If the point is behind the near plane, it is outside the camera frustum. + // So offset1 is not in frustum. + CHECK_FALSE(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset1)); + // If |right| > 5 / 2 or |up| > 2.5 / 2, the point is outside the camera frustum. + // So offset2 is in frustum and offset3 and offset4 are not. + CHECK(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset2)); + CHECK_FALSE(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset3)); + CHECK_FALSE(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset4)); + // offset5 is beyond the far plane, so it is not in frustum. + CHECK_FALSE(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset5)); + } + } + + SUBCASE("Perspective projection") { + test_camera->set_projection(Camera3D::ProjectionType::PROJECTION_PERSPECTIVE); + // Camera at origin, looking at +Z. + test_camera->set_global_position(Vector3(0, 0, 0)); + test_camera->set_global_rotation(Vector3(0, 0, 0)); + // Keep width, so horizontal fov = 120. + // Since the near plane distance is 1, + // with trig we know the near plane's width is 2 * sqrt(3), so its height is sqrt(3). + test_camera->set_perspective(120.0f, 1.0f, 1000.0f); + + SUBCASE("is_position_behind") { + CHECK_FALSE(test_camera->is_position_behind(Vector3(0, 0, -1.5f))); + CHECK(test_camera->is_position_behind(Vector3(2, 0, -0.2f))); + } + + SUBCASE("is_position_in_frustum") { + CHECK(test_camera->is_position_in_frustum(Vector3(-1.3f, 0, -1.1f))); + CHECK_FALSE(test_camera->is_position_in_frustum(Vector3(2, 0, -1.1f))); + CHECK(test_camera->is_position_in_frustum(Vector3(1, 0.5f, -1.1f))); + CHECK_FALSE(test_camera->is_position_in_frustum(Vector3(1, 1, -1.1f))); + CHECK(test_camera->is_position_in_frustum(Vector3(0, 0, -1.5f))); + CHECK_FALSE(test_camera->is_position_in_frustum(Vector3(0, 0, -0.5f))); + } + } + + memdelete(test_camera); + memdelete(mock_viewport); +} + +TEST_CASE("[SceneTree][Camera3D] Project/Unproject position") { + // Cameras need a viewport to know how to compute their frustums, so we make a fake one here. + Camera3D *test_camera = memnew(Camera3D); + SubViewport *mock_viewport = memnew(SubViewport); + // 4:2. + mock_viewport->set_size(Vector2(400, 200)); + SceneTree::get_singleton()->get_root()->add_child(mock_viewport); + mock_viewport->add_child(test_camera); + test_camera->set_global_position(Vector3(0, 0, 0)); + test_camera->set_global_rotation(Vector3(0, 0, 0)); + test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_HEIGHT); + + SUBCASE("project_position") { + SUBCASE("Orthogonal projection") { + test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f); + // Center. + CHECK(test_camera->project_position(Vector2(200, 100), 0.5f).is_equal_approx(Vector3(0, 0, -0.5f))); + // Top left. + CHECK(test_camera->project_position(Vector2(0, 0), 1.5f).is_equal_approx(Vector3(-5.0f, 2.5f, -1.5f))); + // Bottom right. + CHECK(test_camera->project_position(Vector2(400, 200), 5.0f).is_equal_approx(Vector3(5.0f, -2.5f, -5.0f))); + } + + SUBCASE("Perspective projection") { + test_camera->set_perspective(120.0f, 0.5f, 1000.0f); + // Center. + CHECK(test_camera->project_position(Vector2(200, 100), 0.5f).is_equal_approx(Vector3(0, 0, -0.5f))); + CHECK(test_camera->project_position(Vector2(200, 100), 100.0f).is_equal_approx(Vector3(0, 0, -100.0f))); + // 3/4th way to Top left. + CHECK(test_camera->project_position(Vector2(100, 50), 0.5f).is_equal_approx(Vector3(-SQRT3 * 0.5f, SQRT3 * 0.25f, -0.5f))); + CHECK(test_camera->project_position(Vector2(100, 50), 1.0f).is_equal_approx(Vector3(-SQRT3, SQRT3 * 0.5f, -1.0f))); + // 3/4th way to Bottom right. + CHECK(test_camera->project_position(Vector2(300, 150), 0.5f).is_equal_approx(Vector3(SQRT3 * 0.5f, -SQRT3 * 0.25f, -0.5f))); + CHECK(test_camera->project_position(Vector2(300, 150), 1.0f).is_equal_approx(Vector3(SQRT3, -SQRT3 * 0.5f, -1.0f))); + } + } + + // Uses cases that are the inverse of the above sub-case. + SUBCASE("unproject_position") { + SUBCASE("Orthogonal projection") { + test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f); + // Center + CHECK(test_camera->unproject_position(Vector3(0, 0, -0.5f)).is_equal_approx(Vector2(200, 100))); + // Top left + CHECK(test_camera->unproject_position(Vector3(-5.0f, 2.5f, -1.5f)).is_equal_approx(Vector2(0, 0))); + // Bottom right + CHECK(test_camera->unproject_position(Vector3(5.0f, -2.5f, -5.0f)).is_equal_approx(Vector2(400, 200))); + } + + SUBCASE("Perspective projection") { + test_camera->set_perspective(120.0f, 0.5f, 1000.0f); + // Center. + CHECK(test_camera->unproject_position(Vector3(0, 0, -0.5f)).is_equal_approx(Vector2(200, 100))); + CHECK(test_camera->unproject_position(Vector3(0, 0, -100.0f)).is_equal_approx(Vector2(200, 100))); + // 3/4th way to Top left. + WARN(test_camera->unproject_position(Vector3(-SQRT3 * 0.5f, SQRT3 * 0.25f, -0.5f)).is_equal_approx(Vector2(100, 50))); + WARN(test_camera->unproject_position(Vector3(-SQRT3, SQRT3 * 0.5f, -1.0f)).is_equal_approx(Vector2(100, 50))); + // 3/4th way to Bottom right. + CHECK(test_camera->unproject_position(Vector3(SQRT3 * 0.5f, -SQRT3 * 0.25f, -0.5f)).is_equal_approx(Vector2(300, 150))); + CHECK(test_camera->unproject_position(Vector3(SQRT3, -SQRT3 * 0.5f, -1.0f)).is_equal_approx(Vector2(300, 150))); + } + } + + memdelete(test_camera); + memdelete(mock_viewport); +} + +TEST_CASE("[SceneTree][Camera3D] Project ray") { + // Cameras need a viewport to know how to compute their frustums, so we make a fake one here. + Camera3D *test_camera = memnew(Camera3D); + SubViewport *mock_viewport = memnew(SubViewport); + // 4:2. + mock_viewport->set_size(Vector2(400, 200)); + SceneTree::get_singleton()->get_root()->add_child(mock_viewport); + mock_viewport->add_child(test_camera); + test_camera->set_global_position(Vector3(0, 0, 0)); + test_camera->set_global_rotation(Vector3(0, 0, 0)); + test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_HEIGHT); + + SUBCASE("project_ray_origin") { + SUBCASE("Orthogonal projection") { + test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f); + // Center. + CHECK(test_camera->project_ray_origin(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -0.5f))); + // Top left. + CHECK(test_camera->project_ray_origin(Vector2(0, 0)).is_equal_approx(Vector3(-5.0f, 2.5f, -0.5f))); + // Bottom right. + CHECK(test_camera->project_ray_origin(Vector2(400, 200)).is_equal_approx(Vector3(5.0f, -2.5f, -0.5f))); + } + + SUBCASE("Perspective projection") { + test_camera->set_perspective(120.0f, 0.5f, 1000.0f); + // Center. + CHECK(test_camera->project_ray_origin(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, 0))); + // Top left. + CHECK(test_camera->project_ray_origin(Vector2(0, 0)).is_equal_approx(Vector3(0, 0, 0))); + // Bottom right. + CHECK(test_camera->project_ray_origin(Vector2(400, 200)).is_equal_approx(Vector3(0, 0, 0))); + } + } + + SUBCASE("project_ray_normal") { + SUBCASE("Orthogonal projection") { + test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f); + // Center. + CHECK(test_camera->project_ray_normal(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -1))); + // Top left. + CHECK(test_camera->project_ray_normal(Vector2(0, 0)).is_equal_approx(Vector3(0, 0, -1))); + // Bottom right. + CHECK(test_camera->project_ray_normal(Vector2(400, 200)).is_equal_approx(Vector3(0, 0, -1))); + } + + SUBCASE("Perspective projection") { + test_camera->set_perspective(120.0f, 0.5f, 1000.0f); + // Center. + CHECK(test_camera->project_ray_normal(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -1))); + // Top left. + CHECK(test_camera->project_ray_normal(Vector2(0, 0)).is_equal_approx(Vector3(-SQRT3, SQRT3 / 2, -0.5f).normalized())); + // Bottom right. + CHECK(test_camera->project_ray_normal(Vector2(400, 200)).is_equal_approx(Vector3(SQRT3, -SQRT3 / 2, -0.5f).normalized())); + } + } + + SUBCASE("project_local_ray_normal") { + test_camera->set_rotation_degrees(Vector3(60, 60, 60)); + + SUBCASE("Orthogonal projection") { + test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f); + // Center. + CHECK(test_camera->project_local_ray_normal(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -1))); + // Top left. + CHECK(test_camera->project_local_ray_normal(Vector2(0, 0)).is_equal_approx(Vector3(0, 0, -1))); + // Bottom right. + CHECK(test_camera->project_local_ray_normal(Vector2(400, 200)).is_equal_approx(Vector3(0, 0, -1))); + } + + SUBCASE("Perspective projection") { + test_camera->set_perspective(120.0f, 0.5f, 1000.0f); + // Center. + CHECK(test_camera->project_local_ray_normal(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -1))); + // Top left. + CHECK(test_camera->project_local_ray_normal(Vector2(0, 0)).is_equal_approx(Vector3(-SQRT3, SQRT3 / 2, -0.5f).normalized())); + // Bottom right. + CHECK(test_camera->project_local_ray_normal(Vector2(400, 200)).is_equal_approx(Vector3(SQRT3, -SQRT3 / 2, -0.5f).normalized())); + } + } + + memdelete(test_camera); + memdelete(mock_viewport); +} + +#undef SQRT3 + +#endif // TEST_CAMERA_3D_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 8c120f6d3a..5187ebd00f 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -94,6 +94,7 @@ #include "tests/scene/test_arraymesh.h" #include "tests/scene/test_audio_stream_wav.h" #include "tests/scene/test_bit_map.h" +#include "tests/scene/test_camera_3d.h" #include "tests/scene/test_code_edit.h" #include "tests/scene/test_color_picker.h" #include "tests/scene/test_control.h" diff --git a/thirdparty/README.md b/thirdparty/README.md index 57bb5b3a52..8b8f40aff3 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -216,6 +216,8 @@ Files extracted from upstream source: ``` - `AUTHORS.txt` and `LICENSE.txt` +Two files (`ProcessRgtc.{cpp,hpp}`) have been added to provide RGTC compression implementation, +based on library's `ProcessDxtc.{cpp,hpp}`. ## fonts |